1 /**
2 Event management module.
3 
4 Copyright: © 2015-2016 Claude Merle
5 Authors: Claude Merle
6 License: This file is part of EntitySysD.
7 
8 EntitySysD is free software: you can redistribute it and/or modify it
9 under the terms of the Lesser GNU General Public License as published
10 by the Free Software Foundation, either version 3 of the License, or
11 (at your option) any later version.
12 
13 EntitySysD is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 Lesser GNU General Public License for more details.
17 
18 You should have received a copy of the Lesser GNU General Public License
19 along with EntitySysD. If not, see $(LINK http://www.gnu.org/licenses/).
20 */
21 
22 module entitysysd.event;
23 
24 import std.algorithm;
25 import std.container;
26 import std.typecons;
27 
28 
29 /// UDA to use upon event struct's and union's.
30 enum event;
31 
32 private alias ReceiverDelegate = void delegate(...);
33 
34 private template isEvent(E)
35 {
36     import std.traits : hasUDA;
37     static if(__traits(compiles, hasUDA!(E, event)))
38         enum bool isEvent = hasUDA!(E, event) &&
39                             (is(E == struct) || is(E == union));
40     else
41         enum bool isEvent = false;
42 }
43 
44 
45 // Used internally by the EventManager.
46 private struct BaseEventCounter
47 {
48     static size_t counter = 0;
49 }
50 
51 private struct EventCounter(Derived)
52 {
53 public:
54     static size_t getId()
55     {
56         static size_t counter = -1;
57         if (counter == -1)
58         {
59             counter = mBaseEventCounter.counter;
60             mBaseEventCounter.counter++;
61         }
62 
63         return counter;
64     }
65 
66 private:
67     BaseEventCounter mBaseEventCounter;
68 }
69 
70 
71 deprecated("Please, use the name `IReceiver` instead.")
72 alias Receiver = IReceiver;
73 
74 /**
75  * Any receiver class needs to derive from this interface using a specific event
76  * type and implement receive.
77  */
78 interface IReceiver(E)
79     if (isEvent!E)
80 {
81     /**
82      * Will be called each time an event of type E is emitted.
83      */
84     void receive(E event);
85 }
86 
87 ///
88 unittest
89 {
90     @event struct MyEvent
91     {
92         int data;
93     }
94 
95     class MySystem : IReceiver!MyEvent
96     {
97         this(EventManager evtManager)
98         {
99             evtManager.subscribe!MyEvent(this);
100         }
101 
102         void receive(MyEvent event)
103         {
104             import std.stdio : write;
105             // do something with event
106             write(event.data);
107         }
108     }
109 }
110 
111 /**
112  * Manages events and receivers.
113  */
114 class EventManager
115 {
116 public:
117     /**
118      * Check whether a receiver class is subscribed to an event.
119      * Returns: true if it is subscribed, false otherwise.
120      */
121     bool isSubscribed(E)(IReceiver!E receiver)
122         if (isEvent!E)
123     {
124         auto receive = cast(ReceiverDelegate)&receiver.receive;
125         auto eventId = EventCounter!E.getId();
126         auto handlerGroup = eventId in mHandlers;
127 
128         if (handlerGroup is null)
129             return false;
130 
131         if ((*handlerGroup).find(receive).length == 0)
132             return false;
133 
134         return true;
135     }
136 
137     /**
138      * Subscribe a receiver class instance to an event.
139      */
140     void subscribe(E)(IReceiver!E receiver)
141         if (isEvent!E)
142     {
143         auto receive = cast(ReceiverDelegate)&receiver.receive;
144         auto eventId = EventCounter!E.getId();
145         auto handlerGroup = eventId in mHandlers;
146 
147         // no subscriber for the event family, so create one, and we're done
148         if (handlerGroup is null)
149         {
150             mHandlers[eventId] = [];
151             mHandlers[eventId] ~= receive;
152             return;
153         }
154 
155         // already subscribed?
156         if ((*handlerGroup).find(receive).length != 0)
157             return;
158 
159         // look for empty spots
160         foreach (ref rcv; *handlerGroup)
161             if (rcv is null)
162             {
163                 rcv = receive;
164                 return;
165             }
166 
167         // else append the subscriber callback to the array
168         *handlerGroup ~= receive;
169     }
170 
171     /**
172      * Unsubscribe a receiver class instance from an event.
173      */
174     void unsubscribe(E)(IReceiver!E receiver)
175         if (isEvent!E)
176     {
177         auto receive = cast(ReceiverDelegate)&receiver.receive;
178         auto eventId = EventCounter!E.getId();
179         auto handlerGroup = eventId in mHandlers;
180 
181         if (handlerGroup is null)
182             return;
183 
184         foreach (ref rcv; *handlerGroup)
185             if (rcv == receive)
186             {
187                 rcv = null;
188                 return; // there should be only one occurence of receive
189             }
190     }
191 
192     /**
193      * Emit an event.
194      *
195      * It will be dispatched to all receivers that subscribed to it.
196      */
197     void emit(E)(in ref E event)
198         if (isEvent!E)
199     {
200         auto eventId = EventCounter!E.getId();
201         auto handlerGroup = eventId in mHandlers;
202 
203         if (handlerGroup is null) // no event-receiver registered yet
204             return;
205 
206         foreach (rcv; *handlerGroup)
207         {
208             // already subscribed
209             if (rcv !is null)
210             {
211                 auto rcvE = cast(void delegate(in E))rcv;
212                 rcvE(event);
213             }
214         }
215     }
216 
217     /** ditto */
218     void emit(E, Args...)(auto ref Args args)
219         if (isEvent!E)
220     {
221         auto event = E(args);
222         emit(event);
223     }
224 
225     ///
226     unittest
227     {
228         @event struct MyEvent
229         {
230             int data;
231         }
232 
233         auto em = new EventManager;
234 
235         auto e = MyEvent(43);
236 
237         em.emit(e);
238         em.emit!MyEvent(42);
239     }
240 
241 private:
242 
243     // For each id of event, we have a list of receiver-delegates
244     ReceiverDelegate[][size_t] mHandlers;
245 }
246 
247 
248 //******************************************************************************
249 //***** UNIT-TESTS
250 //******************************************************************************
251 
252 version(unittest)
253 {
254 
255 import std.conv;
256 import std.stdio;
257 
258 @event struct TestEvent
259 {
260     string data;
261 }
262 
263 @event struct IntEvent
264 {
265     int data;
266 }
267 
268 class TestReceiver0 : IReceiver!TestEvent
269 {
270     string str;
271 
272     this(EventManager evtManager)
273     {
274         evtManager.subscribe!TestEvent(this);
275     }
276 
277     void receive(TestEvent event)
278     {
279         str ~= event.data;
280     }
281 }
282 
283 class TestReceiver1 : IReceiver!IntEvent
284 {
285     string str;
286 
287     this(EventManager evtManager)
288     {
289         evtManager.subscribe!IntEvent(this);
290         // do it aagain, it should silently return without subscribing it
291         // a second time
292         evtManager.subscribe!IntEvent(this);
293     }
294 
295     void receive(IntEvent event)
296     {
297         str ~= to!string(event.data);
298     }
299 }
300 
301 class TestReceiver2 : IReceiver!TestEvent, IReceiver!IntEvent
302 {
303     string str;
304 
305     this(EventManager evtManager)
306     {
307         evtManager.subscribe!TestEvent(this);
308         assert(evtManager.isSubscribed!TestEvent(this));
309         assert(!evtManager.isSubscribed!IntEvent(this));
310         evtManager.subscribe!IntEvent(this);
311     }
312 
313     void receive(TestEvent event)
314     {
315         str ~= event.data;
316     }
317     void receive(IntEvent event)
318     {
319         str ~= event.data.to!(string)();
320     }
321 }
322 
323 }
324 
325 unittest
326 {
327     auto strEvt0 = TestEvent("hello");
328     auto strEvt1 = TestEvent("world");
329     auto intEvt0 = IntEvent(123);
330     auto intEvt1 = IntEvent(456);
331 
332     //*** create a new event manager ***
333     auto evtManager = new EventManager;
334 
335     //*** test with one receiver ***
336     auto testRcv0 = new TestReceiver0(evtManager);
337 
338     evtManager.emit!(TestEvent)("goodbye ");
339     evtManager.emit(strEvt1);
340 
341     assert(testRcv0.str == "goodbye world");
342 
343     //*** test with multiple receiver and multiple events ***
344     auto testRcv1 = new TestReceiver1(evtManager);
345     auto testRcv2 = new TestReceiver2(evtManager);
346     testRcv0.str = ""; // reset string
347 
348     evtManager.emit(intEvt0);
349     evtManager.emit(strEvt1);
350     evtManager.emit(strEvt0);
351     evtManager.emit(intEvt1);
352     evtManager.emit(strEvt0);
353     evtManager.emit(intEvt0);
354     evtManager.emit(intEvt1);
355 
356     assert(testRcv0.str == "worldhellohello");
357     assert(testRcv1.str == "123456123456");
358     assert(testRcv2.str == "123worldhello456hello123456");
359 
360     //*** test unsubscribe ***
361     evtManager.unsubscribe!TestEvent(testRcv2);
362     testRcv0.str = ""; // reset string
363     testRcv1.str = ""; // reset string
364     testRcv2.str = ""; // reset string
365 
366     evtManager.emit(intEvt0);
367     evtManager.emit(strEvt0);
368 
369     assert(testRcv0.str == "hello");
370     assert(testRcv1.str == "123");
371     assert(testRcv2.str == "123");
372 
373     evtManager.unsubscribe!TestEvent(testRcv0);
374     evtManager.unsubscribe!IntEvent(testRcv2);
375     evtManager.subscribe!TestEvent(testRcv2);
376 
377     evtManager.emit(strEvt1);
378     evtManager.emit(intEvt1);
379 
380     assert(testRcv0.str == "hello");
381     assert(testRcv1.str == "123456");
382     assert(testRcv2.str == "123world");
383 }
384 
385 
386 //******************************************************************************
387 //***** UNIT-TESTS
388 //******************************************************************************
389 
390 // validate that sending an event with no registered receivers does not crash
391 unittest
392 {
393     auto evtManager = new EventManager;
394 
395     // registers a handler for StringEvent, but not IntEvent
396     auto testRcv0 = new TestReceiver0(evtManager);
397 
398     // a bug caused this to fail when at least 1 receiver was registered but
399     // no receivers were registered for this event type
400     evtManager.emit!IntEvent(123);
401 }