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.event; 14 15 import std.traits; 16 17 debug import std.stdio; 18 19 private interface BaseReceiver 20 { 21 } 22 23 template typename(C) 24 { 25 alias typename = mangledName!C; 26 } 27 28 /// Recieves events of type E. 29 interface Receiver(E) : BaseReceiver 30 { 31 /// Event callback for an event E. 32 void receive(E event) pure nothrow; 33 } 34 35 /// Manages event subscription and emission. 36 class EventManager 37 { 38 public: 39 /// Subscribe reciever to a certain event E. 40 void subscribe(E)(Receiver!E receiver) pure nothrow @safe 41 { 42 auto name = typename!E; 43 if (name in _receivers) 44 { 45 _receivers[name] ~= receiver; 46 } 47 else 48 { 49 _receivers[name] = [receiver]; 50 } 51 } 52 53 /// Notify all receivers of an event E. Calls their receive callback. 54 void emit(E)(E event) pure nothrow @trusted 55 { 56 if (typename!E in _receivers) 57 { 58 foreach(r; _receivers[typename!E]) 59 { 60 auto receiver = cast(Receiver!E) r; 61 receiver.receive(event); 62 } 63 } 64 } 65 private: 66 BaseReceiver[][string] _receivers; 67 } 68 69 unittest 70 { 71 struct Explosion { } 72 73 class Block : Receiver!Explosion 74 { 75 bool destroyed = false; 76 void receive(Explosion event) 77 { 78 destroyed = true; 79 } 80 } 81 82 auto manager = new EventManager; 83 auto block = new Block; 84 assert(block.destroyed == false); 85 86 manager.subscribe!Explosion(block); 87 assert(manager._receivers.length == 1); 88 assert(typename!Explosion in manager._receivers); 89 assert(manager._receivers[typename!Explosion].length == 1); 90 91 bool hasReceiver(E)(EventManager manager, Receiver!E receiver) 92 { 93 bool result = false; 94 foreach(r; manager._receivers[typename!E]) 95 { 96 if (r == receiver) 97 { 98 result = true; 99 } 100 } 101 return result; 102 } 103 104 assert(hasReceiver!Explosion(manager, block)); 105 106 manager.emit(Explosion()); 107 assert(block.destroyed == true); 108 }