diff --git a/docs/coverage/DefaultEcs_AComponentSystem_2.htm b/docs/coverage/DefaultEcs_AComponentSystem_2.htm index ab7bfb2e..c487d712 100644 --- a/docs/coverage/DefaultEcs_AComponentSystem_2.htm +++ b/docs/coverage/DefaultEcs_AComponentSystem_2.htm @@ -17,11 +17,11 @@
48
/// </summary>
49
/// <param name="state">The state to use.</param>
50
/// <param name="component">The <typeparamref name="TComponent"/> to update.</param>
51
protected virtual void Update(TState state, ref TComponent component) { }
51
protected virtual void Update(TState state, ref TComponent component) { }
52
53
/// <summary>
54
/// Update the given <typeparamref name="TComponent"/> once.
98
}
19
public event ActionIn<Entity> EntityAdded
20
{
21
add => _set.EntityAdded += value;
22
remove => _set.EntityAdded -= value;
22
remove => _set.EntityAdded -= value;
23
}
24
25
/// <summary>
28
public event ActionIn<Entity> EntityRemoved
29
{
30
add => _set.EntityRemoved += value;
31
remove => _set.EntityRemoved -= value;
31
remove => _set.EntityRemoved -= value;
32
}
33
34
#endregion
88
/// </summary>
89
/// <param name="state">The state to use.</param>
90
/// <param name="entity">The <see cref="Entity"/> instance to update.</param>
91
protected virtual void Update(T state, in Entity entity) { }
91
protected virtual void Update(T state, in Entity entity) { }
92
93
/// <summary>
94
/// Update the given <see cref="Entity"/> instances once.
155
}
25
26
private int _referencesCount;
27
28
public Resource(TResource value)
28
public Resource(TResource value)
29
{
30
Value = value;
31
_referencesCount = 0;
32
}
30
Value = value;
31
_referencesCount = 0;
32
}
33
34
public void AddReference() => ++_referencesCount;
34
public void AddReference() => ++_referencesCount;
35
36
public bool RemoveReference() => --_referencesCount == 0;
36
public bool RemoveReference() => --_referencesCount == 0;
37
}
38
39
#endregion
64
{
65
Resource resource;
66
67
lock (_lockObject)
67
lock (_lockObject)
68
{
69
if (!_resources.TryGetValue(message.ManagedResource.Info, out resource))
69
if (!_resources.TryGetValue(message.ManagedResource.Info, out resource))
70
{
71
resource = new Resource(Load(message.ManagedResource.Info));
72
_resources.Add(message.ManagedResource.Info, resource);
71
resource = new Resource(Load(message.ManagedResource.Info));
72
_resources.Add(message.ManagedResource.Info, resource);
73
}
74
75
resource.AddReference();
76
}
75
resource.AddReference();
76
}
77
78
OnResourceLoaded(message.Entity, message.ManagedResource.Info, resource.Value);
79
}
78
OnResourceLoaded(message.Entity, message.ManagedResource.Info, resource.Value);
79
}
80
81
private void On(in ManagedResourceReleaseMessage<ManagedResource<TInfo, TResource>> message)
82
{
83
lock (_lockObject)
83
lock (_lockObject)
84
{
85
if (_resources.TryGetValue(message.ManagedResource.Info, out Resource resource) && resource.RemoveRefere
85
if (_resources.TryGetValue(message.ManagedResource.Info, out Resource resource) && resource.RemoveRefere
86
{
87
resource.Value?.Dispose();
88
_resources.Remove(message.ManagedResource.Info);
87
resource.Value?.Dispose();
88
_resources.Remove(message.ManagedResource.Info);
89
}
90
}
91
}
90
}
91
}
92
93
private void On(in ManagedResourceRequestMessage<ManagedResource<TInfo[], TResource>> message)
94
{
142
}
143
144
ComponentPool<ManagedResource<TInfo, TResource>> components = ComponentManager<ManagedResource<TInfo, TResou
145
if (components != null)
145
if (components != null)
146
{
147
foreach (Entity entity in world.GetAllEntities().Where(e => components.Has(e.EntityId)))
147
foreach (Entity entity in world.GetAllEntities().Where(e => components.Has(e.EntityId)))
148
{
149
On(new ManagedResourceRequestMessage<ManagedResource<TInfo, TResource>>(entity, components.Get(entit
149
On(new ManagedResourceRequestMessage<ManagedResource<TInfo, TResource>>(entity, components.Get(entit
150
}
151
}
152
162
/// </summary>
163
public void Dispose()
164
{
165
foreach (Resource resource in _resources.Values)
165
foreach (Resource resource in _resources.Values)
166
{
167
resource.Value?.Dispose();
167
resource.Value?.Dispose();
168
}
169
170
_resources.Clear();
175
}
26
IsEnabled = true;
27
}
28
29
/// <summary>
30
/// Initialise a new instance of the <see cref="ASystem{T}"/> class.
31
/// </summary>
32
protected ASystem()
33
: this(null)
34
{ }
35
36
#endregion
37
38
#region Methods
39
40
internal abstract void Update(int index, int maxIndex);
41
42
/// <summary>
43
/// Performs a pre-update treatment.
44
/// </summary>
45
/// <param name="state">The state to use.</param>
46
protected virtual void PreUpdate(T state) { }
47
48
/// <summary>
49
/// Performs a post-update treatment.
50
/// </summary>
51
/// <param name="state">The state to use.</param>
52
protected virtual void PostUpdate(T state) { }
53
54
#endregion
29
#endregion
30
31
#region Methods
32
33
internal abstract void Update(int index, int maxIndex);
34
35
/// <summary>
36
/// Performs a pre-update treatment.
37
/// </summary>
38
/// <param name="state">The state to use.</param>
39
protected virtual void PreUpdate(T state) { }
40
41
/// <summary>
42
/// Performs a post-update treatment.
43
/// </summary>
44
/// <param name="state">The state to use.</param>
45
protected virtual void PostUpdate(T state) { }
46
47
#endregion
48
49
#region ISystem
50
51
/// <summary>
52
/// Gets or sets whether the current <see cref="ASystem{T}"/> instance should update or not.
53
/// </summary>
54
public virtual bool IsEnabled { get; set; }
55
56
#region ISystem
57
58
/// <summary>
59
/// Gets or sets whether the current <see cref="ASystem{T}"/> instance should update or not.
60
/// </summary>
61
public virtual bool IsEnabled { get; set; }
62
63
/// <summary>
64
/// Updates the system once.
65
/// </summary>
66
/// <param name="state">The state to use.</param>
67
public void Update(T state)
68
{
69
if (IsEnabled)
70
{
71
CurrentState = state;
72
73
PreUpdate(CurrentState);
74
75
_runner.Update(this);
76
77
PostUpdate(CurrentState);
78
}
79
}
80
81
#endregion
56
/// <summary>
57
/// Updates the system once.
58
/// </summary>
59
/// <param name="state">The state to use.</param>
60
public void Update(T state)
61
{
62
if (IsEnabled)
63
{
64
CurrentState = state;
65
66
PreUpdate(CurrentState);
67
68
_runner.Update(this);
69
70
PostUpdate(CurrentState);
71
}
72
}
73
74
#endregion
75
76
#region IDisposable
77
78
/// <summary>
79
/// Does nothing.
80
/// </summary>
81
public abstract void Dispose();
82
83
#region IDisposable
84
85
/// <summary>
86
/// Does nothing.
87
/// </summary>
88
public abstract void Dispose();
89
90
#endregion
91
}
92
}
83
#endregion
84
}
85
}
63
}
8
[MethodImpl(MethodImplOptions.NoInlining)]
9
private static void InnerEnsureLength<T>(ref T[] array, int index, int maxLength)
10
{
11
int newLength = Math.Max(1, array.Length);
11
int newLength = Math.Max(1, array.Length);
12
do
13
{
14
newLength *= 2;
14
newLength *= 2;
15
}
16
while (index >= newLength);
17
Array.Resize(ref array, Math.Min(maxLength, newLength));
18
}
16
while (index >= newLength);
17
Array.Resize(ref array, Math.Min(maxLength, newLength));
18
}
19
20
[MethodImpl(MethodImplOptions.AggressiveInlining)]
21
public static void Fill<T>(this T[] array, in T value, int start = 0)
22
{
23
for (int i = start; i < array.Length; ++i)
23
for (int i = start; i < array.Length; ++i)
24
{
25
array[i] = value;
25
array[i] = value;
26
}
27
}
27
}
28
29
[MethodImpl(MethodImplOptions.AggressiveInlining)]
30
public static void EnsureLength<T>(ref T[] array, int index, int maxLength = int.MaxValue)
31
{
32
if (index >= array.Length)
32
if (index >= array.Length)
33
{
34
InnerEnsureLength(ref array, index, maxLength);
34
InnerEnsureLength(ref array, index, maxLength);
35
}
36
}
36
}
37
38
[MethodImpl(MethodImplOptions.AggressiveInlining)]
39
public static void EnsureLength<T>(ref T[] array, int index, int maxLength, in T defaultValue)
40
{
41
if (index >= array.Length)
41
if (index >= array.Length)
42
{
43
int oldLength = array.Length;
43
int oldLength = array.Length;
44
45
InnerEnsureLength(ref array, index, maxLength);
46
array.Fill(defaultValue, oldLength);
45
InnerEnsureLength(ref array, index, maxLength);
46
array.Fill(defaultValue, oldLength);
47
}
48
}
48
}
49
}
50
}
27
{
28
#region IOperation
29
30
public void SetMaximumComponentCount(World world, int maxComponentCount) => world.SetMaximumComponentCount<T
30
public void SetMaximumComponentCount(World world, int maxComponentCount) => world.SetMaximumComponentCount<T
31
32
public void SetComponent(in Entity entity, Stream stream, byte[] buffer, byte* bufferP) => entity.Set(Conver
32
public void SetComponent(in Entity entity, Stream stream, byte[] buffer, byte* bufferP) => entity.Set(Conver
33
34
public void SetSameAsComponent(in Entity entity, in Entity reference) => entity.SetSameAs<T>(reference);
35
73
Dictionary<ushort, IOperation> operations = new Dictionary<ushort, IOperation>();
74
75
int entryType;
76
while ((entryType = stream.ReadByte()) >= 0)
76
while ((entryType = stream.ReadByte()) >= 0)
77
{
78
switch ((EntryType)entryType)
78
switch ((EntryType)entryType)
79
{
80
case EntryType.ComponentType:
81
stream.Read(buffer, 0, sizeof(ushort));
82
operations.Add(
83
*(ushort*)bufferP,
84
_operations.GetOrAdd(
85
Type.GetType(Converter<string>.Read(stream, buffer, bufferP), true),
86
t => (IOperation)Activator.CreateInstance(typeof(Operation<>).MakeGenericTyp
87
break;
81
stream.Read(buffer, 0, sizeof(ushort));
82
operations.Add(
83
*(ushort*)bufferP,
84
_operations.GetOrAdd(
85
Type.GetType(Converter<string>.Read(stream, buffer, bufferP), true),
86
t => (IOperation)Activator.CreateInstance(typeof(Operation<>).MakeGenericTyp
87
break;
88
89
case EntryType.MaxComponentCount:
90
stream.Read(buffer, 0, sizeof(ushort) + sizeof(int));
91
operations[*(ushort*)bufferP].SetMaximumComponentCount(world, *(int*)((ushort*)buffe
92
break;
90
stream.Read(buffer, 0, sizeof(ushort) + sizeof(int));
91
operations[*(ushort*)bufferP].SetMaximumComponentCount(world, *(int*)((ushort*)buffe
92
break;
93
94
case EntryType.Entity:
95
currentEntity = world.CreateEntity();
97
break;
98
99
case EntryType.Component:
100
stream.Read(buffer, 0, sizeof(ushort));
101
operations[*(ushort*)bufferP].SetComponent(currentEntity, stream, buffer, bufferP);
102
break;
100
stream.Read(buffer, 0, sizeof(ushort));
101
operations[*(ushort*)bufferP].SetComponent(currentEntity, stream, buffer, bufferP);
102
break;
103
104
case EntryType.ComponentSameAs:
105
stream.Read(buffer, 0, sizeof(ushort) + sizeof(int));
272
}
100
}
7
8
public ComponentAddedMessage(int entityId, ComponentEnum components)
9
{
10
EntityId = entityId;
11
Components = components;
12
}
10
EntityId = entityId;
11
Components = components;
12
}
13
}
14
}
229
}
7
8
public ComponentChangedMessage(int entityId, ComponentEnum components)
9
{
10
EntityId = entityId;
11
Components = components;
12
}
10
EntityId = entityId;
11
Components = components;
12
}
13
}
14
}
19
public bool this[ComponentFlag flag]
20
{
21
[MethodImpl(MethodImplOptions.AggressiveInlining)]
22
get => flag.Index < _bitArray?.Length && (_bitArray[flag.Index] & flag.Bit) != 0;
22
get => flag.Index < _bitArray?.Length && (_bitArray[flag.Index] & flag.Bit) != 0;
23
[MethodImpl(MethodImplOptions.AggressiveInlining)]
24
set
25
{
26
if ((_bitArray?.Length ?? 0) < flag.Index + 1)
26
if ((_bitArray?.Length ?? 0) < flag.Index + 1)
27
{
28
uint[] newBitArray = new uint[flag.Index + 1];
29
_bitArray?.CopyTo(newBitArray, 0);
30
_bitArray = newBitArray;
28
uint[] newBitArray = new uint[flag.Index + 1];
29
_bitArray?.CopyTo(newBitArray, 0);
30
_bitArray = newBitArray;
31
}
32
33
if (value)
33
if (value)
34
{
35
_bitArray[flag.Index] |= flag.Bit;
36
}
35
_bitArray[flag.Index] |= flag.Bit;
36
}
37
else
38
{
39
_bitArray[flag.Index] &= ~flag.Bit;
39
_bitArray[flag.Index] &= ~flag.Bit;
40
}
41
}
41
}
42
}
43
44
#endregion
48
[MethodImpl(MethodImplOptions.AggressiveInlining)]
49
public bool Contains(ComponentEnum filter)
50
{
51
if (filter._bitArray != null)
51
if (filter._bitArray != null)
52
{
53
for (int i = 0; i < filter._bitArray.Length; ++i)
53
for (int i = 0; i < filter._bitArray.Length; ++i)
54
{
55
uint part = filter._bitArray[i];
56
if (part != 0
57
&& (i >= _bitArray?.Length || (_bitArray[i] & part) != part))
55
uint part = filter._bitArray[i];
56
if (part != 0
57
&& (i >= _bitArray?.Length || (_bitArray[i] & part) != part))
58
{
59
return false;
59
return false;
60
}
61
}
62
}
63
64
return true;
64
return true;
65
}
66
67
[MethodImpl(MethodImplOptions.AggressiveInlining)]
94
}
95
96
[MethodImpl(MethodImplOptions.AggressiveInlining)]
97
public void Clear() => _bitArray?.Fill(0u);
97
public void Clear() => _bitArray.Fill(0u);
98
99
public override unsafe string ToString()
100
{
109
}
24
25
private ComponentFlag(int index, uint bit)
26
{
27
Index = index;
28
Bit = bit;
29
}
27
Index = index;
28
Bit = bit;
29
}
30
31
#endregion
32
34
35
public static ComponentFlag GetNextFlag()
36
{
37
lock (_lockObject)
37
lock (_lockObject)
38
{
39
ComponentFlag flag = _lastFlag;
40
_lastFlag = _lastFlag.Bit != 0x8000_0000 ? new ComponentFlag(_lastFlag.Index, _lastFlag.Bit << 1) : new
39
ComponentFlag flag = _lastFlag;
40
_lastFlag = _lastFlag.Bit != 0x8000_0000 ? new ComponentFlag(_lastFlag.Index, _lastFlag.Bit << 1) : new
41
42
return flag;
42
return flag;
43
}
44
}
44
}
45
46
#endregion
47
}
48
}
13
14
public ComponentLink(int entityId)
15
{
16
EntityId = entityId;
17
ReferenceCount = 1;
18
}
16
EntityId = entityId;
17
ReferenceCount = 1;
18
}
19
20
#endregion
21
}
22
}
20
21
static ComponentManager()
22
{
23
_lockObject = new object();
23
_lockObject = new object();
24
25
Flag = ComponentFlag.GetNextFlag();
25
Flag = ComponentFlag.GetNextFlag();
26
27
Pools = EmptyArray<ComponentPool<T>>.Value;
27
Pools = EmptyArray<ComponentPool<T>>.Value;
28
29
Publisher<WorldDisposedMessage>.Subscribe(0, On);
30
}
29
Publisher<WorldDisposedMessage>.Subscribe(0, On);
30
}
31
32
#endregion
33
35
36
private static void On(in WorldDisposedMessage message)
37
{
38
lock (_lockObject)
38
lock (_lockObject)
39
{
40
if (message.WorldId < Pools.Length)
40
if (message.WorldId < Pools.Length)
41
{
42
Pools[message.WorldId] = null;
42
Pools[message.WorldId] = null;
43
}
44
}
45
}
44
}
45
}
46
47
#endregion
48
51
[MethodImpl(MethodImplOptions.NoInlining)]
52
private static ComponentPool<T> Add(int worldId, int maxEntityCount, int maxComponentCount)
53
{
54
lock (_lockObject)
54
lock (_lockObject)
55
{
56
ArrayExtension.EnsureLength(ref Pools, worldId);
56
ArrayExtension.EnsureLength(ref Pools, worldId);
57
58
ref ComponentPool<T> pool = ref Pools[worldId];
59
if (pool == null)
58
ref ComponentPool<T> pool = ref Pools[worldId];
59
if (pool == null)
60
{
61
pool = new ComponentPool<T>(worldId, maxEntityCount, maxComponentCount);
61
pool = new ComponentPool<T>(worldId, maxEntityCount, maxComponentCount);
62
}
63
64
return pool;
64
return pool;
65
}
66
}
66
}
67
68
[MethodImpl(MethodImplOptions.AggressiveInlining)]
69
public static ComponentPool<T> Get(int worldId) => worldId < Pools.Length ? Pools[worldId] : null;
69
public static ComponentPool<T> Get(int worldId) => worldId < Pools.Length ? Pools[worldId] : null;
70
71
[MethodImpl(MethodImplOptions.AggressiveInlining)]
72
public static ComponentPool<T> GetOrCreate(int worldId, int maxComponentCount) => Get(worldId) ?? Add(worldId, W
72
public static ComponentPool<T> GetOrCreate(int worldId, int maxComponentCount) => Get(worldId) ?? Add(worldId, W
73
74
[MethodImpl(MethodImplOptions.AggressiveInlining)]
75
public static ComponentPool<T> GetOrCreate(int worldId) => Get(worldId) ?? Add(worldId, World.Infos[worldId].Max
75
public static ComponentPool<T> GetOrCreate(int worldId) => Get(worldId) ?? Add(worldId, World.Infos[worldId].Max
76
77
#endregion
78
}
79
}
27
28
#region Properties
29
30
public int MaxComponentCount { get; }
30
public int MaxComponentCount { get; }
31
32
public bool IsNotEmpty => _lastComponentIndex > -1;
33
37
38
static ComponentPool()
39
{
40
TypeInfo typeInfo = typeof(T).GetTypeInfo();
40
TypeInfo typeInfo = typeof(T).GetTypeInfo();
41
42
_isReferenceType = !typeInfo.IsValueType;
43
_isFlagType = typeInfo.IsFlagType();
44
_isManagedResourceType = typeInfo.GenericTypeArguments.Length > 0 && typeInfo.GetGenericTypeDefinition() ==
45
}
42
_isReferenceType = !typeInfo.IsValueType;
43
_isFlagType = typeInfo.IsFlagType();
44
_isManagedResourceType = typeInfo.GenericTypeArguments.Length > 0 && typeInfo.GetGenericTypeDefinition() ==
45
}
46
47
public ComponentPool(int worldId, int maxEntityCount, int maxComponentCount)
47
public ComponentPool(int worldId, int maxEntityCount, int maxComponentCount)
48
{
49
_worldId = worldId;
50
_maxEntityCount = maxEntityCount;
51
MaxComponentCount = _isFlagType ? 1 : Math.Min(maxEntityCount, maxComponentCount);
49
_worldId = worldId;
50
_maxEntityCount = maxEntityCount;
51
MaxComponentCount = _isFlagType ? 1 : Math.Min(maxEntityCount, maxComponentCount);
52
53
_mapping = EmptyArray<int>.Value;
54
_links = EmptyArray<ComponentLink>.Value;
55
_components = EmptyArray<T>.Value;
56
_lastComponentIndex = -1;
53
_mapping = EmptyArray<int>.Value;
54
_links = EmptyArray<ComponentLink>.Value;
55
_components = EmptyArray<T>.Value;
56
_lastComponentIndex = -1;
57
58
Publisher<ComponentTypeReadMessage>.Subscribe(_worldId, On);
59
Publisher<EntityDisposedMessage>.Subscribe(_worldId, On);
60
Publisher<EntityCopyMessage>.Subscribe(_worldId, On);
61
Publisher<ComponentReadMessage>.Subscribe(_worldId, On);
58
Publisher<ComponentTypeReadMessage>.Subscribe(_worldId, On);
59
Publisher<EntityDisposedMessage>.Subscribe(_worldId, On);
60
Publisher<EntityCopyMessage>.Subscribe(_worldId, On);
61
Publisher<ComponentReadMessage>.Subscribe(_worldId, On);
62
63
if (_isManagedResourceType)
63
if (_isManagedResourceType)
64
{
65
Publisher<ManagedResourceReleaseAllMessage>.Subscribe(_worldId, On);
66
}
67
}
67
}
68
69
#endregion
70
71
#region Callbacks
72
73
private void On(in ComponentTypeReadMessage message) => message.Reader.OnRead<T>(MaxComponentCount);
73
private void On(in ComponentTypeReadMessage message) => message.Reader.OnRead<T>(MaxComponentCount);
74
75
private void On(in EntityDisposedMessage message) => Remove(message.EntityId);
75
private void On(in EntityDisposedMessage message) => Remove(message.EntityId);
76
77
private void On(in EntityCopyMessage message)
78
{
84
85
private void On(in ComponentReadMessage message)
86
{
87
int componentIndex = message.EntityId < _mapping.Length ? _mapping[message.EntityId] : -1;
88
if (componentIndex != -1)
87
int componentIndex = message.EntityId < _mapping.Length ? _mapping[message.EntityId] : -1;
88
if (componentIndex != -1)
89
{
90
message.Reader.OnRead(ref _components[componentIndex], new Entity(_worldId, _links[componentIndex].Entit
90
message.Reader.OnRead(ref _components[componentIndex], new Entity(_worldId, _links[componentIndex].Entit
91
}
92
}
92
}
93
94
private void On(in ManagedResourceReleaseAllMessage message)
95
{
110
private void ThrowMaxNumberOfComponentReached() => throw new InvalidOperationException($"Max number of component
111
112
[MethodImpl(MethodImplOptions.AggressiveInlining)]
113
public bool Has(int entityId) => entityId < _mapping.Length && _mapping[entityId] != -1;
113
public bool Has(int entityId) => entityId < _mapping.Length && _mapping[entityId] != -1;
114
115
[MethodImpl(MethodImplOptions.AggressiveInlining)]
116
public bool Set(int entityId, in T component)
117
{
118
ArrayExtension.EnsureLength(ref _mapping, entityId, _maxEntityCount, -1);
118
ArrayExtension.EnsureLength(ref _mapping, entityId, _maxEntityCount, -1);
119
120
ref int componentIndex = ref _mapping[entityId];
121
if (componentIndex != -1)
120
ref int componentIndex = ref _mapping[entityId];
121
if (componentIndex != -1)
122
{
123
if (_isManagedResourceType)
123
if (_isManagedResourceType)
124
{
125
Publisher.Publish(_worldId, new ManagedResourceReleaseMessage<T>(_components[componentIndex]));
125
Publisher.Publish(_worldId, new ManagedResourceReleaseMessage<T>(_components[componentIndex]));
126
}
127
128
_components[componentIndex] = component;
128
_components[componentIndex] = component;
129
130
if (_isManagedResourceType)
130
if (_isManagedResourceType)
131
{
132
Publisher.Publish(_worldId, new ManagedResourceRequestMessage<T>(new Entity(_worldId, entityId), com
132
Publisher.Publish(_worldId, new ManagedResourceRequestMessage<T>(new Entity(_worldId, entityId), com
133
}
134
135
return false;
135
return false;
136
}
137
138
if (_lastComponentIndex == MaxComponentCount - 1)
138
if (_lastComponentIndex == MaxComponentCount - 1)
139
{
140
if (_isFlagType)
141
{
145
ThrowMaxNumberOfComponentReached();
146
}
147
148
componentIndex = ++_lastComponentIndex;
148
componentIndex = ++_lastComponentIndex;
149
150
ArrayExtension.EnsureLength(ref _components, _lastComponentIndex, MaxComponentCount);
151
ArrayExtension.EnsureLength(ref _links, _lastComponentIndex, MaxComponentCount);
150
ArrayExtension.EnsureLength(ref _components, _lastComponentIndex, MaxComponentCount);
151
ArrayExtension.EnsureLength(ref _links, _lastComponentIndex, MaxComponentCount);
152
153
_components[_lastComponentIndex] = component;
154
_links[_lastComponentIndex] = new ComponentLink(entityId);
153
_components[_lastComponentIndex] = component;
154
_links[_lastComponentIndex] = new ComponentLink(entityId);
155
156
if (_isManagedResourceType)
156
if (_isManagedResourceType)
157
{
158
Publisher.Publish(_worldId, new ManagedResourceRequestMessage<T>(new Entity(_worldId, entityId), compone
159
}
160
161
return true;
161
return true;
162
}
163
164
[MethodImpl(MethodImplOptions.AggressiveInlining)]
195
[MethodImpl(MethodImplOptions.AggressiveInlining)]
196
public bool Remove(int entityId)
197
{
198
if (entityId >= _mapping.Length)
198
if (entityId >= _mapping.Length)
199
{
200
return false;
201
}
202
203
ref int componentIndex = ref _mapping[entityId];
204
if (componentIndex == -1)
203
ref int componentIndex = ref _mapping[entityId];
204
if (componentIndex == -1)
205
{
206
return false;
206
return false;
207
}
208
209
if (_isManagedResourceType)
209
if (_isManagedResourceType)
210
{
211
Publisher.Publish(_worldId, new ManagedResourceReleaseMessage<T>(_components[componentIndex]));
212
}
213
214
ref ComponentLink link = ref _links[componentIndex];
215
if (--link.ReferenceCount == 0)
214
ref ComponentLink link = ref _links[componentIndex];
215
if (--link.ReferenceCount == 0)
216
{
217
if (componentIndex != _lastComponentIndex)
217
if (componentIndex != _lastComponentIndex)
218
{
219
ComponentLink lastLink = _links[_lastComponentIndex];
220
_links[componentIndex] = lastLink;
221
_components[componentIndex] = _components[_lastComponentIndex];
222
if (lastLink.ReferenceCount == 1)
219
ComponentLink lastLink = _links[_lastComponentIndex];
220
_links[componentIndex] = lastLink;
221
_components[componentIndex] = _components[_lastComponentIndex];
222
if (lastLink.ReferenceCount == 1)
223
{
224
_mapping[lastLink.EntityId] = componentIndex;
225
}
224
_mapping[lastLink.EntityId] = componentIndex;
225
}
226
else
227
{
228
for (int i = 0; i < _mapping.Length; ++i)
235
}
236
}
237
238
if (_isReferenceType)
238
if (_isReferenceType)
239
{
240
_components[_lastComponentIndex] = default;
240
_components[_lastComponentIndex] = default;
241
}
242
--_lastComponentIndex;
243
}
242
--_lastComponentIndex;
243
}
244
else if (link.EntityId == entityId)
245
{
246
int linkIndex = componentIndex;
254
}
255
}
256
257
componentIndex = -1;
257
componentIndex = -1;
258
259
return true;
259
return true;
260
}
261
262
[MethodImpl(MethodImplOptions.AggressiveInlining)]
263
public ref T Get(int entityId) => ref _components[_mapping[entityId]];
263
public ref T Get(int entityId) => ref _components[_mapping[entityId]];
264
265
[MethodImpl(MethodImplOptions.AggressiveInlining)]
266
public Span<T> GetAll() => new Span<T>(_components, 0, _lastComponentIndex + 1);
270
}
16
}
7
8
public ComponentRemovedMessage(int entityId, ComponentEnum components)
9
{
10
EntityId = entityId;
11
Components = components;
12
}
10
EntityId = entityId;
11
Components = components;
12
}
13
}
14
}
14
}
36
37
void IComponentTypeReader.OnRead<T>(int maxComponentCount)
38
{
39
_types.Add(typeof(T), _currentType);
39
_types.Add(typeof(T), _currentType);
40
41
*_bufferP = (byte)EntryType.ComponentType;
42
ushort* entryType = (ushort*)(_bufferP + 1);
43
*(entryType++) = _currentType;
44
_stream.Write(_buffer, 0, sizeof(byte) + sizeof(ushort));
45
Converter<string>.Write(typeof(T).AssemblyQualifiedName, _stream, _buffer, _bufferP);
41
*_bufferP = (byte)EntryType.ComponentType;
42
ushort* entryType = (ushort*)(_bufferP + 1);
43
*(entryType++) = _currentType;
44
_stream.Write(_buffer, 0, sizeof(byte) + sizeof(ushort));
45
Converter<string>.Write(typeof(T).AssemblyQualifiedName, _stream, _buffer, _bufferP);
46
47
if (maxComponentCount != _maxEntityCount)
47
if (maxComponentCount != _maxEntityCount)
48
{
49
*_bufferP = (byte)EntryType.MaxComponentCount;
50
entryType = (ushort*)(_bufferP + 1);
51
*(entryType++) = _currentType;
52
*(int*)entryType = maxComponentCount;
49
*_bufferP = (byte)EntryType.MaxComponentCount;
50
entryType = (ushort*)(_bufferP + 1);
51
*(entryType++) = _currentType;
52
*(int*)entryType = maxComponentCount;
53
54
_stream.Write(_buffer, 0, sizeof(byte) + sizeof(ushort) + sizeof(int));
54
_stream.Write(_buffer, 0, sizeof(byte) + sizeof(ushort) + sizeof(int));
55
}
56
57
++_currentType;
58
}
57
++_currentType;
58
}
59
60
#endregion
61
}
62
}
31
32
void IComponentTypeReader.OnRead<T>(int maxComponentCount)
33
{
34
string shortName = typeof(T).Name;
34
string shortName = typeof(T).Name;
35
36
int repeatCount = 1;
37
while (_types.ContainsValue(shortName))
36
int repeatCount = 1;
37
while (_types.ContainsValue(shortName))
38
{
39
shortName = $"{typeof(T).Name}_{repeatCount++}";
39
shortName = $"{typeof(T).Name}_{repeatCount++}";
40
}
41
42
_types.Add(typeof(T), shortName);
42
_types.Add(typeof(T), shortName);
43
44
_writer.WriteLine($"{nameof(EntryType.ComponentType)} {shortName} {typeof(T).FullName}, {typeof(T).GetTypeIn
45
if (maxComponentCount != _maxEntityCount)
44
_writer.WriteLine($"{nameof(EntryType.ComponentType)} {shortName} {typeof(T).FullName}, {typeof(T).GetTypeIn
45
if (maxComponentCount != _maxEntityCount)
46
{
47
_writer.WriteLine($"{nameof(EntryType.MaxComponentCount)} {shortName} {maxComponentCount}");
47
_writer.WriteLine($"{nameof(EntryType.MaxComponentCount)} {shortName} {maxComponentCount}");
48
}
49
}
49
}
50
51
#endregion
52
}
53
}
95
96
static Converter()
97
{
98
_type = typeof(T);
99
TypeInfo typeInfo = _type.GetTypeInfo();
98
_type = typeof(T);
99
TypeInfo typeInfo = _type.GetTypeInfo();
100
101
if (_type == typeof(string))
101
if (_type == typeof(string))
102
{
103
_writeAction = (Converter.WriteAction<T>)(Delegate)new Converter.WriteAction<string>(StringConverter.Wri
104
_readAction = (Converter.ReadAction<T>)(Delegate)new Converter.ReadAction<string>(StringConverter.Read);
105
}
106
else if (typeInfo.IsArray)
106
else if (typeInfo.IsArray)
107
{
108
Type elementType = typeInfo.GetElementType();
109
114
{
115
try
116
{
117
if (typeInfo.IsUnmanaged())
117
if (typeInfo.IsUnmanaged())
118
{
119
TypeInfo unmanagedType = typeof(UnmanagedConverter<>).MakeGenericType(_type).GetTypeInfo();
119
TypeInfo unmanagedType = typeof(UnmanagedConverter<>).MakeGenericType(_type).GetTypeInfo();
120
121
_writeAction = (Converter.WriteAction<T>)unmanagedType.GetDeclaredMethod(nameof(UnmanagedConvert
122
_readAction = (Converter.ReadAction<T>)unmanagedType.GetDeclaredMethod(nameof(UnmanagedConverter
123
}
121
_writeAction = (Converter.WriteAction<T>)unmanagedType.GetDeclaredMethod(nameof(UnmanagedConvert
122
_readAction = (Converter.ReadAction<T>)unmanagedType.GetDeclaredMethod(nameof(UnmanagedConverter
123
}
124
else
125
{
126
DynamicMethod writeMethod = new DynamicMethod($"Write_{nameof(T)}", typeof(void), new[] { _type.
175
readGenerator.Emit(OpCodes.Ret);
176
_readAction = (Converter.ReadAction<T>)readMethod.CreateDelegate(typeof(Converter.ReadAction<T>)
177
}
178
}
178
}
179
catch
180
{
181
_writeAction = (in T _, Stream __, byte[] ___, byte* ____) => throw new InvalidOperationException($"
182
_readAction = (_, __, ___) => throw new InvalidOperationException($"Unable to handle type {_type.Ful
183
}
184
}
185
}
185
}
186
187
#endregion
188
229
230
public static void Write(in T value, Stream stream, byte[] buffer, byte* bufferP)
231
{
232
if (value == default)
232
if (value == default)
233
{
234
stream.WriteByte(0);
235
}
236
else if (_type == value.GetType())
236
else if (_type == value.GetType())
237
{
238
stream.WriteByte(1);
239
_writeAction(value, stream, buffer, bufferP);
240
}
238
stream.WriteByte(1);
239
_writeAction(value, stream, buffer, bufferP);
240
}
241
else
242
{
243
stream.WriteByte(2);
249
250
public static T Read(Stream stream, byte[] buffer, byte* bufferP)
251
{
252
switch (stream.ReadByte())
252
switch (stream.ReadByte())
253
{
254
case 0:
255
return default;
256
257
case 1:
258
return _readAction(stream, buffer, bufferP);
258
return _readAction(stream, buffer, bufferP);
259
260
default:
261
return Converter.GetReadAction<T>(StringConverter.Read(stream, buffer, bufferP))(stream, buffer, buf
267
}
96
private const string _arrayEnd = "]";
97
98
private static readonly bool _isValueType;
99
private static readonly char[] _split = new[] { ' ', '\t' };
99
private static readonly char[] _split = new[] { ' ', '\t' };
100
private static readonly Dictionary<string, ReadFieldAction> _readFieldActions;
101
private static readonly Converter.WriteAction<T> _writeAction;
102
private static readonly Converter.ReadAction<T> _readAction;
107
108
static Converter()
109
{
110
TypeInfo typeInfo = typeof(T).GetTypeInfo();
110
TypeInfo typeInfo = typeof(T).GetTypeInfo();
111
112
_isValueType = typeInfo.IsValueType;
112
_isValueType = typeInfo.IsValueType;
113
114
_readFieldActions = new Dictionary<string, ReadFieldAction>();
114
_readFieldActions = new Dictionary<string, ReadFieldAction>();
115
116
_writeAction = (in T v, StreamWriter w, int _) => w.WriteLine(v.ToString());
116
_writeAction = (in T v, StreamWriter w, int _) => w.WriteLine(v.ToString());
117
118
#region clr types
119
if (typeof(T) == typeof(bool))
119
if (typeof(T) == typeof(bool))
120
{
121
_readAction = Converter.ConvertRead<T>(Converter<bool>.GetReadAction(bool.Parse));
122
}
123
else if (typeof(T) == typeof(sbyte))
123
else if (typeof(T) == typeof(sbyte))
124
{
125
_readAction = Converter.ConvertRead<T>(Converter<sbyte>.GetReadAction(sbyte.Parse));
126
}
127
else if (typeof(T) == typeof(byte))
127
else if (typeof(T) == typeof(byte))
128
{
129
_readAction = Converter.ConvertRead<T>(Converter<byte>.GetReadAction(byte.Parse));
130
}
131
else if (typeof(T) == typeof(short))
131
else if (typeof(T) == typeof(short))
132
{
133
_readAction = Converter.ConvertRead<T>(Converter<short>.GetReadAction(short.Parse));
134
}
135
else if (typeof(T) == typeof(ushort))
135
else if (typeof(T) == typeof(ushort))
136
{
137
_readAction = Converter.ConvertRead<T>(Converter<ushort>.GetReadAction(ushort.Parse));
138
}
139
else if (typeof(T) == typeof(int))
139
else if (typeof(T) == typeof(int))
140
{
141
_readAction = Converter.ConvertRead<T>(Converter<int>.GetReadAction(int.Parse));
142
}
143
else if (typeof(T) == typeof(uint))
143
else if (typeof(T) == typeof(uint))
144
{
145
_readAction = Converter.ConvertRead<T>(Converter<uint>.GetReadAction(uint.Parse));
146
}
147
else if (typeof(T) == typeof(long))
147
else if (typeof(T) == typeof(long))
148
{
149
_readAction = Converter.ConvertRead<T>(Converter<long>.GetReadAction(long.Parse));
150
}
151
else if (typeof(T) == typeof(ulong))
151
else if (typeof(T) == typeof(ulong))
152
{
153
_readAction = Converter.ConvertRead<T>(Converter<ulong>.GetReadAction(ulong.Parse));
154
}
155
else if (typeof(T) == typeof(char))
155
else if (typeof(T) == typeof(char))
156
{
157
_readAction = Converter.ConvertRead<T>(Converter<char>.GetReadAction(CharParse));
158
}
159
else if (typeof(T) == typeof(decimal))
159
else if (typeof(T) == typeof(decimal))
160
{
161
_readAction = Converter.ConvertRead<T>(Converter<decimal>.GetReadAction(decimal.Parse));
162
}
163
else if (typeof(T) == typeof(double))
163
else if (typeof(T) == typeof(double))
164
{
165
_readAction = Converter.ConvertRead<T>(Converter<double>.GetReadAction(double.Parse));
166
}
167
else if (typeof(T) == typeof(float))
167
else if (typeof(T) == typeof(float))
168
{
169
_readAction = Converter.ConvertRead<T>(Converter<float>.GetReadAction(float.Parse));
170
}
171
else if (typeof(T) == typeof(string))
171
else if (typeof(T) == typeof(string))
172
{
173
_readAction = Converter.ConvertRead<T>(Converter<string>.GetReadAction(s => s));
174
}
175
else if (typeInfo.IsEnum)
175
else if (typeInfo.IsEnum)
176
{
177
ParameterExpression line = Expression.Parameter(typeof(string));
178
ParameterExpression reader = Expression.Parameter(typeof(StreamReader));
184
185
_readAction = Expression.Lambda<Converter.ReadAction<T>>(Expression.Call(typeof(Converter<T>).GetTypeInf
186
}
187
else if (typeInfo.IsArray)
187
else if (typeInfo.IsArray)
188
{
189
Type elementType = typeInfo.GetElementType();
190
198
199
bool Parameters_string(MethodInfo methodInfo)
200
{
201
ParameterInfo[] parameters = methodInfo.GetParameters();
201
ParameterInfo[] parameters = methodInfo.GetParameters();
202
203
return parameters.Length == 1
204
&& parameters[0].ParameterType == typeof(string);
203
return parameters.Length == 1
204
&& parameters[0].ParameterType == typeof(string);
205
}
206
207
bool Parameters_string_string(MethodInfo methodInfo)
208
{
209
ParameterInfo[] parameters = methodInfo.GetParameters();
209
ParameterInfo[] parameters = methodInfo.GetParameters();
210
211
return parameters.Length == 2
212
&& parameters[0].ParameterType == typeof(string)
213
&& parameters[1].ParameterType == typeof(string);
211
return parameters.Length == 2
212
&& parameters[0].ParameterType == typeof(string)
213
&& parameters[1].ParameterType == typeof(string);
214
}
215
216
#endregion
217
218
MethodInfo write = typeof(StreamWriter).GetTypeInfo().GetDeclaredMethods(nameof(StreamWriter.Write)).Fir
219
MethodInfo writeLineString = typeof(Converter<T>).GetTypeInfo().GetDeclaredMethod(nameof(StreamWriterWri
220
MethodInfo stringConcat = typeof(string).GetTypeInfo().GetDeclaredMethods(nameof(string.Concat)).First(P
218
MethodInfo write = typeof(StreamWriter).GetTypeInfo().GetDeclaredMethods(nameof(StreamWriter.Write)).Fir
219
MethodInfo writeLineString = typeof(Converter<T>).GetTypeInfo().GetDeclaredMethod(nameof(StreamWriterWri
220
MethodInfo stringConcat = typeof(string).GetTypeInfo().GetDeclaredMethods(nameof(string.Concat)).First(P
221
222
ParameterExpression value = Expression.Parameter(typeof(T).MakeByRefType());
223
ParameterExpression writer = Expression.Parameter(typeof(StreamWriter));
224
ParameterExpression indentation = Expression.Parameter(typeof(int));
222
ParameterExpression value = Expression.Parameter(typeof(T).MakeByRefType());
223
ParameterExpression writer = Expression.Parameter(typeof(StreamWriter));
224
ParameterExpression indentation = Expression.Parameter(typeof(int));
225
226
ParameterExpression space = Expression.Variable(typeof(string));
227
Expression assignSpace = Expression.Assign(space, Expression.Call(typeof(Converter<T>).GetTypeInfo().Get
226
ParameterExpression space = Expression.Variable(typeof(string));
227
Expression assignSpace = Expression.Assign(space, Expression.Call(typeof(Converter<T>).GetTypeInfo().Get
228
229
List<Expression> writeExpressions = new List<Expression>
230
{
231
Expression.Call(writeLineString, writer,Expression.Constant(_objectBegin)),
232
Expression.PreIncrementAssign(indentation),
233
assignSpace
234
};
229
List<Expression> writeExpressions = new List<Expression>
230
{
231
Expression.Call(writeLineString, writer,Expression.Constant(_objectBegin)),
232
Expression.PreIncrementAssign(indentation),
233
assignSpace
234
};
235
236
while (typeInfo != null)
236
while (typeInfo != null)
237
{
238
foreach (FieldInfo fieldInfo in typeInfo.DeclaredFields.Where(f => !f.IsStatic))
238
foreach (FieldInfo fieldInfo in typeInfo.DeclaredFields.Where(f => !f.IsStatic))
239
{
240
string name = GetFriendlyName(fieldInfo.Name);
241
Expression writeField = Expression.Block(
265
_readFieldActions.Add(name.ToUpperInvariant(), (ReadFieldAction)readMethod.CreateDelegate(typeof
266
}
267
268
typeInfo = typeInfo.BaseType?.GetTypeInfo();
268
typeInfo = typeInfo.BaseType?.GetTypeInfo();
269
}
270
271
writeExpressions.Add(Expression.PreDecrementAssign(indentation));
272
writeExpressions.Add(assignSpace);
273
writeExpressions.Add(Expression.Call(writeLineString, writer, Expression.Call(stringConcat, space, Expre
271
writeExpressions.Add(Expression.PreDecrementAssign(indentation));
272
writeExpressions.Add(assignSpace);
273
writeExpressions.Add(Expression.Call(writeLineString, writer, Expression.Call(stringConcat, space, Expre
274
275
_writeAction = Expression.Lambda<Converter.WriteAction<T>>(Expression.Block(new[] { space }, writeExpres
275
_writeAction = Expression.Lambda<Converter.WriteAction<T>>(Expression.Block(new[] { space }, writeExpres
276
277
_readAction = ReadAnyType;
277
_readAction = ReadAnyType;
278
}
279
}
279
}
280
281
#endregion
282
306
307
private static T ReadAnyType(string line, StreamReader reader)
308
{
309
while ((string.IsNullOrWhiteSpace(line) || line.Split(_split, StringSplitOptions.RemoveEmptyEntries)[0] != _
309
while ((string.IsNullOrWhiteSpace(line) || line.Split(_split, StringSplitOptions.RemoveEmptyEntries)[0] != _
310
{
311
line = reader.ReadLine();
312
}
313
314
T value = _isValueType ? default : (T)ObjectInitializer.Create(typeof(T));
314
T value = _isValueType ? default : (T)ObjectInitializer.Create(typeof(T));
315
316
while (!reader.EndOfStream)
316
while (!reader.EndOfStream)
317
{
318
line = reader.ReadLine();
319
string[] parts = line.Split(_split, 2, StringSplitOptions.RemoveEmptyEntries);
320
if (parts.Length > 0)
318
line = reader.ReadLine();
319
string[] parts = line.Split(_split, 2, StringSplitOptions.RemoveEmptyEntries);
320
if (parts.Length > 0)
321
{
322
if (parts[0] == _objectEnd)
322
if (parts[0] == _objectEnd)
323
{
324
break;
325
}
330
}
331
}
332
333
return value;
333
return value;
334
}
335
336
private static T[] ReadArray(string line, StreamReader reader)
364
return Enum.TryParse(entry, out TEnum value) ? value : throw new ArgumentException($"Unable to convert '{ent
365
}
366
367
private static string CreateIndentation(int indentation) => string.Join(string.Empty, Enumerable.Repeat('\t', in
367
private static string CreateIndentation(int indentation) => string.Join(string.Empty, Enumerable.Repeat('\t', in
368
369
private static void StreamWriterWriteLine(StreamWriter writer, string line) => writer.WriteLine(line);
369
private static void StreamWriterWriteLine(StreamWriter writer, string line) => writer.WriteLine(line);
370
371
private static void WriteArray(in T[] values, StreamWriter writer, int indentation)
372
{
385
386
public static void Write(in T value, StreamWriter writer, int indentation)
387
{
388
if (value == default)
388
if (value == default)
389
{
390
writer.WriteLine("null");
391
}
392
else if (typeof(T) == value.GetType())
392
else if (typeof(T) == value.GetType())
393
{
394
_writeAction(value, writer, indentation);
395
}
394
_writeAction(value, writer, indentation);
395
}
396
else
397
{
398
Type type = value.GetType();
403
404
public static T Read(string line, StreamReader reader)
405
{
406
while (string.IsNullOrWhiteSpace(line) && !reader.EndOfStream)
406
while (string.IsNullOrWhiteSpace(line) && !reader.EndOfStream)
407
{
408
line = reader.ReadLine();
409
}
410
411
if (line?.StartsWith("null") ?? true)
411
if (line?.StartsWith("null") ?? true)
412
{
413
return default;
414
}
415
else if (line.StartsWith("$type"))
415
else if (line.StartsWith("$type"))
416
{
417
return Converter.GetReadAction<T>(Type.GetType(line.Split(_split, 2)[1], true))(string.Empty, reader);
418
}
419
else
420
{
421
return _readAction(line, reader);
421
return _readAction(line, reader);
422
}
423
}
424
427
}
9
#if NETSTANDARD1_1
10
public static T[] Value { get; } = new T[0];
11
#else
12
public static T[] Value => Array.Empty<T>();
12
public static T[] Value => Array.Empty<T>();
13
#endif
14
}
15
}
28
29
internal Entity(int worldId, int entityId)
30
{
31
WorldId = worldId;
32
EntityId = entityId;
33
}
31
WorldId = worldId;
32
EntityId = entityId;
33
}
34
35
#endregion
36
37
#region Properties
38
39
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
40
private ref ComponentEnum Components => ref World.Infos[WorldId].EntityInfos[EntityId].Components;
40
private ref ComponentEnum Components => ref World.Infos[WorldId].EntityInfos[EntityId].Components;
41
42
/// <summary>
43
/// Gets whether the current <see cref="Entity"/> is alive or not.
87
/// <exception cref="InvalidOperationException"><see cref="Entity"/> was not created from a <see cref="World"/>.
88
public void Enable()
89
{
90
if (WorldId == 0) Throw("Entity was not created from a World");
90
if (WorldId == 0) Throw("Entity was not created from a World");
91
92
ref ComponentEnum components = ref Components;
93
if (!components[World.IsEnabledFlag])
92
ref ComponentEnum components = ref Components;
93
if (!components[World.IsEnabledFlag])
94
{
95
components[World.IsEnabledFlag] = true;
96
Publisher.Publish(WorldId, new EntityEnabledMessage(EntityId, components));
95
components[World.IsEnabledFlag] = true;
96
Publisher.Publish(WorldId, new EntityEnabledMessage(EntityId, components));
97
}
98
}
98
}
99
100
/// <summary>
101
/// Disables the current <see cref="Entity"/> so it does not appear in <see cref="EntitySet"/>.
119
/// <typeparam name="T">The type of the component.</typeparam>
120
/// <returns>true if the <see cref="Entity"/> has a component of type <typeparamref name="T"/> enabled; otherwis
121
[MethodImpl(MethodImplOptions.AggressiveInlining)]
122
public bool IsEnabled<T>() => WorldId == 0 ? false : Components[ComponentManager<T>.Flag];
122
public bool IsEnabled<T>() => WorldId == 0 ? false : Components[ComponentManager<T>.Flag];
123
124
/// <summary>
125
/// Enables the current <see cref="Entity"/> component of type <typeparamref name="T"/> so it can appear in <see
169
/// <exception cref="InvalidOperationException">Max number of component of type <typeparamref name="T"/> reached
170
public void Set<T>(in T component = default)
171
{
172
if (WorldId == 0) Throw("Entity was not created from a World");
172
if (WorldId == 0) Throw("Entity was not created from a World");
173
174
ref ComponentEnum components = ref Components;
175
if (ComponentManager<T>.GetOrCreate(WorldId).Set(EntityId, component))
174
ref ComponentEnum components = ref Components;
175
if (ComponentManager<T>.GetOrCreate(WorldId).Set(EntityId, component))
176
{
177
components[ComponentManager<T>.Flag] = true;
178
if (components[World.IsEnabledFlag])
177
components[ComponentManager<T>.Flag] = true;
178
if (components[World.IsEnabledFlag])
179
{
180
Publisher.Publish(WorldId, new ComponentAddedMessage<T>(EntityId, components));
180
Publisher.Publish(WorldId, new ComponentAddedMessage<T>(EntityId, components));
181
}
182
}
183
else if (components[World.IsEnabledFlag] && components[ComponentManager<T>.Flag])
182
}
183
else if (components[World.IsEnabledFlag] && components[ComponentManager<T>.Flag])
184
{
185
Publisher.Publish(WorldId, new ComponentChangedMessage<T>(EntityId, components));
185
Publisher.Publish(WorldId, new ComponentChangedMessage<T>(EntityId, components));
186
}
187
}
187
}
188
189
/// <summary>
190
/// Sets the value of the component of type <typeparamref name="T"/> on the current <see cref="Entity"/> to the
222
/// <typeparam name="T">The type of the component.</typeparam>
223
public void Remove<T>()
224
{
225
if (ComponentManager<T>.Get(WorldId)?.Remove(EntityId) ?? false)
225
if (ComponentManager<T>.Get(WorldId)?.Remove(EntityId) ?? false)
226
{
227
ref ComponentEnum components = ref Components;
228
components[ComponentManager<T>.Flag] = false;
229
if (components[World.IsEnabledFlag])
227
ref ComponentEnum components = ref Components;
228
components[ComponentManager<T>.Flag] = false;
229
if (components[World.IsEnabledFlag])
230
{
231
Publisher.Publish(WorldId, new ComponentRemovedMessage<T>(EntityId, components));
231
Publisher.Publish(WorldId, new ComponentRemovedMessage<T>(EntityId, components));
232
}
233
}
234
}
234
}
235
236
/// <summary>
237
/// Returns whether the current <see cref="Entity"/> has a component of type <typeparamref name="T"/>.
239
/// <typeparam name="T">The type of the component.</typeparam>
240
/// <returns>true if the <see cref="Entity"/> has a component of type <typeparamref name="T"/>; otherwise, false
241
[MethodImpl(MethodImplOptions.AggressiveInlining)]
242
public bool Has<T>() => WorldId < ComponentManager<T>.Pools.Length && (ComponentManager<T>.Pools[WorldId]?.Has(E
242
public bool Has<T>() => WorldId < ComponentManager<T>.Pools.Length && (ComponentManager<T>.Pools[WorldId]?.Has(E
243
244
/// <summary>
245
/// Gets the component of type <typeparamref name="T"/> on the current <see cref="Entity"/>.
248
/// <returns>A reference to the component.</returns>
249
/// <exception cref="Exception"><see cref="Entity"/> was not created from a <see cref="World"/> or does not have
250
[MethodImpl(MethodImplOptions.AggressiveInlining)]
251
public ref T Get<T>() => ref ComponentManager<T>.Pools[WorldId].Get(EntityId);
251
public ref T Get<T>() => ref ComponentManager<T>.Pools[WorldId].Get(EntityId);
252
253
/// <summary>
254
/// Makes it so when given <see cref="Entity"/> is disposed, current <see cref="Entity"/> will also be disposed.
382
/// </summary>
383
/// <param name="other">An object to compare with this object.</param>
384
/// <returns>true if the current object is equal to the other parameter; otherwise, false.</returns>
385
public bool Equals(Entity other) => EntityId == other.EntityId && WorldId == other.WorldId;
385
public bool Equals(Entity other) => EntityId == other.EntityId && WorldId == other.WorldId;
386
387
#endregion
388
396
/// true if obj and this instance are the same type and represent the same value;
397
/// otherwise, false.
398
/// </returns>
399
public override bool Equals(object obj) => obj is Entity entity && Equals(entity);
399
public override bool Equals(object obj) => obj is Entity entity && Equals(entity);
400
401
/// <summary>
402
/// Returns the hash code for this instance.
403
/// </summary>
404
/// <returns>A 32-bit signed integer that is the hash code for this instance.</returns>
405
public override int GetHashCode() => EntityId;
405
public override int GetHashCode() => EntityId;
406
407
/// <summary>
408
/// Returns a string representation of this instance.
415
}
31
}
26
/// <summary>
27
/// Gets the maximum capacity the current instance can grow to.
28
/// </summary>
29
public int MaxCapacity { get; }
29
public int MaxCapacity { get; }
30
31
/// <summary>
32
/// Gets current capacity of the current instance.
118
119
do
120
{
121
commandOffset = _nextCommandOffset;
122
nextCommandOffset = commandOffset + commandSize;
123
if (nextCommandOffset > _memory.Length)
121
commandOffset = _nextCommandOffset;
122
nextCommandOffset = commandOffset + commandSize;
123
if (nextCommandOffset > _memory.Length)
124
{
125
if (nextCommandOffset > MaxCapacity)
125
if (nextCommandOffset > MaxCapacity)
126
{
127
Throw();
128
}
129
130
_lockObject?.EnterWriteLock();
130
_lockObject?.EnterWriteLock();
131
try
132
{
133
ArrayExtension.EnsureLength(ref _memory, nextCommandOffset, MaxCapacity);
134
}
133
ArrayExtension.EnsureLength(ref _memory, nextCommandOffset, MaxCapacity);
134
}
135
finally
136
{
137
_lockObject?.ExitWriteLock();
138
}
137
_lockObject?.ExitWriteLock();
138
}
139
}
140
}
141
while (Interlocked.CompareExchange(ref _nextCommandOffset, nextCommandOffset, commandOffset) != commandOffse
141
while (Interlocked.CompareExchange(ref _nextCommandOffset, nextCommandOffset, commandOffset) != commandOffse
142
143
return commandOffset;
144
}
240
}
6
7
public EntityCreatedMessage(int entityId)
8
{
9
EntityId = entityId;
10
}
9
EntityId = entityId;
10
}
11
}
12
}
12
}
12
}
12
}
7
8
public EntityEnabledMessage(int entityId, ComponentEnum components)
9
{
10
EntityId = entityId;
11
Components = components;
12
}
10
EntityId = entityId;
11
Components = components;
12
}
13
}
14
}
100
}
100
}
100
}
45
}
46
EntityAddedEvent += value;
47
}
48
remove => EntityAddedEvent -= value;
48
remove => EntityAddedEvent -= value;
49
}
50
51
/// <summary>
62
/// </summary>
63
public int Count
64
{
65
get;
66
private set;
65
get;
66
private set;
67
}
68
69
#endregion
100
{
101
if (_filter(world.Info.EntityInfos[i].Components))
102
{
103
Add(i);
103
Add(i);
104
}
105
}
106
}
113
[MethodImpl(MethodImplOptions.AggressiveInlining)]
114
private void Add(int entityId)
115
{
116
ArrayExtension.EnsureLength(ref _mapping, entityId, _maxEntityCount, -1);
116
ArrayExtension.EnsureLength(ref _mapping, entityId, _maxEntityCount, -1);
117
118
ref int index = ref _mapping[entityId];
119
if (index == -1)
118
ref int index = ref _mapping[entityId];
119
if (index == -1)
120
{
121
index = Count++;
121
index = Count++;
122
123
ArrayExtension.EnsureLength(ref _entities, index, _maxEntityCount);
123
ArrayExtension.EnsureLength(ref _entities, index, _maxEntityCount);
124
125
_entities[index] = new Entity(_worldId, entityId);
126
EntityAddedEvent?.Invoke(_entities[index]);
125
_entities[index] = new Entity(_worldId, entityId);
126
EntityAddedEvent?.Invoke(_entities[index]);
127
}
128
}
129
130
[MethodImpl(MethodImplOptions.AggressiveInlining)]
131
private void Remove(int entityId)
132
{
133
if (entityId < _mapping.Length)
133
if (entityId < _mapping.Length)
134
{
135
ref int index = ref _mapping[entityId];
136
if (index != -1)
135
ref int index = ref _mapping[entityId];
136
if (index != -1)
137
{
138
Entity entity = _entities[index];
139
--Count;
138
Entity entity = _entities[index];
139
--Count;
140
141
if (index != Count)
141
if (index != Count)
142
{
143
_entities[index] = _entities[Count];
144
_mapping[_entities[Count].EntityId] = index;
145
}
146
147
index = -1;
147
index = -1;
148
149
EntityRemoved?.Invoke(entity);
149
EntityRemoved?.Invoke(entity);
150
}
151
}
152
}
155
156
internal void CheckedAdd(in EntityEnabledMessage message)
157
{
158
if (_filter(message.Components))
158
if (_filter(message.Components))
159
{
160
Add(message.EntityId);
160
Add(message.EntityId);
161
}
162
}
162
}
163
164
internal void CheckedAdd<T>(in ComponentAddedMessage<T> message)
165
{
166
if (_filter(message.Components))
166
if (_filter(message.Components))
167
{
168
Add(message.EntityId);
168
Add(message.EntityId);
169
}
170
}
170
}
171
172
internal void CheckedAdd<T>(in ComponentChangedMessage<T> message)
173
{
187
188
internal void Remove(in EntityDisposingMessage message) => Remove(message.EntityId);
189
190
internal void Remove(in EntityDisabledMessage message) => Remove(message.EntityId);
190
internal void Remove(in EntityDisabledMessage message) => Remove(message.EntityId);
191
192
internal void Remove<T>(in ComponentRemovedMessage<T> message) => Remove(message.EntityId);
192
internal void Remove<T>(in ComponentRemovedMessage<T> message) => Remove(message.EntityId);
193
194
internal void Remove<T>(in ComponentAddedMessage<T> message) => Remove(message.EntityId);
195
210
}
211
212
[MethodImpl(MethodImplOptions.AggressiveInlining)]
213
internal ReadOnlySpan<Entity> GetEntities(int start, int length) => new ReadOnlySpan<Entity>(_entities, start, l
213
internal ReadOnlySpan<Entity> GetEntities(int start, int length) => new ReadOnlySpan<Entity>(_entities, start, l
214
215
/// <summary>
216
/// Gets the <see cref="Entity"/> contained in the current <see cref="EntitySet"/>.
217
/// </summary>
218
/// <returns>A <see cref="ReadOnlySpan{T}"/> of the <see cref="Entity"/> contained in the current <see cref="Ent
219
[MethodImpl(MethodImplOptions.AggressiveInlining)]
220
public ReadOnlySpan<Entity> GetEntities() => GetEntities(0, Count);
220
public ReadOnlySpan<Entity> GetEntities() => GetEntities(0, Count);
221
222
/// <summary>
223
/// Clears current instance of its entities if it was created with some reactive filter (<seealso cref="EntitySe
247
}
495
}
103
}
64
}
35
{
36
if (withoutEitherFilters is null)
37
{
38
return c => c.Contains(withFilter);
38
return c => c.Contains(withFilter);
39
}
40
41
if (withoutEitherFilters.Count == 1)
129
}
77
78
void IComponentReader.OnRead<T>(ref T component, in Entity componentOwner)
79
{
80
if (!_types.TryGetValue(typeof(T), out ushort _))
80
if (!_types.TryGetValue(typeof(T), out ushort _))
81
{
82
_types.Add(typeof(T), _currentType);
82
_types.Add(typeof(T), _currentType);
83
84
*_bufferP = (byte)EntryType.ComponentType;
85
ushort* entryType = (ushort*)(_bufferP + 1);
86
*(entryType++) = _currentType;
87
_stream.Write(_buffer, 0, sizeof(byte) + sizeof(ushort));
88
Converter<string>.Write(typeof(T).AssemblyQualifiedName, _stream, _buffer, _bufferP);
84
*_bufferP = (byte)EntryType.ComponentType;
85
ushort* entryType = (ushort*)(_bufferP + 1);
86
*(entryType++) = _currentType;
87
_stream.Write(_buffer, 0, sizeof(byte) + sizeof(ushort));
88
Converter<string>.Write(typeof(T).AssemblyQualifiedName, _stream, _buffer, _bufferP);
89
90
++_currentType;
90
++_currentType;
91
}
92
93
Tuple<Entity, Type> componentKey = Tuple.Create(componentOwner, typeof(T));
94
if (_components.TryGetValue(componentKey, out int key))
93
Tuple<Entity, Type> componentKey = Tuple.Create(componentOwner, typeof(T));
94
if (_components.TryGetValue(componentKey, out int key))
95
{
96
*_bufferP = (byte)(_currentEntity.IsEnabled<T>() ? EntryType.ComponentSameAs : EntryType.DisabledCompone
97
ushort* typeId = (ushort*)(_bufferP + 1);
102
}
103
else
104
{
105
_components.Add(componentKey, _entityCount);
106
*_bufferP = (byte)(_currentEntity.IsEnabled<T>() ? EntryType.Component : EntryType.DisabledComponent);
107
*(ushort*)(_bufferP + 1) = _types[typeof(T)];
105
_components.Add(componentKey, _entityCount);
106
*_bufferP = (byte)(_currentEntity.IsEnabled<T>() ? EntryType.Component : EntryType.DisabledComponent);
107
*(ushort*)(_bufferP + 1) = _types[typeof(T)];
108
109
_stream.Write(_buffer, 0, sizeof(byte) + sizeof(ushort));
109
_stream.Write(_buffer, 0, sizeof(byte) + sizeof(ushort));
110
111
Converter<T>.Write(component, _stream, _buffer, _bufferP);
111
Converter<T>.Write(component, _stream, _buffer, _bufferP);
112
}
113
}
113
}
114
115
#endregion
116
}
117
}
66
67
void IComponentReader.OnRead<T>(ref T component, in Entity componentOwner)
68
{
69
if (!_types.TryGetValue(typeof(T), out _))
69
if (!_types.TryGetValue(typeof(T), out _))
70
{
71
string typeName = typeof(T).Name;
71
string typeName = typeof(T).Name;
72
73
int repeatCount = 1;
74
while (_types.ContainsValue(typeName))
73
int repeatCount = 1;
74
while (_types.ContainsValue(typeName))
75
{
76
typeName = $"{typeof(T).Name}_{repeatCount++}";
76
typeName = $"{typeof(T).Name}_{repeatCount++}";
77
}
78
79
_types.Add(typeof(T), typeName);
79
_types.Add(typeof(T), typeName);
80
81
_writer.WriteLine($"{nameof(EntryType.ComponentType)} {typeName} {typeof(T).FullName}, {typeof(T).GetTyp
81
_writer.WriteLine($"{nameof(EntryType.ComponentType)} {typeName} {typeof(T).FullName}, {typeof(T).GetTyp
82
}
83
84
Tuple<Entity, Type> componentKey = Tuple.Create(componentOwner, typeof(T));
85
if (_components.TryGetValue(componentKey, out int key))
84
Tuple<Entity, Type> componentKey = Tuple.Create(componentOwner, typeof(T));
85
if (_components.TryGetValue(componentKey, out int key))
86
{
87
string entry = _currentEntity.IsEnabled<T>() ? nameof(EntryType.ComponentSameAs) : nameof(EntryType.Disa
88
_writer.WriteLine($"{entry} {_types[typeof(T)]} {key}");
89
}
90
else
91
{
92
_components.Add(componentKey, _entityCount);
93
string entry = _currentEntity.IsEnabled<T>() ? nameof(EntryType.Component) : nameof(EntryType.DisabledCo
94
_writer.Write($"{entry} {_types[typeof(T)]} ");
95
Converter<T>.Write(component, _writer, 0);
92
_components.Add(componentKey, _entityCount);
93
string entry = _currentEntity.IsEnabled<T>() ? nameof(EntryType.Component) : nameof(EntryType.DisabledCo
94
_writer.Write($"{entry} {_types[typeof(T)]} ");
95
Converter<T>.Write(component, _writer, 0);
96
}
97
}
97
}
98
99
#endregion
100
}
101
}
18
19
#region Initialisation
20
21
public DisposableGroup(IEnumerable<IDisposable> disposables)
21
public DisposableGroup(IEnumerable<IDisposable> disposables)
22
{
23
_disposables = GetDisposables(disposables).ToArray();
24
}
23
_disposables = GetDisposables(disposables).ToArray();
24
}
25
26
#endregion
27
29
30
private IEnumerable<IDisposable> GetDisposables(IEnumerable<IDisposable> disposables)
31
{
32
foreach (IDisposable disposable in disposables)
32
foreach (IDisposable disposable in disposables)
33
{
34
if (disposable is DisposableGroup group)
34
if (disposable is DisposableGroup group)
35
{
36
foreach (IDisposable child in group._disposables)
37
{
40
}
41
else
42
{
43
yield return disposable;
43
yield return disposable;
44
}
45
}
46
}
46
}
47
48
#endregion
49
66
67
#region Methods
68
69
public static IDisposable Merge(this IEnumerable<IDisposable> disposables) => new DisposableGroup(disposables ??
69
public static IDisposable Merge(this IEnumerable<IDisposable> disposables) => new DisposableGroup(disposables ??
70
71
#endregion
72
}
73
}
28
29
private static IDisposable Subscribe(IPublisher publisher, Type type, object target)
30
{
31
List<IDisposable> subscriptions = new List<IDisposable>();
31
List<IDisposable> subscriptions = new List<IDisposable>();
32
33
try
34
{
35
while (type != null)
35
while (type != null)
36
{
37
foreach (MethodInfo method in type.GetTypeInfo().DeclaredMethods
38
.Where(m => m.GetCustomAttribute<SubscribeAttribute>(false) != null && (m.IsStatic || target !=
37
foreach (MethodInfo method in type.GetTypeInfo().DeclaredMethods
38
.Where(m => m.GetCustomAttribute<SubscribeAttribute>(false) != null && (m.IsStatic || target !=
39
{
40
ParameterInfo[] parameters = method.GetParameters();
40
ParameterInfo[] parameters = method.GetParameters();
41
42
if (parameters.Length != 1
43
|| !parameters[0].ParameterType.IsByRef
44
|| method.ReturnType != typeof(void))
42
if (parameters.Length != 1
43
|| !parameters[0].ParameterType.IsByRef
44
|| method.ReturnType != typeof(void))
45
{
46
throw new NotSupportedException($"Can't apply {nameof(SubscribeAttribute)} to \"{method.Name
47
}
48
49
Type argType = parameters[0].ParameterType.GetElementType();
50
subscriptions.Add((IDisposable)_subscribeMethod.MakeGenericMethod(argType).Invoke(
51
publisher,
52
new object[]
53
{
54
method.IsStatic
55
? method.CreateDelegate(typeof(ActionIn<>).MakeGenericType(argType))
56
: method.CreateDelegate(typeof(ActionIn<>).MakeGenericType(argType), target)
57
}));
49
Type argType = parameters[0].ParameterType.GetElementType();
50
subscriptions.Add((IDisposable)_subscribeMethod.MakeGenericMethod(argType).Invoke(
51
publisher,
52
new object[]
53
{
54
method.IsStatic
55
? method.CreateDelegate(typeof(ActionIn<>).MakeGenericType(argType))
56
: method.CreateDelegate(typeof(ActionIn<>).MakeGenericType(argType), target)
57
}));
58
}
59
60
type = type.GetTypeInfo().BaseType;
60
type = type.GetTypeInfo().BaseType;
61
}
62
}
62
}
63
catch
64
{
65
foreach (IDisposable disposable in subscriptions)
70
throw;
71
}
72
73
return subscriptions.Count > 1 ? subscriptions.Merge() : subscriptions.FirstOrDefault();
73
return subscriptions.Count > 1 ? subscriptions.Merge() : subscriptions.FirstOrDefault();
74
}
75
76
/// <summary>
111
public static IDisposable Subscribe<T>(this IPublisher publisher, T target)
112
where T : class
113
{
114
return Subscribe(
115
publisher ?? throw new ArgumentNullException(nameof(publisher)),
116
(target ?? throw new ArgumentNullException(nameof(target))).GetType(),
117
target);
114
return Subscribe(
115
publisher ?? throw new ArgumentNullException(nameof(publisher)),
116
(target ?? throw new ArgumentNullException(nameof(target))).GetType(),
117
target);
118
}
119
120
#endregion
122
}
Method | Cyclomatic complexity | NPath complexity | Sequence coverage | Branch coverage |
---|---|---|---|---|
Serialize(...) | 1 | 0 | 0% | 100% |
Serialize(...) | 1 | 0 | 100% | 100% |
14
/// <param name="serializer">The <see cref="ISerializer"/> instance to use.</param>
15
/// <param name="stream">The <see cref="Stream"/> in which the data will be saved.</param>
16
/// <param name="entities">The <see cref="Entity"/> instances to save.</param>
17
public static void Serialize(this ISerializer serializer, Stream stream, params Entity[] entities) => serializer
17
public static void Serialize(this ISerializer serializer, Stream stream, params Entity[] entities) => serializer
18
}
19
20
/// <summary>
54
}
15
16
#region Properties
17
18
public int LastInt => _lastInt;
18
public int LastInt => _lastInt;
19
20
#endregion
21
22
#region Initialisation
23
24
public IntDispenser(int startInt)
24
public IntDispenser(int startInt)
25
{
26
_freeInts = new ConcurrentStack<int>();
26
_freeInts = new ConcurrentStack<int>();
27
28
_lastInt = startInt;
29
}
28
_lastInt = startInt;
29
}
30
31
#endregion
32
34
35
public int GetFreeInt()
36
{
37
if (!_freeInts.TryPop(out int freeInt))
37
if (!_freeInts.TryPop(out int freeInt))
38
{
39
freeInt = Interlocked.Increment(ref _lastInt);
39
freeInt = Interlocked.Increment(ref _lastInt);
40
}
41
42
return freeInt;
42
return freeInt;
43
}
44
45
public void ReleaseInt(int releasedInt) => _freeInts.Push(releasedInt);
45
public void ReleaseInt(int releasedInt) => _freeInts.Push(releasedInt);
46
47
#endregion
48
}
49
}
23
}
6
7
public ManagedResourceReleaseMessage(T managedResource)
8
{
9
ManagedResource = managedResource;
10
}
9
ManagedResource = managedResource;
10
}
11
}
12
}
10
11
public ManagedResourceRequestMessage(in Entity entity, T managedResource)
12
{
13
Entity = entity;
14
ManagedResource = managedResource;
15
}
13
Entity = entity;
14
ManagedResource = managedResource;
15
}
16
}
17
}
21
/// <param name="info">The info used to identify the resource.</param>
22
public ManagedResource(TInfo info)
23
{
24
Info = info;
25
}
24
Info = info;
25
}
26
}
27
}
87
}
8
internal static class Publisher
9
{
10
[MethodImpl(MethodImplOptions.AggressiveInlining)]
11
public static void Publish<T>(int worldId, in T message) => Publisher<T>.Publish(worldId, message);
11
public static void Publish<T>(int worldId, in T message) => Publisher<T>.Publish(worldId, message);
12
}
13
14
internal static class Publisher<T>
117
}
28
29
public Subscription(int worldId, ActionIn<T> action)
30
{
31
_worldId = worldId;
32
_action = action;
33
}
31
_worldId = worldId;
32
_action = action;
33
}
34
35
#endregion
36
61
62
static Publisher()
63
{
64
_lockObject = new object();
64
_lockObject = new object();
65
66
Actions = new ActionIn<T>[2];
66
Actions = new ActionIn<T>[2];
67
68
if (typeof(T) != typeof(WorldDisposedMessage))
68
if (typeof(T) != typeof(WorldDisposedMessage))
69
{
70
Publisher<WorldDisposedMessage>.Subscribe(0, On);
70
Publisher<WorldDisposedMessage>.Subscribe(0, On);
71
}
72
}
72
}
73
74
#endregion
75
77
78
private static void On(in WorldDisposedMessage message)
79
{
80
lock (_lockObject)
80
lock (_lockObject)
81
{
82
if (message.WorldId < Actions.Length)
82
if (message.WorldId < Actions.Length)
83
{
84
Actions[message.WorldId] = null;
84
Actions[message.WorldId] = null;
85
}
86
}
87
}
86
}
87
}
88
89
#endregion
90
93
[MethodImpl(MethodImplOptions.AggressiveInlining)]
94
public static IDisposable Subscribe(int worldId, ActionIn<T> action)
95
{
96
lock (_lockObject)
96
lock (_lockObject)
97
{
98
ArrayExtension.EnsureLength(ref Actions, worldId);
98
ArrayExtension.EnsureLength(ref Actions, worldId);
99
100
Actions[worldId] += action;
101
}
100
Actions[worldId] += action;
101
}
102
103
return new Subscription(worldId, action);
103
return new Subscription(worldId, action);
104
}
105
106
[MethodImpl(MethodImplOptions.AggressiveInlining)]
107
public static void Publish(int worldId, in T message)
108
{
109
if (worldId < Actions.Length)
109
if (worldId < Actions.Length)
110
{
111
Actions[worldId]?.Invoke(message);
111
Actions[worldId]?.Invoke(message);
112
}
113
}
113
}
114
115
#endregion
116
}
117
}
70
}
9
10
public static void Write(in string value, Stream stream, byte[] buffer, byte* bufferP)
11
{
12
int* lengthP = (int*)bufferP;
13
*lengthP++ = value.Length;
14
char* valueP = (char*)lengthP;
12
int* lengthP = (int*)bufferP;
13
*lengthP++ = value.Length;
14
char* valueP = (char*)lengthP;
15
16
int count = sizeof(int);
16
int count = sizeof(int);
17
18
foreach (char c in value)
18
foreach (char c in value)
19
{
20
if (count + sizeof(char) > buffer.Length)
20
if (count + sizeof(char) > buffer.Length)
21
{
22
stream.Write(buffer, 0, count);
23
valueP = (char*)bufferP;
24
count = 0;
25
}
26
27
*valueP++ = c;
28
count += sizeof(char);
27
*valueP++ = c;
28
count += sizeof(char);
29
}
30
31
stream.Write(buffer, 0, count);
32
}
31
stream.Write(buffer, 0, count);
32
}
33
34
public static string Read(Stream stream, byte[] buffer, byte* bufferP)
35
{
36
string value = null;
36
string value = null;
37
38
if (stream.Read(buffer, 0, sizeof(int)) == sizeof(int))
38
if (stream.Read(buffer, 0, sizeof(int)) == sizeof(int))
39
{
40
int totalLength = *(int*)bufferP * sizeof(char);
40
int totalLength = *(int*)bufferP * sizeof(char);
41
42
while (totalLength > 0)
42
while (totalLength > 0)
43
{
44
int length = stream.Read(buffer, 0, Math.Min(buffer.Length, totalLength));
44
int length = stream.Read(buffer, 0, Math.Min(buffer.Length, totalLength));
45
46
if (length <= 0)
46
if (length <= 0)
47
{
48
throw new EndOfStreamException($"Could not deserialize type {typeof(string).FullName}");
49
}
50
51
totalLength -= length;
52
value += new string((char*)bufferP, 0, length / sizeof(char));
51
totalLength -= length;
52
value += new string((char*)bufferP, 0, length / sizeof(char));
53
}
54
}
55
56
return value;
56
return value;
57
}
58
59
#endregion
61
}
Method | Cyclomatic complexity | NPath complexity | Sequence coverage | Branch coverage |
---|---|---|---|---|
.cctor() | 1 | 0 | 100% | 100% |
.ctor(...) | 6 | 0 | 100% | 83.33% |
Update(...) | 2 | 0 | 100% | 100% |
Update(...) | 4 | 0 | 100% | 100% |
Dispose() | 4 | 0 | 100% | 50% |
.cctor() | 1 | 0 | 100% | 100% |
.ctor(...) | 6 | 0 | 100% | 83.33% |
Update(...) | 2 | 0 | 100% | 100% |
Update(...) | 4 | 0 | 100% | 100% |
Dispose() | 4 | 0 | 100% | 50% |
8
9
namespace DefaultEcs.System
10
{
11
/// <summary>
12
/// Represents an helper object used to update a system in parallel.
13
/// </summary>
14
/// <typeparam name="T">The type of the object used as state to update the system.</typeparam>
15
public sealed class SystemRunner<T> : IDisposable
16
{
17
#region Fields
18
19
internal static readonly SystemRunner<T> Default = new SystemRunner<T>(1);
11
//public interface ISystemRunner<T> : IDisposable
12
//{
13
// void Update(ASystem<T> system);
14
//}
15
16
//public interface IRunnableSystem<T> : ISystem<T>
17
//{
18
// void Update(int index, int maxIndex);
19
//}
20
21
private readonly CancellationTokenSource _disposeHandle;
22
private readonly WorkerBarrier _barrier;
23
private readonly Task[] _tasks;
24
25
private ASystem<T> _currentSystem;
26
27
#endregion
21
/// <summary>
22
/// Represents an helper object used to update a system in parallel.
23
/// </summary>
24
/// <typeparam name="T">The type of the object used as state to update the system.</typeparam>
25
public sealed class SystemRunner<T> : IDisposable
26
{
27
#region Fields
28
29
#region Initialisation
29
internal static readonly SystemRunner<T> Default = new SystemRunner<T>(1);
30
31
/// <summary>
32
/// Initialises a new instance of the <see cref="SystemRunner{T}"/> class.
33
/// </summary>
34
/// <param name="degreeOfParallelism">The number of <see cref="Task"/> instances used to update a system in para
35
/// <exception cref="ArgumentException"><paramref name="degreeOfParallelism"/> cannot be inferior to one.</excep
36
public SystemRunner(int degreeOfParallelism)
37
{
38
IEnumerable<int> indices = degreeOfParallelism >= 1 ? Enumerable.Range(0, degreeOfParallelism - 1) : throw n
39
40
_disposeHandle = new CancellationTokenSource();
41
_tasks = indices.Select(index => new Task(Update, index, TaskCreationOptions.LongRunning)).ToArray();
42
_barrier = degreeOfParallelism > 1 ? new WorkerBarrier(_tasks.Length) : null;
43
44
foreach (Task task in _tasks)
45
{
46
task.Start(TaskScheduler.Default);
47
}
48
}
31
private readonly CancellationTokenSource _disposeHandle;
32
private readonly WorkerBarrier _barrier;
33
private readonly Task[] _tasks;
34
35
private ASystem<T> _currentSystem;
36
37
#endregion
38
39
#region Initialisation
40
41
/// <summary>
42
/// Initialises a new instance of the <see cref="SystemRunner{T}"/> class.
43
/// </summary>
44
/// <param name="degreeOfParallelism">The number of <see cref="Task"/> instances used to update a system in para
45
/// <exception cref="ArgumentException"><paramref name="degreeOfParallelism"/> cannot be inferior to one.</excep
46
public SystemRunner(int degreeOfParallelism)
47
{
48
IEnumerable<int> indices = degreeOfParallelism >= 1 ? Enumerable.Range(0, degreeOfParallelism - 1) : throw n
49
50
#endregion
51
52
#region Methods
50
_disposeHandle = new CancellationTokenSource();
51
_tasks = indices.Select(index => new Task(Update, index, TaskCreationOptions.LongRunning)).ToArray();
52
_barrier = degreeOfParallelism > 1 ? new WorkerBarrier(_tasks.Length) : null;
53
54
private void Update(object state)
55
{
56
int index = (int)state;
57
58
goto Start;
54
foreach (Task task in _tasks)
55
{
56
task.Start(TaskScheduler.Default);
57
}
58
}
59
60
Work:
61
Volatile.Read(ref _currentSystem).Update(index, _tasks.Length);
62
63
_barrier.Signal();
64
65
Start:
66
_barrier.Start();
60
#endregion
61
62
#region Methods
63
64
private void Update(object state)
65
{
66
int index = (int)state;
67
68
if (!_disposeHandle.IsCancellationRequested)
69
{
70
goto Work;
71
}
72
}
73
74
[MethodImpl(MethodImplOptions.AggressiveInlining)]
75
internal void Update(ASystem<T> system)
76
{
77
Volatile.Write(ref _currentSystem, system);
78
79
_barrier?.StartWorkers();
80
81
system.Update(_tasks.Length, _tasks.Length);
82
83
_barrier?.WaitForWorkers();
84
}
85
86
#endregion
87
88
#region IDisposable
89
90
/// <summary>
91
/// Releases all the resources used by the current <see cref="SystemRunner{T}"/> instance.
92
/// </summary>
93
public void Dispose()
94
{
95
_disposeHandle.Cancel();
96
97
_barrier?.StartWorkers();
98
99
Task.WaitAll(_tasks);
100
101
_barrier?.Dispose();
102
_disposeHandle.Dispose();
103
}
104
105
#endregion
106
}
107
}
68
goto Start;
69
70
Work:
71
Volatile.Read(ref _currentSystem).Update(index, _tasks.Length);
72
73
_barrier.Signal();
74
75
Start:
76
_barrier.Start();
77
78
if (!_disposeHandle.IsCancellationRequested)
79
{
80
goto Work;
81
}
82
}
83
84
[MethodImpl(MethodImplOptions.AggressiveInlining)]
85
internal void Update(ASystem<T> system)
86
{
87
Volatile.Write(ref _currentSystem, system);
88
89
_barrier?.StartWorkers();
90
91
system.Update(_tasks.Length, _tasks.Length);
92
93
_barrier?.WaitForWorkers();
94
}
95
96
#endregion
97
98
#region IDisposable
99
100
/// <summary>
101
/// Releases all the resources used by the current <see cref="SystemRunner{T}"/> instance.
102
/// </summary>
103
public void Dispose()
104
{
105
_disposeHandle.Cancel();
106
107
_barrier?.StartWorkers();
108
109
Task.WaitAll(_tasks);
110
111
_barrier?.Dispose();
112
_disposeHandle.Dispose();
113
}
114
115
#endregion
116
}
117
}
30
{
31
#region IOperation
32
33
public void SetMaximumComponentCount(World world, int maxComponentCount) => world.SetMaximumComponentCount<T
33
public void SetMaximumComponentCount(World world, int maxComponentCount) => world.SetMaximumComponentCount<T
34
35
public void SetComponent(in Entity entity, string line, StreamReader reader)
36
{
37
try
38
{
39
entity.Set(Converter<T>.Read(line, reader));
40
}
39
entity.Set(Converter<T>.Read(line, reader));
40
}
41
catch (Exception exception)
42
{
43
throw new ArgumentException("Error while parsing", exception);
44
}
45
}
45
}
46
47
public void SetSameAsComponent(in Entity entity, in Entity reference) => entity.SetSameAs<T>(reference);
48
86
Entity currentEntity = default;
87
Dictionary<string, IOperation> operations = new Dictionary<string, IOperation>();
88
89
while (!reader.EndOfStream)
89
while (!reader.EndOfStream)
90
{
91
string[] lineParts = reader.ReadLine()?.Split(_split, 2, StringSplitOptions.RemoveEmptyEntries);
91
string[] lineParts = reader.ReadLine()?.Split(_split, 2, StringSplitOptions.RemoveEmptyEntries);
92
93
if (lineParts?.Length > 0)
93
if (lineParts?.Length > 0)
94
{
95
string entry = lineParts[0];
96
if (nameof(EntryType.Entity).Equals(entry))
95
string entry = lineParts[0];
96
if (nameof(EntryType.Entity).Equals(entry))
97
{
98
if (world == null)
99
{
106
entities.Add(lineParts[1], currentEntity);
107
}
108
}
109
else if (nameof(EntryType.DisabledEntity).Equals(entry))
109
else if (nameof(EntryType.DisabledEntity).Equals(entry))
110
{
111
if (world == null)
112
{
119
entities.Add(lineParts[1], currentEntity);
120
}
121
}
122
else if (lineParts.Length > 1)
122
else if (lineParts.Length > 1)
123
{
124
switch (entry)
125
{
141
break;
142
143
case nameof(EntryType.ComponentType):
144
string[] componentTypeEntry = lineParts[1].Split(_split, 2, StringSplitOptions.R
145
Type type = Type.GetType(componentTypeEntry[1], false) ?? throw new ArgumentExce
144
string[] componentTypeEntry = lineParts[1].Split(_split, 2, StringSplitOptions.R
145
Type type = Type.GetType(componentTypeEntry[1], false) ?? throw new ArgumentExce
146
147
operations.Add(
148
componentTypeEntry[0],
149
_operations.GetOrAdd(
150
type,
151
t => (IOperation)Activator.CreateInstance(typeof(Operation<>).MakeGeneri
152
break;
147
operations.Add(
148
componentTypeEntry[0],
149
_operations.GetOrAdd(
150
type,
151
t => (IOperation)Activator.CreateInstance(typeof(Operation<>).MakeGeneri
152
break;
153
154
case nameof(EntryType.MaxComponentCount):
155
if (!isNewWorld)
155
if (!isNewWorld)
156
{
157
throw new ArgumentException("Encoutered MaxComponentCount line");
158
}
159
string[] maxComponentCountEntry = lineParts[1].Split(_split, StringSplitOptions.
160
if (maxComponentCountEntry.Length < 2)
159
string[] maxComponentCountEntry = lineParts[1].Split(_split, StringSplitOptions.
160
if (maxComponentCountEntry.Length < 2)
161
{
162
throw new ArgumentException($"Unable to get {nameof(EntryType.MaxComponentCo
163
}
164
if (!operations.TryGetValue(maxComponentCountEntry[0], out IOperation operation)
164
if (!operations.TryGetValue(maxComponentCountEntry[0], out IOperation operation)
165
{
166
throw new ArgumentException($"Unknown component type used '{maxComponentCoun
167
}
168
if (!int.TryParse(maxComponentCountEntry[1], NumberStyles.Any, CultureInfo.Invar
168
if (!int.TryParse(maxComponentCountEntry[1], NumberStyles.Any, CultureInfo.Invar
169
{
170
throw new ArgumentException($"Unable to convert '{maxComponentCountEntry[1]}
171
}
172
173
if (world == null)
173
if (world == null)
174
{
175
world = new World();
176
}
177
178
operation.SetMaximumComponentCount(world, maxComponentCount);
179
break;
178
operation.SetMaximumComponentCount(world, maxComponentCount);
179
break;
180
181
case nameof(EntryType.Component):
182
if (currentEntity.Equals(default))
182
if (currentEntity.Equals(default))
183
{
184
throw new ArgumentException("Encountered a component before creation of an E
185
}
186
string[] componentEntry = lineParts[1].Split(_split, 2, StringSplitOptions.Remov
187
if (componentEntry.Length < 1)
186
string[] componentEntry = lineParts[1].Split(_split, 2, StringSplitOptions.Remov
187
if (componentEntry.Length < 1)
188
{
189
throw new ArgumentException($"Unable to get component type information from
190
}
191
if (!operations.TryGetValue(componentEntry[0].TrimEnd(':', '='), out operation))
191
if (!operations.TryGetValue(componentEntry[0].TrimEnd(':', '='), out operation))
192
{
193
throw new ArgumentException($"Unknown component type used '{componentEntry[0
194
}
195
196
operation.SetComponent(currentEntity, componentEntry.Length > 1 ? componentEntry
197
break;
196
operation.SetComponent(currentEntity, componentEntry.Length > 1 ? componentEntry
197
break;
198
199
case nameof(EntryType.ComponentSameAs):
200
if (currentEntity.Equals(default))
418
}
6
{
7
internal static class TypeExtension
8
{
9
public static bool IsFlagType(this TypeInfo typeInfo) => typeInfo.IsValueType && !typeInfo.IsEnum && !typeInfo.I
9
public static bool IsFlagType(this TypeInfo typeInfo) => typeInfo.IsValueType && !typeInfo.IsEnum && !typeInfo.I
10
11
public static bool IsUnmanaged(this TypeInfo typeInfo)
12
{
13
return typeInfo.IsEnum || (typeInfo.IsValueType && (typeInfo.IsPrimitive || typeInfo.DeclaredFields.Where(f
13
return typeInfo.IsEnum || (typeInfo.IsValueType && (typeInfo.IsPrimitive || typeInfo.DeclaredFields.Where(f
14
}
15
16
public static bool IsUnmanaged(this Type type) => type.GetTypeInfo().IsUnmanaged();
18
}
21
}
10
11
public static void Write(in T value, Stream stream, byte[] buffer, byte* bufferP)
12
{
13
if (sizeof(T) <= buffer.Length)
13
if (sizeof(T) <= buffer.Length)
14
{
15
*(T*)bufferP = value;
15
*(T*)bufferP = value;
16
17
stream.Write(buffer, 0, sizeof(T));
18
}
17
stream.Write(buffer, 0, sizeof(T));
18
}
19
else
20
{
21
int count = 0;
40
41
public static T Read(Stream stream, byte[] buffer, byte* bufferP)
42
{
43
if (sizeof(T) <= buffer.Length)
43
if (sizeof(T) <= buffer.Length)
44
{
45
if (stream.Read(buffer, 0, sizeof(T)) == sizeof(T))
45
if (stream.Read(buffer, 0, sizeof(T)) == sizeof(T))
46
{
47
return *(T*)bufferP;
47
return *(T*)bufferP;
48
}
49
else
50
{
85
}
229
}
229
}
229
}
229
}
229
}
229
}
229
}
229
}
41
42
#region Properties
43
44
internal int LastEntityId => _entityIdDispenser.LastInt;
44
internal int LastEntityId => _entityIdDispenser.LastInt;
45
46
/// <summary>
47
/// Gets the maximum number of <see cref="Entity"/> this <see cref="World"/> can create.
48
/// </summary>
49
public int MaxEntityCount => Info.MaxEntityCount;
49
public int MaxEntityCount => Info.MaxEntityCount;
50
51
#endregion
52
68
/// </summary>
69
/// <param name="maxEntityCount">The maximum number of <see cref="Entity"/> that can exist in this <see cref="Wo
70
/// <exception cref="ArgumentException"><paramref name="maxEntityCount"/> cannot be negative.</exception>
71
public World(int maxEntityCount)
71
public World(int maxEntityCount)
72
{
73
if (maxEntityCount < 0)
73
if (maxEntityCount < 0)
74
{
75
throw new ArgumentException("Argument cannot be negative", nameof(maxEntityCount));
75
throw new ArgumentException("Argument cannot be negative", nameof(maxEntityCount));
76
}
77
78
_entityIdDispenser = new IntDispenser(-1);
79
WorldId = _worldIdDispenser.GetFreeInt();
78
_entityIdDispenser = new IntDispenser(-1);
79
WorldId = _worldIdDispenser.GetFreeInt();
80
81
Info = new WorldInfo(maxEntityCount);
81
Info = new WorldInfo(maxEntityCount);
82
83
lock (_lockObject)
83
lock (_lockObject)
84
{
85
ArrayExtension.EnsureLength(ref Infos, WorldId);
85
ArrayExtension.EnsureLength(ref Infos, WorldId);
86
87
Infos[WorldId] = Info;
88
}
87
Infos[WorldId] = Info;
88
}
89
90
this.Subscribe(this);
91
}
90
this.Subscribe(this);
91
}
92
93
/// <summary>
94
/// Initializes a new instance of the <see cref="World"/> class.
105
[Subscribe]
106
private void On(in EntityDisposingMessage message)
107
{
108
Info.EntityInfos[message.EntityId].Components.Clear();
109
EntityDisposed?.Invoke(new Entity(WorldId, message.EntityId));
110
}
108
EntityDisposed?.Invoke(new Entity(WorldId, message.EntityId));
109
Info.EntityInfos[message.EntityId].Components.Clear();
110
}
111
112
[Subscribe]
113
private void On(in EntityDisposedMessage message)
161
/// <exception cref="InvalidOperationException">Max number of <see cref="Entity"/> reached.</exception>
162
public Entity CreateEntity()
163
{
164
int entityId = _entityIdDispenser.GetFreeInt();
164
int entityId = _entityIdDispenser.GetFreeInt();
165
166
if (entityId >= MaxEntityCount)
166
if (entityId >= MaxEntityCount)
167
{
168
throw new InvalidOperationException("Max number of Entity reached");
169
}
170
171
ArrayExtension.EnsureLength(ref Info.EntityInfos, entityId, MaxEntityCount);
171
ArrayExtension.EnsureLength(ref Info.EntityInfos, entityId, MaxEntityCount);
172
173
Info.EntityInfos[entityId].Components[IsAliveFlag] = true;
174
Info.EntityInfos[entityId].Components[IsEnabledFlag] = true;
175
Publish(new EntityCreatedMessage(entityId));
173
Info.EntityInfos[entityId].Components[IsAliveFlag] = true;
174
Info.EntityInfos[entityId].Components[IsEnabledFlag] = true;
175
Publish(new EntityCreatedMessage(entityId));
176
177
return new Entity(WorldId, entityId);
177
return new Entity(WorldId, entityId);
178
}
179
180
/// <summary>
187
/// <exception cref="ArgumentException"><paramref name="maxComponentCount"/> cannot be negative.</exception>
188
public bool SetMaximumComponentCount<T>(int maxComponentCount)
189
{
190
if (maxComponentCount < 0)
190
if (maxComponentCount < 0)
191
{
192
throw new ArgumentException("Argument cannot be negative", nameof(maxComponentCount));
193
}
194
195
return ComponentManager<T>.GetOrCreate(WorldId, maxComponentCount).MaxComponentCount == maxComponentCount;
195
return ComponentManager<T>.GetOrCreate(WorldId, maxComponentCount).MaxComponentCount == maxComponentCount;
196
}
197
198
/// <summary>
224
/// <returns>All the <see cref="Entity"/> of the current <see cref="World"/>.</returns>
225
public IEnumerable<Entity> GetAllEntities()
226
{
227
for (int i = 0; i <= Math.Min(Info.EntityInfos.Length, LastEntityId); ++i)
227
for (int i = 0; i <= Math.Min(Info.EntityInfos.Length, LastEntityId); ++i)
228
{
229
if (Info.EntityInfos[i].Components[IsAliveFlag])
229
if (Info.EntityInfos[i].Components[IsAliveFlag])
230
{
231
yield return new Entity(WorldId, i);
231
yield return new Entity(WorldId, i);
232
}
233
}
234
}
234
}
235
236
/// <summary>
237
/// Calls on <paramref name="reader"/> with all the maximum number of component of the current <see cref="World"
251
/// <param name="action">The delegate to be called back.</param>
252
/// <returns>An <see cref="IDisposable"/> object used to unsubscribe.</returns>
253
[MethodImpl(MethodImplOptions.AggressiveInlining)]
254
public IDisposable Subscribe<T>(ActionIn<T> action) => Publisher<T>.Subscribe(WorldId, action);
254
public IDisposable Subscribe<T>(ActionIn<T> action) => Publisher<T>.Subscribe(WorldId, action);
255
256
/// <summary>
257
/// Publishes a <typeparamref name="T"/> object.
259
/// <typeparam name="T">The type of the object to publish.</typeparam>
260
/// <param name="message">The object to publish.</param>
261
[MethodImpl(MethodImplOptions.AggressiveInlining)]
262
public void Publish<T>(in T message) => Publisher<T>.Publish(WorldId, message);
262
public void Publish<T>(in T message) => Publisher<T>.Publish(WorldId, message);
263
264
#endregion
265
271
/// </summary>
272
public void Dispose()
273
{
274
lock (_lockObject)
274
lock (_lockObject)
275
{
276
Infos[WorldId] = null;
277
}
276
Infos[WorldId] = null;
277
}
278
279
Publish(new ManagedResourceReleaseAllMessage());
280
Publisher.Publish(0, new WorldDisposedMessage(WorldId));
281
_worldIdDispenser.ReleaseInt(WorldId);
279
Publish(new ManagedResourceReleaseAllMessage());
280
Publisher.Publish(0, new WorldDisposedMessage(WorldId));
281
_worldIdDispenser.ReleaseInt(WorldId);
282
283
GC.SuppressFinalize(this);
284
}
283
GC.SuppressFinalize(this);
284
}
285
286
#endregion
287
298
}
6
7
public WorldDisposedMessage(int worldId)
8
{
9
WorldId = worldId;
10
}
9
WorldId = worldId;
10
}
11
}
12
}
8
9
public EntityInfo[] EntityInfos;
10
11
public WorldInfo(int maxEntityCount)
11
public WorldInfo(int maxEntityCount)
12
{
13
MaxEntityCount = maxEntityCount;
13
MaxEntityCount = maxEntityCount;
14
15
EntityInfos = EmptyArray<EntityInfo>.Value;
16
}
15
EntityInfos = EmptyArray<EntityInfo>.Value;
16
}
17
}
18
}