DragonECS/src/EcsPipeline.cs

318 lines
13 KiB
C#
Raw Normal View History

2023-05-26 04:31:31 +08:00
using DCFApixels.DragonECS.Internal;
using DCFApixels.DragonECS.RunnersCore;
2023-04-23 17:55:01 +08:00
using System;
using System.Collections;
2023-03-26 11:19:03 +08:00
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
2023-03-26 11:19:03 +08:00
using System.Runtime.CompilerServices;
2023-06-26 01:57:50 +08:00
using static DCFApixels.DragonECS.EcsThrowHalper;
2023-03-26 11:19:03 +08:00
namespace DCFApixels.DragonECS
{
2023-03-30 05:32:43 +08:00
public sealed class EcsPipeline
2023-03-26 11:19:03 +08:00
{
2023-05-30 18:27:30 +08:00
private IEcsProcess[] _allSystems;
2023-03-26 11:19:03 +08:00
private Dictionary<Type, IEcsRunner> _runners;
private IEcsRunProcess _runRunnerCache;
2023-03-26 11:19:03 +08:00
2023-05-30 18:27:30 +08:00
private ReadOnlyCollection<IEcsProcess> _allSystemsSealed;
2023-03-26 11:19:03 +08:00
private ReadOnlyDictionary<Type, IEcsRunner> _allRunnersSealed;
private bool _isInit;
2023-03-26 11:19:03 +08:00
private bool _isDestoryed;
#region Properties
2023-05-30 18:27:30 +08:00
public ReadOnlyCollection<IEcsProcess> AllSystems => _allSystemsSealed;
2023-03-26 11:19:03 +08:00
public ReadOnlyDictionary<Type, IEcsRunner> AllRunners => _allRunnersSealed;
2023-04-01 20:45:37 +08:00
public bool IsInit => _isInit;
2023-03-26 11:19:03 +08:00
public bool IsDestoryed => _isDestoryed;
#endregion
#region Constructors
2023-05-30 18:27:30 +08:00
private EcsPipeline(IEcsProcess[] systems)
2023-03-26 11:19:03 +08:00
{
_allSystems = systems;
_runners = new Dictionary<Type, IEcsRunner>();
2023-05-30 18:27:30 +08:00
_allSystemsSealed = new ReadOnlyCollection<IEcsProcess>(_allSystems);
2023-03-26 11:19:03 +08:00
_allRunnersSealed = new ReadOnlyDictionary<Type, IEcsRunner>(_runners);
_isInit = false;
2023-03-26 11:19:03 +08:00
_isDestoryed = false;
}
#endregion
#region Runners
2023-05-30 18:27:30 +08:00
public T GetRunner<T>() where T : IEcsProcess
2023-03-26 11:19:03 +08:00
{
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()
{
2023-04-01 20:45:37 +08:00
if (_isInit == true)
{
2023-03-30 05:32:43 +08:00
EcsDebug.Print("[Warning]", $"This {nameof(EcsPipeline)} has already been initialized");
return;
}
2023-03-30 11:17:17 +08:00
var ecsPipelineInjectRunner = GetRunner<IEcsInject<EcsPipeline>>();
ecsPipelineInjectRunner.Inject(this);
EcsRunner.Destroy(ecsPipelineInjectRunner);
var preInitRunner = GetRunner<IEcsPreInitProcess>();
2023-03-30 11:17:17 +08:00
preInitRunner.PreInit(this);
EcsRunner.Destroy(preInitRunner);
var initRunner = GetRunner<IEcsInitProcess>();
2023-03-30 11:17:17 +08:00
initRunner.Init(this);
EcsRunner.Destroy(initRunner);
_runRunnerCache = GetRunner<IEcsRunProcess>();
2023-06-10 19:46:35 +08:00
_isInit = true;
}
2023-03-26 11:19:03 +08:00
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Run()
{
2023-06-02 01:20:46 +08:00
#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS
2023-06-26 01:57:50 +08:00
if (_isInit) Throw.Pipeline_MethodCalledBeforeInitialisation(nameof(Run));
if (_isDestoryed) Throw.Pipeline_MethodCalledAfterDestruction(nameof(Run));
2023-03-26 11:19:03 +08:00
#endif
_runRunnerCache.Run(this);
}
public void Destroy()
{
2023-06-02 01:20:46 +08:00
#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS
2023-06-26 01:57:50 +08:00
if (_isInit) Throw.Pipeline_MethodCalledBeforeInitialisation(nameof(Destroy));
2023-03-26 11:19:03 +08:00
#endif
if (_isDestoryed == true)
{
2023-06-26 01:57:50 +08:00
EcsDebug.PrintWarning($"This {nameof(EcsPipeline)} has already been destroyed");
return;
}
2023-03-26 11:19:03 +08:00
_isDestoryed = true;
GetRunner<IEcsDestroyProcess>().Destroy(this);
2023-03-26 11:19:03 +08:00
}
#endregion
#region Builder
2023-05-28 06:35:33 +08:00
public static Builder New() => new Builder();
2023-03-26 11:19:03 +08:00
public class Builder
{
private const int KEYS_CAPACITY = 4;
2023-03-30 05:32:43 +08:00
private HashSet<Type> _uniqueTypes;
2023-05-30 18:27:30 +08:00
private readonly Dictionary<string, List<IEcsProcess>> _systems;
private readonly string _basicLayer;
public readonly LayerList Layers;
2023-03-26 11:19:03 +08:00
public Builder()
{
_basicLayer = EcsConsts.BASIC_LAYER;
Layers = new LayerList(this, _basicLayer);
Layers.Insert(EcsConsts.BASIC_LAYER, EcsConsts.PRE_BEGIN_LAYER, EcsConsts.BEGIN_LAYER);
Layers.InsertAfter(EcsConsts.BASIC_LAYER, EcsConsts.END_LAYER, EcsConsts.POST_END_LAYER);
_uniqueTypes = new HashSet<Type>();
2023-05-30 18:27:30 +08:00
_systems = new Dictionary<string, List<IEcsProcess>>(KEYS_CAPACITY);
2023-03-26 11:19:03 +08:00
}
2023-05-30 18:27:30 +08:00
public Builder Add(IEcsProcess system, string layerName = null)
2023-03-30 05:32:43 +08:00
{
AddInternal(system, layerName, false);
2023-03-30 05:32:43 +08:00
return this;
}
2023-05-30 18:27:30 +08:00
public Builder AddUnique(IEcsProcess system, string layerName = null)
2023-03-30 05:32:43 +08:00
{
AddInternal(system, layerName, true);
2023-03-30 05:32:43 +08:00
return this;
}
2023-05-23 01:47:28 +08:00
public Builder Remove<TSystem>()
{
_uniqueTypes.Remove(typeof(TSystem));
foreach (var list in _systems.Values)
list.RemoveAll(o => o is TSystem);
return this;
}
2023-05-30 18:27:30 +08:00
private void AddInternal(IEcsProcess system, string layerName, bool isUnique)
2023-03-26 11:19:03 +08:00
{
if (layerName == null) layerName = _basicLayer;
2023-05-30 18:27:30 +08:00
List<IEcsProcess> list;
if (!_systems.TryGetValue(layerName, out list))
2023-03-26 11:19:03 +08:00
{
2023-05-30 18:27:30 +08:00
list = new List<IEcsProcess> { new SystemsLayerMarkerSystem(layerName.ToString()) };
_systems.Add(layerName, list);
2023-03-26 11:19:03 +08:00
}
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);
if (system is IEcsModule module)//если система одновременно явялется и системой и модулем то за один Add будет вызван Add и AddModule
AddModule(module);
2023-03-26 11:19:03 +08:00
}
public Builder AddModule(IEcsModule module)
2023-03-26 11:19:03 +08:00
{
2023-05-30 17:56:53 +08:00
module.Import(this);
2023-03-26 11:19:03 +08:00
return this;
}
public EcsPipeline Build()
2023-03-26 11:19:03 +08:00
{
Add(new DeleteEmptyEntitesSystem(), EcsConsts.POST_END_LAYER);
2023-05-30 18:27:30 +08:00
List<IEcsProcess> result = new List<IEcsProcess>(32);
List<IEcsProcess> basicBlockList = _systems[_basicLayer];
foreach (var item in _systems)
{
2023-05-30 00:13:05 +08:00
if (!Layers.Contains(item.Key))
basicBlockList.AddRange(item.Value);
}
foreach (var item in Layers)
{
2023-05-30 18:30:10 +08:00
if (_systems.TryGetValue(item, out var list))
result.AddRange(list);
}
return new EcsPipeline(result.ToArray());
}
public class LayerList : IEnumerable<string>
2023-03-26 11:19:03 +08:00
{
private const string ADD_LAYER = nameof(ADD_LAYER); // автоматический слой нужный только для метода Add
private Builder _source;
private List<string> _layers;
private string _basicLayerName;
public LayerList(Builder source, string basicLayerName)
2023-03-26 11:19:03 +08:00
{
_source = source;
_layers = new List<string>(16) { basicLayerName, ADD_LAYER };
2023-05-30 18:30:10 +08:00
_basicLayerName = basicLayerName;
2023-03-26 11:19:03 +08:00
}
public Builder Add(string newLayer) => Insert(ADD_LAYER, newLayer);
public Builder Insert(string targetLayer, string newLayer)
{
2023-05-30 00:13:05 +08:00
if (Contains(newLayer)) return _source;
2023-03-26 11:19:03 +08:00
int index = _layers.IndexOf(targetLayer);
if (index < 0)
throw new KeyNotFoundException($"Layer {targetLayer} not found");
_layers.Insert(index, newLayer);
return _source;
}
public Builder InsertAfter(string targetLayer, string newLayer)
{
2023-05-30 00:13:05 +08:00
if (Contains(newLayer)) return _source;
2023-03-26 11:19:03 +08:00
if (targetLayer == _basicLayerName) // нужно чтобы метод Add работал правильно. _basicLayerName и ADD_LAYER считается одним слоем, поэтому Before = _basicLayerName After = ADD_LAYER
targetLayer = ADD_LAYER;
2023-03-26 11:19:03 +08:00
int index = _layers.IndexOf(targetLayer);
if (index < 0)
throw new KeyNotFoundException($"Layer {targetLayer} not found");
if (++index >= _layers.Count)
_layers.Add(newLayer);
else
_layers.Insert(index, newLayer);
return _source;
}
public Builder Move(string targetLayer, string movingLayer)
2023-03-26 11:19:03 +08:00
{
_layers.Remove(movingLayer);
return Insert(targetLayer, movingLayer);
2023-03-26 11:19:03 +08:00
}
public Builder MoveAfter(string targetLayer, string movingLayer)
2023-03-26 11:19:03 +08:00
{
if (targetLayer == _basicLayerName) // нужно чтобы метод Add работал правильно. _basicLayerName и ADD_LAYER считается одним слоем, поэтому Before = _basicLayerName After = ADD_LAYER
targetLayer = ADD_LAYER;
_layers.Remove(movingLayer);
return InsertAfter(targetLayer, movingLayer);
2023-03-26 11:19:03 +08:00
}
public Builder Add(params string[] newLayers) => Insert(ADD_LAYER, newLayers);
public Builder Insert(string targetLayer, params string[] newLayers)
{
int index = _layers.IndexOf(targetLayer);
if (index < 0)
throw new KeyNotFoundException($"Layer {targetLayer} not found");
2023-05-30 00:13:05 +08:00
_layers.InsertRange(index, newLayers.Where(o => !Contains(o)));
return _source;
}
public Builder InsertAfter(string targetLayer, params string[] newLayers)
{
int index = _layers.IndexOf(targetLayer);
if (index < 0)
throw new KeyNotFoundException($"Layer {targetLayer} not found");
if (targetLayer == _basicLayerName) // нужно чтобы метод Add работал правильно. _basicLayerName и ADD_LAYER считается одним слоем, поэтому Before = _basicLayerName After = ADD_LAYER
targetLayer = ADD_LAYER;
if (++index >= _layers.Count)
2023-05-30 00:13:05 +08:00
_layers.AddRange(newLayers.Where(o => !Contains(o)));
else
2023-05-30 00:13:05 +08:00
_layers.InsertRange(index, newLayers.Where(o => !Contains(o)));
return _source;
}
public Builder Move(string targetLayer, params string[] movingLayers)
{
foreach (var movingLayer in movingLayers)
_layers.Remove(movingLayer);
return Insert(targetLayer, movingLayers);
}
public Builder MoveAfter(string targetLayer, params string[] movingLayers)
{
if (targetLayer == _basicLayerName) // нужно чтобы метод Add работал правильно. _basicLayerName и ADD_LAYER считается одним слоем, поэтому Before = _basicLayerName After = ADD_LAYER
targetLayer = ADD_LAYER;
foreach (var movingLayer in movingLayers)
_layers.Remove(movingLayer);
return InsertAfter(targetLayer, movingLayers);
}
2023-05-30 00:13:05 +08:00
public bool Contains(string layer) => _layers.Contains(layer);
public List<string>.Enumerator GetEnumerator() => _layers.GetEnumerator();
IEnumerator<string> IEnumerable<string>.GetEnumerator() => _layers.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => _layers.GetEnumerator();
2023-03-26 11:19:03 +08:00
}
}
#endregion
}
public interface IEcsModule
{
2023-05-30 17:56:53 +08:00
void Import(EcsPipeline.Builder b);
2023-03-26 11:19:03 +08:00
}
2023-03-30 05:32:43 +08:00
#region Extensions
2023-06-22 14:31:13 +08:00
public static partial class EcsPipelineExtensions
2023-03-26 11:19:03 +08:00
{
2023-05-26 00:24:38 +08:00
public static bool IsNullOrDestroyed(this EcsPipeline self) => self == null || self.IsDestoryed;
2023-05-30 18:27:30 +08:00
public static EcsPipeline.Builder Add(this EcsPipeline.Builder self, IEnumerable<IEcsProcess> range, string layerName = null)
2023-03-26 11:19:03 +08:00
{
2023-05-26 00:24:38 +08:00
foreach (var item in range) self.Add(item, layerName);
2023-03-26 11:19:03 +08:00
return self;
}
2023-05-30 18:27:30 +08:00
public static EcsPipeline.Builder AddUnique(this EcsPipeline.Builder self, IEnumerable<IEcsProcess> range, string layerName = null)
2023-03-30 05:32:43 +08:00
{
2023-05-26 00:24:38 +08:00
foreach (var item in range) self.AddUnique(item, layerName);
2023-03-30 05:32:43 +08:00
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
}