1 /** 2 * Retrograde Engine 3 * 4 * Authors: 5 * Mike Bierlee, m.bierlee@lostmoment.com 6 * Copyright: 2014-2021 Mike Bierlee 7 * License: 8 * This software is licensed under the terms of the MIT license. 9 * The full terms of the license can be found in the LICENSE.txt file. 10 */ 11 12 module retrograde.messaging; 13 14 import retrograde.stringid; 15 16 import std.format; 17 import std.signals; 18 import std.conv; 19 import std.exception; 20 21 interface MessageData {} 22 23 struct Message { 24 StringId type; 25 double magnitude; 26 MessageData data; 27 } 28 29 alias Event = Message; 30 alias Command = Message; 31 32 class EventHandler { 33 public bool handleJoystickEvents; 34 public bool handleMouseEvents; 35 public bool handleKeyboardEvents; 36 37 public abstract void handleEvents(); 38 } 39 40 abstract class MessageChannel { 41 mixin Signal!(const(Message)); 42 } 43 44 abstract class EventChannel : MessageChannel {} 45 46 abstract class CommandChannel : MessageChannel {} 47 48 struct MessageRoute { 49 StringId type; 50 MessageChannel source; 51 MessageChannel target; 52 } 53 54 class MessageRouter { 55 private MessageRoute[StringId] routes; 56 private bool[MessageChannel] sources; 57 private const(Message) function(const(Message))[MessageRoute] adjusters; 58 59 public void addRoute(MessageRoute route, const(Message) function(const(Message)) messageAdjuster = null) { 60 enforce(route.source !is null, "Route's source channel cannot be null"); 61 enforce(route.target !is null, "Route's target channel cannot be null"); 62 routes[route.type] = route; 63 64 if (messageAdjuster !is null) { 65 adjusters[route] = messageAdjuster; 66 } 67 68 if ((route.source in sources) is null) { 69 route.source.connect(&routeMessage); 70 sources[route.source] = true; 71 } 72 } 73 74 private void routeMessage(const(Message) message) { 75 auto route = message.type in routes; 76 if (route) { 77 auto adjuster = *route in adjusters; 78 if (adjuster) { 79 auto adjustedMessage = (*adjuster)(message); 80 route.target.emit(adjustedMessage); 81 } else { 82 route.target.emit(message); 83 } 84 } 85 } 86 } 87 88 public const(Event) dropMessageData(const(Message) message) { 89 return Message(message.type, message.magnitude); 90 } 91 92 class MessageProcessor { 93 private MessageChannel[] sourceChannels; 94 95 this(MessageChannel sourceChannel) { 96 this([sourceChannel]); 97 } 98 99 this(MessageChannel[] sourceChannels) { 100 this.sourceChannels = sourceChannels; 101 } 102 103 public void initialize() { 104 foreach(channel; sourceChannels) { 105 channel.connect(&this.handleMessage); 106 } 107 } 108 109 protected abstract void handleMessage(const(Message) message); 110 111 public void update() {} 112 }