diff --git a/src/Builtin/BaseProcesses.cs b/src/Builtin/BaseProcesses.cs index 4e4bb4c..ba689b1 100644 --- a/src/Builtin/BaseProcesses.cs +++ b/src/Builtin/BaseProcesses.cs @@ -30,6 +30,13 @@ namespace DCFApixels.DragonECS { void Run(); } + [MetaName(nameof(RunFinally))] + [MetaColor(MetaColor.DragonRose)] + [MetaGroup(EcsConsts.PACK_GROUP, EcsConsts.PROCESSES_GROUP)] + public interface IEcsRunFinally : IEcsProcess + { + void RunFinally(); + } [MetaName(nameof(Destroy))] [MetaColor(MetaColor.DragonRose)] [MetaGroup(EcsConsts.PACK_GROUP, EcsConsts.PROCESSES_GROUP)] @@ -97,26 +104,45 @@ namespace DCFApixels.DragonECS.Internal [MetaID("2098527C9201F260C840BFD50BC7E0BA")] internal sealed class EcsRunRunner : EcsRunner, IEcsRun { + private readonly struct Pair + { + public readonly IEcsRun run; + public readonly IEcsRunFinally cleanup; + public Pair(IEcsRun run) + { + this.run = run; + cleanup = run as IEcsRunFinally; + } + } + private Pair[] _pairs; #if DEBUG && !DISABLE_DEBUG private EcsProfilerMarker[] _markers; +#endif protected override void OnSetup() { + _pairs = new Pair[Process.Length]; + for (int i = 0; i < Process.Length; i++) + { + _pairs[i] = new Pair(Process[i]); + } +#if DEBUG && !DISABLE_DEBUG _markers = new EcsProfilerMarker[Process.Length]; for (int i = 0; i < Process.Length; i++) { _markers[i] = new EcsProfilerMarker($"{Process[i].GetMeta().Name}.{nameof(Run)}"); } - } #endif + } public void Run() { #if DEBUG && !DISABLE_DEBUG - for (int i = 0, n = Process.Length < _markers.Length ? Process.Length : _markers.Length; i < n; i++) + for (int i = 0, n = _pairs.Length < _markers.Length ? _pairs.Length : _markers.Length; i < n; i++) { + var pair = _pairs[i]; _markers[i].Begin(); try { - Process[i].Run(); + pair.run.Run(); } catch (Exception e) { @@ -125,6 +151,10 @@ namespace DCFApixels.DragonECS.Internal #endif EcsDebug.PrintError(e); } + finally + { + pair.cleanup?.RunFinally(); + } _markers[i].End(); } #else diff --git a/src/EcsAspect.cs b/src/EcsAspect.cs index 4bbfbed..145cfc8 100644 --- a/src/EcsAspect.cs +++ b/src/EcsAspect.cs @@ -25,7 +25,63 @@ namespace DCFApixels.DragonECS public static implicit operator Singleton(SingletonMarker a) { return new Singleton(a.Builder.World.ID); } } - public abstract class EcsAspect : ITemplateNode, IComponentMask + public interface IEcsAspect + { + EcsMask Mask { get; set; } + } + + #region IEcsAspectExtensions tmp +// public static class IEcsAspectExtensions +// { +// public static void Apply(this IEcsAspect aspect, short worldID, int entityID) +// { +// EcsWorld world = EcsWorld.GetWorld(worldID); +// EcsMask mask = aspect.Mask; +// foreach (var incTypeID in mask._incs) +// { +// var pool = world.FindPoolInstance(incTypeID); +// if (pool != null) +// { +// if (pool.Has(entityID) == false) +// { +// pool.AddEmpty(entityID); +// } +// } +//#if DEBUG +// else +// { +// EcsDebug.PrintWarning("Component has not been added because the pool has not been initialized yet."); +// } +//#endif +// } +// foreach (var excTypeID in mask._excs) +// { +// var pool = world.FindPoolInstance(excTypeID); +// if (pool != null && pool.Has(entityID)) +// { +// pool.Del(entityID); +// } +// } +// } +// } + #endregion + + public static partial class API + { + public static IncludeMarker Inc + { + get { return EcsAspect.CurrentBuilder.Inc; } + } + public static ExcludeMarker Exc + { + get { return EcsAspect.CurrentBuilder.Exc; } + } + public static OptionalMarker Opt + { + get { return EcsAspect.CurrentBuilder.Opt; } + } + } + public abstract class EcsAspect : IEcsAspect, ITemplateNode, IComponentMask { #region Initialization Halpers [ThreadStatic] @@ -43,7 +99,7 @@ namespace DCFApixels.DragonECS return _constructorBuildersStack[_constructorBuildersStackIndex]; } } - protected static Builder CurrentBuilder + public static Builder CurrentBuilder { get { return B; } } @@ -77,6 +133,7 @@ namespace DCFApixels.DragonECS public EcsMask Mask { get { return _mask; } + set { } } public EcsWorld World { @@ -138,7 +195,8 @@ namespace DCFApixels.DragonECS #region Constructors/New private Builder() { } - internal static unsafe TAspect New(EcsWorld world) where TAspect : EcsAspect, new() + + internal static unsafe (TAspect aspect, EcsMask mask) New(EcsWorld world) where TAspect : new() { //Get Builder if (_constructorBuildersStack == null) @@ -168,26 +226,34 @@ namespace DCFApixels.DragonECS //Building TAspect newAspect = new TAspect(); - newAspect._source = world; - newAspect.Init(builder); + EcsAspect builtinAspect = newAspect as EcsAspect; + if(builtinAspect != null) + { + builtinAspect._source = world; + builtinAspect.Init(builder); + } //Build Mask if (staticMask == null) { staticMask = builder._maskBuilder.Build(); builder._maskBuilder = default; - if (newAspect.IsStaticInitialization) + if (builtinAspect == null || builtinAspect.IsStaticInitialization) { _staticMaskCache.Add(typeof(TAspect), staticMask); } } - newAspect._mask = staticMask.ToMask(world); - //var pools = new IEcsPool[builder._poolsBufferCount]; - //Array.Copy(builder._poolsBuffer, pools, pools.Length); - newAspect._isBuilt = true; + EcsMask mask = staticMask.ToMask(world); + if(builtinAspect != null) + { + builtinAspect._mask = mask; + //var pools = new IEcsPool[builder._poolsBufferCount]; + //Array.Copy(builder._poolsBuffer, pools, pools.Length); + builtinAspect._isBuilt = true; + } _constructorBuildersStackIndex--; - return newAspect; + return (newAspect, mask); } #endregion @@ -386,7 +452,9 @@ namespace DCFApixels.DragonECS } } #endregion - +} +namespace DCFApixels.DragonECS.Core +{ #region Constraint Markers public readonly ref struct IncludeMarker { diff --git a/src/EcsPipeline.Builder.cs b/src/EcsPipeline.Builder.cs index 5f116d9..672b119 100644 --- a/src/EcsPipeline.Builder.cs +++ b/src/EcsPipeline.Builder.cs @@ -214,7 +214,7 @@ namespace DCFApixels.DragonECS { case IEcsProcess system: return AddSystem_Internal(system, settedAddParams); case IEcsModule module: return AddModule_Internal(module, settedAddParams); - default: Throw.ArgumentException("Unsupported type"); return this; + default: Throw.ArgumentException($"{raw.GetMeta().TypeName} Unsupported type"); return this; } } #endregion diff --git a/src/EcsPipeline.cs b/src/EcsPipeline.cs index 93460ad..89403df 100644 --- a/src/EcsPipeline.cs +++ b/src/EcsPipeline.cs @@ -68,7 +68,7 @@ namespace DCFApixels.DragonECS { get { return _isInit; } } - public bool IsDestoryed + public bool IsDestroyed { get { return _isDestoryed; } } @@ -250,7 +250,7 @@ namespace DCFApixels.DragonECS { public static bool IsNullOrDestroyed(this EcsPipeline self) { - return self == null || self.IsDestoryed; + return self == null || self.IsDestroyed; } public static EcsPipeline.Builder Add(this EcsPipeline.Builder self, IEnumerable range, string layerName = null) { diff --git a/src/EcsRunner.cs b/src/EcsRunner.cs index 4f6902e..7a30daa 100644 --- a/src/EcsRunner.cs +++ b/src/EcsRunner.cs @@ -4,6 +4,7 @@ using System; using System.Linq; using System.Runtime.CompilerServices; using static DCFApixels.DragonECS.EcsDebugUtility; +#pragma warning disable CS0162 // Обнаружен недостижимый код namespace DCFApixels.DragonECS { @@ -16,6 +17,7 @@ namespace DCFApixels.DragonECS namespace RunnersCore { + //добавить инъекцию в раннеры public abstract class EcsRunner { internal abstract void Init_Internal(EcsPipeline source); @@ -57,6 +59,8 @@ namespace DCFApixels.DragonECS #endif } #endregion + + public delegate void ActionWithData(TProcess process, ref T Data); } [MetaColor(MetaColor.DragonRose)] @@ -124,7 +128,12 @@ namespace DCFApixels.DragonECS #endregion #region RunHelper - public struct RunHelper +#if DEBUG && !DISABLE_DEBUG + public +#else + public readonly +#endif + struct RunHelper { private readonly EcsProcess _process; #if DEBUG && !DISABLE_DEBUG @@ -182,7 +191,6 @@ namespace DCFApixels.DragonECS #endregion #region Do -#pragma warning disable CS0162 // Обнаружен недостижимый код [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Run(Action translationCallback) { @@ -223,7 +231,7 @@ namespace DCFApixels.DragonECS } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Run(Action translationCallback, T0 t0) + public void Run(ActionWithData translationCallback, ref TData data) { #if DEBUG && !DISABLE_DEBUG CheckCache(translationCallback); @@ -232,7 +240,7 @@ namespace DCFApixels.DragonECS _markers[i].Begin(); try { - translationCallback(_process[i], t0); + translationCallback(_process[i], ref data); } catch (Exception e) { @@ -248,7 +256,7 @@ namespace DCFApixels.DragonECS { try { - translationCallback(item, t0); + translationCallback(item, ref data); } catch (Exception e) { @@ -260,128 +268,205 @@ namespace DCFApixels.DragonECS } #endif } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Run(Action translationCallback, T0 t0, T1 t1) - { -#if DEBUG && !DISABLE_DEBUG - CheckCache(translationCallback); - for (int i = 0, n = _process.Length < _markers.Length ? _process.Length : _markers.Length; i < n; i++) - { - _markers[i].Begin(); - try - { - translationCallback(_process[i], t0, t1); - } - catch (Exception e) - { -#if DISABLE_CATH_EXCEPTIONS - throw; -#endif - EcsDebug.PrintError(e); - } - _markers[i].End(); - } -#else - foreach (var item in _process) - { - try - { - translationCallback(item, t0, t1); - } - catch (Exception e) - { -#if DISABLE_CATH_EXCEPTIONS - throw; -#endif - EcsDebug.PrintError(e); - } - } -#endif - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Run(Action translationCallback, T0 t0, T1 t1, T2 t2) - { -#if DEBUG && !DISABLE_DEBUG - CheckCache(translationCallback); - for (int i = 0, n = _process.Length < _markers.Length ? _process.Length : _markers.Length; i < n; i++) - { - _markers[i].Begin(); - try - { - translationCallback(_process[i], t0, t1, t2); - } - catch (Exception e) - { -#if DISABLE_CATH_EXCEPTIONS - throw; -#endif - EcsDebug.PrintError(e); - } - _markers[i].End(); - } -#else - foreach (var item in _process) - { - try - { - translationCallback(item, t0, t1, t2); - } - catch (Exception e) - { -#if DISABLE_CATH_EXCEPTIONS - throw; -#endif - EcsDebug.PrintError(e); - } - } -#endif - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Run(Action translationCallback, T0 t0, T1 t1, T2 t2, T3 t3) - { -#if DEBUG && !DISABLE_DEBUG - CheckCache(translationCallback); - for (int i = 0, n = _process.Length < _markers.Length ? _process.Length : _markers.Length; i < n; i++) - { - _markers[i].Begin(); - try - { - translationCallback(_process[i], t0, t1, t2, t3); - } - catch (Exception e) - { -#if DISABLE_CATH_EXCEPTIONS - throw; -#endif - EcsDebug.PrintError(e); - } - _markers[i].End(); - } -#else - foreach (var item in _process) - { - try - { - translationCallback(item, t0, t1, t2, t3); - } - catch (Exception e) - { -#if DISABLE_CATH_EXCEPTIONS - throw; -#endif - EcsDebug.PrintError(e); - } - } -#endif - } -#pragma warning restore CS0162 // Обнаружен недостижимый код - //------------------------ #endregion } #endregion + + #region RunHelperWithFinally +#if DEBUG && !DISABLE_DEBUG + public +#else + public readonly +#endif + struct RunHelperWithFinally where TProcessFinally : class, IEcsProcess + { + private readonly Pair[] _pairs; +#if DEBUG && !DISABLE_DEBUG + private Delegate _cacheCheck; + private Delegate _cacheCheckF; + private bool _cacheCheckInit; + private readonly EcsProfilerMarker[] _markers; +#endif + + #region Constructors + public RunHelperWithFinally(EcsRunner runner) : this(runner, +#if DEBUG && !DISABLE_DEBUG + typeof(TProcess).ToMeta().Name) +#else + string.Empty) +#endif + { } + + public RunHelperWithFinally(EcsRunner runner, string methodName) + { + _pairs = new Pair[runner.Process.Length]; + for (int i = 0; i < runner.Process.Length; i++) + { + _pairs[i] = new Pair(runner.Process[i]); + } +#if DEBUG && !DISABLE_DEBUG + _cacheCheck = null; + _cacheCheckF = null; + _cacheCheckInit = false; + _markers = new EcsProfilerMarker[_pairs.Length]; + for (int i = 0; i < _pairs.Length; i++) + { + _markers[i] = new EcsProfilerMarker($"{_pairs[i].run.GetMeta().Name}.{methodName}"); + } +#endif + } + #endregion + + #region Utils + private readonly struct Pair + { + public readonly TProcess run; + public readonly TProcessFinally runFinally; + public Pair(TProcess run) + { + this.run = run; + runFinally = run as TProcessFinally; + } + } +#if DEBUG && !DISABLE_DEBUG + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void CheckCache(Delegate d, Delegate df) + { + if (_cacheCheckInit == false) + { + if (_cacheCheck == null) + { + _cacheCheck = d; + _cacheCheckF = df; + } + else + { + if (ReferenceEquals(_cacheCheck, d) == false || ReferenceEquals(_cacheCheckF, df) == false) + { + EcsDebug.PrintWarning("The delegate is not cached"); + } + _cacheCheckInit = true; + } + } + } +#endif + #endregion + + #region Do + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Run( + Action translationCallback, + Action translationFinnalyCallback) + { +#if DEBUG && !DISABLE_DEBUG + CheckCache(translationCallback, translationFinnalyCallback); + for (int i = 0, n = _pairs.Length < _markers.Length ? _pairs.Length : _markers.Length; i < n; i++) + { + var pair = _pairs[i]; + _markers[i].Begin(); + try + { + translationCallback(pair.run); + } + catch (Exception e) + { +#if DISABLE_CATH_EXCEPTIONS + throw; +#endif + EcsDebug.PrintError(e); + } + finally + { + if(pair.runFinally != null) + { + translationFinnalyCallback(pair.runFinally); + } + } + _markers[i].End(); + } +#else + foreach (var item in _pairs) + { + try + { + translationCallback(item.run); + } + catch (Exception e) + { +#if DISABLE_CATH_EXCEPTIONS + throw; +#endif + EcsDebug.PrintError(e); + } + finally + { + translationFinnalyCallback(item.runFinally); + } + } +#endif + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Run( + ActionWithData translationCallback, + ActionWithData translationFinnalyCallback, + ref TData data) + { +#if DEBUG && !DISABLE_DEBUG + CheckCache(translationCallback, translationFinnalyCallback); + for (int i = 0, n = _pairs.Length < _markers.Length ? _pairs.Length : _markers.Length; i < n; i++) + { + var pair = _pairs[i]; + _markers[i].Begin(); + try + { + translationCallback(pair.run, ref data); + } + catch (Exception e) + { +#if DISABLE_CATH_EXCEPTIONS + throw; +#endif + EcsDebug.PrintError(e); + } + finally + { + if (pair.runFinally != null) + { + translationFinnalyCallback(pair.runFinally, ref data); + } + } + _markers[i].End(); + } +#else + foreach (var pair in _pairs) + { + try + { + translationCallback(pair.run, t0); + } + catch (Exception e) + { +#if DISABLE_CATH_EXCEPTIONS + throw; +#endif + EcsDebug.PrintError(e); + } + finally + { + if (pair.runFinally != null) + { + translationFinnalyCallback(pair.runFinally, t0); + } + } + } +#endif + } + #endregion + } + #endregion + + //---- } } diff --git a/src/EcsWorld.cache.cs b/src/EcsWorld.cache.cs index 14fccb7..bcffb82 100644 --- a/src/EcsWorld.cache.cs +++ b/src/EcsWorld.cache.cs @@ -20,13 +20,22 @@ namespace DCFApixels.DragonECS } } internal readonly struct AspectCache : IEcsWorldComponent> - where T : EcsAspect, new() + where T : new() { public readonly T Instance; - public AspectCache(T instance) { Instance = instance; } + public readonly EcsMask Mask; + public AspectCache(T instance, EcsMask mask) + { + Instance = instance; + Mask = mask; + } void IEcsWorldComponent>.Init(ref AspectCache component, EcsWorld world) { - component = new AspectCache(EcsAspect.Builder.New(world)); +#if DEBUG + AllowedInWorldsAttribute.CheckAllows(world); +#endif + var result = EcsAspect.Builder.New(world); + component = new AspectCache(result.aspect, result.mask); } void IEcsWorldComponent>.OnDestroy(ref AspectCache component, EcsWorld world) { @@ -36,7 +45,7 @@ namespace DCFApixels.DragonECS internal readonly struct WhereQueryCache : IEcsWorldComponent> where TExecutor : MaskQueryExecutor, new() - where TAspcet : EcsAspect, new() + where TAspcet : new() { public readonly TExecutor Executor; public readonly TAspcet Aspcet; @@ -47,9 +56,9 @@ namespace DCFApixels.DragonECS } void IEcsWorldComponent>.Init(ref WhereQueryCache component, EcsWorld world) { - TAspcet aspect = world.GetAspect(); - TExecutor instance = world.GetExecutorForMask(aspect.Mask); - instance.Initialize(world, aspect.Mask); + TAspcet aspect = world.GetAspect(out EcsMask mask); + TExecutor instance = world.GetExecutorForMask(mask); + instance.Initialize(world, mask); component = new WhereQueryCache(instance, aspect); } void IEcsWorldComponent>.OnDestroy(ref WhereQueryCache component, EcsWorld world) diff --git a/src/EcsWorld.cs b/src/EcsWorld.cs index e2d6bd1..3c36bf6 100644 --- a/src/EcsWorld.cs +++ b/src/EcsWorld.cs @@ -250,14 +250,21 @@ namespace DCFApixels.DragonECS [UnityEngine.Scripting.Preserve] #endif [MethodImpl(MethodImplOptions.AggressiveInlining)] - public TAspect GetAspect() where TAspect : EcsAspect, new() + public TAspect GetAspect() where TAspect : new() { return Get>().Instance; } [MethodImpl(MethodImplOptions.AggressiveInlining)] + public TAspect GetAspect(out EcsMask mask) where TAspect : new() + { + var result = Get>(); + mask = result.Mask; + return result.Instance; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void GetQueryCache(out TExecutor executor, out TAspect aspect) where TExecutor : MaskQueryExecutor, new() - where TAspect : EcsAspect, new() + where TAspect : new() { ref var cmp = ref Get>(); executor = cmp.Executor; @@ -270,6 +277,11 @@ namespace DCFApixels.DragonECS return ref WorldComponentPool.GetForWorld(ID); } [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Has() where T : struct + { + return WorldComponentPool.Has(ID); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public ref T GetUnchecked() where T : struct { return ref WorldComponentPool.GetForWorldUnchecked(ID); @@ -280,6 +292,11 @@ namespace DCFApixels.DragonECS return ref WorldComponentPool.GetForWorld(worldID); } [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool Has(short worldID) where T : struct + { + return WorldComponentPool.Has(worldID); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ref T GetUnchecked(short worldID) where T : struct { return ref WorldComponentPool.GetForWorldUnchecked(worldID); @@ -874,6 +891,8 @@ namespace DCFApixels.DragonECS #region Debug Components [ThreadStatic] private static int[] _componentIDsBuffer; + [ThreadStatic] + private static object[] _componentsBuffer; public ReadOnlySpan GetComponentTypeIDsFor(int entityID) { int count = GetComponentTypeIDsFor_Internal(entityID, ref _componentIDsBuffer); @@ -920,6 +939,19 @@ namespace DCFApixels.DragonECS } } } + public ReadOnlySpan GetComponentsFor(int entityID) + { + int count = GetComponentTypeIDsFor_Internal(entityID, ref _componentIDsBuffer); + if (_componentsBuffer == null || _componentsBuffer.Length < count) + { + _componentsBuffer = new object[count]; + } + for (int i = 0; i < count; i++) + { + _componentsBuffer[i] = _pools[_componentIDsBuffer[i]].GetRaw(entityID); + } + return new ReadOnlySpan(_componentsBuffer, 0, count); + } public void GetComponentsFor(int entityID, List list) { list.Clear(); diff --git a/src/EcsWorld.static.cs b/src/EcsWorld.static.cs index 38dffa5..d364a2f 100644 --- a/src/EcsWorld.static.cs +++ b/src/EcsWorld.static.cs @@ -169,6 +169,10 @@ namespace DCFApixels.DragonECS Array.Resize(ref _items, _items.Length << 1); } +#if DEBUG + AllowedInWorldsAttribute.CheckAllows(_worlds[worldID]); +#endif + _interface.Init(ref _items[itemIndex], _worlds[worldID]); var world = GetWorld(worldID); diff --git a/src/Executors/Queries.cs b/src/Executors/Queries.cs index 214b658..171386f 100644 --- a/src/Executors/Queries.cs +++ b/src/Executors/Queries.cs @@ -13,7 +13,7 @@ namespace DCFApixels.DragonECS { #region Where public static EcsSpan Where(this TCollection entities, out TAspect aspect) - where TAspect : EcsAspect, new() + where TAspect : new() where TCollection : IEntityStorage { if (ReferenceEquals(entities, entities.World)) @@ -24,12 +24,12 @@ namespace DCFApixels.DragonECS return entities.ToSpan().Where(out aspect); } public static EcsSpan Where(this EcsReadonlyGroup group, out TAspect aspect) - where TAspect : EcsAspect, new() + where TAspect : new() { return group.ToSpan().Where(out aspect); } public static EcsSpan Where(this EcsSpan span, out TAspect aspect) - where TAspect : EcsAspect, new() + where TAspect : new() { span.World.GetQueryCache(out EcsWhereExecutor executor, out aspect); return executor.ExecuteFor(span); @@ -58,7 +58,7 @@ namespace DCFApixels.DragonECS #region Where with sort public static EcsSpan Where(this TCollection entities, out TAspect aspect, Comparison comparison) - where TAspect : EcsAspect, new() + where TAspect : new() where TCollection : IEntityStorage { if (ReferenceEquals(entities, entities.World)) @@ -69,12 +69,12 @@ namespace DCFApixels.DragonECS return entities.ToSpan().Where(out aspect, comparison); } public static EcsSpan Where(this EcsReadonlyGroup group, out TAspect aspect, Comparison comparison) - where TAspect : EcsAspect, new() + where TAspect : new() { return group.ToSpan().Where(out aspect, comparison); } public static EcsSpan Where(this EcsSpan span, out TAspect aspect, Comparison comparison) - where TAspect : EcsAspect, new() + where TAspect : new() { span.World.GetQueryCache(out EcsWhereExecutor executor, out aspect); return executor.ExecuteFor(span, comparison); @@ -103,7 +103,7 @@ namespace DCFApixels.DragonECS #region WhereToGroup public static EcsReadonlyGroup WhereToGroup(this TCollection entities, out TAspect aspect) - where TAspect : EcsAspect, new() + where TAspect : new() where TCollection : IEntityStorage { if (ReferenceEquals(entities, entities.World)) @@ -114,12 +114,12 @@ namespace DCFApixels.DragonECS return entities.ToSpan().WhereToGroup(out aspect); } public static EcsReadonlyGroup WhereToGroup(this EcsReadonlyGroup group, out TAspect aspect) - where TAspect : EcsAspect, new() + where TAspect : new() { return group.ToSpan().WhereToGroup(out aspect); } public static EcsReadonlyGroup WhereToGroup(this EcsSpan span, out TAspect aspect) - where TAspect : EcsAspect, new() + where TAspect : new() { span.World.GetQueryCache(out EcsWhereToGroupExecutor executor, out aspect); return executor.ExecuteFor(span); diff --git a/src/Utils/AllowedInWorldsAttribute.cs b/src/Utils/AllowedInWorldsAttribute.cs new file mode 100644 index 0000000..038cda3 --- /dev/null +++ b/src/Utils/AllowedInWorldsAttribute.cs @@ -0,0 +1,43 @@ +using DCFApixels.DragonECS.Internal; +using System; + +namespace DCFApixels.DragonECS +{ + [AttributeUsage(AttributeTargets.Struct | AttributeTargets.Class, Inherited = false, AllowMultiple = true)] + public sealed class AllowedInWorldsAttribute : Attribute + { + public object[] AllowedWorlds; + public AllowedInWorldsAttribute(params object[] allowedWorlds) + { + AllowedWorlds = allowedWorlds; + } + + + public static void CheckAllows(EcsWorld world) + { + Type componentType = typeof(T); + Type worldType = world.GetType(); + if (componentType.TryGetAttribute(out AllowedInWorldsAttribute attribute)) + { + foreach (var worldTag in attribute.AllowedWorlds) + { + bool result = false; + if (worldTag is Type worldTypeTag) + { + result = worldTypeTag == worldType; + } + else + { + string worldStringTag = worldTag.ToString(); + result = world.Name == worldStringTag; + } + if (result) + { + return; + } + } + throw new EcsFrameworkException($"Using component {componentType.ToMeta().TypeName} is not allowed in the {worldType.ToMeta().TypeName} world."); + } + } + } +} diff --git a/src/Utils/AllowedInWorldsAttribute.cs.meta b/src/Utils/AllowedInWorldsAttribute.cs.meta new file mode 100644 index 0000000..2888e88 --- /dev/null +++ b/src/Utils/AllowedInWorldsAttribute.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a39694cb8b5876346968ef45a8272000 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: