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 }