499 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C#
		
	
	
	
			
		
		
	
	
			499 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C#
		
	
	
	
// Disable unreachable code warning caused by DEBUG
 | 
						|
#pragma warning disable 0162
 | 
						|
 | 
						|
namespace SRF.Service
 | 
						|
{
 | 
						|
    using System;
 | 
						|
    using System.Collections.Generic;
 | 
						|
    using System.Linq;
 | 
						|
    using System.Reflection;
 | 
						|
    using Components;
 | 
						|
    using Helpers;
 | 
						|
    using Internal;
 | 
						|
    using UnityEngine;
 | 
						|
    using Object = UnityEngine.Object;
 | 
						|
 | 
						|
    [AddComponentMenu(ComponentMenuPaths.SRServiceManager)]
 | 
						|
    public class SRServiceManager : SRAutoSingleton<SRServiceManager>
 | 
						|
    {
 | 
						|
#if SRDEBUG
 | 
						|
		public const bool EnableLogging = true;
 | 
						|
#else
 | 
						|
        public const bool EnableLogging = false;
 | 
						|
#endif
 | 
						|
 | 
						|
#if (!UNITY_2017 && !UNITY_2018 && !UNITY_2019) || UNITY_2019_3_OR_NEWER
 | 
						|
        [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
 | 
						|
        public static void RuntimeInitialize()
 | 
						|
        {
 | 
						|
            // To handle entering play mode without a domain reload, need to reset the state of the service manager.
 | 
						|
            _hasQuit = false;
 | 
						|
        }
 | 
						|
#endif
 | 
						|
 | 
						|
        /// <summary>
 | 
						|
        /// Register the assembly that contains type <typeparamref name="TType"/> with the service manager.
 | 
						|
        /// </summary>
 | 
						|
        /// <typeparam name="TType"></typeparam>
 | 
						|
        public static void RegisterAssembly<TType>()
 | 
						|
        {
 | 
						|
#if NETFX_CORE
 | 
						|
            var assembly = typeof(TType).GetTypeInfo().Assembly;
 | 
						|
#else
 | 
						|
            var assembly = typeof(TType).Assembly;
 | 
						|
#endif
 | 
						|
 | 
						|
            if (_assemblies.Contains(assembly))
 | 
						|
            {
 | 
						|
                return;
 | 
						|
            }
 | 
						|
 | 
						|
            _assemblies.Add(assembly);
 | 
						|
        }
 | 
						|
 | 
						|
        /// <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 (var 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 (var 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 object Object;
 | 
						|
            public Type Type;
 | 
						|
        }
 | 
						|
 | 
						|
        [Serializable]
 | 
						|
        private class ServiceStub
 | 
						|
        {
 | 
						|
            public Func<object> Constructor;
 | 
						|
            public Type InterfaceType;
 | 
						|
            public Func<Type> Selector;
 | 
						|
            public Type Type;
 | 
						|
 | 
						|
            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 static readonly List<Assembly> _assemblies = new List<Assembly>(2);
 | 
						|
 | 
						|
        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;
 | 
						|
            }
 | 
						|
 | 
						|
            RegisterAssembly<SRServiceManager>();
 | 
						|
          
 | 
						|
            _serviceStubs = new List<ServiceStub>();
 | 
						|
 | 
						|
            var types = new List<Type>();
 | 
						|
 | 
						|
            foreach (var assembly in _assemblies)
 | 
						|
            {
 | 
						|
                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);
 | 
						|
            }
 | 
						|
 | 
						|
            if (EnableLogging)
 | 
						|
            {
 | 
						|
                var serviceStrings =
 | 
						|
                    _serviceStubs.Select(p => "	{0}".Fmt(p)).ToArray();
 | 
						|
 | 
						|
                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
 | 
						|
                {
 | 
						|
                    var 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);
 | 
						|
                }
 | 
						|
 | 
						|
                var m = method;
 | 
						|
                stub.Constructor = () => m.Invoke(null, null);
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
#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
 | 
						|
        }
 | 
						|
 | 
						|
#endregion
 | 
						|
    }
 | 
						|
}
 |