Commit 4bf718669f177dfce2d1bd248920ef7c0ad2fe50
1 parent
61965517
adding MessageBus
Showing
5 changed files
with
507 additions
and
7 deletions
java/client/com/mumfrey/liteloader/client/api/LiteLoaderCoreAPIClient.java
@@ -14,6 +14,7 @@ import com.mumfrey.liteloader.client.LiteLoaderCoreProviderClient; | @@ -14,6 +14,7 @@ import com.mumfrey.liteloader.client.LiteLoaderCoreProviderClient; | ||
14 | import com.mumfrey.liteloader.core.LiteLoader; | 14 | import com.mumfrey.liteloader.core.LiteLoader; |
15 | import com.mumfrey.liteloader.core.api.LiteLoaderCoreAPI; | 15 | import com.mumfrey.liteloader.core.api.LiteLoaderCoreAPI; |
16 | import com.mumfrey.liteloader.interfaces.ObjectFactory; | 16 | import com.mumfrey.liteloader.interfaces.ObjectFactory; |
17 | +import com.mumfrey.liteloader.messaging.MessageBus; | ||
17 | 18 | ||
18 | /** | 19 | /** |
19 | * Client side of the core API | 20 | * Client side of the core API |
@@ -113,7 +114,8 @@ public class LiteLoaderCoreAPIClient extends LiteLoaderCoreAPI | @@ -113,7 +114,8 @@ public class LiteLoaderCoreAPIClient extends LiteLoaderCoreAPI | ||
113 | ( | 114 | ( |
114 | objectFactory.getEventBroker(), | 115 | objectFactory.getEventBroker(), |
115 | objectFactory.getClientPluginChannels(), | 116 | objectFactory.getClientPluginChannels(), |
116 | - objectFactory.getServerPluginChannels() | 117 | + objectFactory.getServerPluginChannels(), |
118 | + MessageBus.getInstance() | ||
117 | ); | 119 | ); |
118 | } | 120 | } |
119 | 121 | ||
@@ -125,7 +127,7 @@ public class LiteLoaderCoreAPIClient extends LiteLoaderCoreAPI | @@ -125,7 +127,7 @@ public class LiteLoaderCoreAPIClient extends LiteLoaderCoreAPI | ||
125 | { | 127 | { |
126 | return ImmutableList.<Observer>of | 128 | return ImmutableList.<Observer>of |
127 | ( | 129 | ( |
128 | - this.getObjectFactory().getModPanelManager() | 130 | + this.getObjectFactory().getPanelManager() |
129 | ); | 131 | ); |
130 | } | 132 | } |
131 | 133 |
java/common/com/mumfrey/liteloader/core/LiteLoader.java
@@ -46,6 +46,7 @@ import com.mumfrey.liteloader.interfaces.PanelManager; | @@ -46,6 +46,7 @@ import com.mumfrey.liteloader.interfaces.PanelManager; | ||
46 | import com.mumfrey.liteloader.interfaces.ObjectFactory; | 46 | import com.mumfrey.liteloader.interfaces.ObjectFactory; |
47 | import com.mumfrey.liteloader.launch.LoaderEnvironment; | 47 | import com.mumfrey.liteloader.launch.LoaderEnvironment; |
48 | import com.mumfrey.liteloader.launch.LoaderProperties; | 48 | import com.mumfrey.liteloader.launch.LoaderProperties; |
49 | +import com.mumfrey.liteloader.messaging.MessageBus; | ||
49 | import com.mumfrey.liteloader.modconfig.ConfigManager; | 50 | import com.mumfrey.liteloader.modconfig.ConfigManager; |
50 | import com.mumfrey.liteloader.modconfig.Exposable; | 51 | import com.mumfrey.liteloader.modconfig.Exposable; |
51 | import com.mumfrey.liteloader.permissions.PermissionsManagerClient; | 52 | import com.mumfrey.liteloader.permissions.PermissionsManagerClient; |
@@ -848,7 +849,7 @@ public final class LiteLoader | @@ -848,7 +849,7 @@ public final class LiteLoader | ||
848 | } | 849 | } |
849 | 850 | ||
850 | // Get the mod panel manager | 851 | // Get the mod panel manager |
851 | - this.panelManager = this.objectFactory.getModPanelManager(); | 852 | + this.panelManager = this.objectFactory.getPanelManager(); |
852 | if (this.panelManager != null) | 853 | if (this.panelManager != null) |
853 | { | 854 | { |
854 | this.panelManager.init(this.mods, this.configManager); | 855 | this.panelManager.init(this.mods, this.configManager); |
@@ -905,15 +906,14 @@ public final class LiteLoader | @@ -905,15 +906,14 @@ public final class LiteLoader | ||
905 | // Set the loader branding in ClientBrandRetriever using reflection | 906 | // Set the loader branding in ClientBrandRetriever using reflection |
906 | LiteLoaderBootstrap.setBranding("LiteLoader"); | 907 | LiteLoaderBootstrap.setBranding("LiteLoader"); |
907 | 908 | ||
908 | - for (CoreProvider coreProvider : this.coreProviders) | ||
909 | - { | ||
910 | - coreProvider.onStartupComplete(); | ||
911 | - } | 909 | + this.coreProviders.all().onStartupComplete(); |
912 | 910 | ||
913 | if (this.panelManager != null) | 911 | if (this.panelManager != null) |
914 | { | 912 | { |
915 | this.panelManager.onStartupComplete(); | 913 | this.panelManager.onStartupComplete(); |
916 | } | 914 | } |
915 | + | ||
916 | + MessageBus.getInstance().onStartupComplete(); | ||
917 | } | 917 | } |
918 | 918 | ||
919 | /** | 919 | /** |
java/common/com/mumfrey/liteloader/messaging/Message.java
0 → 100644
1 | +package com.mumfrey.liteloader.messaging; | ||
2 | + | ||
3 | +import java.util.HashMap; | ||
4 | +import java.util.Map; | ||
5 | +import java.util.regex.Pattern; | ||
6 | + | ||
7 | +import com.google.common.collect.ImmutableMap; | ||
8 | + | ||
9 | +/** | ||
10 | + * Class used to encapsulate a MessageBus message | ||
11 | + * | ||
12 | + * @author Adam Mummery-Smith | ||
13 | + */ | ||
14 | +public class Message | ||
15 | +{ | ||
16 | + /** | ||
17 | + * Regex for matching valid channels | ||
18 | + */ | ||
19 | + private static final Pattern channelPattern = Pattern.compile("^[a-z0-9]([a-z0-9_\\-]*[a-z0-9])?:[a-z0-9]([a-z0-9_\\-]*[a-z0-9])?$", Pattern.CASE_INSENSITIVE); | ||
20 | + | ||
21 | + private final String channel, replyChannel; | ||
22 | + private final Messenger sender; | ||
23 | + private final Map<String, ?> payload; | ||
24 | + | ||
25 | + Message(String channel, Object value, Messenger sender) | ||
26 | + { | ||
27 | + this(channel, value, sender, null); | ||
28 | + } | ||
29 | + | ||
30 | + Message(String channel, Object value, Messenger sender, String replyChannel) | ||
31 | + { | ||
32 | + Message.validateChannel(channel); | ||
33 | + | ||
34 | + this.channel = channel; | ||
35 | + this.payload = ImmutableMap.<String, Object>of("value", value); | ||
36 | + this.sender = sender; | ||
37 | + this.replyChannel = replyChannel; | ||
38 | + } | ||
39 | + | ||
40 | + Message(String channel, Map<String, ?> payload, Messenger sender) | ||
41 | + { | ||
42 | + this(channel, payload, sender, null); | ||
43 | + } | ||
44 | + | ||
45 | + Message(String channel, Map<String, ?> payload, Messenger sender, String replyChannel) | ||
46 | + { | ||
47 | + Message.validateChannel(channel); | ||
48 | + | ||
49 | + this.channel = channel; | ||
50 | + this.payload = payload != null ? ImmutableMap.copyOf(payload) : ImmutableMap.<String, String>of(); | ||
51 | + this.sender = sender; | ||
52 | + this.replyChannel = replyChannel; | ||
53 | + } | ||
54 | + | ||
55 | + /** | ||
56 | + * Get the channel (fully qualified) that this message was sent on | ||
57 | + */ | ||
58 | + public String getChannel() | ||
59 | + { | ||
60 | + return this.channel; | ||
61 | + } | ||
62 | + | ||
63 | + /** | ||
64 | + * Get the channel category for this message | ||
65 | + */ | ||
66 | + public String getCategory() | ||
67 | + { | ||
68 | + return this.channel.substring(0, this.channel.indexOf(':')); | ||
69 | + } | ||
70 | + | ||
71 | + /** | ||
72 | + * Get the specified reply channel (if any) for this message - may return null | ||
73 | + */ | ||
74 | + public String getReplyChannel() | ||
75 | + { | ||
76 | + return this.replyChannel; | ||
77 | + } | ||
78 | + | ||
79 | + /** | ||
80 | + * Get the message sender (if any) for this message - may return null | ||
81 | + */ | ||
82 | + public Messenger getSender() | ||
83 | + { | ||
84 | + return this.sender; | ||
85 | + } | ||
86 | + | ||
87 | + /** | ||
88 | + * Get the message payload | ||
89 | + */ | ||
90 | + public Map<String, ?> getPayload() | ||
91 | + { | ||
92 | + return this.payload; | ||
93 | + } | ||
94 | + | ||
95 | + /** | ||
96 | + * Check if this message is on the specified channel | ||
97 | + * | ||
98 | + * @param channel Full name of the channel to check against (case sensitive) | ||
99 | + * @return | ||
100 | + */ | ||
101 | + public boolean isChannel(String channel) | ||
102 | + { | ||
103 | + return this.channel.equals(channel); | ||
104 | + } | ||
105 | + | ||
106 | + /** | ||
107 | + * Check if this message has the specified category | ||
108 | + * | ||
109 | + * @param category | ||
110 | + * @return | ||
111 | + */ | ||
112 | + public boolean isCategory(String category) | ||
113 | + { | ||
114 | + return this.getCategory().equals(category); | ||
115 | + } | ||
116 | + | ||
117 | + /** | ||
118 | + * Get (and implicit cast) a value from this message's payload | ||
119 | + * | ||
120 | + * @param key | ||
121 | + * @return | ||
122 | + */ | ||
123 | + @SuppressWarnings("unchecked") | ||
124 | + public <T> T get(String key) | ||
125 | + { | ||
126 | + return (T)this.payload.get(key); | ||
127 | + } | ||
128 | + | ||
129 | + @SuppressWarnings("unchecked") | ||
130 | + public <T> T get(String key, T defaultValue) | ||
131 | + { | ||
132 | + Object value = this.payload.get(key); | ||
133 | + if (value != null) | ||
134 | + { | ||
135 | + return (T)value; | ||
136 | + } | ||
137 | + return defaultValue; | ||
138 | + } | ||
139 | + | ||
140 | + /** | ||
141 | + * Gets the payload with the key "value", which is used with messages constructed using a string-only payload | ||
142 | + */ | ||
143 | + public <T> T getValue() | ||
144 | + { | ||
145 | + return this.get("value"); | ||
146 | + } | ||
147 | + | ||
148 | + public static void validateChannel(String channel) throws IllegalArgumentException | ||
149 | + { | ||
150 | + if (channel == null) | ||
151 | + { | ||
152 | + throw new IllegalArgumentException("Channel name cannot be null"); | ||
153 | + } | ||
154 | + | ||
155 | + if (!Message.isValidChannel(channel)) | ||
156 | + { | ||
157 | + throw new IllegalArgumentException("'" + channel + "' is not a valid channel name"); | ||
158 | + } | ||
159 | + } | ||
160 | + | ||
161 | + public static boolean isValidChannel(String channel) | ||
162 | + { | ||
163 | + return Message.channelPattern.matcher(channel).matches(); | ||
164 | + } | ||
165 | + | ||
166 | + /** | ||
167 | + * Build a KV map from interleaved keys and values, convenience function | ||
168 | + * | ||
169 | + * @param args | ||
170 | + * @return | ||
171 | + */ | ||
172 | + public static Map<String, ?> buildMap(Object... args) | ||
173 | + { | ||
174 | + Map<String, Object> payload = new HashMap<String, Object>(); | ||
175 | + for (int i = 0; i < args.length - 1; i += 2) | ||
176 | + { | ||
177 | + if (args[i] instanceof String) | ||
178 | + { | ||
179 | + payload.put((String)args[i], args[i + 1]); | ||
180 | + } | ||
181 | + } | ||
182 | + | ||
183 | + return payload; | ||
184 | + } | ||
185 | +} |
java/common/com/mumfrey/liteloader/messaging/MessageBus.java
0 → 100644
1 | +package com.mumfrey.liteloader.messaging; | ||
2 | + | ||
3 | +import java.util.Deque; | ||
4 | +import java.util.HashMap; | ||
5 | +import java.util.LinkedList; | ||
6 | +import java.util.List; | ||
7 | +import java.util.Map; | ||
8 | + | ||
9 | +import com.mumfrey.liteloader.api.InterfaceProvider; | ||
10 | +import com.mumfrey.liteloader.api.Listener; | ||
11 | +import com.mumfrey.liteloader.core.InterfaceRegistrationDelegate; | ||
12 | +import com.mumfrey.liteloader.core.event.HandlerList; | ||
13 | +import com.mumfrey.liteloader.interfaces.FastIterable; | ||
14 | +import com.mumfrey.liteloader.util.log.LiteLoaderLogger; | ||
15 | + | ||
16 | +/** | ||
17 | + * Intra-mod messaging bus, allows mods to send arbitrary notifications to each other without having to | ||
18 | + * create an explicit dependency or resort to reflection | ||
19 | + * | ||
20 | + * @author Adam Mummery-Smith | ||
21 | + */ | ||
22 | +public class MessageBus implements InterfaceProvider | ||
23 | +{ | ||
24 | + /** | ||
25 | + * Singleton | ||
26 | + */ | ||
27 | + private static MessageBus instance; | ||
28 | + | ||
29 | + /** | ||
30 | + * Messengers subscribed to each channel | ||
31 | + */ | ||
32 | + private final Map<String, FastIterable<Messenger>> messengers = new HashMap<String, FastIterable<Messenger>>(); | ||
33 | + | ||
34 | + /** | ||
35 | + * Pending messages dispatched pre-startup | ||
36 | + */ | ||
37 | + private final Deque<Message> messageQueue = new LinkedList<Message>(); | ||
38 | + | ||
39 | + private boolean enableMessaging = false; | ||
40 | + | ||
41 | + private MessageBus() | ||
42 | + { | ||
43 | + } | ||
44 | + | ||
45 | + /** | ||
46 | + * Get the singleton instance | ||
47 | + */ | ||
48 | + public static MessageBus getInstance() | ||
49 | + { | ||
50 | + if (MessageBus.instance == null) | ||
51 | + { | ||
52 | + MessageBus.instance = new MessageBus(); | ||
53 | + } | ||
54 | + | ||
55 | + return MessageBus.instance; | ||
56 | + } | ||
57 | + | ||
58 | + /* (non-Javadoc) | ||
59 | + * @see com.mumfrey.liteloader.api.InterfaceProvider#getListenerBaseType() | ||
60 | + */ | ||
61 | + @Override | ||
62 | + public Class<? extends Listener> getListenerBaseType() | ||
63 | + { | ||
64 | + return Listener.class; | ||
65 | + } | ||
66 | + | ||
67 | + /* (non-Javadoc) | ||
68 | + * @see com.mumfrey.liteloader.api.InterfaceProvider#registerInterfaces(com.mumfrey.liteloader.core.InterfaceRegistrationDelegate) | ||
69 | + */ | ||
70 | + @Override | ||
71 | + public void registerInterfaces(InterfaceRegistrationDelegate delegate) | ||
72 | + { | ||
73 | + delegate.registerInterface(Messenger.class); | ||
74 | + } | ||
75 | + | ||
76 | + /* (non-Javadoc) | ||
77 | + * @see com.mumfrey.liteloader.api.InterfaceProvider#initProvider() | ||
78 | + */ | ||
79 | + @Override | ||
80 | + public void initProvider() | ||
81 | + { | ||
82 | + } | ||
83 | + | ||
84 | + /** | ||
85 | + * | ||
86 | + */ | ||
87 | + public void onStartupComplete() | ||
88 | + { | ||
89 | + this.enableMessaging = true; | ||
90 | + | ||
91 | + while (this.messageQueue.size() > 0) | ||
92 | + { | ||
93 | + Message msg = this.messageQueue.pop(); | ||
94 | + this.dispatchMessage(msg); | ||
95 | + } | ||
96 | + } | ||
97 | + | ||
98 | + public void registerMessenger(Messenger messenger) | ||
99 | + { | ||
100 | + List<String> messageChannels = messenger.getMessageChannels(); | ||
101 | + if (messageChannels == null) | ||
102 | + { | ||
103 | + LiteLoaderLogger.warning("Listener %s returned a null channel list for getMessageChannels(), this could indicate a problem with the listener", messenger.getName()); | ||
104 | + return; | ||
105 | + } | ||
106 | + | ||
107 | + for (String channel : messageChannels) | ||
108 | + { | ||
109 | + if (channel != null && Message.isValidChannel(channel)) | ||
110 | + { | ||
111 | + LiteLoaderLogger.info("Listener %s is registering MessageBus channel %s", messenger.getName(), channel); | ||
112 | + this.getMessengerList(channel).add(messenger); | ||
113 | + } | ||
114 | + else | ||
115 | + { | ||
116 | + LiteLoaderLogger.warning("Listener %s tried to register invalid MessageBus channel %s", messenger.getName(), channel); | ||
117 | + } | ||
118 | + } | ||
119 | + } | ||
120 | + | ||
121 | + /** | ||
122 | + * @param message | ||
123 | + */ | ||
124 | + private void sendMessage(Message message) | ||
125 | + { | ||
126 | + if (this.enableMessaging) | ||
127 | + { | ||
128 | + this.dispatchMessage(message); | ||
129 | + return; | ||
130 | + } | ||
131 | + | ||
132 | + this.messageQueue.push(message); | ||
133 | + } | ||
134 | + | ||
135 | + /** | ||
136 | + * @param message | ||
137 | + */ | ||
138 | + private void dispatchMessage(Message message) | ||
139 | + { | ||
140 | + try | ||
141 | + { | ||
142 | + FastIterable<Messenger> messengerList = this.messengers.get(message.getChannel()); | ||
143 | + if (messengerList != null) | ||
144 | + { | ||
145 | + messengerList.all().receiveMessage(message); | ||
146 | + } | ||
147 | + } | ||
148 | + catch (StackOverflowError err) | ||
149 | + { | ||
150 | + // A listener tried to reply on the same channel and ended up calling itself | ||
151 | + throw new RuntimeException("Stack overflow encountered dispatching message on channel '" + message.getChannel() + "'. Did you reply to yourself?"); | ||
152 | + } | ||
153 | + } | ||
154 | + | ||
155 | + /** | ||
156 | + * Get messengers for the specified channel | ||
157 | + * | ||
158 | + * @param channel | ||
159 | + * @return | ||
160 | + */ | ||
161 | + private FastIterable<Messenger> getMessengerList(String channel) | ||
162 | + { | ||
163 | + FastIterable<Messenger> messengerList = this.messengers.get(channel); | ||
164 | + if (messengerList == null) | ||
165 | + { | ||
166 | + messengerList = new HandlerList<Messenger>(Messenger.class); | ||
167 | + this.messengers.put(channel, messengerList); | ||
168 | + } | ||
169 | + | ||
170 | + return messengerList; | ||
171 | + } | ||
172 | + | ||
173 | + /** | ||
174 | + * Send an empty message on the specified channel, this is useful for messages which are basically just notifications | ||
175 | + * | ||
176 | + * @param channel | ||
177 | + */ | ||
178 | + public static void send(String channel) | ||
179 | + { | ||
180 | + Message message = new Message(channel, null, null); | ||
181 | + MessageBus.getInstance().sendMessage(message); | ||
182 | + } | ||
183 | + | ||
184 | + /** | ||
185 | + * Send a message with a value on the specified channel | ||
186 | + * | ||
187 | + * @param channel | ||
188 | + * @param value | ||
189 | + */ | ||
190 | + public static void send(String channel, String value) | ||
191 | + { | ||
192 | + Message message = new Message(channel, value, null); | ||
193 | + MessageBus.getInstance().sendMessage(message); | ||
194 | + } | ||
195 | + | ||
196 | + /** | ||
197 | + * Send a message with a value on the specified channel from the specified sender | ||
198 | + * | ||
199 | + * @param channel | ||
200 | + * @param value | ||
201 | + * @param sender | ||
202 | + */ | ||
203 | + public static void send(String channel, String value, Messenger sender) | ||
204 | + { | ||
205 | + Message message = new Message(channel, value, sender); | ||
206 | + MessageBus.getInstance().sendMessage(message); | ||
207 | + } | ||
208 | + | ||
209 | + /** | ||
210 | + * Send a message with a value on the specified channel from the specified sender | ||
211 | + * | ||
212 | + * @param channel | ||
213 | + * @param value | ||
214 | + * @param sender | ||
215 | + * @param replyChannel | ||
216 | + */ | ||
217 | + public static void send(String channel, String value, Messenger sender, String replyChannel) | ||
218 | + { | ||
219 | + Message message = new Message(channel, value, sender, replyChannel); | ||
220 | + MessageBus.getInstance().sendMessage(message); | ||
221 | + } | ||
222 | + | ||
223 | + /** | ||
224 | + * Send a message with a supplied payload on the specified channel | ||
225 | + * | ||
226 | + * @param channel | ||
227 | + * @param payload | ||
228 | + */ | ||
229 | + public static void send(String channel, Map<String, ?> payload) | ||
230 | + { | ||
231 | + Message message = new Message(channel, payload, null); | ||
232 | + MessageBus.getInstance().sendMessage(message); | ||
233 | + } | ||
234 | + | ||
235 | + /** | ||
236 | + * Send a message with a supplied payload on the specified channel from the specified sender | ||
237 | + * | ||
238 | + * @param channel | ||
239 | + * @param payload | ||
240 | + * @param sender | ||
241 | + */ | ||
242 | + public static void send(String channel, Map<String, ?> payload, Messenger sender) | ||
243 | + { | ||
244 | + Message message = new Message(channel, payload, sender); | ||
245 | + MessageBus.getInstance().sendMessage(message); | ||
246 | + } | ||
247 | + | ||
248 | + /** | ||
249 | + * Send a message with a supplied payload on the specified channel from the specified sender | ||
250 | + * | ||
251 | + * @param channel | ||
252 | + * @param payload | ||
253 | + * @param sender | ||
254 | + * @param replyChannel | ||
255 | + */ | ||
256 | + public static void send(String channel, Map<String, ?> payload, Messenger sender, String replyChannel) | ||
257 | + { | ||
258 | + Message message = new Message(channel, payload, sender, replyChannel); | ||
259 | + MessageBus.getInstance().sendMessage(message); | ||
260 | + } | ||
261 | +} |
java/common/com/mumfrey/liteloader/messaging/Messenger.java
0 → 100644
1 | +package com.mumfrey.liteloader.messaging; | ||
2 | + | ||
3 | +import java.util.List; | ||
4 | + | ||
5 | +import com.mumfrey.liteloader.api.Listener; | ||
6 | + | ||
7 | +/** | ||
8 | + * Interface for listeners that want to receive (or send) | ||
9 | + * | ||
10 | + * @author Adam Mummery-Smith | ||
11 | + */ | ||
12 | +public interface Messenger extends Listener | ||
13 | +{ | ||
14 | + /** | ||
15 | + * Get listening channels for this Messenger. Channel names must follow the format: | ||
16 | + * | ||
17 | + * {category}:{channel} | ||
18 | + * | ||
19 | + * where both {category} and {channel} are alpha-numeric identifiers which can contain underscore or dash | ||
20 | + * but must begin and end with only alpha-numeric characters: for example the following channel names are | ||
21 | + * valid: | ||
22 | + * | ||
23 | + * * foo:bar | ||
24 | + * * foo-bar:baz | ||
25 | + * * foo-bar:baz_derp | ||
26 | + * | ||
27 | + * The following are INVALID: | ||
28 | + * | ||
29 | + * * foo | ||
30 | + * * foo_:bar | ||
31 | + * * _foo:bar | ||
32 | + * | ||
33 | + * In general, your listener should listen on channels all beginning with the same category, which may match | ||
34 | + * your mod id. Channel names and categories are case-sensitive. | ||
35 | + * | ||
36 | + * @return List of channels to listen on | ||
37 | + */ | ||
38 | + public abstract List<String> getMessageChannels(); | ||
39 | + | ||
40 | + /** | ||
41 | + * Called when a message matching a channel you have elected to listen on is dispatched by any agent. | ||
42 | + * WARNING: this method is called if you dispatch a message on a channel you are listening to, thus you should | ||
43 | + * AVOID replying on channels you are listening to UNLESS you specifically filter messages based on their sender: | ||
44 | + * | ||
45 | + * if (message.getSender() == this) return; | ||
46 | + * | ||
47 | + * Messages may have a null sender or payload but will never have a null channel. | ||
48 | + * | ||
49 | + * @param message | ||
50 | + */ | ||
51 | + public abstract void receiveMessage(Message message); | ||
52 | +} |