com.elestrago.unity.entitas-redux 3.2.5 → 3.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,26 @@
2
2
 
3
3
  ---
4
4
 
5
+ ## [3.3.1](https://gitlab.com/elestrago-pkg/entitas-redux/-/tags/3.3.1)
6
+
7
+ ### Added
8
+
9
+ - `Optional` flag to `CommandAttribute`
10
+
11
+ ---
12
+
13
+ ## [3.3.0](https://gitlab.com/elestrago-pkg/entitas-redux/-/tags/3.3.0)
14
+
15
+ ### Added
16
+
17
+ - `Register<TCommand>` method to `ICommandBuffer` for create pool
18
+
19
+ ### Changed
20
+
21
+ - Create new command only allow for registered commands
22
+
23
+ ---
24
+
5
25
  ## [3.2.5](https://gitlab.com/elestrago-pkg/entitas-redux/-/tags/3.2.5)
6
26
 
7
27
  ### Fixed
@@ -5,5 +5,6 @@ namespace JCMG.EntitasRedux.Commands
5
5
  [AttributeUsage(AttributeTargets.Struct)]
6
6
  public class CommandAttribute : Attribute
7
7
  {
8
+ public bool Optional;
8
9
  }
9
10
  }
@@ -1,56 +1,132 @@
1
1
  using System;
2
- using System.Collections.Generic;
2
+ using System.Reflection;
3
+ using System.Threading;
3
4
 
4
5
  namespace JCMG.EntitasRedux.Commands
5
6
  {
6
- public abstract class CommandBuffer : ICommandBuffer, IDisposable
7
+ public abstract class CommandBuffer : ICommandBuffer
7
8
  {
8
- private static readonly List<Func<ICommandPool>> PoolFactories = new();
9
+ // Helper to get a unique, static index for each command type.
10
+ private static class CommandType
11
+ {
12
+ private static int _indexCounter;
13
+
14
+ internal static class Info<TCommand> where TCommand : struct
15
+ {
16
+ // This value is calculated once per TCommand and cached.
17
+ public static readonly int Index = Interlocked.Increment(ref _indexCounter) - 1;
18
+ public static readonly bool Optional;
19
+
20
+ static Info()
21
+ {
22
+ var type = typeof(TCommand);
23
+ var attribute = type.GetCustomAttribute<CommandAttribute>();
24
+ if (attribute == null)
25
+ return;
9
26
 
10
- private static int _commandsCounter;
11
- private static event Action<Func<ICommandPool>> PoolFactoryRegister;
27
+ Optional = attribute.Optional;
28
+ }
29
+ }
30
+ }
12
31
 
13
- private readonly List<ICommandPool> _pools = new();
32
+ // Use a raw array for performance. It's not readonly because we resize it.
33
+ private ICommandPool[] _pools = new ICommandPool[4];
14
34
 
15
- protected CommandBuffer()
35
+ /// <summary>
36
+ /// Registers a pool for the specified command type. This must be called
37
+ /// before creating or accessing commands of this type.
38
+ /// </summary>
39
+ public void Register<TCommand>() where TCommand : struct
16
40
  {
17
- lock (PoolFactories)
41
+ var index = CommandType.Info<TCommand>.Index;
42
+
43
+ // Ensure the array is large enough, resizing if necessary.
44
+ if (index >= _pools.Length)
18
45
  {
19
- foreach (var poolFactory in PoolFactories)
20
- _pools.Add(poolFactory());
46
+ // Resize to the exact size needed to include the new index.
47
+ Array.Resize(ref _pools, index + 1);
21
48
  }
22
49
 
23
- PoolFactoryRegister += OnPoolFactoryRegister;
50
+ // Create the pool if it doesn't exist for this buffer instance.
51
+ ref var commandPool = ref _pools[index];
52
+ commandPool ??= new CommandPool<TCommand>();
24
53
  }
25
54
 
26
- public void Dispose()
55
+ public ref TCommand Create<TCommand>() where TCommand : struct
27
56
  {
28
- PoolFactoryRegister -= OnPoolFactoryRegister;
29
- }
57
+ var pool = GetPool<TCommand>();
58
+ if (pool != null)
59
+ return ref pool.Create();
30
60
 
31
- private void OnPoolFactoryRegister(Func<ICommandPool> poolFactory) => _pools.Add(poolFactory());
61
+ if (!CommandType.Info<TCommand>.Optional)
62
+ {
63
+ #if DEBUG
64
+ throw new Exception($"[CommandBuffer] Command type '{typeof(TCommand).Name}' is not registered. " +
65
+ $"Call Register<{typeof(TCommand).Name}>() first. Command will be ignored.");
66
+ #endif
67
+ }
32
68
 
33
- public ref TCommand Create<TCommand>() where TCommand : struct
34
- {
35
- var pool = _pools[CommandPool<TCommand>.Index] as CommandPool<TCommand>;
36
- return ref pool.Create();
69
+ // Return a reference to a dummy variable to satisfy the ref return.
70
+ return ref Dummy<TCommand>.Value;
37
71
  }
38
72
 
39
73
  public void Create<TCommand>(TCommand command) where TCommand : struct
40
74
  {
41
- var pool = _pools[CommandPool<TCommand>.Index] as CommandPool<TCommand>;
42
- pool.Create(command);
75
+ var pool = GetPool<TCommand>();
76
+ if (pool != null)
77
+ {
78
+ pool.Create(command);
79
+ return;
80
+ }
81
+
82
+ if (CommandType.Info<TCommand>.Optional)
83
+ return;
84
+
85
+ #if DEBUG
86
+ throw new Exception($"[CommandBuffer] Command type '{typeof(TCommand).Name}' is not registered. " +
87
+ $"Call Register<{typeof(TCommand).Name}>() first. Command will be ignored.");
88
+ #endif
43
89
  }
44
90
 
45
91
  public Span<TCommand> GetCommands<TCommand>() where TCommand : struct
46
92
  {
47
- var pool = _pools[CommandPool<TCommand>.Index] as CommandPool<TCommand>;
48
- return pool.Read();
93
+ var pool = GetPool<TCommand>();
94
+ return pool != null ? pool.Read() : Span<TCommand>.Empty;
95
+ }
96
+
97
+ private CommandPool<TCommand> GetPool<TCommand>() where TCommand : struct
98
+ {
99
+ var index = CommandType.Info<TCommand>.Index;
100
+
101
+ // Use .Length for array boundary check.
102
+ if (index >= _pools.Length)
103
+ return null;
104
+
105
+ return _pools[index] as CommandPool<TCommand>;
49
106
  }
50
107
 
51
108
  public void Clear<TCommand>() where TCommand : struct
52
- => _pools[CommandPool<TCommand>.Index].Clear();
109
+ {
110
+ var index = CommandType.Info<TCommand>.Index;
111
+
112
+ // Use .Length for array boundary check.
113
+ if (index < _pools.Length)
114
+ _pools[index]?.Clear();
115
+ }
116
+
117
+ public void ClearAll()
118
+ {
119
+ for (var i = 0; i < _pools.Length; i++)
120
+ _pools[i]?.Clear();
121
+ }
53
122
 
123
+ // A helper to provide a valid ref return when a pool doesn't exist.
124
+ private static class Dummy<T> where T : struct
125
+ {
126
+ public static T Value;
127
+ }
128
+
129
+ // The interface and pool implementation are now much simpler.
54
130
  private interface ICommandPool
55
131
  {
56
132
  void Clear();
@@ -59,23 +135,9 @@ namespace JCMG.EntitasRedux.Commands
59
135
  private class CommandPool<TCommand> : ICommandPool
60
136
  where TCommand : struct
61
137
  {
62
- public static readonly int Index;
63
-
64
138
  private TCommand[] _pool = new TCommand[4];
65
139
  private int _position;
66
140
 
67
- static CommandPool()
68
- {
69
- lock (PoolFactories)
70
- {
71
- Index = _commandsCounter++;
72
- PoolFactories.Add(Factory);
73
- PoolFactoryRegister?.Invoke(Factory);
74
- }
75
- }
76
-
77
- private static CommandPool<TCommand> Factory() => new();
78
-
79
141
  public ref TCommand Create()
80
142
  {
81
143
  var position = _position++;
@@ -98,8 +160,9 @@ namespace JCMG.EntitasRedux.Commands
98
160
 
99
161
  public void Clear()
100
162
  {
163
+ // Clear only the portion of the array that was used.
164
+ Array.Clear(_pool, 0, _position);
101
165
  _position = 0;
102
- Array.Clear(_pool, 0, _pool.Length);
103
166
  }
104
167
  }
105
168
  }
@@ -4,6 +4,12 @@ namespace JCMG.EntitasRedux.Commands
4
4
  {
5
5
  public interface ICommandBuffer
6
6
  {
7
+ /// <summary>
8
+ /// Registers a pool for the specified command type. This must be called
9
+ /// before creating or accessing commands of this type.
10
+ /// </summary>
11
+ void Register<TCommand>() where TCommand : struct;
12
+
7
13
  ref TCommand Create<TCommand>() where TCommand : struct;
8
14
 
9
15
  void Create<TCommand>(TCommand command) where TCommand : struct;
@@ -11,5 +17,7 @@ namespace JCMG.EntitasRedux.Commands
11
17
  Span<TCommand> GetCommands<TCommand>() where TCommand : struct;
12
18
 
13
19
  void Clear<TCommand>() where TCommand : struct;
20
+
21
+ void ClearAll();
14
22
  }
15
23
  }
@@ -6,12 +6,18 @@ namespace JCMG.EntitasRedux.Commands
6
6
  where TCommand : struct
7
7
  {
8
8
  private readonly ICommandBuffer _commandBuffer;
9
+ private readonly bool _cleanUp;
9
10
 
10
- protected virtual bool CleanUp => true;
11
-
12
- protected CommandSystem(ICommandBuffer commandBuffer)
11
+ protected CommandSystem(ICommandBuffer commandBuffer, bool cleanUp = true)
13
12
  {
14
13
  _commandBuffer = commandBuffer;
14
+ _cleanUp = cleanUp;
15
+
16
+ // We only need to register the command type with the buffer if this system
17
+ // is responsible for cleaning it up. The buffer uses this registration to
18
+ // know which command types to manage for its internal collections.
19
+ if (_cleanUp)
20
+ _commandBuffer.Register<TCommand>();
15
21
  }
16
22
 
17
23
  protected void ExecuteCommands()
@@ -21,7 +27,7 @@ namespace JCMG.EntitasRedux.Commands
21
27
  return;
22
28
 
23
29
  Execute(commands);
24
- if (CleanUp)
30
+ if (_cleanUp)
25
31
  _commandBuffer.Clear<TCommand>();
26
32
  }
27
33
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "com.elestrago.unity.entitas-redux",
3
- "version": "3.2.5",
3
+ "version": "3.3.1",
4
4
  "displayName": "JCMG Entitas Redux",
5
5
  "description": "Entitas Redux is an fast, accessible, and feature-rich ECS framework for Unity. It leverages code generation and an extensible plugin framework to make life easier for developers.",
6
6
  "category": "Unity",