using System; using System.Buffers; using System.Collections.Generic; namespace AlicizaX { public static class ModuleSystem { internal const int DesignModuleCount = 32; private static readonly Dictionary _moduleMaps = new Dictionary(DesignModuleCount); private static readonly GameFrameworkLinkedList _modules = new GameFrameworkLinkedList(); private static readonly GameFrameworkLinkedList _updateModules = new GameFrameworkLinkedList(); private static readonly IModuleUpdate[] _updateExecuteArray = new IModuleUpdate[DesignModuleCount]; private static readonly GameFrameworkLinkedList _lateUpdateModules = new GameFrameworkLinkedList(); private static readonly IModuleLateUpdate[] _lateUpdateExecuteArray = new IModuleLateUpdate[DesignModuleCount]; private static readonly GameFrameworkLinkedList _fixedUpdateModules = new GameFrameworkLinkedList(); private static readonly IModuleFixedUpdate[] _fixedUpdateExecuteArray = new IModuleFixedUpdate[DesignModuleCount]; private static readonly GameFrameworkLinkedList _gizmosUpdateModules = new GameFrameworkLinkedList(); private static readonly IModuleDrawGizmos[] _gizmosUpdateExecuteArray = new IModuleDrawGizmos[DesignModuleCount]; private static readonly GameFrameworkLinkedList _guiUpdateModules = new GameFrameworkLinkedList(); private static readonly IModuleGUI[] _guiUpdateExecuteArray = new IModuleGUI[DesignModuleCount]; private static bool _isExecuteListDirty; private static bool _isLateExecuteListDirty; private static bool _isFixedExecuteListDirty; private static bool _isGizmosExecuteListDirty; private static bool _isGUIExecuteListDirty; internal static void Dispose() { _updateModules.Clear(); Array.Clear(_updateExecuteArray, 0, _updateExecuteArray.Length); _lateUpdateModules.Clear(); Array.Clear(_lateUpdateExecuteArray, 0, _lateUpdateExecuteArray.Length); _fixedUpdateModules.Clear(); Array.Clear(_fixedUpdateExecuteArray, 0, _fixedUpdateExecuteArray.Length); _gizmosUpdateModules.Clear(); Array.Clear(_gizmosUpdateExecuteArray, 0, _gizmosUpdateExecuteArray.Length); _guiUpdateModules.Clear(); Array.Clear(_guiUpdateExecuteArray, 0, _guiUpdateExecuteArray.Length); for (LinkedListNode current = _modules.Last; current != null; current = current.Previous) { current.Value.Dispose(); } _modules.Clear(); _moduleMaps.Clear(); } #region Public Manager public static T RegisterManager() where T : AManager, new() { T manager = new T(); if (manager is IModuleAwake awakeManager) { awakeManager.Awake(); } AddManager(manager); return manager; } private static void AddManager(AManager manager) { Type type = manager.GetType(); _moduleMaps[type] = manager; _modules.AddLast(manager); manager.Register(); CheckSystemLife(manager); } #endregion #region Public System public static T RegisterModule(Type implType) { return (T)RegisterModule(typeof(T), implType); } public static IModule RegisterModule(Type interfaceType, Type implType) { if (!interfaceType.IsInterface) { throw new GameFrameworkException(Utility.Text.Format("You must register module by interface, but '{0}' is not.", interfaceType.FullName)); } if (!implType.IsClass || implType.IsInterface || implType.IsAbstract) { throw new GameFrameworkException(Utility.Text.Format("You must register module by Class and not Interface and Abstract, but '{0}' is not.", implType.FullName)); } if (!typeof(IModule).IsAssignableFrom(interfaceType)) { throw new GameFrameworkException(Utility.Text.Format("You must register module by Class and not Interface and Abstract, but '{0}' is not.", implType.FullName)); } if (GetModule(interfaceType) != null) { Log.Error("Already Register {0}", interfaceType.FullName); return default; } return SetModuleInstance(interfaceType, implType); } public static T GetModule() where T : class { Type interfaceType = typeof(T); if (!interfaceType.IsInterface) { throw new GameFrameworkException(Utility.Text.Format("You must get module by interface, but '{0}' is not.", interfaceType.FullName)); } return GetModule(interfaceType) as T; } public static IModule GetModule(Type moduleType) { if (_moduleMaps.TryGetValue(moduleType, out var ret)) { return ret; } return default; } private static IModule SetModuleInstance(Type interfaceType, Type implType) { IModule module = (IModule)Activator.CreateInstance(implType); _moduleMaps[interfaceType] = module; _modules.AddLast(module); if (module is IModuleAwake awakeSystem) { awakeSystem.Awake(); } CheckSystemLife(module); return module; } #endregion private static void CheckSystemLife(IModule module) { if (module is IModuleUpdate updateSystem) { BindSystemLife(updateSystem, _updateModules, ref _isExecuteListDirty); } if (module is IModuleLateUpdate lateUpdate) { BindSystemLife(lateUpdate, _lateUpdateModules, ref _isLateExecuteListDirty); } if (module is IModuleFixedUpdate fixedUpdate) { BindSystemLife(fixedUpdate, _fixedUpdateModules, ref _isFixedExecuteListDirty); } if (module is IModuleDrawGizmos drawGizmosUpdate) { BindSystemLife(drawGizmosUpdate, _gizmosUpdateModules, ref _isGizmosExecuteListDirty); } if (module is IModuleGUI guiUpdate) { BindSystemLife(guiUpdate, _guiUpdateModules, ref _isGUIExecuteListDirty); } } internal static void BindSystemLife(T system, GameFrameworkLinkedList updateModule, ref bool executeDirty) where T : IExecuteSystem { LinkedListNode currentUpdate = updateModule.First; while (currentUpdate != null) { if (system.Priority > currentUpdate.Value.Priority) { break; } currentUpdate = currentUpdate.Next; } if (currentUpdate != null) { updateModule.AddBefore(currentUpdate, system); } else { updateModule.AddLast(system); } executeDirty = true; } #region BuildExecuteList private static void BuildExecuteList() { if (_isExecuteListDirty) { _isExecuteListDirty = false; Array.Clear(_updateExecuteArray, 0, _updateExecuteArray.Length); int index = 0; foreach (var module in _updateModules) { _updateExecuteArray[index++] = module; } } } private static void BuildLateExecuteList() { if (_isLateExecuteListDirty) { _isLateExecuteListDirty = false; Array.Clear(_lateUpdateExecuteArray, 0, _lateUpdateExecuteArray.Length); int index = 0; foreach (var module in _lateUpdateModules) { _lateUpdateExecuteArray[index++] = module; } } } private static void BuildFixedExecuteList() { if (_isFixedExecuteListDirty) { _isFixedExecuteListDirty = false; Array.Clear(_fixedUpdateExecuteArray, 0, _fixedUpdateExecuteArray.Length); int index = 0; foreach (var module in _fixedUpdateModules) { _fixedUpdateExecuteArray[index++] = module; } } } private static void BuildGizmosExecuteList() { if (_isGizmosExecuteListDirty) { _isGizmosExecuteListDirty = false; Array.Clear(_gizmosUpdateExecuteArray, 0, _gizmosUpdateExecuteArray.Length); int index = 0; foreach (var module in _gizmosUpdateModules) { _gizmosUpdateExecuteArray[index++] = module; } } } private static void BuildGUIExecuteList() { if (_isGUIExecuteListDirty) { _isGUIExecuteListDirty = false; Array.Clear(_guiUpdateExecuteArray, 0, _guiUpdateExecuteArray.Length); int index = 0; foreach (var module in _guiUpdateModules) { _guiUpdateExecuteArray[index++] = module; } } } #endregion #region RunExecuteList internal static void UpdateExecuteList(float elapseSeconds, float realElapseSeconds) { BuildExecuteList(); int executeCount = _updateExecuteArray.Length; for (int i = 0; i < executeCount; i++) { _updateExecuteArray[i]?.Update(elapseSeconds, realElapseSeconds); } } internal static void UpdateLateExecuteList() { BuildLateExecuteList(); int executeCount = _lateUpdateExecuteArray.Length; for (int i = 0; i < executeCount; i++) { _lateUpdateExecuteArray[i]?.LateUpdate(); } } internal static void UpdateFixedExecuteList() { BuildFixedExecuteList(); int executeCount = _fixedUpdateExecuteArray.Length; for (int i = 0; i < executeCount; i++) { _fixedUpdateExecuteArray[i]?.FixedUpdate(); } } internal static void UpdateGizmosExecuteList() { BuildGizmosExecuteList(); int executeCount = _gizmosUpdateExecuteArray.Length; for (int i = 0; i < executeCount; i++) { _gizmosUpdateExecuteArray[i]?.DrawGizmos(); } } internal static void UpdateGUIExecuteList() { BuildGUIExecuteList(); int executeCount = _guiUpdateExecuteArray.Length; for (int i = 0; i < executeCount; i++) { _guiUpdateExecuteArray[i]?.OnGUI(); } } #endregion } }