Skip to content

Commit b467bb8

Browse files
authored
Merge pull request #7 from simulation-tree/dev/system-attributes
System attributes
2 parents 8273300 + 1a72999 commit b467bb8

10 files changed

Lines changed: 399 additions & 17 deletions

README.md

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
[![Test](https://github.com/simulation-tree/simulation/actions/workflows/test.yml/badge.svg)](https://github.com/simulation-tree/simulation/actions/workflows/test.yml)
44

5-
Library providing a way to organize and update systems with support for message handling.
5+
Library providing a way to broadcast messages to added listeners.
66

77
### Running simulators
88

@@ -18,21 +18,16 @@ public static void Main()
1818
simulator.Remove<ProgramSystems>();
1919
}
2020

21-
public class ProgramSystems : ISystem, IDisposable
21+
public class ProgramSystems : IDisposable
2222
{
2323
public ProgramSystems()
2424
{
25-
//initialize
25+
//before addition
2626
}
2727

2828
public void Dispose()
2929
{
30-
//clean up
31-
}
32-
33-
void ISystem.Update(Simulator simulator, double deltaTime)
34-
{
35-
//do work
30+
//after removal
3631
}
3732
}
3833
```
@@ -50,7 +45,6 @@ public partial class ListenerSystem : IListener<float>
5045
{
5146
void IListener<float>.Receive(ref float message)
5247
{
53-
//do something with this
5448
}
5549
}
5650
```
@@ -76,6 +70,32 @@ public struct LoadRequest
7670
}
7771
```
7872

73+
### Global simulator
74+
75+
Another way to have listeners and broadcasting setup, is using the included `GlobalSimulator` type.
76+
This approach is slimmer than with the `Simulator`, at the cost of the listeners being global to the entire
77+
runtime.
78+
```cs
79+
public class Program
80+
{
81+
public static void Main()
82+
{
83+
GlobalSimulatorLoader.Load();
84+
GlobalSimulator.Broadcast(32f);
85+
GlobalSimulator.Broadcast(32f);
86+
GlobalSimulator.Broadcast(32f);
87+
}
88+
}
89+
90+
public static class Systems
91+
{
92+
[Listener<float>]
93+
public static void Update(ref float message)
94+
{
95+
}
96+
}
97+
```
98+
7999
### Contributing and design
80100

81101
This library is created for composing behaviour of programs using systems, ideally created by
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
using System;
2+
3+
namespace Simulation
4+
{
5+
/// <summary>
6+
/// Attribute to mark static methods as listeners for messages of type <typeparamref name="T"/>.
7+
/// </summary>
8+
[AttributeUsage(AttributeTargets.Method)]
9+
public class ListenerAttribute<T> : Attribute where T : unmanaged
10+
{
11+
}
12+
}

core/GlobalSimulator.cs

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
using System;
2+
using System.Collections.Generic;
3+
4+
namespace Simulation
5+
{
6+
/// <summary>
7+
/// Dispatches messages for static methods with the <see cref="ListenerAttribute{T}"/>.
8+
/// </summary>
9+
public static class GlobalSimulator
10+
{
11+
private static readonly List<Action> clearFunctions = new();
12+
13+
/// <summary>
14+
/// Registers a listener for messages of type <typeparamref name="T"/>.
15+
/// </summary>
16+
public static void Register<T>(Receive<T> receive) where T : unmanaged
17+
{
18+
Array.Resize(ref Listeners<T>.list, Listeners<T>.list.Length + 1);
19+
Listeners<T>.list[^1] = receive;
20+
}
21+
22+
/// <summary>
23+
/// Registers a <paramref name="listener"/>.
24+
/// </summary>
25+
public static void Register<T>(IListener<T> listener) where T : unmanaged
26+
{
27+
Receive<T> receive = listener.Receive;
28+
Array.Resize(ref Listeners<T>.list, Listeners<T>.list.Length + 1);
29+
Listeners<T>.list[^1] = receive;
30+
}
31+
32+
/// <summary>
33+
/// Resets the global simulator to initial state, with no listeners registered.
34+
/// </summary>
35+
public static void Reset()
36+
{
37+
foreach (Action clearFunction in clearFunctions)
38+
{
39+
clearFunction();
40+
}
41+
}
42+
43+
/// <summary>
44+
/// Broadcasts the given <paramref name="message"/>.
45+
/// </summary>
46+
public static void Broadcast<T>(T message) where T : unmanaged
47+
{
48+
int length = Listeners<T>.list.Length;
49+
for (int i = 0; i < length; i++)
50+
{
51+
Listeners<T>.list[i](ref message);
52+
}
53+
}
54+
55+
/// <summary>
56+
/// Broadcasts the given <paramref name="message"/>.
57+
/// </summary>
58+
public static void Broadcast<T>(ref T message) where T : unmanaged
59+
{
60+
int length = Listeners<T>.list.Length;
61+
for (int i = 0; i < length; i++)
62+
{
63+
Listeners<T>.list[i](ref message);
64+
}
65+
}
66+
67+
private static class Listeners<T> where T : unmanaged
68+
{
69+
public static Receive<T>[] list = [];
70+
71+
static Listeners()
72+
{
73+
clearFunctions.Add(Reset);
74+
}
75+
76+
public static void Reset()
77+
{
78+
Array.Resize(ref list, 0);
79+
}
80+
}
81+
82+
/// <summary>
83+
/// Delegate for message receivers.
84+
/// </summary>
85+
public delegate void Receive<T>(ref T message) where T : unmanaged;
86+
}
87+
}

generator/Constants.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,4 @@
1-
using System.Runtime.CompilerServices;
2-
3-
[assembly: InternalsVisibleTo("Simulation.Generator.Tests")]
4-
namespace Simulation
1+
namespace Simulation
52
{
63
internal static class Constants
74
{
@@ -10,5 +7,8 @@ internal static class Constants
107
public const string ListenerInterfaceTypeName = Namespace + ".IListener";
118
public const string SystemBankTypeNameFormat = "{0}SystemBank";
129
public const string PluralSystemBankTypeNameFormat = "{0}SystemsBank";
10+
public const string GlobalSimulatorTypeName = "GlobalSimulator";
11+
public const string GlobalSimulatorLoaderTypeName = GlobalSimulatorTypeName + "Loader";
12+
public const string ListenerAttributeTypeName = "ListenerAttribute";
1313
}
1414
}

0 commit comments

Comments
 (0)