-
-
Save Simie/297fa9d0940ef54f52be to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
using System.Reflection; | |
using SRF.Components; | |
using SRF.Helpers; | |
using SRF.Internal; | |
using UnityEngine; | |
using Object = UnityEngine.Object; | |
// Disable unreachable code warning caused by DEBUG | |
#pragma warning disable 0162 | |
namespace SRF.Service | |
{ | |
[AddComponentMenu(ComponentMenuPaths.SRServiceManager)] | |
public class SRServiceManager : SRAutoSingleton<SRServiceManager> | |
{ | |
#if DEBUG | |
public const bool EnableLogging = true; | |
#else | |
public const bool EnableLogging = false; | |
#endif | |
/// <summary> | |
/// Is there a service loading? | |
/// </summary> | |
public static bool IsLoading { get { return LoadingCount > 0; } } | |
public static int LoadingCount = 0; | |
public static T GetService<T>() where T : class | |
{ | |
var s = GetServiceInternal(typeof(T)) as T; | |
if(s == null && (!_hasQuit || EnableLogging)) | |
Debug.LogWarning("Service {0} not found. (HasQuit: {1})".Fmt(typeof(T).Name, _hasQuit)); | |
return s; | |
} | |
public static object GetService(Type t) | |
{ | |
var s = GetServiceInternal(t); | |
if (s == null && (!_hasQuit || EnableLogging)) | |
Debug.LogWarning("Service {0} not found. (HasQuit: {1})".Fmt(t.Name, _hasQuit)); | |
return s; | |
} | |
private static object GetServiceInternal(Type t) | |
{ | |
if (_hasQuit || !Application.isPlaying) | |
return null; | |
var services = Instance._services; | |
for (int i = 0; i < services.Count; i++) { | |
var s = services[i]; | |
if (t.IsAssignableFrom(s.Type)) { | |
if (s.Object == null) { | |
UnRegisterService(t); | |
break; | |
} | |
return s.Object; | |
} | |
} | |
return Instance.AutoCreateService(t); | |
} | |
public static bool HasService<T>() where T : class | |
{ | |
return HasService(typeof (T)); | |
} | |
public static bool HasService(Type t) | |
{ | |
if (_hasQuit || !Application.isPlaying) | |
return false; | |
var services = Instance._services; | |
for (int i = 0; i < services.Count; i++) { | |
var s = services[i]; | |
if (t.IsAssignableFrom(s.Type)) | |
return s.Object != null; | |
} | |
return false; | |
} | |
public static void RegisterService<T>(object service) where T : class | |
{ | |
RegisterService(typeof(T), service); | |
} | |
private static void RegisterService(Type t, object service) | |
{ | |
if (_hasQuit) | |
return; | |
if (HasService(t)) { | |
if (GetServiceInternal(t) == service) | |
return; | |
throw new Exception("Service already registered for type " + t.Name); | |
} | |
UnRegisterService(t); | |
if (!t.IsInstanceOfType(service)) { | |
throw new ArgumentException("service {0} must be assignable from type {1}".Fmt(service.GetType(), t)); | |
} | |
Instance._services.Add(new Service { | |
Object = service, | |
Type = t | |
}); | |
} | |
public static void UnRegisterService<T>() where T : class | |
{ | |
UnRegisterService(typeof(T)); | |
} | |
private static void UnRegisterService(Type t) | |
{ | |
if (_hasQuit || !HasInstance) | |
return; | |
if (!HasService(t)) { | |
return; | |
} | |
var services = Instance._services; | |
for (var i = services.Count - 1; i >= 0; i--) { | |
var s = services[i]; | |
if (s.Type == t) | |
services.RemoveAt(i); | |
} | |
} | |
[Serializable] | |
private class Service | |
{ | |
public Type Type; | |
public object Object; | |
} | |
[Serializable] | |
private class ServiceStub | |
{ | |
public Type InterfaceType; | |
public Type Type; | |
public Func<Type> Selector; | |
public Func<object> Constructor; | |
public override string ToString() | |
{ | |
var s = InterfaceType.Name + " ("; | |
if (Type != null) | |
s += "Type: " + Type; | |
else if (Selector != null) | |
s += "Selector: " + Selector; | |
else if (Constructor != null) | |
s += "Constructor: " + Constructor; | |
s += ")"; | |
return s; | |
} | |
} | |
private readonly SRList<Service> _services = new SRList<Service>(); | |
private List<ServiceStub> _serviceStubs; | |
private static bool _hasQuit; | |
protected override void Awake() | |
{ | |
_hasQuit = false; | |
base.Awake(); | |
DontDestroyOnLoad(CachedGameObject); | |
CachedGameObject.hideFlags = HideFlags.NotEditable; | |
} | |
protected void UpdateStubs() | |
{ | |
if (_serviceStubs != null) | |
return; | |
_serviceStubs = new List<ServiceStub>(); | |
var types = new List<Type>(); | |
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies()) { | |
var n = assembly.GetName().Name; | |
// Filter down to user assemblies only | |
if(n == "mscorlib" || | |
n == "System" || | |
n == "UnityEngine" || | |
n.StartsWith("System.") || | |
n.StartsWith("UnityEngine.") || | |
n.StartsWith("Mono.") || | |
n.StartsWith("Boo.") || | |
n.StartsWith("UnityEditor") || | |
n.StartsWith("Unity.") || | |
n.StartsWith("UnityScript") || | |
n.StartsWith("nunit.") || | |
n.StartsWith("I18N") || | |
n.StartsWith("ICSharpCode")) | |
continue; | |
try { | |
#if NETFX_CORE | |
types.AddRange(assembly.ExportedTypes); | |
#else | |
types.AddRange(assembly.GetExportedTypes()); | |
#endif | |
} catch (Exception e) { | |
Debug.LogError("[SRServiceManager] Error loading assembly {0}".Fmt(assembly.FullName), this); | |
Debug.LogException(e); | |
} | |
} | |
foreach (var type in types) { | |
ScanType(type); | |
} | |
var serviceStrings = | |
_serviceStubs.Select(p => " {0}".Fmt(p)).ToArray(); | |
if (EnableLogging) { | |
Debug.Log("[SRServiceManager] Services Discovered: {0} \n {1}".Fmt(serviceStrings.Length, | |
string.Join("\n ", serviceStrings))); | |
} | |
} | |
protected object AutoCreateService(Type t) | |
{ | |
UpdateStubs(); | |
foreach (var stub in _serviceStubs) { | |
if (stub.InterfaceType != t) | |
continue; | |
object service = null; | |
if (stub.Constructor != null) { | |
service = stub.Constructor(); | |
} else { | |
Type serviceType = stub.Type; | |
if (serviceType == null) { | |
serviceType = stub.Selector(); | |
} | |
service = DefaultServiceConstructor(t, serviceType); | |
} | |
if(!HasService(t)) | |
RegisterService(t, service); | |
if(EnableLogging) | |
Debug.Log("[SRServiceManager] Auto-created service: {0} ({1})".Fmt(stub.Type, stub.InterfaceType), service as Object); | |
return service; | |
} | |
return null; | |
} | |
protected void OnApplicationQuit() | |
{ | |
_hasQuit = true; | |
} | |
private static object DefaultServiceConstructor(Type serviceIntType, Type implType) | |
{ | |
// If mono-behaviour based, create a gameobject for this service | |
if (typeof (MonoBehaviour).IsAssignableFrom(implType)) { | |
var go = new GameObject("_S_" + serviceIntType.Name); | |
return go.AddComponent(implType); | |
} | |
// If ScriptableObject based, create an instance | |
if (typeof (ScriptableObject).IsAssignableFrom(implType)) { | |
var obj = ScriptableObject.CreateInstance(implType); | |
return obj; | |
} | |
// If just a standard C# object, just create an instance | |
return Activator.CreateInstance(implType); | |
} | |
#region Type Scanning | |
private void ScanType(Type type) | |
{ | |
var attribute = SRReflection.GetAttribute<ServiceAttribute>(type); | |
if (attribute != null) { | |
_serviceStubs.Add(new ServiceStub { | |
Type = type, | |
InterfaceType = attribute.ServiceType | |
}); | |
} | |
ScanTypeForConstructors(type, _serviceStubs); | |
ScanTypeForSelectors(type, _serviceStubs); | |
} | |
private static void ScanTypeForSelectors(Type t, List<ServiceStub> stubs) | |
{ | |
var methods = GetStaticMethods(t); | |
foreach (var method in methods) { | |
var attrib = SRReflection.GetAttribute<ServiceSelectorAttribute>(method); | |
if (attrib == null) | |
continue; | |
if (method.ReturnType != typeof (Type)) { | |
Debug.LogError("ServiceSelector must have return type of Type ({0}.{1}())".Fmt(t.Name, method.Name)); | |
continue; | |
} | |
if (method.GetParameters().Length > 0) { | |
Debug.LogError("ServiceSelector must have no parameters ({0}.{1}())".Fmt(t.Name, method.Name)); | |
continue; | |
} | |
var stub = stubs.FirstOrDefault(p => p.InterfaceType == attrib.ServiceType); | |
if (stub == null) { | |
stub = new ServiceStub { | |
InterfaceType = attrib.ServiceType | |
}; | |
stubs.Add(stub); | |
} | |
#if NETFX_CORE | |
stub.Selector = (Func<Type>) method.CreateDelegate(typeof(Func<Type>)); | |
#else | |
stub.Selector = (Func<Type>)Delegate.CreateDelegate(typeof(Func<Type>), method); | |
#endif | |
} | |
} | |
private static void ScanTypeForConstructors(Type t, List<ServiceStub> stubs) | |
{ | |
var methods = GetStaticMethods(t); | |
foreach (var method in methods) { | |
var attrib = SRReflection.GetAttribute<ServiceConstructorAttribute>(method); | |
if (attrib == null) | |
continue; | |
if (method.ReturnType != attrib.ServiceType) { | |
Debug.LogError("ServiceConstructor must have return type of {2} ({0}.{1}())".Fmt(t.Name, method.Name, attrib.ServiceType)); | |
continue; | |
} | |
if (method.GetParameters().Length > 0) { | |
Debug.LogError("ServiceConstructor must have no parameters ({0}.{1}())".Fmt(t.Name, method.Name)); | |
continue; | |
} | |
var stub = stubs.FirstOrDefault(p => p.InterfaceType == attrib.ServiceType); | |
if (stub == null) { | |
stub = new ServiceStub { | |
InterfaceType = attrib.ServiceType | |
}; | |
stubs.Add(stub); | |
} | |
#if NETFX_CORE | |
stub.Constructor = (Func<object>)method.CreateDelegate(typeof(Func<object>)); | |
#else | |
stub.Constructor = (Func<object>)Delegate.CreateDelegate(t, method); | |
#endif | |
} | |
} | |
#endregion | |
#region Reflection | |
private static MethodInfo[] GetStaticMethods(Type t) | |
{ | |
#if !NETFX_CORE | |
return t.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); | |
#else | |
return t.GetTypeInfo().DeclaredMethods.Where(p => p.IsStatic).ToArray(); | |
#endif | |
} | |
#if NETFX_CORE | |
private sealed class AppDomain | |
{ | |
public static AppDomain CurrentDomain { get; private set; } | |
static AppDomain() | |
{ | |
CurrentDomain = new AppDomain(); | |
} | |
public Assembly[] GetAssemblies() | |
{ | |
return GetAssemblyListAsync().Result.ToArray(); | |
} | |
private async System.Threading.Tasks.Task<IEnumerable<Assembly>> GetAssemblyListAsync() | |
{ | |
var folder = Windows.ApplicationModel.Package.Current.InstalledLocation; | |
var assemblies = new List<Assembly>(); | |
foreach (var file in await folder.GetFilesAsync()) | |
{ | |
if (file.FileType == ".dll" || file.FileType == ".exe") | |
{ | |
System.Diagnostics.Debug.WriteLine("Found: " + file.Path); | |
try { | |
var name = new AssemblyName() {Name = file.DisplayName}; | |
var asm = Assembly.Load(name); | |
assemblies.Add(asm); | |
} catch { | |
System.Diagnostics.Debug.WriteLine("Error loading " + file.Name); | |
} | |
} | |
} | |
return assemblies; | |
} | |
} | |
#endif | |
#endregion | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment