1 ///
2 /// Defines an architecture for event receiving and subscribing.
3 /// Events may be structs or components.
4 /// Event receivers must implement the Receiver(E) interface.
5 ///
6 /// Copyright: Copyright (c) 2014 James Zhu.
7 ///
8 /// License: MIT License (Expat). See accompanying file LICENSE.
9 ///
10 /// Authors: James Zhu <github.com/jzhu98>
11 ///
12 
13 module star.entity.event;
14 
15 import std.traits;
16 
17 private interface BaseReceiver
18 {
19 }
20 
21 template typename(C)
22 {
23     alias typename = mangledName!C;
24 }
25 
26 /// Recieves events of type E.
27 interface Receiver(E) : BaseReceiver
28 {
29     /// Event callback for an event E.
30     void receive(E event) pure nothrow;
31 }
32 
33 /// Manages event subscription and emission.
34 class EventManager
35 {
36 public:
37     /// Subscribe reciever to a certain event E.
38     void subscribe(E)(Receiver!E receiver) pure nothrow @safe
39     {
40         auto name = typename!E;
41         if (name in _receivers)
42         {
43             _receivers[name] ~= receiver;
44         }
45         else
46         {
47             _receivers[name] = [receiver];
48         }
49     }
50 
51     /// Notify all receivers of an event E. Calls their receive callback.
52     void emit(E)(E event) pure nothrow @trusted
53     {
54         if (typename!E in _receivers)
55         {
56             foreach(r; _receivers[typename!E])
57             {
58                 auto receiver = cast(Receiver!E) r;
59                 receiver.receive(event);
60             }
61         }
62     }
63 private:
64     BaseReceiver[][string] _receivers;
65 }
66 
67 unittest
68 {
69     struct Explosion { }
70 
71     class Block : Receiver!Explosion
72     {
73         bool destroyed = false;
74         void receive(Explosion event)
75         {
76             destroyed = true;
77         }
78     }
79 
80     auto manager = new EventManager;
81     auto block = new Block;
82     assert(block.destroyed == false);
83 
84     manager.subscribe!Explosion(block);
85     assert(manager._receivers.length == 1);
86     assert(typename!Explosion in manager._receivers);
87     assert(manager._receivers[typename!Explosion].length == 1);
88 
89     bool hasReceiver(E)(EventManager manager, Receiver!E receiver)
90     {
91         bool result = false;
92         foreach(r; manager._receivers[typename!E])
93         {
94             if (r == receiver)
95             {
96                 result = true;
97             }
98         }
99         return result;
100     }
101 
102     assert(hasReceiver!Explosion(manager, block));
103 
104     manager.emit(Explosion());
105     assert(block.destroyed == true);
106 }