Skip to content

Commit 757f739

Browse files
authored
Add basic reactive system implementation (#5)
* Add basic reactive system implementation Allows for basic observable triggering systems, this should have been implemented originally really but better late than never. * Made recommended Codacity changes
1 parent f7dff49 commit 757f739

9 files changed

Lines changed: 168 additions & 5 deletions

File tree

src/SystemRx.Tests.Infrastructure/SanityTests/DependencyInjectionSanityTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ public void should_bind_and_resolve_framework_module(IDependencyRegistry registr
7272
var systems = resolver.ResolveAll<IConventionalSystemHandler>();
7373
Assert.NotNull(systems);
7474
Assert.NotEmpty(systems);
75-
Assert.Equal(3, systems.Count());
75+
Assert.Equal(4, systems.Count());
7676

7777
var timeTracker = resolver.Resolve<ITimeTracker>();
7878
Assert.NotNull(timeTracker);

src/SystemsRx.Infrastructure/Modules/FrameworkModule.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ public void Setup(IDependencyRegistry registry)
2020
registry.Bind<IConventionalSystemHandler, ManualSystemHandler>();
2121
registry.Bind<IConventionalSystemHandler, BasicSystemHandler>();
2222
registry.Bind<IConventionalSystemHandler, ReactToEventSystemHandler>();
23+
registry.Bind<IConventionalSystemHandler, ReactiveSystemHandler>();
2324
registry.Bind<ISystemExecutor, SystemExecutor>();
2425
registry.Bind<IUpdateScheduler, DefaultUpdateScheduler>();
2526
registry.Bind<ITimeTracker>(x => x.ToBoundType(typeof(IUpdateScheduler)));

src/SystemsRx.Plugins.Transforms/Extensions/Transform2DExtensions.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@ namespace SystemsRx.Plugins.Transforms.Extensions
66
{
77
public static class Transform2DExtensions
88
{
9-
const float RadiansToDegrees = 180.0f / MathF.PI;
10-
const float DegreesToRadians = MathF.PI / 180.0f;
11-
const float Radian90Degrees = 90 * DegreesToRadians;
9+
public static readonly float RadiansToDegrees = 180.0f / MathF.PI;
10+
public static readonly float DegreesToRadians = MathF.PI / 180.0f;
11+
public static readonly float Radian90Degrees = 90 * DegreesToRadians;
1212

1313
/// <summary>
1414
/// Returns the transforms rotation but converted to degrees
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
using System;
2+
using System.Numerics;
3+
4+
namespace SystemsRx.Plugins.Transforms.Extensions
5+
{
6+
public static class VectorExtensions
7+
{
8+
public static float ToAngle(this Vector2 vector)
9+
{ return MathF.Atan2(vector.Y, vector.X) * Transform2DExtensions.RadiansToDegrees; }
10+
}
11+
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
using System;
2+
using System.Linq;
3+
using NSubstitute;
4+
using SystemsRx.Events;
5+
using SystemsRx.Executor.Handlers.Conventional;
6+
using SystemsRx.MicroRx.Subjects;
7+
using SystemsRx.Systems;
8+
using SystemsRx.Systems.Conventional;
9+
using SystemsRx.Tests.Models;
10+
using SystemsRx.Tests.SystemsRx.Handlers.Helpers;
11+
using Xunit;
12+
13+
namespace SystemsRx.Tests.SystemsRx.Handlers
14+
{
15+
public class ReactiveSystemHandlerTests
16+
{
17+
public interface MultipleOfSameInterface : IReactiveSystem<int>, IReactiveSystem<float>
18+
{
19+
20+
}
21+
22+
[Fact]
23+
public void should_correctly_handle_systems()
24+
{
25+
var reactiveSystemHandler = new ReactiveSystemHandler();
26+
27+
var fakeMatchingSystem = Substitute.For<IReactiveSystem<int>>();
28+
var fakeNonMatchingSystem1 = Substitute.For<ISystem>();
29+
var fakeNonMatchingSystem2 = Substitute.For<ISystem>();
30+
31+
Assert.True(reactiveSystemHandler.CanHandleSystem(fakeMatchingSystem));
32+
Assert.False(reactiveSystemHandler.CanHandleSystem(fakeNonMatchingSystem1));
33+
Assert.False(reactiveSystemHandler.CanHandleSystem(fakeNonMatchingSystem2));
34+
}
35+
36+
[Fact]
37+
public void should_destroy_and_dispose_system()
38+
{
39+
var mockSystem = Substitute.For<IReactiveSystem<int>>();
40+
var mockDisposable = Substitute.For<IDisposable>();
41+
42+
var systemHandler = new ReactiveSystemHandler();
43+
systemHandler._systemSubscriptions.Add(mockSystem, mockDisposable);
44+
systemHandler.DestroySystem(mockSystem);
45+
46+
mockDisposable.Received(1).Dispose();
47+
Assert.Equal(0, systemHandler._systemSubscriptions.Count);
48+
}
49+
50+
[Fact]
51+
public void should_process_events_with_multiple_interfaces()
52+
{
53+
var dummyIntSubject = new Subject<int>();
54+
var dummyFloatSubject = new Subject<float>();
55+
56+
var mockSystem = Substitute.For<MultipleOfSameInterface>();
57+
var dummyInt = 100;
58+
var dummyFloat = 2.5f;
59+
60+
(mockSystem as IReactiveSystem<int>).ReactTo().Returns(dummyIntSubject);
61+
(mockSystem as IReactiveSystem<float>).ReactTo().Returns(dummyFloatSubject);
62+
63+
var systemHandler = new ReactiveSystemHandler();
64+
systemHandler.SetupSystem(mockSystem);
65+
dummyIntSubject.OnNext(dummyInt);
66+
dummyFloatSubject.OnNext(dummyFloat);
67+
68+
(mockSystem as IReactiveSystem<int>).Received(1).Execute(Arg.Is(dummyInt));
69+
(mockSystem as IReactiveSystem<float>).Received(1).Execute(Arg.Is(dummyFloat));
70+
}
71+
}
72+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Reflection;
5+
using SystemsRx.Attributes;
6+
using SystemsRx.Events;
7+
using SystemsRx.Extensions;
8+
using SystemsRx.MicroRx.Disposables;
9+
using SystemsRx.MicroRx.Extensions;
10+
using SystemsRx.Systems;
11+
using SystemsRx.Systems.Conventional;
12+
13+
namespace SystemsRx.Executor.Handlers.Conventional
14+
{
15+
[Priority(6)]
16+
public class ReactiveSystemHandler : IConventionalSystemHandler
17+
{
18+
private readonly MethodInfo _setupSystemGenericMethodInfo;
19+
public readonly IDictionary<ISystem, IDisposable> _systemSubscriptions;
20+
21+
public ReactiveSystemHandler()
22+
{
23+
_systemSubscriptions = new Dictionary<ISystem, IDisposable>();
24+
_setupSystemGenericMethodInfo = typeof(ReactiveSystemHandler).GetMethod(nameof(SetupSystemGeneric));
25+
}
26+
27+
public bool CanHandleSystem(ISystem system)
28+
{ return system.IsReactiveSystem(); }
29+
30+
public Type[] GetMatchingInterfaces(ISystem system)
31+
{ return system.GetGenericInterfacesFor(typeof(IReactiveSystem<>)).ToArray(); }
32+
33+
public void SetupSystem(ISystem system)
34+
{
35+
var matchingInterfaces = GetMatchingInterfaces(system);
36+
var disposables = new List<IDisposable>();
37+
foreach (var matchingInterface in matchingInterfaces)
38+
{
39+
var dataType = matchingInterface.GetGenericArguments()[0];
40+
var disposable = (IDisposable)_setupSystemGenericMethodInfo.MakeGenericMethod(dataType).Invoke(this, new object[] { system });
41+
disposables.Add(disposable);
42+
}
43+
_systemSubscriptions.Add(system, new CompositeDisposable(disposables));
44+
}
45+
46+
public IDisposable SetupSystemGeneric<T>(IReactiveSystem<T> system)
47+
{ return system.ReactTo().Subscribe(system.Execute); }
48+
49+
public void DestroySystem(ISystem system)
50+
{ _systemSubscriptions.RemoveAndDispose(system); }
51+
52+
public void Dispose()
53+
{
54+
_systemSubscriptions.Values.DisposeAll();
55+
_systemSubscriptions.Clear();
56+
}
57+
}
58+
}

src/SystemsRx/Extensions/ISystemExtensions.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,5 +37,8 @@ public static IEnumerable<Type> GetGenericInterfaceType(this ISystem system, Typ
3737

3838
public static bool IsReactToEventSystem(this ISystem system)
3939
{ return system.MatchesSystemTypeWithGeneric(typeof(IReactToEventSystem<>)); }
40+
41+
public static bool IsReactiveSystem(this ISystem system)
42+
{ return system.MatchesSystemTypeWithGeneric(typeof(IReactiveSystem<>)); }
4043
}
4144
}

src/SystemsRx/Systems/Conventional/IBasicSystem.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ namespace SystemsRx.Systems.Conventional
1212
public interface IBasicSystem : ISystem
1313
{
1414
/// <summary>
15-
/// The processor to handle the entity
15+
/// The method to execute every update
1616
/// </summary>
1717
void Execute(ElapsedTime elapsedTime);
1818
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
using System;
2+
3+
namespace SystemsRx.Systems.Conventional
4+
{
5+
public interface IReactiveSystem<T> : ISystem
6+
{
7+
/// <summary>
8+
/// Returns and observable indicating when the system should execute
9+
/// </summary>
10+
/// <returns>Observable indicating when the system should execute</returns>
11+
IObservable<T> ReactTo();
12+
13+
/// <summary>
14+
/// Thhe method to execute on triggering
15+
/// </summary>
16+
void Execute(T data);
17+
}
18+
}

0 commit comments

Comments
 (0)