7502018d20
There is an asset in the store I grabbed. the coding is WAY above my head, I got about half of it and integrated and adapted what I can to it. im going as far as I can with it and ill come back in a few month when I understand t better.
560 lines
21 KiB
C#
560 lines
21 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Net;
|
|
using System.Net.NetworkInformation;
|
|
using System.Net.Sockets;
|
|
using UnityEditor.AddressableAssets.Build.DataBuilders;
|
|
using UnityEditor.AddressableAssets.Settings;
|
|
using UnityEngine;
|
|
using UnityEngine.Serialization;
|
|
|
|
// ReSharper disable DelegateSubtraction
|
|
|
|
namespace UnityEditor.AddressableAssets.HostingServices
|
|
{
|
|
/// <summary>
|
|
/// Manages the hosting services.
|
|
/// </summary>
|
|
[Serializable]
|
|
public class HostingServicesManager : ISerializationCallbackReceiver
|
|
{
|
|
internal const string KPrivateIpAddressKey = "PrivateIpAddress";
|
|
internal string GetPrivateIpAddressKey(int id = 0)
|
|
{
|
|
if (id == 0)
|
|
return KPrivateIpAddressKey;
|
|
return $"{KPrivateIpAddressKey}_{id}";
|
|
}
|
|
|
|
[Serializable]
|
|
internal class HostingServiceInfo
|
|
{
|
|
[SerializeField]
|
|
internal string classRef;
|
|
[SerializeField]
|
|
internal KeyDataStore dataStore;
|
|
}
|
|
|
|
[FormerlySerializedAs("m_hostingServiceInfos")]
|
|
[SerializeField]
|
|
List<HostingServiceInfo> m_HostingServiceInfos;
|
|
[FormerlySerializedAs("m_settings")]
|
|
[SerializeField]
|
|
AddressableAssetSettings m_Settings;
|
|
[FormerlySerializedAs("m_nextInstanceId")]
|
|
[SerializeField]
|
|
int m_NextInstanceId;
|
|
[FormerlySerializedAs("m_registeredServiceTypeRefs")]
|
|
[SerializeField]
|
|
List<string> m_RegisteredServiceTypeRefs;
|
|
|
|
readonly Type[] m_BuiltinServiceTypes =
|
|
{
|
|
typeof(HttpHostingService)
|
|
};
|
|
|
|
Dictionary<IHostingService, HostingServiceInfo> m_HostingServiceInfoMap;
|
|
ILogger m_Logger;
|
|
List<Type> m_RegisteredServiceTypes;
|
|
|
|
internal bool exitingEditMode = false;
|
|
|
|
/// <summary>
|
|
/// Key/Value pairs valid for profile variable substitution
|
|
/// </summary>
|
|
public Dictionary<string, string> GlobalProfileVariables { get; private set; }
|
|
|
|
internal static readonly string k_GlobalProfileVariablesCountKey = $"com.unity.addressables.{nameof(GlobalProfileVariables)}Count";
|
|
internal static string GetSessionStateKey(int id) { return $"com.unity.addressables.{nameof(GlobalProfileVariables)}{id}"; }
|
|
|
|
/// <summary>
|
|
/// Direct logging output of all managed services
|
|
/// </summary>
|
|
public ILogger Logger
|
|
{
|
|
get { return m_Logger; }
|
|
set
|
|
{
|
|
m_Logger = value ?? Debug.unityLogger;
|
|
foreach (var svc in HostingServices)
|
|
svc.Logger = m_Logger;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Static method for use in starting up the HostingServicesManager in batch mode.
|
|
/// </summary>
|
|
/// <param name="settings"> </param>
|
|
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();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Static method for use in starting up the HostingServicesManager in batch mode. This method
|
|
/// without parameters will find and use the default <see cref="AddressableAssetSettings"/> object.
|
|
/// </summary>
|
|
public static void BatchMode()
|
|
{
|
|
BatchMode(AddressableAssetSettingsDefaultObject.Settings);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Indicates whether or not this HostingServiceManager is initialized
|
|
/// </summary>
|
|
public bool IsInitialized
|
|
{
|
|
get { return m_Settings != null; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Return an enumerable list of all configured <see cref="IHostingService"/> objects
|
|
/// </summary>
|
|
public ICollection<IHostingService> HostingServices
|
|
{
|
|
get { return m_HostingServiceInfoMap.Keys; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get an array of all <see cref="IHostingService"/> types that have been used by the manager, or are known
|
|
/// built-in types available for use.
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
public Type[] RegisteredServiceTypes
|
|
{
|
|
get
|
|
{
|
|
if (m_RegisteredServiceTypes.Count == 0)
|
|
m_RegisteredServiceTypes.AddRange(m_BuiltinServiceTypes);
|
|
|
|
return m_RegisteredServiceTypes.ToArray();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// The id value that will be assigned to the next <see cref="IHostingService"/> add to the manager.
|
|
/// </summary>
|
|
public int NextInstanceId
|
|
{
|
|
get { return m_NextInstanceId; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Create a new <see cref="HostingServicesManager"/>
|
|
/// </summary>
|
|
public HostingServicesManager()
|
|
{
|
|
GlobalProfileVariables = new Dictionary<string, string>();
|
|
m_HostingServiceInfos = new List<HostingServiceInfo>();
|
|
m_HostingServiceInfoMap = new Dictionary<IHostingService, HostingServiceInfo>();
|
|
m_RegisteredServiceTypes = new List<Type>();
|
|
m_RegisteredServiceTypeRefs = new List<string>();
|
|
m_Logger = Debug.unityLogger;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initialize manager with the given <see cref="AddressableAssetSettings"/> object.
|
|
/// </summary>
|
|
/// <param name="settings"></param>
|
|
public void Initialize(AddressableAssetSettings settings)
|
|
{
|
|
if (IsInitialized) return;
|
|
m_Settings = settings;
|
|
RefreshGlobalProfileVariables();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Calls <see cref="IHostingService.StopHostingService"/> on all managed <see cref="IHostingService"/> instances
|
|
/// where <see cref="IHostingService.IsHostingServiceRunning"/> is true
|
|
/// </summary>
|
|
public void StopAllServices()
|
|
{
|
|
foreach (var svc in HostingServices)
|
|
{
|
|
try
|
|
{
|
|
if (svc.IsHostingServiceRunning)
|
|
svc.StopHostingService();
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
m_Logger.LogFormat(LogType.Error, e.Message);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Calls <see cref="IHostingService.StartHostingService"/> on all managed <see cref="IHostingService"/> instances
|
|
/// where <see cref="IHostingService.IsHostingServiceRunning"/> is false
|
|
/// </summary>
|
|
public void StartAllServices()
|
|
{
|
|
foreach (var svc in HostingServices)
|
|
{
|
|
try
|
|
{
|
|
if (!svc.IsHostingServiceRunning)
|
|
svc.StartHostingService();
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
m_Logger.LogFormat(LogType.Error, e.Message);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Add a new hosting service instance of the given type. The <paramref name="serviceType"/> must implement the
|
|
/// <see cref="IHostingService"/> interface, or an <see cref="ArgumentException"/> is thrown.
|
|
/// </summary>
|
|
/// <param name="serviceType">A <see cref="Type"/> object for the service. Must implement <see cref="IHostingService"/></param>
|
|
/// <param name="name">A descriptive name for the new service instance.</param>
|
|
/// <returns></returns>
|
|
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);
|
|
AddressableAssetUtility.OpenAssetIfUsingVCIntegration(m_Settings);
|
|
|
|
m_NextInstanceId++;
|
|
return svc;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Stops the given <see cref="IHostingService"/>, unregisters callbacks, and removes it from management. This
|
|
/// function does nothing if the service is not being managed by this <see cref="HostingServicesManager"/>
|
|
/// </summary>
|
|
/// <param name="svc"></param>
|
|
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);
|
|
AddressableAssetUtility.OpenAssetIfUsingVCIntegration(m_Settings);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Should be called by parent <see cref="ScriptableObject"/> instance OnEnable method
|
|
/// </summary>
|
|
public void OnEnable()
|
|
{
|
|
Debug.Assert(IsInitialized);
|
|
|
|
m_Settings.OnModification -= OnSettingsModification;
|
|
m_Settings.OnModification += OnSettingsModification;
|
|
m_Settings.profileSettings.RegisterProfileStringEvaluationFunc(EvaluateGlobalProfileVariableKey);
|
|
|
|
bool hasAnEnabledService = false;
|
|
foreach (var svc in HostingServices)
|
|
{
|
|
svc.Logger = m_Logger;
|
|
m_Settings.profileSettings.RegisterProfileStringEvaluationFunc(svc.EvaluateProfileString);
|
|
var baseSvc = svc as BaseHostingService;
|
|
baseSvc?.OnEnable();
|
|
|
|
if (!hasAnEnabledService)
|
|
hasAnEnabledService = svc.IsHostingServiceRunning || baseSvc != null && baseSvc.WasEnabled;
|
|
}
|
|
|
|
if (!exitingEditMode || exitingEditMode && hasAnEnabledService && IsUsingPackedPlayMode())
|
|
RefreshGlobalProfileVariables();
|
|
else
|
|
LoadSessionStateKeys();
|
|
}
|
|
|
|
bool IsUsingPackedPlayMode()
|
|
{
|
|
return m_Settings.ActivePlayModeDataBuilder != null && m_Settings.ActivePlayModeDataBuilder.GetType() == typeof(BuildScriptPackedPlayMode);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Should be called by parent <see cref="ScriptableObject"/> instance OnDisable method
|
|
/// </summary>
|
|
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);
|
|
(svc as BaseHostingService)?.OnDisable();
|
|
}
|
|
SaveSessionStateKeys();
|
|
}
|
|
|
|
internal void LoadSessionStateKeys()
|
|
{
|
|
int numKeys = SessionState.GetInt(k_GlobalProfileVariablesCountKey, 0);
|
|
for (int i = 0; i < numKeys; i++)
|
|
{
|
|
string profileVar = SessionState.GetString(GetSessionStateKey(i), string.Empty);
|
|
if (!string.IsNullOrEmpty(profileVar))
|
|
GlobalProfileVariables.Add(GetPrivateIpAddressKey(i), profileVar);
|
|
}
|
|
}
|
|
|
|
internal void SaveSessionStateKeys()
|
|
{
|
|
int prevNumKeys = SessionState.GetInt(k_GlobalProfileVariablesCountKey, 0);
|
|
SessionState.SetInt(k_GlobalProfileVariablesCountKey, GlobalProfileVariables.Count);
|
|
|
|
int profileVarIdx = 0;
|
|
foreach (KeyValuePair<string, string> pair in GlobalProfileVariables)
|
|
{
|
|
SessionState.SetString(GetSessionStateKey(profileVarIdx), pair.Value);
|
|
profileVarIdx++;
|
|
}
|
|
EraseSessionStateKeys(profileVarIdx, prevNumKeys);
|
|
}
|
|
|
|
internal static void EraseSessionStateKeys()
|
|
{
|
|
int numKeys = SessionState.GetInt(k_GlobalProfileVariablesCountKey, 0);
|
|
EraseSessionStateKeys(0, numKeys);
|
|
SessionState.EraseInt(k_GlobalProfileVariablesCountKey);
|
|
}
|
|
|
|
static void EraseSessionStateKeys(int min, int max)
|
|
{
|
|
for (int i = min; i < max; i++)
|
|
{
|
|
SessionState.EraseString(GetSessionStateKey(i));
|
|
}
|
|
}
|
|
|
|
/// <summary> Ensure object is ready for serialization, and calls <see cref="IHostingService.OnBeforeSerialize"/> methods
|
|
/// on all managed <see cref="IHostingService"/> instances
|
|
/// </summary>
|
|
public void OnBeforeSerialize()
|
|
{
|
|
// https://docs.unity3d.com/ScriptReference/EditorWindow.OnInspectorUpdate.html
|
|
// Because the manager is a serialized field in the Addressables settings, this method is called
|
|
// at 10 frames per second when the settings are opened in the inspector...
|
|
// Be careful what you put in there...
|
|
|
|
m_HostingServiceInfos.Clear();
|
|
foreach (var svc in HostingServices)
|
|
{
|
|
var info = m_HostingServiceInfoMap[svc];
|
|
m_HostingServiceInfos.Add(info);
|
|
svc.OnBeforeSerialize(info.dataStore);
|
|
}
|
|
|
|
m_RegisteredServiceTypeRefs.Clear();
|
|
foreach (var type in m_RegisteredServiceTypes)
|
|
m_RegisteredServiceTypeRefs.Add(TypeToClassRef(type));
|
|
}
|
|
|
|
/// <summary> Ensure object is ready for serialization, and calls <see cref="IHostingService.OnBeforeSerialize"/> methods
|
|
/// on all managed <see cref="IHostingService"/> instances
|
|
/// </summary>
|
|
public void OnAfterDeserialize()
|
|
{
|
|
m_HostingServiceInfoMap = new Dictionary<IHostingService, HostingServiceInfo>();
|
|
foreach (var svcInfo in m_HostingServiceInfos)
|
|
{
|
|
IHostingService svc = CreateHostingServiceInstance(svcInfo.classRef);
|
|
|
|
if (svc == null) continue;
|
|
svc.OnAfterDeserialize(svcInfo.dataStore);
|
|
m_HostingServiceInfoMap.Add(svc, svcInfo);
|
|
}
|
|
|
|
m_RegisteredServiceTypes = new List<Type>();
|
|
foreach (var typeRef in m_RegisteredServiceTypeRefs)
|
|
{
|
|
var type = Type.GetType(typeRef, false);
|
|
if (type == null) continue;
|
|
m_RegisteredServiceTypes.Add(type);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Refresh values in the global profile variables table.
|
|
/// </summary>
|
|
public void RefreshGlobalProfileVariables()
|
|
{
|
|
var vars = GlobalProfileVariables;
|
|
vars.Clear();
|
|
|
|
var ipAddressList = FilterValidIPAddresses(NetworkInterface.GetAllNetworkInterfaces()
|
|
.Where(n => n.NetworkInterfaceType != NetworkInterfaceType.Loopback && n.OperationalStatus == OperationalStatus.Up)
|
|
.SelectMany(n => n.GetIPProperties().UnicastAddresses)
|
|
.Where(a => a.Address.AddressFamily == AddressFamily.InterNetwork)
|
|
.Select(a => a.Address).ToList());
|
|
|
|
if (ipAddressList.Count > 0)
|
|
{
|
|
vars.Add(KPrivateIpAddressKey, ipAddressList[0].ToString());
|
|
|
|
if (ipAddressList.Count > 1)
|
|
{
|
|
for (var i = 1; i < ipAddressList.Count; 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.ProfileModified:
|
|
var profileRemoteBuildPath = m_Settings.profileSettings.GetValueByName(m_Settings.activeProfileId, AddressableAssetSettings.kRemoteBuildPath);
|
|
if (profileRemoteBuildPath != null && (profileRemoteBuildPath.Contains('[') || !CurrentContentRootsContain(profileRemoteBuildPath)))
|
|
ConfigureAllHostingServices();
|
|
break;
|
|
case AddressableAssetSettings.ModificationEvent.BatchModification:
|
|
ConfigureAllHostingServices();
|
|
break;
|
|
}
|
|
}
|
|
|
|
bool CurrentContentRootsContain(string root)
|
|
{
|
|
foreach (var svc in HostingServices)
|
|
{
|
|
if (!svc.HostingServiceContentRoots.Contains(root))
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
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<string>();
|
|
foreach (var group in m_Settings.groups)
|
|
{
|
|
if (group != null)
|
|
{
|
|
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; }
|
|
}
|
|
|
|
private List<IPAddress> FilterValidIPAddresses(List<IPAddress> ipAddresses)
|
|
{
|
|
List<IPAddress> validIpList = new List<IPAddress>();
|
|
|
|
foreach (IPAddress address in ipAddresses)
|
|
{
|
|
var sender = new System.Net.NetworkInformation.Ping();
|
|
var reply = sender.Send(address.ToString(), 5000);
|
|
if (reply.Status == IPStatus.Success)
|
|
{
|
|
validIpList.Add(address);
|
|
}
|
|
}
|
|
return validIpList;
|
|
}
|
|
}
|
|
}
|