DragonECS/src/EcsPipeline.cs

269 lines
8.8 KiB
C#
Raw Normal View History

2023-03-26 11:19:03 +08:00
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Runtime.CompilerServices;
namespace DCFApixels.DragonECS
{
2023-03-30 05:32:43 +08:00
public sealed class EcsPipeline
2023-03-26 11:19:03 +08:00
{
private IEcsSystem[] _allSystems;
private Dictionary<Type, IEcsRunner> _runners;
private IEcsRunSystem _runRunnerCache;
private ReadOnlyCollection<IEcsSystem> _allSystemsSealed;
private ReadOnlyDictionary<Type, IEcsRunner> _allRunnersSealed;
private bool _isInit;
2023-03-26 11:19:03 +08:00
private bool _isDestoryed;
#region Properties
public ReadOnlyCollection<IEcsSystem> AllSystems => _allSystemsSealed;
public ReadOnlyDictionary<Type, IEcsRunner> AllRunners => _allRunnersSealed;
public bool IsDestoryed => _isDestoryed;
#endregion
#region Constructors
2023-03-30 05:32:43 +08:00
private EcsPipeline(IEcsSystem[] systems)
2023-03-26 11:19:03 +08:00
{
_allSystems = systems;
_runners = new Dictionary<Type, IEcsRunner>();
_allSystemsSealed = new ReadOnlyCollection<IEcsSystem>(_allSystems);
_allRunnersSealed = new ReadOnlyDictionary<Type, IEcsRunner>(_runners);
_isInit = false;
2023-03-26 11:19:03 +08:00
_isDestoryed = false;
}
#endregion
#region Runners
public T GetRunner<T>() where T : IEcsSystem
{
Type type = typeof(T);
if (_runners.TryGetValue(type, out IEcsRunner result))
return (T)result;
2023-03-29 15:09:00 +08:00
result = (IEcsRunner)EcsRunner<T>.Instantiate(this);
2023-03-26 11:19:03 +08:00
_runners.Add(type, result);
return (T)result;
}
2023-03-30 05:32:43 +08:00
internal void OnRunnerDestroy(IEcsRunner runner)
{
_runners.Remove(runner.Interface);
}
2023-03-26 11:19:03 +08:00
#endregion
#region LifeCycle
public void Init()
{
if(_isInit == true)
{
2023-03-30 05:32:43 +08:00
EcsDebug.Print("[Warning]", $"This {nameof(EcsPipeline)} has already been initialized");
return;
}
_isInit = true;
2023-03-30 11:17:17 +08:00
var ecsPipelineInjectRunner = GetRunner<IEcsInject<EcsPipeline>>();
ecsPipelineInjectRunner.Inject(this);
EcsRunner.Destroy(ecsPipelineInjectRunner);
var preInitRunner = GetRunner<IEcsPreInitSystem>();
preInitRunner.PreInit(this);
EcsRunner.Destroy(preInitRunner);
var initRunner = GetRunner<IEcsInitSystem>();
initRunner.Init(this);
EcsRunner.Destroy(initRunner);
_runRunnerCache = GetRunner<IEcsRunSystem>();
}
2023-03-26 11:19:03 +08:00
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Run()
{
#if DEBUG || !DRAGONECS_NO_SANITIZE_CHECKS
CheckBeforeInitForMethod(nameof(Run));
2023-03-26 11:19:03 +08:00
CheckAfterDestroyForMethod(nameof(Run));
#endif
_runRunnerCache.Run(this);
}
public void Destroy()
{
#if DEBUG || !DRAGONECS_NO_SANITIZE_CHECKS
CheckBeforeInitForMethod(nameof(Run));
2023-03-26 11:19:03 +08:00
#endif
if (_isDestoryed == true)
{
2023-03-30 05:32:43 +08:00
EcsDebug.Print("[Warning]", $"This {nameof(EcsPipeline)} has already been destroyed");
return;
}
2023-03-26 11:19:03 +08:00
_isDestoryed = true;
GetRunner<IEcsDestroySystem>().Destroy(this);
}
#endregion
#region StateChecks
#if DEBUG || !DRAGONECS_NO_SANITIZE_CHECKS
private void CheckBeforeInitForMethod(string methodName)
{
if (!_isInit)
2023-03-30 05:32:43 +08:00
throw new MethodAccessException($"It is forbidden to call {methodName}, before initialization {nameof(EcsPipeline)}");
}
private void CheckAfterInitForMethod(string methodName)
{
if (_isInit)
2023-03-30 05:32:43 +08:00
throw new MethodAccessException($"It is forbidden to call {methodName}, after initialization {nameof(EcsPipeline)}");
}
2023-03-26 11:19:03 +08:00
private void CheckAfterDestroyForMethod(string methodName)
{
if (_isDestoryed)
2023-03-30 05:32:43 +08:00
throw new MethodAccessException($"It is forbidden to call {methodName}, after destroying {nameof(EcsPipeline)}");
2023-03-26 11:19:03 +08:00
}
#endif
#endregion
#region Builder
public static Builder New()
{
return new Builder();
}
public class Builder
{
private const int KEYS_CAPACITY = 4;
2023-03-30 05:32:43 +08:00
private HashSet<Type> _uniqueTypes;
2023-03-26 11:19:03 +08:00
private readonly List<object> _blockExecutionOrder;
private readonly Dictionary<object, List<IEcsSystem>> _systems;
private readonly object _basicBlocKey;
private bool _isBasicBlockDeclared;
private bool _isOnlyBasicBlock;
public Builder()
{
2023-03-27 20:31:45 +08:00
_basicBlocKey = "Basic";
2023-03-30 05:32:43 +08:00
_uniqueTypes = new HashSet<Type>();
2023-03-26 11:19:03 +08:00
_blockExecutionOrder = new List<object>(KEYS_CAPACITY);
_systems = new Dictionary<object, List<IEcsSystem>>(KEYS_CAPACITY);
_isBasicBlockDeclared = false;
_isOnlyBasicBlock = true;
}
public Builder Add(IEcsSystem system, object blockKey = null)
2023-03-30 05:32:43 +08:00
{
AddInternal(system, blockKey, false);
return this;
}
public Builder AddUnique(IEcsSystem system, object blockKey = null)
{
AddInternal(system, blockKey, true);
return this;
}
private void AddInternal(IEcsSystem system, object blockKey, bool isUnique)
2023-03-26 11:19:03 +08:00
{
if (blockKey == null) blockKey = _basicBlocKey;
List<IEcsSystem> list;
if (!_systems.TryGetValue(blockKey, out list))
{
list = new List<IEcsSystem>();
2023-03-27 20:31:45 +08:00
list.Add(new SystemsBlockMarkerSystem(blockKey.ToString()));
2023-03-26 11:19:03 +08:00
_systems.Add(blockKey, list);
}
2023-03-30 05:32:43 +08:00
if ((_uniqueTypes.Add(system.GetType()) == false && isUnique))
return;
2023-03-26 11:19:03 +08:00
list.Add(system);
}
public Builder Add(IEcsModule module)
{
module.ImportSystems(this);
return this;
}
public Builder BasicSystemsBlock()
{
_isBasicBlockDeclared = true;
_blockExecutionOrder.Add(_basicBlocKey);
return this;
}
public Builder SystemsBlock(object blockKey)
{
if (blockKey == null)
return BasicSystemsBlock();
_isOnlyBasicBlock = false;
_blockExecutionOrder.Add(blockKey);
return this;
}
2023-03-30 05:32:43 +08:00
public EcsPipeline Build()
2023-03-26 11:19:03 +08:00
{
if (_isOnlyBasicBlock)
{
2023-03-30 05:32:43 +08:00
return new EcsPipeline(_systems[_basicBlocKey].ToArray());
2023-03-26 11:19:03 +08:00
}
if(_isBasicBlockDeclared == false)
_blockExecutionOrder.Insert(0, _basicBlocKey);
List<IEcsSystem> result = new List<IEcsSystem>(32);
List<IEcsSystem> basicBlockList = _systems[_basicBlocKey];
foreach (var item in _systems)
{
if (!_blockExecutionOrder.Contains(item.Key))
{
basicBlockList.AddRange(item.Value);
}
}
foreach (var item in _blockExecutionOrder)
{
2023-03-29 19:40:18 +08:00
if(_systems.TryGetValue(item, out var list))
{
result.AddRange(list);
}
2023-03-26 11:19:03 +08:00
}
2023-03-30 05:32:43 +08:00
return new EcsPipeline(result.ToArray());
2023-03-26 11:19:03 +08:00
}
}
#endregion
}
public interface IEcsModule
{
2023-03-30 05:32:43 +08:00
public void ImportSystems(EcsPipeline.Builder builder);
2023-03-26 11:19:03 +08:00
}
2023-03-30 05:32:43 +08:00
#region Extensions
public static class EcsSystemsExtensions
2023-03-26 11:19:03 +08:00
{
2023-03-30 05:32:43 +08:00
public static bool IsNullOrDestroyed(this EcsPipeline self)
2023-03-26 11:19:03 +08:00
{
return self == null || self.IsDestoryed;
}
2023-03-30 05:32:43 +08:00
public static EcsPipeline.Builder Add(this EcsPipeline.Builder self, IEnumerable<IEcsSystem> range, object blockKey = null)
2023-03-26 11:19:03 +08:00
{
foreach (var item in range)
{
self.Add(item, blockKey);
}
return self;
}
2023-03-30 05:32:43 +08:00
public static EcsPipeline.Builder AddUnique(this EcsPipeline.Builder self, IEnumerable<IEcsSystem> range, object blockKey = null)
{
foreach (var item in range)
{
self.AddUnique(item, blockKey);
}
return self;
}
public static EcsPipeline BuildAndInit(this EcsPipeline.Builder self)
{
2023-03-30 05:32:43 +08:00
EcsPipeline result = self.Build();
result.Init();
return result;
}
2023-03-26 11:19:03 +08:00
}
2023-03-30 05:32:43 +08:00
#endregion
2023-03-26 11:19:03 +08:00
}