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 }