Skip to content

Commit cbabb12

Browse files
committed
Fix MultiReactive system retaining entities multiple times. Closes #818
1 parent 8bc660c commit cbabb12

File tree

4 files changed

+121
-34
lines changed

4 files changed

+121
-34
lines changed

Entitas/Entitas/Systems/MultiReactiveSystem.cs

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,16 +12,19 @@ public abstract class MultiReactiveSystem<TEntity, TContexts> : IReactiveSystem
1212
where TContexts : class, IContexts {
1313

1414
readonly ICollector[] _collectors;
15+
readonly HashSet<TEntity> _collectedEntities;
1516
readonly List<TEntity> _buffer;
1617
string _toStringCache;
1718

1819
protected MultiReactiveSystem(TContexts contexts) {
1920
_collectors = GetTrigger(contexts);
21+
_collectedEntities = new HashSet<TEntity>();
2022
_buffer = new List<TEntity>();
2123
}
2224

2325
protected MultiReactiveSystem(ICollector[] collectors) {
2426
_collectors = collectors;
27+
_collectedEntities = new HashSet<TEntity>();
2528
_buffer = new List<TEntity>();
2629
}
2730

@@ -65,24 +68,26 @@ public void Execute() {
6568
for (int i = 0; i < _collectors.Length; i++) {
6669
var collector = _collectors[i];
6770
if (collector.count != 0) {
68-
foreach (var e in collector.GetCollectedEntities<TEntity>()) {
69-
if (Filter(e)) {
70-
e.Retain(this);
71-
_buffer.Add(e);
72-
}
73-
}
74-
71+
_collectedEntities.UnionWith(collector.GetCollectedEntities<TEntity>());
7572
collector.ClearCollectedEntities();
7673
}
7774
}
7875

76+
foreach (var e in _collectedEntities) {
77+
if (Filter(e)) {
78+
e.Retain(this);
79+
_buffer.Add(e);
80+
}
81+
}
82+
7983
if (_buffer.Count != 0) {
8084
try {
8185
Execute(_buffer);
8286
} finally {
8387
for (int i = 0; i < _buffer.Count; i++) {
8488
_buffer[i].Release(this);
8589
}
90+
_collectedEntities.Clear();
8691
_buffer.Clear();
8792
}
8893
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using Entitas;
4+
5+
public class MultiTriggeredMultiReactiveSystemSpy : MultiReactiveSystem<IMyEntity, Contexts> {
6+
7+
public int didExecute { get { return _didExecute; } }
8+
public IEntity[] entities { get { return _entities; } }
9+
10+
public Action<List<IMyEntity>> executeAction;
11+
12+
protected int _didExecute;
13+
protected IEntity[] _entities;
14+
15+
public MultiTriggeredMultiReactiveSystemSpy(Contexts contexts) : base(contexts) {
16+
}
17+
18+
protected override ICollector[] GetTrigger(Contexts contexts) {
19+
return new ICollector[] {
20+
contexts.test.CreateCollector(TestMatcher.NameAge),
21+
contexts.test.CreateCollector(TestMatcher.NameAge.Removed())
22+
};
23+
}
24+
25+
protected override bool Filter(IMyEntity entity) {
26+
return true;
27+
}
28+
29+
protected override void Execute(List<IMyEntity> entities) {
30+
_didExecute += 1;
31+
32+
if (entities != null) {
33+
_entities = entities.ToArray();
34+
} else {
35+
_entities = null;
36+
}
37+
38+
if (executeAction != null) {
39+
executeAction(entities);
40+
}
41+
}
42+
}

Tests/Tests/Tests.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
<Reference Include="System" />
5353
</ItemGroup>
5454
<ItemGroup>
55+
<Compile Include="Fixtures\Systems\MultiTriggeredMultiReactiveSystemSpy.cs" />
5556
<Compile Include="Fixtures\Systems\TestJobSystem.cs" />
5657
<Compile Include="Fixtures\TestProperties.cs" />
5758
<Compile Include="Tests\Entitas.Migration\describe_M0450.cs" />

Tests/Tests/Tests/Entitas/describe_MultiReactiveSystem.cs

Lines changed: 66 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -4,43 +4,82 @@ class describe_MultiReactiveSystem : nspec {
44

55
void when_executing() {
66

7-
MultiReactiveSystemSpy system = null;
8-
TestEntity e1 = null;
9-
Test2Entity e2 = null;
7+
context["when triggered"] = () => {
108

11-
before = () => {
12-
var contexts = new Contexts();
13-
system = new MultiReactiveSystemSpy(contexts);
14-
system.executeAction = entities => {
15-
foreach (var e in entities) {
16-
e.nameAge.age += 10;
17-
}
9+
MultiReactiveSystemSpy system = null;
10+
TestEntity e1 = null;
11+
Test2Entity e2 = null;
12+
13+
before = () => {
14+
var contexts = new Contexts();
15+
system = new MultiReactiveSystemSpy(contexts);
16+
system.executeAction = entities => {
17+
foreach (var e in entities) {
18+
e.nameAge.age += 10;
19+
}
20+
};
21+
22+
e1 = contexts.test.CreateEntity();
23+
e1.AddNameAge("Max", 42);
24+
25+
e2 = contexts.test2.CreateEntity();
26+
e2.AddNameAge("Jack", 24);
27+
28+
system.Execute();
1829
};
1930

20-
e1 = contexts.test.CreateEntity();
21-
e1.AddNameAge("Max", 42);
31+
it["processes entities from different contexts"] = () => {
32+
system.entities.Length.should_be(2);
33+
system.entities.should_contain(e1);
34+
system.entities.should_contain(e2);
2235

23-
e2 = contexts.test2.CreateEntity();
24-
e2.AddNameAge("Jack", 24);
36+
e1.nameAge.age.should_be(52);
37+
e2.nameAge.age.should_be(34);
38+
};
2539

26-
system.Execute();
27-
};
40+
it["executes once"] = () => {
41+
system.didExecute.should_be(1);
42+
};
2843

29-
it["processes entities from different contexts"] = () => {
30-
system.entities.Length.should_be(2);
31-
system.entities.should_contain(e1);
32-
system.entities.should_contain(e2);
44+
it["retains once even when multiple collectors contain entity"] = () => {
45+
system.didExecute.should_be(1);
46+
};
3347

34-
e1.nameAge.age.should_be(52);
35-
e2.nameAge.age.should_be(34);
48+
it["can ToString"] = () => {
49+
system.ToString().should_be("MultiReactiveSystem(MultiReactiveSystemSpy)");
50+
};
3651
};
3752

38-
it["executes once"] = () => {
39-
system.didExecute.should_be(1);
40-
};
4153

42-
it["can ToString"] = () => {
43-
system.ToString().should_be("MultiReactiveSystem(MultiReactiveSystemSpy)");
54+
55+
context["when multiple collectors are triggered with same entity"] = () => {
56+
57+
MultiTriggeredMultiReactiveSystemSpy system = null;
58+
TestEntity e1 = null;
59+
60+
before = () => {
61+
var contexts = new Contexts();
62+
system = new MultiTriggeredMultiReactiveSystemSpy(contexts);
63+
e1 = contexts.test.CreateEntity();
64+
e1.AddNameAge("Max", 42);
65+
66+
e1.RemoveNameAge();
67+
68+
system.Execute();
69+
};
70+
71+
it["executes once"] = () => {
72+
system.didExecute.should_be(1);
73+
};
74+
75+
it["merges collected entities and removes duplicates"] = () => {
76+
system.entities.Length.should_be(1);
77+
};
78+
79+
it["clears merged collected entities"] = () => {
80+
system.Execute();
81+
system.didExecute.should_be(1);
82+
};
4483
};
4584
}
4685
}

0 commit comments

Comments
 (0)