using System; using System.Collections.Generic; using System.Linq; using System.Net.NetworkInformation; using System.Net.Sockets; using UnityEditor.AddressableAssets; using UnityEditor.AddressableAssets.Settings; using UnityEngine; using UnityEngine.Serialization; // ReSharper disable DelegateSubtraction namespace UnityEditor.AddressableAssets.HostingServices { /// /// Manages the hosting services. /// [Serializable] public class HostingServicesManager : ISerializationCallbackReceiver { internal const string KPrivateIpAddressKey = "PrivateIpAddress"; [Serializable] class HostingServiceInfo { [SerializeField] internal string classRef; [SerializeField] internal KeyDataStore dataStore; } [FormerlySerializedAs("m_hostingServiceInfos")] [SerializeField] List m_HostingServiceInfos; [FormerlySerializedAs("m_settings")] [SerializeField] AddressableAssetSettings m_Settings; [FormerlySerializedAs("m_nextInstanceId")] [SerializeField] int m_NextInstanceId; [FormerlySerializedAs("m_registeredServiceTypeRefs")] [SerializeField] List m_RegisteredServiceTypeRefs; readonly Type[] m_BuiltinServiceTypes = { typeof(HttpHostingService) }; Dictionary m_HostingServiceInfoMap; ILogger m_Logger; List m_RegisteredServiceTypes; /// /// Direct logging output of all managed services /// public ILogger Logger { get { return m_Logger; } set { m_Logger = value ?? Debug.unityLogger; foreach (var svc in HostingServices) svc.Logger = m_Logger; } } /// /// Static method for use in starting up the HostingServicesManager in batch mode. /// /// public static void BatchMode(AddressableAssetSettings settings) { if (settings == null) { Debug.LogError("Could not load Addressable Assets settings - aborting."); return; } var manager = settings.HostingServicesManager; if (manager == null) { Debug.LogError("Could not load HostingServicesManager - aborting."); return; } manager.StartAllServices(); } /// /// Static method for use in starting up the HostingServicesManager in batch mode. This method /// without parameters will find and use the default object. /// public static void BatchMode() { BatchMode(AddressableAssetSettingsDefaultObject.Settings); } /// /// Key/Value pairs valid for profile variable substitution /// public Dictionary GlobalProfileVariables { get; private set; } /// /// Indicates whether or not this HostingServiceManager is initialized /// public bool IsInitialized { get { return m_Settings != null; } } /// /// Return an enumerable list of all configured objects /// public ICollection HostingServices { get { return m_HostingServiceInfoMap.Keys; } } /// /// Get an array of all types that have been used by the manager, or are known /// built-in types available for use. /// /// public Type[] RegisteredServiceTypes { get { if (m_RegisteredServiceTypes.Count == 0) m_RegisteredServiceTypes.AddRange(m_BuiltinServiceTypes); return m_RegisteredServiceTypes.ToArray(); } } /// /// The id value that will be assigned to the next add to the manager. /// public int NextInstanceId { get { return m_NextInstanceId; } } /// /// Create a new /// public HostingServicesManager() { GlobalProfileVariables = new Dictionary(); m_HostingServiceInfoMap = new Dictionary(); m_RegisteredServiceTypes = new List(); m_Logger = Debug.unityLogger; } /// /// Initialize manager with the given object. /// /// public void Initialize(AddressableAssetSettings settings) { if (IsInitialized) return; m_Settings = settings; RefreshGlobalProfileVariables(); } /// /// Calls on all managed instances /// where is true /// public void StopAllServices() { foreach (var svc in HostingServices) { try { if (svc.IsHostingServiceRunning) svc.StopHostingService(); } catch (Exception e) { m_Logger.LogFormat(LogType.Error, e.Message); } } } /// /// Calls on all managed instances /// where is false /// public void StartAllServices() { foreach (var svc in HostingServices) { try { if (!svc.IsHostingServiceRunning) svc.StartHostingService(); } catch (Exception e) { m_Logger.LogFormat(LogType.Error, e.Message); } } } /// /// Add a new hosting service instance of the given type. The must implement the /// interface, or an is thrown. /// /// A object for the service. Must implement /// A descriptive name for the new service instance. /// public IHostingService AddHostingService(Type serviceType, string name) { var svc = Activator.CreateInstance(serviceType) as IHostingService; if (svc == null) throw new ArgumentException("Provided type does not implement IHostingService", "serviceType"); if (!m_RegisteredServiceTypes.Contains(serviceType)) m_RegisteredServiceTypes.Add(serviceType); var info = new HostingServiceInfo { classRef = TypeToClassRef(serviceType), dataStore = new KeyDataStore() }; svc.Logger = m_Logger; svc.DescriptiveName = name; svc.InstanceId = m_NextInstanceId; svc.HostingServiceContentRoots.AddRange(GetAllContentRoots()); m_Settings.profileSettings.RegisterProfileStringEvaluationFunc(svc.EvaluateProfileString); m_HostingServiceInfoMap.Add(svc, info); m_Settings.SetDirty(AddressableAssetSettings.ModificationEvent.HostingServicesManagerModified, this, true, true); m_NextInstanceId++; return svc; } /// /// Stops the given , unregisters callbacks, and removes it from management. This /// function does nothing if the service is not being managed by this /// /// public void RemoveHostingService(IHostingService svc) { if (!m_HostingServiceInfoMap.ContainsKey(svc)) return; svc.StopHostingService(); m_Settings.profileSettings.UnregisterProfileStringEvaluationFunc(svc.EvaluateProfileString); m_HostingServiceInfoMap.Remove(svc); m_Settings.SetDirty(AddressableAssetSettings.ModificationEvent.HostingServicesManagerModified, this, true, true); } /// /// Should be called by parent instance OnEnable method /// public void OnEnable() { Debug.Assert(IsInitialized); m_Settings.OnModification -= OnSettingsModification; m_Settings.OnModification += OnSettingsModification; m_Settings.profileSettings.RegisterProfileStringEvaluationFunc(EvaluateGlobalProfileVariableKey); foreach (var svc in HostingServices) { svc.Logger = m_Logger; m_Settings.profileSettings.RegisterProfileStringEvaluationFunc(svc.EvaluateProfileString); } RefreshGlobalProfileVariables(); } /// /// Should be called by parent instance OnDisable method /// public void OnDisable() { Debug.Assert(IsInitialized); // ReSharper disable once DelegateSubtraction m_Settings.OnModification -= OnSettingsModification; m_Settings.profileSettings.UnregisterProfileStringEvaluationFunc(EvaluateGlobalProfileVariableKey); foreach (var svc in HostingServices) { svc.Logger = null; m_Settings.profileSettings.UnregisterProfileStringEvaluationFunc(svc.EvaluateProfileString); } } /// /// Ensure object is ready for serialization, and calls methods /// on all managed instances /// public void OnBeforeSerialize() { m_HostingServiceInfos = new List(); foreach (var svc in HostingServices) { var info = m_HostingServiceInfoMap[svc]; m_HostingServiceInfos.Add(info); svc.OnBeforeSerialize(info.dataStore); } m_RegisteredServiceTypeRefs = new List(); foreach (var type in m_RegisteredServiceTypes) m_RegisteredServiceTypeRefs.Add(TypeToClassRef(type)); } /// /// Ensure object is ready for serialization, and calls methods /// on all managed instances /// public void OnAfterDeserialize() { m_HostingServiceInfoMap = new Dictionary(); foreach (var svcInfo in m_HostingServiceInfos) { var classRef = svcInfo.classRef; //handle change to namespace that happened just before Addressables 1.0.0. Can remove once upgrades from 0.5.x are no longer expected if (classRef.Contains("UnityEditor.AddressableAssets.HttpHostingService")) classRef = classRef.Replace("UnityEditor.AddressableAssets.HttpHostingService", "UnityEditor.AddressableAssets.HostingServices.HttpHostingService"); var svc = CreateHostingServiceInstance(classRef); if (svc == null) continue; svc.OnAfterDeserialize(svcInfo.dataStore); m_HostingServiceInfoMap.Add(svc, svcInfo); } m_RegisteredServiceTypes = new List(); foreach (var typeRef in m_RegisteredServiceTypeRefs) { var type = Type.GetType(typeRef, false); if (type == null) continue; m_RegisteredServiceTypes.Add(type); } } /// /// Refresh values in the global profile variables table. /// public void RefreshGlobalProfileVariables() { var vars = GlobalProfileVariables; vars.Clear(); var ipAddressList = NetworkInterface.GetAllNetworkInterfaces() .Where(n => n.NetworkInterfaceType != NetworkInterfaceType.Loopback) .SelectMany(n => n.GetIPProperties().UnicastAddresses) .Where(a => a.Address.AddressFamily == AddressFamily.InterNetwork) .Select(a => a.Address).ToArray(); if (ipAddressList.Length > 0) { vars.Add(KPrivateIpAddressKey, ipAddressList[0].ToString()); if (ipAddressList.Length > 1) { for (var i = 1; i < ipAddressList.Length; i++) vars.Add(KPrivateIpAddressKey + "_" + i, ipAddressList[i].ToString()); } } } // Internal for unit tests internal string EvaluateGlobalProfileVariableKey(string key) { string retVal; GlobalProfileVariables.TryGetValue(key, out retVal); return retVal; } void OnSettingsModification(AddressableAssetSettings s, AddressableAssetSettings.ModificationEvent evt, object obj) { switch (evt) { case AddressableAssetSettings.ModificationEvent.GroupAdded: case AddressableAssetSettings.ModificationEvent.GroupRemoved: case AddressableAssetSettings.ModificationEvent.GroupSchemaAdded: case AddressableAssetSettings.ModificationEvent.GroupSchemaRemoved: case AddressableAssetSettings.ModificationEvent.GroupSchemaModified: case AddressableAssetSettings.ModificationEvent.ActiveProfileSet: case AddressableAssetSettings.ModificationEvent.BuildSettingsChanged: case AddressableAssetSettings.ModificationEvent.BatchModification: ConfigureAllHostingServices(); break; } } void ConfigureAllHostingServices() { var contentRoots = GetAllContentRoots(); foreach (var svc in HostingServices) { svc.HostingServiceContentRoots.Clear(); svc.HostingServiceContentRoots.AddRange(contentRoots); } } string[] GetAllContentRoots() { Debug.Assert(IsInitialized); var contentRoots = new List(); foreach (var group in m_Settings.groups) { foreach (var schema in group.Schemas) { var configProvider = schema as IHostingServiceConfigurationProvider; if (configProvider != null) { var groupRoot = configProvider.HostingServicesContentRoot; if (groupRoot != null && !contentRoots.Contains(groupRoot)) contentRoots.Add(groupRoot); } } } return contentRoots.ToArray(); } IHostingService CreateHostingServiceInstance(string classRef) { try { var objType = Type.GetType(classRef, true); var svc = (IHostingService) Activator.CreateInstance(objType); return svc; } catch (Exception e) { m_Logger.LogFormat(LogType.Error, "Could not create IHostingService from class ref '{0}'", classRef); m_Logger.LogFormat(LogType.Error, e.Message); } return null; } static string TypeToClassRef(Type t) { return string.Format("{0}, {1}", t.FullName, t.Assembly.GetName().Name); } // For unit tests internal AddressableAssetSettings Settings { get { return m_Settings; } } } }