SRServiceManager.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530
  1. // Disable unreachable code warning caused by DEBUG
  2. #pragma warning disable 0162
  3. namespace SRF.Service
  4. {
  5. using System;
  6. using System.Collections.Generic;
  7. using System.Linq;
  8. using System.Reflection;
  9. using Components;
  10. using Helpers;
  11. using Internal;
  12. using UnityEngine;
  13. using Object = UnityEngine.Object;
  14. [AddComponentMenu(ComponentMenuPaths.SRServiceManager)]
  15. public class SRServiceManager : SRAutoSingleton<SRServiceManager>
  16. {
  17. #if SRDEBUG
  18. public const bool EnableLogging = true;
  19. #else
  20. public const bool EnableLogging = false;
  21. #endif
  22. #if UNITY_EDITOR && ((!UNITY_2017 && !UNITY_2018 && !UNITY_2019) || UNITY_2019_3_OR_NEWER)
  23. [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
  24. public static void RuntimeInitialize()
  25. {
  26. // To handle entering play mode without a domain reload, need to reset the state of the service manager.
  27. _hasQuit = false;
  28. }
  29. #endif
  30. /// <summary>
  31. /// Register the assembly that contains type <typeparamref name="TType"/> with the service manager.
  32. /// </summary>
  33. /// <typeparam name="TType"></typeparam>
  34. public static void RegisterAssembly<TType>()
  35. {
  36. #if NETFX_CORE
  37. var assembly = typeof(TType).GetTypeInfo().Assembly;
  38. #else
  39. var assembly = typeof(TType).Assembly;
  40. #endif
  41. if (_assemblies.Contains(assembly))
  42. {
  43. return;
  44. }
  45. _assemblies.Add(assembly);
  46. }
  47. /// <summary>
  48. /// Is there a service loading?
  49. /// </summary>
  50. public static bool IsLoading
  51. {
  52. get { return LoadingCount > 0; }
  53. }
  54. public static int LoadingCount = 0;
  55. public static T GetService<T>() where T : class
  56. {
  57. var s = GetServiceInternal(typeof(T)) as T;
  58. if (s == null && (!_hasQuit || EnableLogging))
  59. {
  60. Debug.LogWarning("Service {0} not found. (HasQuit: {1})".Fmt(typeof(T).Name, _hasQuit));
  61. }
  62. return s;
  63. }
  64. public static object GetService(Type t)
  65. {
  66. var s = GetServiceInternal(t);
  67. if (s == null && (!_hasQuit || EnableLogging))
  68. {
  69. Debug.LogWarning("Service {0} not found. (HasQuit: {1})".Fmt(t.Name, _hasQuit));
  70. }
  71. return s;
  72. }
  73. private static object GetServiceInternal(Type t)
  74. {
  75. if (_hasQuit || !Application.isPlaying)
  76. {
  77. return null;
  78. }
  79. var services = Instance._services;
  80. for (var i = 0; i < services.Count; i++)
  81. {
  82. var s = services[i];
  83. if (t.IsAssignableFrom(s.Type))
  84. {
  85. if (s.Object == null)
  86. {
  87. UnRegisterService(t);
  88. break;
  89. }
  90. return s.Object;
  91. }
  92. }
  93. return Instance.AutoCreateService(t);
  94. }
  95. public static bool HasService<T>() where T : class
  96. {
  97. return HasService(typeof(T));
  98. }
  99. public static bool HasService(Type t)
  100. {
  101. if (_hasQuit || !Application.isPlaying)
  102. {
  103. return false;
  104. }
  105. var services = Instance._services;
  106. for (var i = 0; i < services.Count; i++)
  107. {
  108. var s = services[i];
  109. if (t.IsAssignableFrom(s.Type))
  110. {
  111. return s.Object != null;
  112. }
  113. }
  114. return false;
  115. }
  116. public static void RegisterService<T>(object service) where T : class
  117. {
  118. RegisterService(typeof(T), service);
  119. }
  120. private static void RegisterService(Type t, object service)
  121. {
  122. if (_hasQuit)
  123. {
  124. return;
  125. }
  126. if (HasService(t))
  127. {
  128. if (GetServiceInternal(t) == service)
  129. {
  130. return;
  131. }
  132. throw new Exception("Service already registered for type " + t.Name);
  133. }
  134. UnRegisterService(t);
  135. if (!t.IsInstanceOfType(service))
  136. {
  137. throw new ArgumentException("service {0} must be assignable from type {1}".Fmt(service.GetType(), t));
  138. }
  139. Instance._services.Add(new Service {
  140. Object = service,
  141. Type = t
  142. });
  143. }
  144. public static void UnRegisterService<T>() where T : class
  145. {
  146. UnRegisterService(typeof(T));
  147. }
  148. private static void UnRegisterService(Type t)
  149. {
  150. if (_hasQuit || !HasInstance)
  151. {
  152. return;
  153. }
  154. if (!HasService(t))
  155. {
  156. return;
  157. }
  158. var services = Instance._services;
  159. for (var i = services.Count - 1; i >= 0; i--)
  160. {
  161. var s = services[i];
  162. if (s.Type == t)
  163. {
  164. services.RemoveAt(i);
  165. }
  166. }
  167. }
  168. private class Service
  169. {
  170. public object Object;
  171. public Type Type;
  172. }
  173. private class ServiceStub
  174. {
  175. public Func<object> Constructor;
  176. public Type InterfaceType;
  177. public Func<Type> Selector;
  178. public Type Type;
  179. public override string ToString()
  180. {
  181. var s = InterfaceType.Name + " (";
  182. if (Type != null)
  183. {
  184. s += "Type: " + Type;
  185. }
  186. else if (Selector != null)
  187. {
  188. s += "Selector: " + Selector;
  189. }
  190. else if (Constructor != null)
  191. {
  192. s += "Constructor: " + Constructor;
  193. }
  194. s += ")";
  195. return s;
  196. }
  197. }
  198. private static readonly List<Assembly> _assemblies = new List<Assembly>(2);
  199. private readonly SRList<Service> _services = new SRList<Service>();
  200. private List<ServiceStub> _serviceStubs;
  201. private static bool _hasQuit;
  202. protected override void Awake()
  203. {
  204. _hasQuit = false;
  205. base.Awake();
  206. DontDestroyOnLoad(CachedGameObject);
  207. CachedGameObject.hideFlags = HideFlags.NotEditable;
  208. }
  209. protected void UpdateStubs()
  210. {
  211. if (_serviceStubs != null)
  212. {
  213. return;
  214. }
  215. RegisterAssembly<SRServiceManager>();
  216. _serviceStubs = new List<ServiceStub>();
  217. var types = new List<Type>();
  218. foreach (var assembly in _assemblies)
  219. {
  220. try
  221. {
  222. #if NETFX_CORE
  223. types.AddRange(assembly.ExportedTypes);
  224. #else
  225. types.AddRange(assembly.GetExportedTypes());
  226. #endif
  227. }
  228. catch (Exception e)
  229. {
  230. Debug.LogError("[SRServiceManager] Error loading assembly {0}".Fmt(assembly.FullName), this);
  231. Debug.LogException(e);
  232. }
  233. }
  234. foreach (var type in types)
  235. {
  236. ScanType(type);
  237. }
  238. if (EnableLogging)
  239. {
  240. var serviceStrings =
  241. _serviceStubs.Select(p => " {0}".Fmt(p)).ToArray();
  242. Debug.Log("[SRServiceManager] Services Discovered: {0} \n {1}".Fmt(serviceStrings.Length,
  243. string.Join("\n ", serviceStrings)));
  244. }
  245. }
  246. protected object AutoCreateService(Type t)
  247. {
  248. UpdateStubs();
  249. foreach (var stub in _serviceStubs)
  250. {
  251. if (stub.InterfaceType != t)
  252. {
  253. continue;
  254. }
  255. object service = null;
  256. if (stub.Constructor != null)
  257. {
  258. service = stub.Constructor();
  259. }
  260. else
  261. {
  262. var serviceType = stub.Type;
  263. if (serviceType == null)
  264. {
  265. serviceType = stub.Selector();
  266. }
  267. service = DefaultServiceConstructor(t, serviceType);
  268. }
  269. if (!HasService(t))
  270. {
  271. RegisterService(t, service);
  272. }
  273. if (EnableLogging)
  274. {
  275. Debug.Log("[SRServiceManager] Auto-created service: {0} ({1})".Fmt(stub.Type, stub.InterfaceType),
  276. service as Object);
  277. }
  278. return service;
  279. }
  280. return null;
  281. }
  282. protected void OnApplicationQuit()
  283. {
  284. _hasQuit = true;
  285. _assemblies.Clear();
  286. }
  287. #if UNITY_EDITOR
  288. protected void OnDisable()
  289. {
  290. if (EnableLogging)
  291. {
  292. Debug.Log("[SRServiceManager] Cleaning up services");
  293. }
  294. // Script recompile is likely in progress - clear up everything.
  295. foreach (Service s in _services)
  296. {
  297. IDisposable disposable = s.Object as IDisposable;
  298. if (disposable != null)
  299. {
  300. disposable.Dispose();
  301. }
  302. Behaviour behaviour = s.Object as Behaviour;
  303. if (behaviour != null)
  304. {
  305. DestroyImmediate(behaviour.gameObject);
  306. } else if (s.Object is Object)
  307. {
  308. DestroyImmediate(s.Object as Object);
  309. }
  310. }
  311. _services.Clear(clean: true);
  312. }
  313. #endif
  314. private static object DefaultServiceConstructor(Type serviceIntType, Type implType)
  315. {
  316. // If mono-behaviour based, create a gameobject for this service
  317. if (typeof(MonoBehaviour).IsAssignableFrom(implType))
  318. {
  319. var go = new GameObject("_S_" + serviceIntType.Name);
  320. return go.AddComponent(implType);
  321. }
  322. // If ScriptableObject based, create an instance
  323. if (typeof(ScriptableObject).IsAssignableFrom(implType))
  324. {
  325. var obj = ScriptableObject.CreateInstance(implType);
  326. return obj;
  327. }
  328. // If just a standard C# object, just create an instance
  329. return Activator.CreateInstance(implType);
  330. }
  331. #region Type Scanning
  332. private void ScanType(Type type)
  333. {
  334. var attribute = SRReflection.GetAttribute<ServiceAttribute>(type);
  335. if (attribute != null)
  336. {
  337. _serviceStubs.Add(new ServiceStub {
  338. Type = type,
  339. InterfaceType = attribute.ServiceType
  340. });
  341. }
  342. ScanTypeForConstructors(type, _serviceStubs);
  343. ScanTypeForSelectors(type, _serviceStubs);
  344. }
  345. private static void ScanTypeForSelectors(Type t, List<ServiceStub> stubs)
  346. {
  347. var methods = GetStaticMethods(t);
  348. foreach (var method in methods)
  349. {
  350. var attrib = SRReflection.GetAttribute<ServiceSelectorAttribute>(method);
  351. if (attrib == null)
  352. {
  353. continue;
  354. }
  355. if (method.ReturnType != typeof(Type))
  356. {
  357. Debug.LogError("ServiceSelector must have return type of Type ({0}.{1}())".Fmt(t.Name, method.Name));
  358. continue;
  359. }
  360. if (method.GetParameters().Length > 0)
  361. {
  362. Debug.LogError("ServiceSelector must have no parameters ({0}.{1}())".Fmt(t.Name, method.Name));
  363. continue;
  364. }
  365. var stub = stubs.FirstOrDefault(p => p.InterfaceType == attrib.ServiceType);
  366. if (stub == null)
  367. {
  368. stub = new ServiceStub {
  369. InterfaceType = attrib.ServiceType
  370. };
  371. stubs.Add(stub);
  372. }
  373. #if NETFX_CORE
  374. stub.Selector = (Func<Type>)method.CreateDelegate(typeof(Func<Type>));
  375. #else
  376. stub.Selector = (Func<Type>)Delegate.CreateDelegate(typeof(Func<Type>), method);
  377. #endif
  378. }
  379. }
  380. private static void ScanTypeForConstructors(Type t, List<ServiceStub> stubs)
  381. {
  382. var methods = GetStaticMethods(t);
  383. foreach (var method in methods)
  384. {
  385. var attrib = SRReflection.GetAttribute<ServiceConstructorAttribute>(method);
  386. if (attrib == null)
  387. {
  388. continue;
  389. }
  390. if (method.ReturnType != attrib.ServiceType)
  391. {
  392. Debug.LogError("ServiceConstructor must have return type of {2} ({0}.{1}())".Fmt(t.Name, method.Name,
  393. attrib.ServiceType));
  394. continue;
  395. }
  396. if (method.GetParameters().Length > 0)
  397. {
  398. Debug.LogError("ServiceConstructor must have no parameters ({0}.{1}())".Fmt(t.Name, method.Name));
  399. continue;
  400. }
  401. var stub = stubs.FirstOrDefault(p => p.InterfaceType == attrib.ServiceType);
  402. if (stub == null)
  403. {
  404. stub = new ServiceStub {
  405. InterfaceType = attrib.ServiceType
  406. };
  407. stubs.Add(stub);
  408. }
  409. var m = method;
  410. stub.Constructor = () => m.Invoke(null, null);
  411. }
  412. }
  413. #endregion
  414. #region Reflection
  415. private static MethodInfo[] GetStaticMethods(Type t)
  416. {
  417. #if !NETFX_CORE
  418. return t.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
  419. #else
  420. return t.GetTypeInfo().DeclaredMethods.Where(p => p.IsStatic).ToArray();
  421. #endif
  422. }
  423. #endregion
  424. }
  425. }