using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using Cysharp.Threading.Tasks; using UnityEngine; namespace DanieleMarotta.RiversongCodeShowcase { public class EngineRunner : MonoBehaviour, IEngine { private ServiceLocator _serviceLocator; private RootGameSystemGroup _rootSystemGroup; private List _systemGroups = new(); private Dictionary _systemGroupsByType = new(); private EngineUpdateFilter _updateFilter = new(); public bool IsInitialized { get; private set; } public List Systems { get; } = new(); public void Start() { StartAsync().Forget(Debug.LogException); } private async UniTask StartAsync() { Debug.Log("Engine started"); _serviceLocator = new ServiceLocator(); _serviceLocator.RegisterService(typeof(IEngine), this); var serviceProviders = GetComponentsInChildren(); foreach (var serviceProvider in serviceProviders) serviceProvider.RegisterServices(_serviceLocator); await UniTask.NextFrame(); _rootSystemGroup = new RootGameSystemGroup(); _systemGroups.Add(_rootSystemGroup); _systemGroupsByType.Add(_rootSystemGroup.GetType(), _rootSystemGroup); Debug.Log("Systems discovery started"); DiscoverSystems(); Debug.Log($"Systems discovery completed. Discovered {_rootSystemGroup.Systems.Count} system groups and {Systems.Count} systems"); await UniTask.NextFrame(); foreach (var systemGroup in _systemGroups) _serviceLocator.Inject(systemGroup); foreach (var system in Systems) _serviceLocator.Inject(system); await UniTask.NextFrame(); var types = Systems.Select(obj => obj.GetType()).Concat(_systemGroups.Select(obj => obj.GetType())).Distinct().ToList(); SystemSorter.InitializeSorters(types); await UniTask.NextFrame(); Debug.Log("Systems initialization started"); await InitializeAsync(); Debug.Log("Systems initialization completed"); IsInitialized = true; } private void DiscoverSystems() { var assemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach (var assembly in assemblies) foreach (var type in assembly.GetTypes()) { if (type.IsAbstract || !type.IsSubclassOf(typeof(GameSystemGroup)) || type.GetCustomAttribute() != null) continue; var systemGroup = (GameSystemGroup)Activator.CreateInstance(type); systemGroup.UpdateFilter = _updateFilter; _systemGroups.Add(systemGroup); _systemGroupsByType.Add(type, systemGroup); } foreach (var systemGroup in _systemGroups) { if (systemGroup == _rootSystemGroup) continue; var parent = GetContainingSystemGroup(systemGroup.GetType(), typeof(RootGameSystemGroup)); parent.Add(systemGroup); } foreach (var assembly in assemblies) foreach (var type in assembly.GetTypes()) { if (type.IsAbstract || !type.IsSubclassOf(typeof(GameSystem)) || type.GetCustomAttribute() != null) continue; var system = CreateSystem(type); Systems.Add(system); var systemGroup = GetContainingSystemGroup(type, typeof(DefaultGameSystemGroup)); systemGroup.Add(system); } } private GameSystem CreateSystem(Type systemType) { var system = (GameSystem)Activator.CreateInstance(systemType, _serviceLocator); var serviceAttributes = systemType.GetCustomAttributes(); foreach (var serviceAttribute in serviceAttributes) { var serviceType = serviceAttribute?.ServiceType ?? systemType; _serviceLocator.RegisterService(serviceType, system); } if (system is IServiceProvider serviceProvider) serviceProvider.RegisterServices(_serviceLocator); return system; } private GameSystemGroup GetContainingSystemGroup(Type type, Type defaultGroup) { var systemGroupAttribute = type.GetCustomAttribute(); var systemGroupType = systemGroupAttribute?.SystemGroupType ?? defaultGroup; return _systemGroupsByType[systemGroupType]; } private async UniTask InitializeAsync() { await _rootSystemGroup.InitializeAsync(); } private void Update() { if (!IsInitialized) return; _rootSystemGroup.Update(); } private void OnDestroy() { Debug.Log("Systems disposing started"); _rootSystemGroup.Dispose(); Debug.Log("Systems disposing completed"); } public void RegisterUpdateFilter(IUpdateFilter filter) { _updateFilter.Filters.Add(filter); } } }