// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. using Microsoft.MixedReality.Toolkit.Utilities; using System; using System.Collections.Generic; using Unity.Profiling; using UnityEngine; namespace Microsoft.MixedReality.Toolkit { /// /// Abstract class for core MRTK system with functionality defined for managing and accessing IMixedRealityDataProviders /// public abstract class BaseDataProviderAccessCoreSystem : BaseCoreSystem, IMixedRealityDataProviderAccess { private readonly List dataProviders = new List(); public override void Reset() { base.Reset(); foreach (var provider in dataProviders) { provider.Reset(); } } /// public override void Enable() { base.Enable(); foreach (var provider in dataProviders) { provider.Enable(); } } private static readonly ProfilerMarker UpdatePerfMarker = new ProfilerMarker("[MRTK] BaseDataProviderAccessCoreSystem.Update"); /// public override void Update() { using (UpdatePerfMarker.Auto()) { base.Update(); foreach (var provider in dataProviders) { provider.Update(); } } } private static readonly ProfilerMarker LateUpdatePerfMarker = new ProfilerMarker("[MRTK] BaseDataProviderAccessCoreSystem.LateUpdate"); /// public override void LateUpdate() { using (LateUpdatePerfMarker.Auto()) { base.LateUpdate(); foreach (var provider in dataProviders) { provider.LateUpdate(); } } } /// /// Constructor. /// /// The instance that loaded the service. /// The configuration profile for the service. [Obsolete("This constructor is obsolete (registrar parameter is no longer required) and will be removed in a future version of the Microsoft Mixed Reality Toolkit.")] protected BaseDataProviderAccessCoreSystem( IMixedRealityServiceRegistrar registrar, BaseMixedRealityProfile profile = null) : this(profile) { Registrar = registrar; } /// /// Constructor. /// /// The configuration profile for the service. protected BaseDataProviderAccessCoreSystem( BaseMixedRealityProfile profile = null) : base(profile) { } #region IMixedRealityDataProviderAccess Implementation /// public virtual IReadOnlyList GetDataProviders() { return dataProviders.AsReadOnly(); } /// public virtual IReadOnlyList GetDataProviders() where T : IMixedRealityDataProvider { List selected = new List(); foreach (var provider in dataProviders) { if (provider is T providerT) { selected.Add(providerT); } } return selected; } /// public virtual IMixedRealityDataProvider GetDataProvider(string name) { foreach (var provider in dataProviders) { if (provider.Name == name) { return provider; } } return null; } /// public virtual T GetDataProvider(string name = null) where T : IMixedRealityDataProvider { foreach (var provider in dataProviders) { if (provider is T providerT) { if (name == null || provider.Name == name) { return providerT; } } } return default(T); } #endregion IMixedRealityDataProviderAccess Implementation /// /// Registers a data provider of the specified type. /// protected bool RegisterDataProvider( Type concreteType, string providerName, SupportedPlatforms supportedPlatforms = (SupportedPlatforms)(-1), params object[] args) where T : IMixedRealityDataProvider { return RegisterDataProviderInternal( true, // Retry with an added IMixedRealityService parameter concreteType, providerName, supportedPlatforms, args); } /// /// Registers a data provider of the specified type. /// [Obsolete("RegisterDataProvider(Type, SupportedPlatforms, param object[]) is obsolete and will be removed from a future version of MRTK\n" + "Please use RegisterDataProvider(Type, string, SupportedPlatforms, params object[])")] protected bool RegisterDataProvider( Type concreteType, SupportedPlatforms supportedPlatforms = (SupportedPlatforms)(-1), params object[] args) where T : IMixedRealityDataProvider { return RegisterDataProvider( concreteType, string.Empty, supportedPlatforms, args); } /// /// Internal method that creates an instance of the specified concrete type and registers the provider. /// private bool RegisterDataProviderInternal( bool retryWithRegistrar, Type concreteType, string providerName, SupportedPlatforms supportedPlatforms = (SupportedPlatforms)(-1), params object[] args) where T : IMixedRealityDataProvider { if (!PlatformUtility.IsPlatformSupported(supportedPlatforms)) { DebugUtilities.LogVerboseFormat( "Not registering data provider of type {0} with name {1} because the current platform is not in supported platforms {2}", concreteType, providerName, supportedPlatforms); return false; } if (concreteType == null) { if (!Application.isEditor) { Debug.LogWarning($"Unable to register {typeof(T).Name} data provider ({(!string.IsNullOrWhiteSpace(providerName) ? providerName : "unknown")}) because the value of concreteType is null.\n" + "This may be caused by code being stripped during linking. The link.xml file in the MixedRealityToolkit.Generated folder is used to control code preservation.\n" + "More information can be found at https://docs.unity3d.com/Manual/ManagedCodeStripping.html."); } return false; } SupportedUnityXRPipelines selectedPipeline = #if UNITY_2020_1_OR_NEWER SupportedUnityXRPipelines.XRSDK; #elif UNITY_2019 XRSettingsUtilities.XRSDKEnabled ? SupportedUnityXRPipelines.XRSDK : SupportedUnityXRPipelines.LegacyXR; #else SupportedUnityXRPipelines.LegacyXR; #endif if (MixedRealityExtensionServiceAttribute.Find(concreteType) is MixedRealityDataProviderAttribute providerAttribute && !providerAttribute.SupportedUnityXRPipelines.IsMaskSet(selectedPipeline)) { DebugUtilities.LogVerboseFormat("{0} not suitable for the current XR pipeline ({1})", concreteType.Name, selectedPipeline); return false; } if (!typeof(IMixedRealityDataProvider).IsAssignableFrom(concreteType)) { Debug.LogError($"Unable to register the {concreteType.Name} data provider. It does not implement {typeof(IMixedRealityDataProvider)}."); return false; } T dataProviderInstance; try { dataProviderInstance = (T)Activator.CreateInstance(concreteType, args); } catch (Exception e) { if (retryWithRegistrar && (e is MissingMethodException)) { Debug.LogWarning($"Failed to find an appropriate constructor for the {concreteType.Name} data provider. Adding the Registrar instance and re-attempting registration."); #pragma warning disable 0618 List updatedArgs = new List(); updatedArgs.Add(Registrar); if (args != null) { updatedArgs.AddRange(args); } return RegisterDataProviderInternal( false, // Do NOT retry, we have already added the configured IMIxedRealityServiceRegistrar concreteType, providerName, supportedPlatforms, updatedArgs.ToArray()); #pragma warning restore 0618 } Debug.LogError($"Failed to register the {concreteType.Name} data provider: {e.GetType()} - {e.Message}"); // Failures to create the concrete type generally surface as nested exceptions - just logging // the top level exception itself may not be helpful. If there is a nested exception (for example, // null reference in the constructor of the object itself), it's helpful to also surface those here. if (e.InnerException != null) { Debug.LogError("Underlying exception information: " + e.InnerException); } return false; } return RegisterDataProvider(dataProviderInstance); } /// /// Registers a service of the specified type. /// /// The interface type of the data provider to be registered. /// An instance of the data provider to be registered. protected bool RegisterDataProvider(T dataProviderInstance) where T : IMixedRealityDataProvider { if (dataProviderInstance == null) { Debug.LogWarning($"Unable to add a {dataProviderInstance.Name} data provider with a null instance."); return false; } dataProviders.Add(dataProviderInstance); dataProviderInstance.Initialize(); return true; } /// /// Unregisters a data provider of the specified type. /// /// The interface type of the data provider to be unregistered. /// The name of the data provider to unregister. /// True if the data provider was successfully unregistered, false otherwise. /// If the name argument is not specified, the first instance will be unregistered protected bool UnregisterDataProvider(string name = null) where T : IMixedRealityDataProvider { T dataProviderInstance = GetDataProvider(name); if (dataProviderInstance == null) { return false; } return UnregisterDataProvider(dataProviderInstance); } /// /// Unregisters a data provider. /// /// The interface type of the data provider to be unregistered. /// The specific data provider instance to unregister. /// True if the data provider was successfully unregistered, false otherwise. protected bool UnregisterDataProvider(T dataProviderInstance) where T : IMixedRealityDataProvider { if (dataProviderInstance == null) { return false; } if (dataProviders.Contains(dataProviderInstance)) { dataProviders.Remove(dataProviderInstance); dataProviderInstance.Disable(); dataProviderInstance.Destroy(); return true; } return false; } } }