Files
riversong-code-showcase/Source/Engine/Core/EngineRunner.cs
2026-05-21 16:04:49 +02:00

155 lines
5.2 KiB
C#

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<GameSystemGroup> _systemGroups = new();
private Dictionary<Type, GameSystemGroup> _systemGroupsByType = new();
private EngineUpdateFilter _updateFilter = new();
public bool IsInitialized { get; private set; }
public List<IGameSystem> 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<IServiceProvider>();
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<DisableDiscoveryAttribute>() != 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<DisableDiscoveryAttribute>() != 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<ServiceAttribute>();
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<GameSystemGroupAttribute>();
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);
}
}
}