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