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 an event has any subscription at all.
119      */
120     bool hasSubscription(E)() @property const
121         if (isEvent!E)
122     {
123         auto eventId = EventCounter!E.getId();
124         auto handlerGroup = eventId in mHandlers;
125 
126         return handlerGroup !is null;
127     }
128 
129 
130     /**
131      * Check whether a receiver class is subscribed to an event.
132      * Returns: true if it is subscribed, false otherwise.
133      */
134     bool isSubscribed(E)(IReceiver!E receiver)
135         if (isEvent!E)
136     {
137         auto receive = cast(ReceiverDelegate)&receiver.receive;
138         auto eventId = EventCounter!E.getId();
139         auto handlerGroup = eventId in mHandlers;
140 
141         if (handlerGroup is null)
142             return false;
143 
144         if ((*handlerGroup).find(receive).length == 0)
145             return false;
146 
147         return true;
148     }
149 
150     /**
151      * Subscribe a receiver class instance to an event.
152      */
153     void subscribe(E)(IReceiver!E receiver)
154         if (isEvent!E)
155     {
156         auto receive = cast(ReceiverDelegate)&receiver.receive;
157         auto eventId = EventCounter!E.getId();
158         auto handlerGroup = eventId in mHandlers;
159 
160         // no subscriber for the event family, so create one, and we're done
161         if (handlerGroup is null)
162         {
163             mHandlers[eventId] = [];
164             mHandlers[eventId] ~= receive;
165             return;
166         }
167 
168         // already subscribed?
169         if ((*handlerGroup).find(receive).length != 0)
170             return;
171 
172         // look for empty spots
173         foreach (ref rcv; *handlerGroup)
174             if (rcv is null)
175             {
176                 rcv = receive;
177                 return;
178             }
179 
180         // else append the subscriber callback to the array
181         *handlerGroup ~= receive;
182     }
183 
184     /**
185      * Unsubscribe a receiver class instance from an event.
186      */
187     void unsubscribe(E)(IReceiver!E receiver)
188         if (isEvent!E)
189     {
190         auto receive = cast(ReceiverDelegate)&receiver.receive;
191         auto eventId = EventCounter!E.getId();
192         auto handlerGroup = eventId in mHandlers;
193 
194         if (handlerGroup is null)
195             return;
196 
197         foreach (ref rcv; *handlerGroup)
198             if (rcv == receive)
199             {
200                 rcv = null;
201                 return; // there should be only one occurence of receive
202             }
203     }
204 
205     /**
206      * Emit an event.
207      *
208      * It will be dispatched to all receivers that subscribed to it.
209      */
210     void emit(E)(in ref E event)
211         if (isEvent!E)
212     {
213         auto eventId = EventCounter!E.getId();
214         auto handlerGroup = eventId in mHandlers;
215 
216         if (handlerGroup is null) // no event-receiver registered yet
217             return;
218 
219         foreach (rcv; *handlerGroup)
220         {
221             // already subscribed
222             if (rcv !is null)
223             {
224                 auto rcvE = cast(void delegate(in E))rcv;
225                 rcvE(event);
226             }
227         }
228     }
229 
230     /** ditto */
231     void emit(E, Args...)(auto ref Args args)
232         if (isEvent!E)
233     {
234         auto event = E(args);
235         emit(event);
236     }
237 
238     ///
239     unittest
240     {
241         @event struct MyEvent
242         {
243             int data;
244         }
245 
246         auto em = new EventManager;
247 
248         auto e = MyEvent(43);
249 
250         em.emit(e);
251         em.emit!MyEvent(42);
252     }
253 
254 private:
255 
256     // For each id of event, we have a list of receiver-delegates
257     ReceiverDelegate[][size_t] mHandlers;
258 }
259 
260 
261 //******************************************************************************
262 //***** UNIT-TESTS
263 //******************************************************************************
264 
265 version(unittest)
266 {
267 
268 import std.conv;
269 import std.stdio;
270 
271 @event struct TestEvent
272 {
273     string data;
274 }
275 
276 @event struct IntEvent
277 {
278     int data;
279 }
280 
281 class TestReceiver0 : IReceiver!TestEvent
282 {
283     string str;
284 
285     this(EventManager evtManager)
286     {
287         evtManager.subscribe!TestEvent(this);
288     }
289 
290     void receive(TestEvent event)
291     {
292         str ~= event.data;
293     }
294 }
295 
296 class TestReceiver1 : IReceiver!IntEvent
297 {
298     string str;
299 
300     this(EventManager evtManager)
301     {
302         evtManager.subscribe!IntEvent(this);
303         // do it aagain, it should silently return without subscribing it
304         // a second time
305         evtManager.subscribe!IntEvent(this);
306     }
307 
308     void receive(IntEvent event)
309     {
310         str ~= to!string(event.data);
311     }
312 }
313 
314 class TestReceiver2 : IReceiver!TestEvent, IReceiver!IntEvent
315 {
316     string str;
317 
318     this(EventManager evtManager)
319     {
320         evtManager.subscribe!TestEvent(this);
321         assert(evtManager.isSubscribed!TestEvent(this));
322         assert(!evtManager.isSubscribed!IntEvent(this));
323         evtManager.subscribe!IntEvent(this);
324     }
325 
326     void receive(TestEvent event)
327     {
328         str ~= event.data;
329     }
330     void receive(IntEvent event)
331     {
332         str ~= event.data.to!(string)();
333     }
334 }
335 
336 } // version(unittest)
337 
338 unittest
339 {
340     auto strEvt0 = TestEvent("hello");
341     auto strEvt1 = TestEvent("world");
342     auto intEvt0 = IntEvent(123);
343     auto intEvt1 = IntEvent(456);
344 
345     //*** create a new event manager ***
346     auto evtManager = new EventManager;
347 
348     //*** test with one receiver ***
349     auto testRcv0 = new TestReceiver0(evtManager);
350 
351     evtManager.emit!(TestEvent)("goodbye ");
352     evtManager.emit(strEvt1);
353 
354     assert(testRcv0.str == "goodbye world");
355 
356     //*** test with multiple receiver and multiple events ***
357     auto testRcv1 = new TestReceiver1(evtManager);
358     auto testRcv2 = new TestReceiver2(evtManager);
359     testRcv0.str = ""; // reset string
360 
361     evtManager.emit(intEvt0);
362     evtManager.emit(strEvt1);
363     evtManager.emit(strEvt0);
364     evtManager.emit(intEvt1);
365     evtManager.emit(strEvt0);
366     evtManager.emit(intEvt0);
367     evtManager.emit(intEvt1);
368 
369     assert(testRcv0.str == "worldhellohello");
370     assert(testRcv1.str == "123456123456");
371     assert(testRcv2.str == "123worldhello456hello123456");
372 
373     //*** test unsubscribe ***
374     evtManager.unsubscribe!TestEvent(testRcv2);
375     testRcv0.str = ""; // reset string
376     testRcv1.str = ""; // reset string
377     testRcv2.str = ""; // reset string
378 
379     evtManager.emit(intEvt0);
380     evtManager.emit(strEvt0);
381 
382     assert(testRcv0.str == "hello");
383     assert(testRcv1.str == "123");
384     assert(testRcv2.str == "123");
385 
386     evtManager.unsubscribe!TestEvent(testRcv0);
387     evtManager.unsubscribe!IntEvent(testRcv2);
388     evtManager.subscribe!TestEvent(testRcv2);
389 
390     evtManager.emit(strEvt1);
391     evtManager.emit(intEvt1);
392 
393     assert(testRcv0.str == "hello");
394     assert(testRcv1.str == "123456");
395     assert(testRcv2.str == "123world");
396 }
397 
398 
399 // validate that sending an event with no registered receivers does not crash
400 unittest
401 {
402     auto evtManager = new EventManager;
403 
404     // registers a handler for StringEvent, but not IntEvent
405     auto testRcv0 = new TestReceiver0(evtManager);
406 
407     // a bug caused this to fail when at least 1 receiver was registered but
408     // no receivers were registered for this event type
409     evtManager.emit!IntEvent(123);
410 }