Skip to content

Commit ff4999b

Browse files
committed
Merge branch 'basic-system'
2 parents 2d2ebee + 99237e5 commit ff4999b

20 files changed

+309
-44
lines changed

appveyor.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
version: 3.7.{build}
1+
version: 3.8.{build}
22
branches:
33
only:
44
- master

src/EcsRx.Infrastructure/Modules/FrameworkModule.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@
99
using EcsRx.Infrastructure.Dependencies;
1010
using EcsRx.Infrastructure.Events;
1111
using EcsRx.Infrastructure.Extensions;
12-
using EcsRx.Infrastructure.Scheduling;
1312
using EcsRx.MicroRx.Events;
1413
using EcsRx.Pools;
14+
using EcsRx.Scheduling;
1515
using EcsRx.Threading;
1616

1717
namespace EcsRx.Infrastructure.Modules

src/EcsRx.Plugins.ReactiveSystems/Extensions/ISystemExtensions.cs

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -35,14 +35,5 @@ public static Type GetGenericInterfaceType(this ISystem system)
3535

3636
return matchingInterface;
3737
}
38-
39-
public static int[] GetGroupAffinities(this ISystem system)
40-
{
41-
var affinity = system.GetType()
42-
.GetCustomAttributes(typeof(CollectionAffinityAttribute), true)
43-
.FirstOrDefault();
44-
45-
return ((CollectionAffinityAttribute) affinity)?.CollectionIds;
46-
}
4738
}
4839
}

src/EcsRx.Plugins.ReactiveSystems/Handlers/ReactToGroupSystemHandler.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,13 +36,13 @@ public void SetupSystem(ISystem system)
3636
{
3737
var affinities = system.GetGroupAffinities();
3838
var observableGroup = _entityCollectionManager.GetObservableGroup(system.Group, affinities);
39-
var hasEntityPredicate = system.Group is IHasPredicate;
39+
var groupPredicate = system.Group as IHasPredicate;
4040
var isExtendedSystem = system is IReactToGroupExSystem;
4141
var castSystem = (IReactToGroupSystem)system;
4242
var reactObservable = castSystem.ReactToGroup(observableGroup);
4343
var runParallel = system.ShouldMutliThread();
4444

45-
if (!hasEntityPredicate)
45+
if (groupPredicate == null)
4646
{
4747
IDisposable noPredicateSub;
4848

@@ -55,7 +55,7 @@ public void SetupSystem(ISystem system)
5555
return;
5656
}
5757

58-
var groupPredicate = system.Group as IHasPredicate;
58+
5959
IDisposable predicateSub;
6060

6161
if (isExtendedSystem)
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using EcsRx.Collections;
4+
using EcsRx.Entities;
5+
using EcsRx.Executor.Handlers;
6+
using EcsRx.Groups;
7+
using EcsRx.Groups.Observable;
8+
using EcsRx.MicroRx.Subjects;
9+
using EcsRx.Scheduling;
10+
using EcsRx.Systems;
11+
using EcsRx.Threading;
12+
using NSubstitute;
13+
using Xunit;
14+
15+
namespace EcsRx.Tests.Framework.Handlers
16+
{
17+
public class BasicSystemHandlerTests
18+
{
19+
[Fact]
20+
public void should_correctly_handle_systems()
21+
{
22+
var mockCollectionManager = Substitute.For<IEntityCollectionManager>();
23+
var threadHandler = Substitute.For<IThreadHandler>();
24+
var observableScheduler = Substitute.For<IObservableScheduler>();
25+
var reactToEntitySystemHandler = new BasicSystemHandler(mockCollectionManager, threadHandler, observableScheduler);
26+
27+
var fakeMatchingSystem = Substitute.For<IBasicSystem>();
28+
var fakeNonMatchingSystem1 = Substitute.For<IManualSystem>();
29+
var fakeNonMatchingSystem2 = Substitute.For<ISystem>();
30+
31+
Assert.True(reactToEntitySystemHandler.CanHandleSystem(fakeMatchingSystem));
32+
Assert.False(reactToEntitySystemHandler.CanHandleSystem(fakeNonMatchingSystem1));
33+
Assert.False(reactToEntitySystemHandler.CanHandleSystem(fakeNonMatchingSystem2));
34+
}
35+
36+
[Fact]
37+
public void should_execute_system_without_predicate()
38+
{
39+
var fakeEntities = new List<IEntity>
40+
{
41+
Substitute.For<IEntity>(),
42+
Substitute.For<IEntity>()
43+
};
44+
45+
var mockObservableGroup = Substitute.For<IObservableGroup>();
46+
mockObservableGroup[0].Returns(fakeEntities[0]);
47+
mockObservableGroup[1].Returns(fakeEntities[1]);
48+
mockObservableGroup.Count.Returns(fakeEntities.Count);
49+
50+
var mockCollectionManager = Substitute.For<IEntityCollectionManager>();
51+
var threadHandler = Substitute.For<IThreadHandler>();
52+
var observableScheduler = Substitute.For<IObservableScheduler>();
53+
var observableSubject = new Subject<TimeSpan>();
54+
observableScheduler.OnUpdate.Returns(observableSubject);
55+
56+
var fakeGroup = new Group();
57+
mockCollectionManager.GetObservableGroup(Arg.Is(fakeGroup), Arg.Any<int[]>()).Returns(mockObservableGroup);
58+
59+
var mockSystem = Substitute.For<IBasicSystem>();
60+
mockSystem.Group.Returns(fakeGroup);
61+
62+
var systemHandler = new BasicSystemHandler(mockCollectionManager, threadHandler, observableScheduler);
63+
systemHandler.SetupSystem(mockSystem);
64+
65+
observableSubject.OnNext(TimeSpan.Zero);
66+
67+
mockSystem.ReceivedWithAnyArgs(2).Process(Arg.Any<IEntity>());
68+
Assert.Equal(1, systemHandler._systemSubscriptions.Count);
69+
Assert.NotNull(systemHandler._systemSubscriptions[mockSystem]);
70+
}
71+
72+
[Fact]
73+
public void should_only_execute_system_when_predicate_met()
74+
{
75+
var entityToMatch = Substitute.For<IEntity>();
76+
var idToMatch = 1;
77+
entityToMatch.Id.Returns(idToMatch);
78+
79+
var fakeEntities = new List<IEntity>
80+
{
81+
entityToMatch,
82+
Substitute.For<IEntity>()
83+
};
84+
85+
var mockObservableGroup = Substitute.For<IObservableGroup>();
86+
mockObservableGroup.GetEnumerator().Returns(fakeEntities.GetEnumerator());
87+
mockObservableGroup[0].Returns(fakeEntities[0]);
88+
mockObservableGroup[1].Returns(fakeEntities[1]);
89+
mockObservableGroup.Count.Returns(fakeEntities.Count);
90+
91+
var mockCollectionManager = Substitute.For<IEntityCollectionManager>();
92+
var threadHandler = Substitute.For<IThreadHandler>();
93+
var observableScheduler = Substitute.For<IObservableScheduler>();
94+
var observableSubject = new Subject<TimeSpan>();
95+
observableScheduler.OnUpdate.Returns(observableSubject);
96+
97+
var fakeGroup = new GroupWithPredicate(x => x.Id == idToMatch);
98+
mockCollectionManager.GetObservableGroup(Arg.Is(fakeGroup), Arg.Any<int[]>()).Returns(mockObservableGroup);
99+
100+
var mockSystem = Substitute.For<IBasicSystem>();
101+
mockSystem.Group.Returns(fakeGroup);
102+
103+
var systemHandler = new BasicSystemHandler(mockCollectionManager, threadHandler, observableScheduler);
104+
systemHandler.SetupSystem(mockSystem);
105+
106+
observableSubject.OnNext(TimeSpan.Zero);
107+
108+
mockSystem.ReceivedWithAnyArgs(1).Process(Arg.Is(entityToMatch));
109+
Assert.Equal(1, systemHandler._systemSubscriptions.Count);
110+
Assert.NotNull(systemHandler._systemSubscriptions[mockSystem]);
111+
}
112+
113+
[Fact]
114+
public void should_destroy_and_dispose_system()
115+
{
116+
var mockCollectionManager = Substitute.For<IEntityCollectionManager>();
117+
var threadHandler = Substitute.For<IThreadHandler>();
118+
var observableScheduler = Substitute.For<IObservableScheduler>();
119+
var mockSystem = Substitute.For<IBasicSystem>();
120+
var mockDisposable = Substitute.For<IDisposable>();
121+
122+
var systemHandler = new BasicSystemHandler(mockCollectionManager, threadHandler, observableScheduler);
123+
systemHandler._systemSubscriptions.Add(mockSystem, mockDisposable);
124+
systemHandler.DestroySystem(mockSystem);
125+
126+
mockDisposable.Received(1).Dispose();
127+
Assert.Equal(0, systemHandler._systemSubscriptions.Count);
128+
}
129+
}
130+
}

src/EcsRx.Tests/Framework/IEnumerableExtensionsTests.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -105,8 +105,8 @@ public void should_correctly_get_matching_entities()
105105
var entityGroup = new [] {hasOneAndTwo, hasAllComponents, hasOneAndThree};
106106

107107
var matchGroup1 = new Group(typeof(TestComponentOne), typeof(TestComponentTwo));
108-
var matchGroup2 = new Group(null, new [] {typeof(TestComponentOne), typeof(TestComponentTwo)}, new[] {typeof(TestComponentThree)});
109-
var matchGroup3 = new Group(null, new Type[0], new[] {typeof(TestComponentTwo)});
108+
var matchGroup2 = new Group(new [] {typeof(TestComponentOne), typeof(TestComponentTwo)}, new[] {typeof(TestComponentThree)});
109+
var matchGroup3 = new Group(new Type[0], new[] {typeof(TestComponentTwo)});
110110

111111

112112
var group1Results1 = entityGroup.MatchingGroup(matchGroup1).ToArray();

src/EcsRx.Tests/Framework/IGroupExtensionTests.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ public class IGroupExtensionTests
1212
public void should_correctly_verify_group_contains_all_required_components()
1313
{
1414
var requiredComponents = new[] {typeof(TestComponentOne), typeof(TestComponentTwo)};
15-
var dummyGroup = new Group(null, requiredComponents, new Type[0]);
15+
var dummyGroup = new Group(requiredComponents, new Type[0]);
1616

1717
var dummyComponents1 = new[] {typeof(TestComponentOne)};
1818
var dummyComponents2 = new[] {typeof(TestComponentOne), typeof(TestComponentTwo)};
@@ -31,7 +31,7 @@ public void should_correctly_verify_group_contains_all_required_components()
3131
public void should_correctly_verify_group_contains_any_required_components()
3232
{
3333
var requiredComponents = new[] {typeof(TestComponentOne), typeof(TestComponentTwo)};
34-
var dummyGroup = new Group(null, requiredComponents, new Type[0]);
34+
var dummyGroup = new Group(requiredComponents, new Type[0]);
3535

3636
var dummyComponents1 = new[] {typeof(TestComponentOne)};
3737
var dummyComponents2 = new[] {typeof(TestComponentOne), typeof(TestComponentTwo)};
@@ -50,7 +50,7 @@ public void should_correctly_verify_group_contains_any_required_components()
5050
public void should_correctly_verify_group_contains_any_excluded_components()
5151
{
5252
var excludedComponents = new[] {typeof(TestComponentOne), typeof(TestComponentTwo)};
53-
var dummyGroup = new Group(null, new Type[0], excludedComponents);
53+
var dummyGroup = new Group(new Type[0], excludedComponents);
5454

5555
var dummyComponents1 = new[] {typeof(TestComponentOne)};
5656
var dummyComponents2 = new[] {typeof(TestComponentOne), typeof(TestComponentTwo)};
@@ -70,7 +70,7 @@ public void should_correctly_verify_group_contains_any_components()
7070
{
7171
var requiredComponents = new[] {typeof(TestComponentOne)};
7272
var excludedComponents = new[] {typeof(TestComponentTwo)};
73-
var dummyGroup = new Group(null, requiredComponents, excludedComponents);
73+
var dummyGroup = new Group(requiredComponents, excludedComponents);
7474

7575
var dummyComponents1 = new[] {typeof(TestComponentOne)};
7676
var dummyComponents2 = new[] {typeof(TestComponentOne), typeof(TestComponentTwo)};

src/EcsRx.Tests/Plugins/ReactiveSystems/Handlers/ReactToDataSystemHandlerTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,7 @@ public void should_only_execute_system_when_predicate_met()
206206

207207
var mockCollectionManager = Substitute.For<IEntityCollectionManager>();
208208

209-
var fakeGroup = new Group(x => x.Id == id1);
209+
var fakeGroup = new GroupWithPredicate(x => x.Id == id1);
210210
mockCollectionManager.GetObservableGroup(Arg.Is(fakeGroup), Arg.Any<int[]>()).Returns(mockObservableGroup);
211211

212212
var firstEntitySubject = new Subject<int>();

src/EcsRx.Tests/Plugins/ReactiveSystems/Handlers/ReactToEntitySystemHandlerTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,7 @@ public void should_only_execute_system_when_predicate_met()
204204

205205
var mockCollectionManager = Substitute.For<IEntityCollectionManager>();
206206

207-
var fakeGroup = new Group(x => x.Id == id1);
207+
var fakeGroup = new GroupWithPredicate(x => x.Id == id1);
208208
mockCollectionManager.GetObservableGroup(Arg.Is(fakeGroup), Arg.Any<int[]>()).Returns(mockObservableGroup);
209209

210210
var firstEntitySubject = new Subject<IEntity>();

src/EcsRx.Tests/Plugins/ReactiveSystems/Handlers/ReactToGroupSystemHandlerTests.cs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,9 @@ public void should_execute_system_without_predicate()
4444
};
4545

4646
var mockObservableGroup = Substitute.For<IObservableGroup>();
47-
mockObservableGroup.GetEnumerator().Returns(fakeEntities.GetEnumerator());
47+
mockObservableGroup[0].Returns(fakeEntities[0]);
48+
mockObservableGroup[1].Returns(fakeEntities[1]);
49+
mockObservableGroup.Count.Returns(fakeEntities.Count);
4850

4951
var mockCollectionManager = Substitute.For<IEntityCollectionManager>();
5052
var threadHandler = Substitute.For<IThreadHandler>();
@@ -78,6 +80,9 @@ public void should_execute_system_without_predicate_with_pre_post()
7880

7981
var mockObservableGroup = Substitute.For<IObservableGroup>();
8082
mockObservableGroup.GetEnumerator().Returns(fakeEntities.GetEnumerator());
83+
mockObservableGroup[0].Returns(fakeEntities[0]);
84+
mockObservableGroup[1].Returns(fakeEntities[1]);
85+
mockObservableGroup.Count.Returns(fakeEntities.Count);
8186

8287
var mockCollectionManager = Substitute.For<IEntityCollectionManager>();
8388
var threadHandler = Substitute.For<IThreadHandler>();
@@ -122,7 +127,7 @@ public void should_only_execute_system_when_predicate_met()
122127
var mockCollectionManager = Substitute.For<IEntityCollectionManager>();
123128
var threadHandler = Substitute.For<IThreadHandler>();
124129

125-
var fakeGroup = new Group(x => x.Id == idToMatch);
130+
var fakeGroup = new GroupWithPredicate(x => x.Id == idToMatch);
126131
mockCollectionManager.GetObservableGroup(Arg.Is(fakeGroup), Arg.Any<int[]>()).Returns(mockObservableGroup);
127132

128133
var observableSubject = new Subject<IObservableGroup>();

src/EcsRx.Tests/Plugins/ReactiveSystems/Handlers/SetupSystemHandlerTests.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ public void should_execute_systems_when_predicate_met()
167167

168168
var mockCollectionManager = Substitute.For<IEntityCollectionManager>();
169169

170-
var fakeGroup = new Group(x => x.Id == fakeEntity1.Id);
170+
var fakeGroup = new GroupWithPredicate(x => x.Id == fakeEntity1.Id);
171171
mockCollectionManager.GetObservableGroup(Arg.Is(fakeGroup), Arg.Any<int[]>()).Returns(mockObservableGroup);
172172

173173
var mockSystem = Substitute.For<ISetupSystem>();
@@ -206,7 +206,7 @@ public void should_execute_systems_when_predicate_met_after_period()
206206

207207
var mockCollectionManager = Substitute.For<IEntityCollectionManager>();
208208

209-
var fakeGroup = new Group(x => x.Id == fakeEntity1.Id && DateTime.Now >= expectedDate);
209+
var fakeGroup = new GroupWithPredicate(x => x.Id == fakeEntity1.Id && DateTime.Now >= expectedDate);
210210
mockCollectionManager.GetObservableGroup(Arg.Is(fakeGroup), Arg.Any<int[]>()).Returns(mockObservableGroup);
211211

212212
var mockSystem = Substitute.For<ISetupSystem>();
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using EcsRx.Attributes;
5+
using EcsRx.Collections;
6+
using EcsRx.Entities;
7+
using EcsRx.Extensions;
8+
using EcsRx.Groups;
9+
using EcsRx.MicroRx.Extensions;
10+
using EcsRx.Scheduling;
11+
using EcsRx.Systems;
12+
using EcsRx.Threading;
13+
14+
namespace EcsRx.Executor.Handlers
15+
{
16+
[Priority(6)]
17+
public class BasicSystemHandler : IConventionalSystemHandler
18+
{
19+
public readonly IEntityCollectionManager _entityCollectionManager;
20+
public readonly IObservableScheduler _observableScheduler;
21+
public readonly IDictionary<ISystem, IDisposable> _systemSubscriptions;
22+
public readonly IThreadHandler _threadHandler;
23+
24+
public BasicSystemHandler(IEntityCollectionManager entityCollectionManager, IThreadHandler threadHandler, IObservableScheduler observableScheduler)
25+
{
26+
_entityCollectionManager = entityCollectionManager;
27+
_threadHandler = threadHandler;
28+
_observableScheduler = observableScheduler;
29+
_systemSubscriptions = new Dictionary<ISystem, IDisposable>();
30+
}
31+
32+
public bool CanHandleSystem(ISystem system)
33+
{ return system is IBasicSystem; }
34+
35+
public void SetupSystem(ISystem system)
36+
{
37+
var affinities = system.GetGroupAffinities();
38+
var observableGroup = _entityCollectionManager.GetObservableGroup(system.Group, affinities);
39+
var hasEntityPredicate = system.Group is IHasPredicate;
40+
var castSystem = (IBasicSystem)system;
41+
var runParallel = system.ShouldMutliThread();
42+
IDisposable subscription;
43+
44+
if (!hasEntityPredicate)
45+
{
46+
subscription = _observableScheduler.OnUpdate
47+
.Subscribe(x => ExecuteForGroup(observableGroup, castSystem, runParallel));
48+
}
49+
else
50+
{
51+
var groupPredicate = system.Group as IHasPredicate;
52+
subscription = _observableScheduler.OnUpdate
53+
.Subscribe(x => ExecuteForGroup(observableGroup
54+
.Where(groupPredicate.CanProcessEntity).ToList(), castSystem, runParallel));
55+
}
56+
57+
_systemSubscriptions.Add(system, subscription);
58+
}
59+
60+
private void ExecuteForGroup(IReadOnlyList<IEntity> entities, IBasicSystem castSystem, bool runParallel = false)
61+
{
62+
if (runParallel)
63+
{
64+
_threadHandler.For(0, entities.Count, i =>
65+
{ castSystem.Process(entities[i]); });
66+
return;
67+
}
68+
69+
for (var i = entities.Count - 1; i >= 0; i--)
70+
{ castSystem.Process(entities[i]); }
71+
}
72+
73+
public void DestroySystem(ISystem system)
74+
{ _systemSubscriptions.RemoveAndDispose(system); }
75+
76+
public void Dispose()
77+
{
78+
_systemSubscriptions.Values.DisposeAll();
79+
_systemSubscriptions.Clear();
80+
}
81+
}
82+
}

0 commit comments

Comments
 (0)