From ce4f4af087f55bed13c5821dd631b520a7286e8c Mon Sep 17 00:00:00 2001 From: Nathan Adams Date: Fri, 13 Jan 2012 08:51:10 +0000 Subject: Implemented new Plugin Message API - see http://dinnerbone.com/blog/2012/01/13/minecraft-plugin-channels-messaging/ --- src/main/java/org/bukkit/Bukkit.java | 5 + src/main/java/org/bukkit/Server.java | 11 +- src/main/java/org/bukkit/World.java | 3 +- src/main/java/org/bukkit/entity/Player.java | 3 +- src/main/java/org/bukkit/plugin/PluginManager.java | 1 + .../messaging/ChannelNameTooLongException.java | 14 + .../messaging/ChannelNotRegisteredException.java | 14 + .../plugin/messaging/MessageTooLargeException.java | 22 + .../org/bukkit/plugin/messaging/Messenger.java | 201 +++++++ .../plugin/messaging/PluginChannelDirection.java | 16 + .../plugin/messaging/PluginMessageListener.java | 19 + .../PluginMessageListenerRegistration.java | 103 ++++ .../plugin/messaging/PluginMessageRecipient.java | 32 ++ .../plugin/messaging/ReservedChannelException.java | 14 + .../bukkit/plugin/messaging/StandardMessenger.java | 476 ++++++++++++++++ .../plugin/messaging/StandardMessengerTest.java | 288 ++++++++++ .../plugin/messaging/TestMessageListener.java | 29 + .../org/bukkit/plugin/messaging/TestPlayer.java | 605 +++++++++++++++++++++ .../org/bukkit/plugin/messaging/TestPlugin.java | 116 ++++ 19 files changed, 1969 insertions(+), 3 deletions(-) create mode 100644 src/main/java/org/bukkit/plugin/messaging/ChannelNameTooLongException.java create mode 100644 src/main/java/org/bukkit/plugin/messaging/ChannelNotRegisteredException.java create mode 100644 src/main/java/org/bukkit/plugin/messaging/MessageTooLargeException.java create mode 100644 src/main/java/org/bukkit/plugin/messaging/Messenger.java create mode 100644 src/main/java/org/bukkit/plugin/messaging/PluginChannelDirection.java create mode 100644 src/main/java/org/bukkit/plugin/messaging/PluginMessageListener.java create mode 100644 src/main/java/org/bukkit/plugin/messaging/PluginMessageListenerRegistration.java create mode 100644 src/main/java/org/bukkit/plugin/messaging/PluginMessageRecipient.java create mode 100644 src/main/java/org/bukkit/plugin/messaging/ReservedChannelException.java create mode 100644 src/main/java/org/bukkit/plugin/messaging/StandardMessenger.java create mode 100644 src/test/java/org/bukkit/plugin/messaging/StandardMessengerTest.java create mode 100644 src/test/java/org/bukkit/plugin/messaging/TestMessageListener.java create mode 100644 src/test/java/org/bukkit/plugin/messaging/TestPlayer.java create mode 100644 src/test/java/org/bukkit/plugin/messaging/TestPlugin.java diff --git a/src/main/java/org/bukkit/Bukkit.java b/src/main/java/org/bukkit/Bukkit.java index d7753329..739c6099 100644 --- a/src/main/java/org/bukkit/Bukkit.java +++ b/src/main/java/org/bukkit/Bukkit.java @@ -17,6 +17,7 @@ import org.bukkit.inventory.Recipe; import org.bukkit.map.MapView; import org.bukkit.plugin.PluginManager; import org.bukkit.plugin.ServicesManager; +import org.bukkit.plugin.messaging.Messenger; import org.bukkit.scheduler.BukkitScheduler; /** @@ -286,4 +287,8 @@ public final class Bukkit { public static File getWorldContainer() { return server.getWorldContainer(); } + + public static Messenger getMessenger() { + return server.getMessenger(); + } } diff --git a/src/main/java/org/bukkit/Server.java b/src/main/java/org/bukkit/Server.java index 54f301e3..6c78def2 100644 --- a/src/main/java/org/bukkit/Server.java +++ b/src/main/java/org/bukkit/Server.java @@ -20,12 +20,14 @@ import org.bukkit.command.ConsoleCommandSender; import org.bukkit.map.MapView; import org.bukkit.plugin.PluginManager; import org.bukkit.plugin.ServicesManager; +import org.bukkit.plugin.messaging.Messenger; +import org.bukkit.plugin.messaging.PluginMessageRecipient; import org.bukkit.scheduler.BukkitScheduler; /** * Represents a server implementation */ -public interface Server { +public interface Server extends PluginMessageRecipient { /** * Used for all administrative messages, such as an operator using a command. * @@ -533,4 +535,11 @@ public interface Server { * @return Array containing all players */ public OfflinePlayer[] getOfflinePlayers(); + + /** + * Gets the {@link Messenger} responsible for this server. + * + * @return Messenger responsible for this server. + */ + public Messenger getMessenger(); } diff --git a/src/main/java/org/bukkit/World.java b/src/main/java/org/bukkit/World.java index d6ae084d..4e2e34bf 100644 --- a/src/main/java/org/bukkit/World.java +++ b/src/main/java/org/bukkit/World.java @@ -12,12 +12,13 @@ import org.bukkit.block.Block; import org.bukkit.entity.*; import org.bukkit.generator.BlockPopulator; import org.bukkit.inventory.ItemStack; +import org.bukkit.plugin.messaging.PluginMessageRecipient; import org.bukkit.util.Vector; /** * Represents a world, which may contain entities, chunks and blocks */ -public interface World { +public interface World extends PluginMessageRecipient { /** * Gets the {@link Block} at the given coordinates diff --git a/src/main/java/org/bukkit/entity/Player.java b/src/main/java/org/bukkit/entity/Player.java index 8fbb9509..26b42dce 100644 --- a/src/main/java/org/bukkit/entity/Player.java +++ b/src/main/java/org/bukkit/entity/Player.java @@ -12,12 +12,13 @@ import org.bukkit.OfflinePlayer; import org.bukkit.Statistic; import org.bukkit.command.CommandSender; import org.bukkit.map.MapView; +import org.bukkit.plugin.messaging.PluginMessageRecipient; /** * Represents a player, connected or not * */ -public interface Player extends HumanEntity, CommandSender, OfflinePlayer { +public interface Player extends HumanEntity, CommandSender, OfflinePlayer, PluginMessageRecipient { /** * Gets the "friendly" name to display of this player. This may include color. * diff --git a/src/main/java/org/bukkit/plugin/PluginManager.java b/src/main/java/org/bukkit/plugin/PluginManager.java index 7d7f1c2b..c70bc22b 100644 --- a/src/main/java/org/bukkit/plugin/PluginManager.java +++ b/src/main/java/org/bukkit/plugin/PluginManager.java @@ -8,6 +8,7 @@ import org.bukkit.event.Event.Priority; import org.bukkit.event.Listener; import org.bukkit.permissions.Permissible; import org.bukkit.permissions.Permission; +import org.bukkit.plugin.messaging.PluginMessageListener; /** * Handles all plugin management from the Server diff --git a/src/main/java/org/bukkit/plugin/messaging/ChannelNameTooLongException.java b/src/main/java/org/bukkit/plugin/messaging/ChannelNameTooLongException.java new file mode 100644 index 00000000..76e06fc1 --- /dev/null +++ b/src/main/java/org/bukkit/plugin/messaging/ChannelNameTooLongException.java @@ -0,0 +1,14 @@ +package org.bukkit.plugin.messaging; + +/** + * Thrown if a Plugin Channel is too long. + */ +public class ChannelNameTooLongException extends RuntimeException { + public ChannelNameTooLongException() { + super("Attempted to send a Plugin Message to a channel that was too large. The maximum length a channel may be is " + Messenger.MAX_CHANNEL_SIZE + " chars."); + } + + public ChannelNameTooLongException(String channel) { + super("Attempted to send a Plugin Message to a channel that was too large. The maximum length a channel may be is " + Messenger.MAX_CHANNEL_SIZE + " chars (attempted " + channel.length() + " - '" + channel + "."); + } +} diff --git a/src/main/java/org/bukkit/plugin/messaging/ChannelNotRegisteredException.java b/src/main/java/org/bukkit/plugin/messaging/ChannelNotRegisteredException.java new file mode 100644 index 00000000..a7a568e9 --- /dev/null +++ b/src/main/java/org/bukkit/plugin/messaging/ChannelNotRegisteredException.java @@ -0,0 +1,14 @@ +package org.bukkit.plugin.messaging; + +/** + * Thrown if a Plugin attempts to send a message on an unregistered channel. + */ +public class ChannelNotRegisteredException extends RuntimeException { + public ChannelNotRegisteredException() { + this("Attempted to send a plugin message through an unregistered channel."); + } + + public ChannelNotRegisteredException(String channel) { + super("Attempted to send a plugin message through an unregistered channel ('" + channel + "'."); + } +} diff --git a/src/main/java/org/bukkit/plugin/messaging/MessageTooLargeException.java b/src/main/java/org/bukkit/plugin/messaging/MessageTooLargeException.java new file mode 100644 index 00000000..3a195c05 --- /dev/null +++ b/src/main/java/org/bukkit/plugin/messaging/MessageTooLargeException.java @@ -0,0 +1,22 @@ +package org.bukkit.plugin.messaging; + +/** + * Thrown if a Plugin Message is sent that is too large to be sent. + */ +public class MessageTooLargeException extends RuntimeException { + public MessageTooLargeException() { + this("Attempted to send a plugin message that was too large. The maximum length a plugin message may be is " + Messenger.MAX_MESSAGE_SIZE + " bytes."); + } + + public MessageTooLargeException(byte[] message) { + this(message.length); + } + + public MessageTooLargeException(int length) { + this("Attempted to send a plugin message that was too large. The maximum length a plugin message may be is " + Messenger.MAX_MESSAGE_SIZE + " bytes (tried to send one that is " + length + " bytes long)."); + } + + public MessageTooLargeException(String msg) { + super(msg); + } +} diff --git a/src/main/java/org/bukkit/plugin/messaging/Messenger.java b/src/main/java/org/bukkit/plugin/messaging/Messenger.java new file mode 100644 index 00000000..5083587e --- /dev/null +++ b/src/main/java/org/bukkit/plugin/messaging/Messenger.java @@ -0,0 +1,201 @@ +package org.bukkit.plugin.messaging; + +import java.util.Set; +import org.bukkit.entity.Player; +import org.bukkit.plugin.Plugin; + +/** + * A class responsible for managing the registrations of plugin channels and their + * listeners. + */ +public interface Messenger { + /** + * Represents the largest size that an individual Plugin Message may be. + */ + public static final int MAX_MESSAGE_SIZE = 32766; + + /** + * Represents the largest size that a Plugin Channel may be. + */ + public static final int MAX_CHANNEL_SIZE = 16; + + /** + * Checks if the specified channel is a reserved name. + * + * @param channel Channel name to check. + * @return True if the channel is reserved, otherwise false. + * @throws IllegalArgumentException Thrown if channel is null. + */ + public boolean isReservedChannel(String channel); + + /** + * Registers the specific plugin to the requested outgoing plugin channel, allowing it + * to send messages through that channel to any clients. + * + * @param plugin Plugin that wishes to send messages through the channel. + * @param channel Channel to register. + * @throws IllegalArgumentException Thrown if plugin or channel is null. + */ + public void registerOutgoingPluginChannel(Plugin plugin, String channel); + + /** + * Unregisters the specific plugin from the requested outgoing plugin channel, no longer + * allowing it to send messages through that channel to any clients. + * + * @param plugin Plugin that no longer wishes to send messages through the channel. + * @param channel Channel to unregister. + * @throws IllegalArgumentException Thrown if plugin or channel is null. + */ + public void unregisterOutgoingPluginChannel(Plugin plugin, String channel); + + /** + * Unregisters the specific plugin from all outgoing plugin channels, no longer allowing + * it to send any plugin messages. + * + * @param plugin Plugin that no longer wishes to send plugin messages. + * @throws IllegalArgumentException Thrown if plugin is null. + */ + public void unregisterOutgoingPluginChannel(Plugin plugin); + + /** + * Registers the specific plugin for listening on the requested incoming plugin channel, + * allowing it to act upon any plugin messages. + * + * @param plugin Plugin that wishes to register to this channel. + * @param channel Channel to register. + * @param listener Listener to receive messages on. + * @returns The resulting registration that was made as a result of this method. + * @throws IllegalArgumentException Thrown if plugin, channel or listener is null, or the listener is already registered for this channel. + */ + public PluginMessageListenerRegistration registerIncomingPluginChannel(Plugin plugin, String channel, PluginMessageListener listener); + + /** + * Unregisters the specific plugin's listener from listening on the requested incoming plugin channel, + * no longer allowing it to act upon any plugin messages. + * + * @param plugin Plugin that wishes to unregister from this channel. + * @param channel Channel to unregister. + * @param listener Listener to stop receiving messages on. + * @throws IllegalArgumentException Thrown if plugin, channel or listener is null. + */ + public void unregisterIncomingPluginChannel(Plugin plugin, String channel, PluginMessageListener listener); + + /** + * Unregisters the specific plugin from listening on the requested incoming plugin channel, + * no longer allowing it to act upon any plugin messages. + * + * @param plugin Plugin that wishes to unregister from this channel. + * @param channel Channel to unregister. + * @throws IllegalArgumentException Thrown if plugin or channel is null. + */ + public void unregisterIncomingPluginChannel(Plugin plugin, String channel); + + /** + * Unregisters the specific plugin from listening on all plugin channels through all listeners. + * + * @param plugin Plugin that wishes to unregister from this channel. + * @throws IllegalArgumentException Thrown if plugin is null. + */ + public void unregisterIncomingPluginChannel(Plugin plugin); + + /** + * Gets a set containing all the outgoing plugin channels. + * + * @return List of all registered outgoing plugin channels. + */ + public Set getOutgoingChannels(); + + /** + * Gets a set containing all the outgoing plugin channels that the specified plugin is registered to. + * + * @param plugin Plugin to retrieve channels for. + * @return List of all registered outgoing plugin channels that a plugin is registered to. + * @throws IllegalArgumentException Thrown if plugin is null. + */ + public Set getOutgoingChannels(Plugin plugin); + + /** + * Gets a set containing all the incoming plugin channels. + * + * @return List of all registered incoming plugin channels. + */ + public Set getIncomingChannels(); + + /** + * Gets a set containing all the incoming plugin channels that the specified plugin is registered for. + * + * @param plugin Plugin to retrieve channels for. + * @return List of all registered incoming plugin channels that the plugin is registered for. + * @throws IllegalArgumentException Thrown if plugin is null. + */ + public Set getIncomingChannels(Plugin plugin); + + /** + * Gets a set containing all the incoming plugin channel registrations that the specified plugin has. + * + * @param plugin Plugin to retrieve registrations for. + * @return List of all registrations that the plugin has. + * @throws IllegalArgumentException Thrown if plugin is null. + */ + public Set getIncomingChannelRegistrations(Plugin plugin); + + /** + * Gets a set containing all the incoming plugin channel registrations that are on the requested channel. + * + * @param channel Channel to retrieve registrations for. + * @return List of all registrations that are on the channel. + * @throws IllegalArgumentException Thrown if channel is null. + */ + public Set getIncomingChannelRegistrations(String channel); + + /** + * Gets a set containing all the incoming plugin channel registrations that the specified plugin has + * on the requested channel. + * + * @param plugin Plugin to retrieve registrations for. + * @param channel Channel to filter registrations by. + * @return List of all registrations that the plugin has. + * @throws IllegalArgumentException Thrown if plugin or channel is null. + */ + public Set getIncomingChannelRegistrations(Plugin plugin, String channel); + + /** + * Checks if the specified plugin message listener registration is valid. + *

+ * A registration is considered valid if it has not be unregistered and that the plugin + * is still enabled. + * + * @param registration Registration to check. + * @return True if the registration is valid, otherwise false. + */ + public boolean isRegistrationValid(PluginMessageListenerRegistration registration); + + /** + * Checks if the specified plugin has registered to receive incoming messages through the requested + * channel. + * + * @param plugin Plugin to check registration for. + * @param channel Channel to test for. + * @return True if the channel is registered, else false. + */ + public boolean isIncomingChannelRegistered(Plugin plugin, String channel); + + /** + * Checks if the specified plugin has registered to send outgoing messages through the requested + * channel. + * + * @param plugin Plugin to check registration for. + * @param channel Channel to test for. + * @return True if the channel is registered, else false. + */ + public boolean isOutgoingChannelRegistered(Plugin plugin, String channel); + + /** + * Dispatches the specified incoming message to any registered listeners. + * + * @param source Source of the message. + * @param channel Channel that the message was sent by. + * @param message Raw payload of the message. + */ + public void dispatchIncomingMessage(Player source, String channel, byte[] message); +} diff --git a/src/main/java/org/bukkit/plugin/messaging/PluginChannelDirection.java b/src/main/java/org/bukkit/plugin/messaging/PluginChannelDirection.java new file mode 100644 index 00000000..3b385484 --- /dev/null +++ b/src/main/java/org/bukkit/plugin/messaging/PluginChannelDirection.java @@ -0,0 +1,16 @@ +package org.bukkit.plugin.messaging; + +/** + * Represents the different directions a plugin channel may go. + */ +public enum PluginChannelDirection { + /** + * The plugin channel is being sent to the server from a client. + */ + INCOMING, + + /** + * The plugin channel is being sent to a client from the server. + */ + OUTGOING +} diff --git a/src/main/java/org/bukkit/plugin/messaging/PluginMessageListener.java b/src/main/java/org/bukkit/plugin/messaging/PluginMessageListener.java new file mode 100644 index 00000000..0e197a6d --- /dev/null +++ b/src/main/java/org/bukkit/plugin/messaging/PluginMessageListener.java @@ -0,0 +1,19 @@ +package org.bukkit.plugin.messaging; + +import org.bukkit.entity.Player; + +/** + * A listener for a specific Plugin Channel, which will receive notifications of messages sent + * from a client. + */ +public interface PluginMessageListener { + /** + * A method that will be thrown when a {@link PluginMessageSource} sends a plugin + * message on a registered channel. + * + * @param channel Channel that the message was sent through. + * @param player Source of the message. + * @param message The raw message that was sent. + */ + public void onPluginMessageReceived(String channel, Player player, byte[] message); +} diff --git a/src/main/java/org/bukkit/plugin/messaging/PluginMessageListenerRegistration.java b/src/main/java/org/bukkit/plugin/messaging/PluginMessageListenerRegistration.java new file mode 100644 index 00000000..f736ce99 --- /dev/null +++ b/src/main/java/org/bukkit/plugin/messaging/PluginMessageListenerRegistration.java @@ -0,0 +1,103 @@ +package org.bukkit.plugin.messaging; + +import org.bukkit.plugin.Plugin; + +/** + * Contains information about a {@link Plugin}s registration to a plugin channel. + */ +public final class PluginMessageListenerRegistration { + private final Messenger messenger; + private final Plugin plugin; + private final String channel; + private final PluginMessageListener listener; + + public PluginMessageListenerRegistration(Messenger messenger, Plugin plugin, String channel, PluginMessageListener listener) { + if (messenger == null) { + throw new IllegalArgumentException("Messenger cannot be null!"); + } + if (plugin == null) { + throw new IllegalArgumentException("Plugin cannot be null!"); + } + if (channel == null) { + throw new IllegalArgumentException("Channel cannot be null!"); + } + if (listener == null) { + throw new IllegalArgumentException("Listener cannot be null!"); + } + + this.messenger = messenger; + this.plugin = plugin; + this.channel = channel; + this.listener = listener; + } + + /** + * Gets the plugin channel that this registration is about. + * + * @return Plugin channel. + */ + public String getChannel() { + return channel; + } + + /** + * Gets the registered listener described by this registration. + * + * @return Registered listener. + */ + public PluginMessageListener getListener() { + return listener; + } + + /** + * Gets the plugin that this registration is for. + * + * @return Registered plugin. + */ + public Plugin getPlugin() { + return plugin; + } + + /** + * Checks if this registration is still valid. + * + * @return True if this registration is still valid, otherwise false. + */ + public boolean isValid() { + return messenger.isRegistrationValid(this); + } + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final PluginMessageListenerRegistration other = (PluginMessageListenerRegistration) obj; + if (this.messenger != other.messenger && (this.messenger == null || !this.messenger.equals(other.messenger))) { + return false; + } + if (this.plugin != other.plugin && (this.plugin == null || !this.plugin.equals(other.plugin))) { + return false; + } + if ((this.channel == null) ? (other.channel != null) : !this.channel.equals(other.channel)) { + return false; + } + if (this.listener != other.listener && (this.listener == null || !this.listener.equals(other.listener))) { + return false; + } + return true; + } + + @Override + public int hashCode() { + int hash = 7; + hash = 53 * hash + (this.messenger != null ? this.messenger.hashCode() : 0); + hash = 53 * hash + (this.plugin != null ? this.plugin.hashCode() : 0); + hash = 53 * hash + (this.channel != null ? this.channel.hashCode() : 0); + hash = 53 * hash + (this.listener != null ? this.listener.hashCode() : 0); + return hash; + } +} diff --git a/src/main/java/org/bukkit/plugin/messaging/PluginMessageRecipient.java b/src/main/java/org/bukkit/plugin/messaging/PluginMessageRecipient.java new file mode 100644 index 00000000..6383166b --- /dev/null +++ b/src/main/java/org/bukkit/plugin/messaging/PluginMessageRecipient.java @@ -0,0 +1,32 @@ +package org.bukkit.plugin.messaging; + +import java.util.Set; +import org.bukkit.plugin.Plugin; + +/** + * Represents a possible recipient for a Plugin Message. + */ +public interface PluginMessageRecipient { + /** + * Sends this recipient a Plugin Message on the specified outgoing channel. + *

+ * The message may not be larger than {@link Messenger#MAX_MESSAGE_SIZE} bytes, and the plugin must be registered to send + * messages on the specified channel. + * + * @param source The plugin that sent this message. + * @param channel The channel to send this message on. + * @param message The raw message to send. + * @throws IllegalArgumentException Thrown if the source plugin is disabled. + * @throws IllegalArgumentException Thrown if source, channel or message is null. + * @throws MessageTooLargeException Thrown if the message is too big. + * @throws ChannelNotRegisteredException Thrown if the channel is not registered for this plugin. + */ + public void sendPluginMessage(Plugin source, String channel, byte[] message); + + /** + * Gets a set containing all the Plugin Channels that this client is listening on. + * + * @return Set containing all the channels that this client may accept. + */ + public Set getListeningPluginChannels(); +} diff --git a/src/main/java/org/bukkit/plugin/messaging/ReservedChannelException.java b/src/main/java/org/bukkit/plugin/messaging/ReservedChannelException.java new file mode 100644 index 00000000..8b20b41a --- /dev/null +++ b/src/main/java/org/bukkit/plugin/messaging/ReservedChannelException.java @@ -0,0 +1,14 @@ +package org.bukkit.plugin.messaging; + +/** + * Thrown if a plugin attempts to register for a reserved channel (such as "REGISTER") + */ +public class ReservedChannelException extends RuntimeException { + public ReservedChannelException() { + this("Attempted to register for a reserved channel name."); + } + + public ReservedChannelException(String name) { + super("Attempted to register for a reserved channel name ('" + name + "')"); + } +} diff --git a/src/main/java/org/bukkit/plugin/messaging/StandardMessenger.java b/src/main/java/org/bukkit/plugin/messaging/StandardMessenger.java new file mode 100644 index 00000000..e8ff0840 --- /dev/null +++ b/src/main/java/org/bukkit/plugin/messaging/StandardMessenger.java @@ -0,0 +1,476 @@ +package org.bukkit.plugin.messaging; + +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.ImmutableSet.Builder; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import org.bukkit.entity.Player; +import org.bukkit.plugin.Plugin; + +/** + * Standard implementation to {@link Messenger} + */ +public class StandardMessenger implements Messenger { + private final Map> incomingByChannel = new HashMap>(); + private final Map> incomingByPlugin = new HashMap>(); + private final Map> outgoingByChannel = new HashMap>(); + private final Map> outgoingByPlugin = new HashMap>(); + private final Object incomingLock = new Object(); + private final Object outgoingLock = new Object(); + + private void addToOutgoing(Plugin plugin, String channel) { + synchronized (outgoingLock) { + Set plugins = outgoingByChannel.get(channel); + Set channels = outgoingByPlugin.get(plugin); + + if (plugins == null) { + plugins = new HashSet(); + outgoingByChannel.put(channel, plugins); + } + + if (channels == null) { + channels = new HashSet(); + outgoingByPlugin.put(plugin, channels); + } + + plugins.add(plugin); + channels.add(channel); + } + } + + private void removeFromOutgoing(Plugin plugin, String channel) { + synchronized (outgoingLock) { + Set plugins = outgoingByChannel.get(channel); + Set channels = outgoingByPlugin.get(plugin); + + if (plugins != null) { + plugins.remove(plugin); + + if (plugins.isEmpty()) { + outgoingByChannel.remove(channel); + } + } + + if (channels != null) { + channels.remove(channel); + + if (channels.isEmpty()) { + outgoingByChannel.remove(channel); + } + } + } + } + + private void removeFromOutgoing(Plugin plugin) { + synchronized (outgoingLock) { + Set channels = outgoingByPlugin.get(plugin); + + if (channels != null) { + String[] toRemove = channels.toArray(new String[0]); + + outgoingByPlugin.remove(plugin); + + for (String channel : toRemove) { + removeFromOutgoing(plugin, channel); + } + } + } + } + + private void addToIncoming(PluginMessageListenerRegistration registration) { + synchronized (incomingLock) { + Set registrations = incomingByChannel.get(registration.getChannel()); + + if (registrations == null) { + registrations = new HashSet(); + incomingByChannel.put(registration.getChannel(), registrations); + } else { + if (registrations.contains(registration)) { + throw new IllegalArgumentException("This registration already exists"); + } + } + + registrations.add(registration); + + registrations = incomingByPlugin.get(registration.getPlugin()); + + if (registrations == null) { + registrations = new HashSet(); + incomingByPlugin.put(registration.getPlugin(), registrations); + } else { + if (registrations.contains(registration)) { + throw new IllegalArgumentException("This registration already exists"); + } + } + + registrations.add(registration); + } + } + + private void removeFromIncoming(PluginMessageListenerRegistration registration) { + synchronized (incomingLock) { + Set registrations = incomingByChannel.get(registration.getChannel()); + + if (registrations != null) { + registrations.remove(registration); + + if (registrations.isEmpty()) { + incomingByChannel.remove(registration.getChannel()); + } + } + + registrations = incomingByPlugin.get(registration.getPlugin()); + + if (registrations != null) { + registrations.remove(registration); + + if (registrations.isEmpty()) { + incomingByPlugin.remove(registration.getPlugin()); + } + } + } + } + + private void removeFromIncoming(Plugin plugin, String channel) { + synchronized (incomingLock) { + Set registrations = incomingByPlugin.get(plugin); + + if (registrations != null) { + PluginMessageListenerRegistration[] toRemove = registrations.toArray(new PluginMessageListenerRegistration[0]); + + for (PluginMessageListenerRegistration registration : toRemove) { + if (registration.getChannel().equals(channel)) { + removeFromIncoming(registration); + } + } + } + } + } + + private void removeFromIncoming(Plugin plugin) { + synchronized (incomingLock) { + Set registrations = incomingByPlugin.get(plugin); + + if (registrations != null) { + PluginMessageListenerRegistration[] toRemove = registrations.toArray(new PluginMessageListenerRegistration[0]); + + incomingByPlugin.remove(plugin); + + for (PluginMessageListenerRegistration registration : toRemove) { + removeFromIncoming(registration); + } + } + } + } + + public boolean isReservedChannel(String channel) { + validateChannel(channel); + + return channel.equals("REGISTER") || channel.equals("UNREGISTER"); + } + + public void registerOutgoingPluginChannel(Plugin plugin, String channel) { + if (plugin == null) { + throw new IllegalArgumentException("Plugin cannot be null"); + } + validateChannel(channel); + if (isReservedChannel(channel)) { + throw new ReservedChannelException(channel); + } + + addToOutgoing(plugin, channel); + } + + public void unregisterOutgoingPluginChannel(Plugin plugin, String channel) { + if (plugin == null) { + throw new IllegalArgumentException("Plugin cannot be null"); + } + validateChannel(channel); + + removeFromOutgoing(plugin, channel); + } + + public void unregisterOutgoingPluginChannel(Plugin plugin) { + if (plugin == null) { + throw new IllegalArgumentException("Plugin cannot be null"); + } + + removeFromOutgoing(plugin); + } + + public PluginMessageListenerRegistration registerIncomingPluginChannel(Plugin plugin, String channel, PluginMessageListener listener) { + if (plugin == null) { + throw new IllegalArgumentException("Plugin cannot be null"); + } + validateChannel(channel); + if (isReservedChannel(channel)) { + throw new ReservedChannelException(channel); + } + if (listener == null) { + throw new IllegalArgumentException("Listener cannot be null"); + } + + PluginMessageListenerRegistration result = new PluginMessageListenerRegistration(this, plugin, channel, listener); + + addToIncoming(result); + + return result; + } + + public void unregisterIncomingPluginChannel(Plugin plugin, String channel, PluginMessageListener listener) { + if (plugin == null) { + throw new IllegalArgumentException("Plugin cannot be null"); + } + if (listener == null) { + throw new IllegalArgumentException("Listener cannot be null"); + } + validateChannel(channel); + + removeFromIncoming(new PluginMessageListenerRegistration(this, plugin, channel, listener)); + } + + public void unregisterIncomingPluginChannel(Plugin plugin, String channel) { + if (plugin == null) { + throw new IllegalArgumentException("Plugin cannot be null"); + } + validateChannel(channel); + + removeFromIncoming(plugin, channel); + } + + public void unregisterIncomingPluginChannel(Plugin plugin) { + if (plugin == null) { + throw new IllegalArgumentException("Plugin cannot be null"); + } + + removeFromIncoming(plugin); + } + + public Set getOutgoingChannels() { + synchronized (outgoingLock) { + Set keys = outgoingByChannel.keySet(); + return ImmutableSet.copyOf(keys); + } + } + + public Set getOutgoingChannels(Plugin plugin) { + if (plugin == null) { + throw new IllegalArgumentException("Plugin cannot be null"); + } + + synchronized (outgoingLock) { + Set channels = outgoingByPlugin.get(plugin); + + if (channels != null) { + return ImmutableSet.copyOf(channels); + } else { + return ImmutableSet.of(); + } + } + } + + public Set getIncomingChannels() { + synchronized (incomingLock) { + Set keys = incomingByChannel.keySet(); + return ImmutableSet.copyOf(keys); + } + } + + public Set getIncomingChannels(Plugin plugin) { + if (plugin == null) { + throw new IllegalArgumentException("Plugin cannot be null"); + } + + synchronized (incomingLock) { + Set registrations = incomingByPlugin.get(plugin); + + if (registrations != null) { + Builder builder = ImmutableSet.builder(); + + for (PluginMessageListenerRegistration registration : registrations) { + builder.add(registration.getChannel()); + } + + return builder.build(); + } else { + return ImmutableSet.of(); + } + } + } + + public Set getIncomingChannelRegistrations(Plugin plugin) { + if (plugin == null) { + throw new IllegalArgumentException("Plugin cannot be null"); + } + + synchronized (incomingLock) { + Set registrations = incomingByPlugin.get(plugin); + + if (registrations != null) { + return ImmutableSet.copyOf(registrations); + } else { + return ImmutableSet.of(); + } + } + } + + public Set getIncomingChannelRegistrations(String channel) { + validateChannel(channel); + + synchronized (incomingLock) { + Set registrations = incomingByChannel.get(channel); + + if (registrations != null) { + return ImmutableSet.copyOf(registrations); + } else { + return ImmutableSet.of(); + } + } + } + + public Set getIncomingChannelRegistrations(Plugin plugin, String channel) { + if (plugin == null) { + throw new IllegalArgumentException("Plugin cannot be null"); + } + validateChannel(channel); + + synchronized (incomingLock) { + Set registrations = incomingByPlugin.get(plugin); + + if (registrations != null) { + Builder builder = ImmutableSet.builder(); + + for (PluginMessageListenerRegistration registration : registrations) { + if (registration.getChannel().equals(channel)) { + builder.add(registration); + } + } + + return builder.build(); + } else { + return ImmutableSet.of(); + } + } + } + + public boolean isRegistrationValid(PluginMessageListenerRegistration registration) { + if (registration == null) { + throw new IllegalArgumentException("Registration cannot be null"); + } + + synchronized (incomingLock) { + Set registrations = incomingByPlugin.get(registration.getPlugin()); + + if (registrations != null) { + return registrations.contains(registration); + } + + return false; + } + } + + public boolean isIncomingChannelRegistered(Plugin plugin, String channel) { + if (plugin == null) { + throw new IllegalArgumentException("Plugin cannot be null"); + } + validateChannel(channel); + + synchronized (incomingLock) { + Set registrations = incomingByPlugin.get(plugin); + + if (registrations != null) { + for (PluginMessageListenerRegistration registration : registrations) { + if (registration.getChannel().equals(channel)) { + return true; + } + } + } + + return false; + } + } + + public boolean isOutgoingChannelRegistered(Plugin plugin, String channel) { + if (plugin == null) { + throw new IllegalArgumentException("Plugin cannot be null"); + } + validateChannel(channel); + + synchronized (outgoingLock) { + Set channels = outgoingByPlugin.get(plugin); + + if (channels != null) { + return channels.contains(channel); + } + + return false; + } + } + + public void dispatchIncomingMessage(Player source, String channel, byte[] message) { + if (source == null) { + throw new IllegalArgumentException("Player source cannot be null"); + } + if (message == null) { + throw new IllegalArgumentException("Message cannot be null"); + } + validateChannel(channel); + + Set registrations = getIncomingChannelRegistrations(channel); + + for (PluginMessageListenerRegistration registration : registrations) { + registration.getListener().onPluginMessageReceived(channel, source, message); + } + } + + /** + * Validates a Plugin Channel name. + * + * @param channel Channel name to validate. + */ + public static void validateChannel(String channel) { + if (channel == null) { + throw new IllegalArgumentException("Channel cannot be null"); + } + if (channel.length() > Messenger.MAX_CHANNEL_SIZE) { + throw new ChannelNameTooLongException(channel); + } + } + + /** + * Validates the input of a Plugin Message, ensuring the arguments are all valid. + * + * @param messenger Messenger to use for validation. + * @param source Source plugin of the Message. + * @param channel Plugin Channel to send the message by. + * @param message Raw message payload to send. + * @throws IllegalArgumentException Thrown if the source plugin is disabled. + * @throws IllegalArgumentException Thrown if source, channel or message is null. + * @throws MessageTooLargeException Thrown if the message is too big. + * @throws ChannelNameTooLongException Thrown if the channel name is too long. + * @throws ChannelNotRegisteredException Thrown if the channel is not registered for this plugin. + */ + public static void validatePluginMessage(Messenger messenger, Plugin source, String channel, byte[] message) { + if (messenger == null) { + throw new IllegalArgumentException("Messenger cannot be null"); + } + if (source == null) { + throw new IllegalArgumentException("Plugin source cannot be null"); + } + if (!source.isEnabled()) { + throw new IllegalArgumentException("Plugin must be enabled to send messages"); + } + if (message == null) { + throw new IllegalArgumentException("Message cannot be null"); + } + if (!messenger.isOutgoingChannelRegistered(source, channel)) { + throw new ChannelNotRegisteredException(channel); + } + if (message.length > Messenger.MAX_MESSAGE_SIZE) { + throw new MessageTooLargeException(message); + } + validateChannel(channel); + } +} diff --git a/src/test/java/org/bukkit/plugin/messaging/StandardMessengerTest.java b/src/test/java/org/bukkit/plugin/messaging/StandardMessengerTest.java new file mode 100644 index 00000000..abf85427 --- /dev/null +++ b/src/test/java/org/bukkit/plugin/messaging/StandardMessengerTest.java @@ -0,0 +1,288 @@ +package org.bukkit.plugin.messaging; + +import org.bukkit.plugin.messaging.ReservedChannelException; +import java.util.Collection; +import org.junit.Test; +import static org.junit.Assert.*; +import static org.hamcrest.CoreMatchers.*; +import static org.junit.matchers.JUnitMatchers.*; + +public class StandardMessengerTest { + public StandardMessenger getMessenger() { + return new StandardMessenger(); + } + + public TestPlugin getPlugin() { + return new TestPlugin(); + } + + @Test + public void testIsReservedChannel() { + Messenger messenger = getMessenger(); + + assertTrue(messenger.isReservedChannel("REGISTER")); + assertFalse(messenger.isReservedChannel("register")); + assertTrue(messenger.isReservedChannel("UNREGISTER")); + assertFalse(messenger.isReservedChannel("unregister")); + assertFalse(messenger.isReservedChannel("notReserved")); + } + + @Test + public void testRegisterAndUnregisterOutgoingPluginChannel() { + Messenger messenger = getMessenger(); + TestPlugin plugin = getPlugin(); + + assertFalse(messenger.isOutgoingChannelRegistered(plugin, "foo")); + messenger.registerOutgoingPluginChannel(plugin, "foo"); + assertTrue(messenger.isOutgoingChannelRegistered(plugin, "foo")); + assertFalse(messenger.isOutgoingChannelRegistered(plugin, "bar")); + + messenger.unregisterOutgoingPluginChannel(plugin, "foo"); + assertFalse(messenger.isOutgoingChannelRegistered(plugin, "foo")); + } + + @Test(expected=ReservedChannelException.class) + public void testReservedOutgoingRegistration() { + Messenger messenger = getMessenger(); + TestPlugin plugin = getPlugin(); + + messenger.registerOutgoingPluginChannel(plugin, "REGISTER"); + } + + @Test + public void testUnregisterOutgoingPluginChannel_Plugin() { + Messenger messenger = getMessenger(); + TestPlugin plugin = getPlugin(); + + assertFalse(messenger.isOutgoingChannelRegistered(plugin, "foo")); + messenger.registerOutgoingPluginChannel(plugin, "foo"); + messenger.registerOutgoingPluginChannel(plugin, "bar"); + assertTrue(messenger.isOutgoingChannelRegistered(plugin, "foo")); + assertTrue(messenger.isOutgoingChannelRegistered(plugin, "bar")); + + messenger.unregisterOutgoingPluginChannel(plugin); + assertFalse(messenger.isOutgoingChannelRegistered(plugin, "foo")); + assertFalse(messenger.isOutgoingChannelRegistered(plugin, "bar")); + } + + @Test + public void testRegisterIncomingPluginChannel() { + Messenger messenger = getMessenger(); + TestPlugin plugin = getPlugin(); + TestMessageListener listener = new TestMessageListener("foo", "bar".getBytes()); + TestPlayer player = new TestPlayer(); + PluginMessageListenerRegistration registration = messenger.registerIncomingPluginChannel(plugin, "foo", listener); + + assertTrue(registration.isValid()); + assertTrue(messenger.isIncomingChannelRegistered(plugin, "foo")); + messenger.dispatchIncomingMessage(player, "foo", "bar".getBytes()); + assertTrue(listener.hasReceived()); + + messenger.unregisterIncomingPluginChannel(plugin, "foo", listener); + listener.reset(); + + assertFalse(registration.isValid()); + assertFalse(messenger.isIncomingChannelRegistered(plugin, "foo")); + messenger.dispatchIncomingMessage(player, "foo", "bar".getBytes()); + assertFalse(listener.hasReceived()); + } + + @Test(expected=ReservedChannelException.class) + public void testReservedIncomingRegistration() { + Messenger messenger = getMessenger(); + TestPlugin plugin = getPlugin(); + + messenger.registerIncomingPluginChannel(plugin, "REGISTER", new TestMessageListener("foo", "bar".getBytes())); + } + + @Test(expected=IllegalArgumentException.class) + public void testDuplicateIncomingRegistration() { + Messenger messenger = getMessenger(); + TestPlugin plugin = getPlugin(); + TestMessageListener listener = new TestMessageListener("foo", "bar".getBytes()); + + messenger.registerIncomingPluginChannel(plugin, "baz", listener); + messenger.registerIncomingPluginChannel(plugin, "baz", listener); + } + + @Test + public void testUnregisterIncomingPluginChannel_Plugin_String() { + Messenger messenger = getMessenger(); + TestPlugin plugin = getPlugin(); + TestMessageListener listener1 = new TestMessageListener("foo", "bar".getBytes()); + TestMessageListener listener2 = new TestMessageListener("baz", "qux".getBytes()); + TestPlayer player = new TestPlayer(); + PluginMessageListenerRegistration registration1 = messenger.registerIncomingPluginChannel(plugin, "foo", listener1); + PluginMessageListenerRegistration registration2 = messenger.registerIncomingPluginChannel(plugin, "baz", listener2); + + assertTrue(registration1.isValid()); + assertTrue(registration2.isValid()); + messenger.dispatchIncomingMessage(player, "foo", "bar".getBytes()); + messenger.dispatchIncomingMessage(player, "baz", "qux".getBytes()); + assertTrue(listener1.hasReceived()); + assertTrue(listener2.hasReceived()); + + messenger.unregisterIncomingPluginChannel(plugin, "foo"); + listener1.reset(); + listener2.reset(); + + assertFalse(registration1.isValid()); + assertTrue(registration2.isValid()); + messenger.dispatchIncomingMessage(player, "foo", "bar".getBytes()); + messenger.dispatchIncomingMessage(player, "baz", "qux".getBytes()); + assertFalse(listener1.hasReceived()); + assertTrue(listener2.hasReceived()); + } + + @Test + public void testUnregisterIncomingPluginChannel_Plugin() { + Messenger messenger = getMessenger(); + TestPlugin plugin = getPlugin(); + TestMessageListener listener1 = new TestMessageListener("foo", "bar".getBytes()); + TestMessageListener listener2 = new TestMessageListener("baz", "qux".getBytes()); + TestPlayer player = new TestPlayer(); + PluginMessageListenerRegistration registration1 = messenger.registerIncomingPluginChannel(plugin, "foo", listener1); + PluginMessageListenerRegistration registration2 = messenger.registerIncomingPluginChannel(plugin, "baz", listener2); + + assertTrue(registration1.isValid()); + assertTrue(registration2.isValid()); + messenger.dispatchIncomingMessage(player, "foo", "bar".getBytes()); + messenger.dispatchIncomingMessage(player, "baz", "qux".getBytes()); + assertTrue(listener1.hasReceived()); + assertTrue(listener2.hasReceived()); + + messenger.unregisterIncomingPluginChannel(plugin); + listener1.reset(); + listener2.reset(); + + assertFalse(registration1.isValid()); + assertFalse(registration2.isValid()); + messenger.dispatchIncomingMessage(player, "foo", "bar".getBytes()); + messenger.dispatchIncomingMessage(player, "baz", "qux".getBytes()); + assertFalse(listener1.hasReceived()); + assertFalse(listener2.hasReceived()); + } + + @Test + public void testGetOutgoingChannels() { + Messenger messenger = getMessenger(); + TestPlugin plugin1 = getPlugin(); + TestPlugin plugin2 = getPlugin(); + + assertEquals(messenger.getOutgoingChannels()); + + messenger.registerOutgoingPluginChannel(plugin1, "foo"); + messenger.registerOutgoingPluginChannel(plugin1, "bar"); + messenger.registerOutgoingPluginChannel(plugin2, "baz"); + messenger.registerOutgoingPluginChannel(plugin2, "baz"); + + assertEquals(messenger.getOutgoingChannels(), "foo", "bar", "baz"); + } + + @Test + public void testGetOutgoingChannels_Plugin() { + Messenger messenger = getMessenger(); + TestPlugin plugin1 = getPlugin(); + TestPlugin plugin2 = getPlugin(); + TestPlugin plugin3 = getPlugin(); + + messenger.registerOutgoingPluginChannel(plugin1, "foo"); + messenger.registerOutgoingPluginChannel(plugin1, "bar"); + messenger.registerOutgoingPluginChannel(plugin2, "baz"); + messenger.registerOutgoingPluginChannel(plugin2, "qux"); + + assertEquals(messenger.getOutgoingChannels(plugin1), "foo", "bar"); + assertEquals(messenger.getOutgoingChannels(plugin2), "baz", "qux"); + assertEquals(messenger.getOutgoingChannels(plugin3)); + } + + @Test + public void testGetIncomingChannels() { + Messenger messenger = getMessenger(); + TestPlugin plugin1 = getPlugin(); + TestPlugin plugin2 = getPlugin(); + + assertEquals(messenger.getIncomingChannels()); + + messenger.registerIncomingPluginChannel(plugin1, "foo", new TestMessageListener("foo", "bar".getBytes())); + messenger.registerIncomingPluginChannel(plugin1, "bar", new TestMessageListener("foo", "bar".getBytes())); + messenger.registerIncomingPluginChannel(plugin2, "baz", new TestMessageListener("foo", "bar".getBytes())); + messenger.registerIncomingPluginChannel(plugin2, "baz", new TestMessageListener("foo", "bar".getBytes())); + + assertEquals(messenger.getIncomingChannels(), "foo", "bar", "baz"); + } + + @Test + public void testGetIncomingChannels_Plugin() { + Messenger messenger = getMessenger(); + TestPlugin plugin1 = getPlugin(); + TestPlugin plugin2 = getPlugin(); + TestPlugin plugin3 = getPlugin(); + + messenger.registerIncomingPluginChannel(plugin1, "foo", new TestMessageListener("foo", "bar".getBytes())); + messenger.registerIncomingPluginChannel(plugin1, "bar", new TestMessageListener("foo", "bar".getBytes())); + messenger.registerIncomingPluginChannel(plugin2, "baz", new TestMessageListener("foo", "bar".getBytes())); + messenger.registerIncomingPluginChannel(plugin2, "qux", new TestMessageListener("foo", "bar".getBytes())); + + assertEquals(messenger.getIncomingChannels(plugin1), "foo", "bar"); + assertEquals(messenger.getIncomingChannels(plugin2), "baz", "qux"); + assertEquals(messenger.getIncomingChannels(plugin3)); + } + + @Test + public void testGetIncomingChannelRegistrations_Plugin() { + Messenger messenger = getMessenger(); + TestPlugin plugin1 = getPlugin(); + TestPlugin plugin2 = getPlugin(); + TestPlugin plugin3 = getPlugin(); + PluginMessageListenerRegistration registration1 = messenger.registerIncomingPluginChannel(plugin1, "foo", new TestMessageListener("foo", "bar".getBytes())); + PluginMessageListenerRegistration registration2 = messenger.registerIncomingPluginChannel(plugin1, "bar", new TestMessageListener("foo", "bar".getBytes())); + PluginMessageListenerRegistration registration3 = messenger.registerIncomingPluginChannel(plugin2, "baz", new TestMessageListener("foo", "bar".getBytes())); + PluginMessageListenerRegistration registration4 = messenger.registerIncomingPluginChannel(plugin2, "qux", new TestMessageListener("foo", "bar".getBytes())); + + assertEquals(messenger.getIncomingChannelRegistrations(plugin1), registration1, registration2); + assertEquals(messenger.getIncomingChannelRegistrations(plugin2), registration3, registration4); + assertEquals(messenger.getIncomingChannels(plugin3)); + } + + @Test + public void testGetIncomingChannelRegistrations_String() { + Messenger messenger = getMessenger(); + TestPlugin plugin1 = getPlugin(); + TestPlugin plugin2 = getPlugin(); + PluginMessageListenerRegistration registration1 = messenger.registerIncomingPluginChannel(plugin1, "foo", new TestMessageListener("foo", "bar".getBytes())); + PluginMessageListenerRegistration registration2 = messenger.registerIncomingPluginChannel(plugin1, "bar", new TestMessageListener("foo", "bar".getBytes())); + PluginMessageListenerRegistration registration3 = messenger.registerIncomingPluginChannel(plugin2, "foo", new TestMessageListener("foo", "bar".getBytes())); + PluginMessageListenerRegistration registration4 = messenger.registerIncomingPluginChannel(plugin2, "bar", new TestMessageListener("foo", "bar".getBytes())); + + assertEquals(messenger.getIncomingChannelRegistrations("foo"), registration1, registration3); + assertEquals(messenger.getIncomingChannelRegistrations("bar"), registration2, registration4); + assertEquals(messenger.getIncomingChannelRegistrations("baz")); + } + + @Test + public void testGetIncomingChannelRegistrations_Plugin_String() { + Messenger messenger = getMessenger(); + TestPlugin plugin1 = getPlugin(); + TestPlugin plugin2 = getPlugin(); + TestPlugin plugin3 = getPlugin(); + PluginMessageListenerRegistration registration1 = messenger.registerIncomingPluginChannel(plugin1, "foo", new TestMessageListener("foo", "bar".getBytes())); + PluginMessageListenerRegistration registration2 = messenger.registerIncomingPluginChannel(plugin1, "foo", new TestMessageListener("foo", "bar".getBytes())); + PluginMessageListenerRegistration registration3 = messenger.registerIncomingPluginChannel(plugin1, "bar", new TestMessageListener("foo", "bar".getBytes())); + PluginMessageListenerRegistration registration4 = messenger.registerIncomingPluginChannel(plugin2, "bar", new TestMessageListener("foo", "bar".getBytes())); + PluginMessageListenerRegistration registration5 = messenger.registerIncomingPluginChannel(plugin2, "baz", new TestMessageListener("foo", "bar".getBytes())); + PluginMessageListenerRegistration registration6 = messenger.registerIncomingPluginChannel(plugin2, "baz", new TestMessageListener("foo", "bar".getBytes())); + + assertEquals(messenger.getIncomingChannelRegistrations(plugin1, "foo"), registration1, registration2); + assertEquals(messenger.getIncomingChannelRegistrations(plugin1, "bar"), registration3); + assertEquals(messenger.getIncomingChannelRegistrations(plugin2, "bar"), registration4); + assertEquals(messenger.getIncomingChannelRegistrations(plugin2, "baz"), registration5, registration6); + assertEquals(messenger.getIncomingChannelRegistrations(plugin1, "baz")); + assertEquals(messenger.getIncomingChannelRegistrations(plugin3, "qux")); + } + + private static void assertEquals(Collection actual, T... expected) { + assertThat("Size of the array", actual.size(), is(expected.length)); + assertThat(actual, hasItems(expected)); + } +} diff --git a/src/test/java/org/bukkit/plugin/messaging/TestMessageListener.java b/src/test/java/org/bukkit/plugin/messaging/TestMessageListener.java new file mode 100644 index 00000000..98860ec1 --- /dev/null +++ b/src/test/java/org/bukkit/plugin/messaging/TestMessageListener.java @@ -0,0 +1,29 @@ +package org.bukkit.plugin.messaging; + +import org.bukkit.entity.Player; +import static org.junit.Assert.*; + +public class TestMessageListener implements PluginMessageListener { + private final String channel; + private final byte[] message; + private boolean received = false; + + public TestMessageListener(String channel, byte[] message) { + this.channel = channel; + this.message = message; + } + + public void onPluginMessageReceived(String channel, Player player, byte[] message) { + assertEquals(this.channel, channel); + assertArrayEquals(this.message, message); + this.received = true; + } + + public boolean hasReceived() { + return received; + } + + public void reset() { + received = false; + } +} diff --git a/src/test/java/org/bukkit/plugin/messaging/TestPlayer.java b/src/test/java/org/bukkit/plugin/messaging/TestPlayer.java new file mode 100644 index 00000000..5c392a87 --- /dev/null +++ b/src/test/java/org/bukkit/plugin/messaging/TestPlayer.java @@ -0,0 +1,605 @@ +package org.bukkit.plugin.messaging; + +import java.net.InetSocketAddress; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.UUID; +import org.bukkit.Achievement; +import org.bukkit.Effect; +import org.bukkit.GameMode; +import org.bukkit.Instrument; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.Note; +import org.bukkit.Server; +import org.bukkit.Statistic; +import org.bukkit.World; +import org.bukkit.block.Block; +import org.bukkit.entity.Arrow; +import org.bukkit.entity.Egg; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Player; +import org.bukkit.entity.Snowball; +import org.bukkit.entity.Vehicle; +import org.bukkit.event.entity.EntityDamageEvent; +import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.PlayerInventory; +import org.bukkit.map.MapView; +import org.bukkit.permissions.Permission; +import org.bukkit.permissions.PermissionAttachment; +import org.bukkit.permissions.PermissionAttachmentInfo; +import org.bukkit.plugin.Plugin; +import org.bukkit.util.Vector; + +public class TestPlayer implements Player { + public String getDisplayName() { + throw new UnsupportedOperationException("Not supported yet."); + } + + public void setDisplayName(String name) { + throw new UnsupportedOperationException("Not supported yet."); + } + + public String getPlayerListName() { + throw new UnsupportedOperationException("Not supported yet."); + } + + public void setPlayerListName(String name) { + throw new UnsupportedOperationException("Not supported yet."); + } + + public void setCompassTarget(Location loc) { + throw new UnsupportedOperationException("Not supported yet."); + } + + public Location getCompassTarget() { + throw new UnsupportedOperationException("Not supported yet."); + } + + public InetSocketAddress getAddress() { + throw new UnsupportedOperationException("Not supported yet."); + } + + public void sendRawMessage(String message) { + throw new UnsupportedOperationException("Not supported yet."); + } + + public void kickPlayer(String message) { + throw new UnsupportedOperationException("Not supported yet."); + } + + public void chat(String msg) { + throw new UnsupportedOperationException("Not supported yet."); + } + + public boolean performCommand(String command) { + throw new UnsupportedOperationException("Not supported yet."); + } + + public boolean isSneaking() { + throw new UnsupportedOperationException("Not supported yet."); + } + + public void setSneaking(boolean sneak) { + throw new UnsupportedOperationException("Not supported yet."); + } + + public boolean isSprinting() { + throw new UnsupportedOperationException("Not supported yet."); + } + + public void setSprinting(boolean sprinting) { + throw new UnsupportedOperationException("Not supported yet."); + } + + public void saveData() { + throw new UnsupportedOperationException("Not supported yet."); + } + + public void loadData() { + throw new UnsupportedOperationException("Not supported yet."); + } + + public void setSleepingIgnored(boolean isSleeping) { + throw new UnsupportedOperationException("Not supported yet."); + } + + public boolean isSleepingIgnored() { + throw new UnsupportedOperationException("Not supported yet."); + } + + public void playNote(Location loc, byte instrument, byte note) { + throw new UnsupportedOperationException("Not supported yet."); + } + + public void playNote(Location loc, Instrument instrument, Note note) { + throw new UnsupportedOperationException("Not supported yet."); + } + + public void playEffect(Location loc, Effect effect, int data) { + throw new UnsupportedOperationException("Not supported yet."); + } + + public void sendBlockChange(Location loc, Material material, byte data) { + throw new UnsupportedOperationException("Not supported yet."); + } + + public boolean sendChunkChange(Location loc, int sx, int sy, int sz, byte[] data) { + throw new UnsupportedOperationException("Not supported yet."); + } + + public void sendBlockChange(Location loc, int material, byte data) { + throw new UnsupportedOperationException("Not supported yet."); + } + + public void sendMap(MapView map) { + throw new UnsupportedOperationException("Not supported yet."); + } + + public void updateInventory() { + throw new UnsupportedOperationException("Not supported yet."); + } + + public void awardAchievement(Achievement achievement) { + throw new UnsupportedOperationException("Not supported yet."); + } + + public void incrementStatistic(Statistic statistic) { + throw new UnsupportedOperationException("Not supported yet."); + } + + public void incrementStatistic(Statistic statistic, int amount) { + throw new UnsupportedOperationException("Not supported yet."); + } + + public void incrementStatistic(Statistic statistic, Material material) { + throw new UnsupportedOperationException("Not supported yet."); + } + + public void incrementStatistic(Statistic statistic, Material material, int amount) { + throw new UnsupportedOperationException("Not supported yet."); + } + + public void setPlayerTime(long time, boolean relative) { + throw new UnsupportedOperationException("Not supported yet."); + } + + public long getPlayerTime() { + throw new UnsupportedOperationException("Not supported yet."); + } + + public long getPlayerTimeOffset() { + throw new UnsupportedOperationException("Not supported yet."); + } + + public boolean isPlayerTimeRelative() { + throw new UnsupportedOperationException("Not supported yet."); + } + + public void resetPlayerTime() { + throw new UnsupportedOperationException("Not supported yet."); + } + + public void giveExp(int amount) { + throw new UnsupportedOperationException("Not supported yet."); + } + + public float getExp() { + throw new UnsupportedOperationException("Not supported yet."); + } + + public void setExp(float exp) { + throw new UnsupportedOperationException("Not supported yet."); + } + + public int getExperience() { + throw new UnsupportedOperationException("Not supported yet."); + } + + public void setExperience(int exp) { + throw new UnsupportedOperationException("Not supported yet."); + } + + public int getLevel() { + throw new UnsupportedOperationException("Not supported yet."); + } + + public void setLevel(int level) { + throw new UnsupportedOperationException("Not supported yet."); + } + + public int getTotalExperience() { + throw new UnsupportedOperationException("Not supported yet."); + } + + public void setTotalExperience(int exp) { + throw new UnsupportedOperationException("Not supported yet."); + } + + public float getExhaustion() { + throw new UnsupportedOperationException("Not supported yet."); + } + + public void setExhaustion(float value) { + throw new UnsupportedOperationException("Not supported yet."); + } + + public float getSaturation() { + throw new UnsupportedOperationException("Not supported yet."); + } + + public void setSaturation(float value) { + throw new UnsupportedOperationException("Not supported yet."); + } + + public int getFoodLevel() { + throw new UnsupportedOperationException("Not supported yet."); + } + + public void setFoodLevel(int value) { + throw new UnsupportedOperationException("Not supported yet."); + } + + public Location getBedSpawnLocation() { + throw new UnsupportedOperationException("Not supported yet."); + } + + public String getName() { + throw new UnsupportedOperationException("Not supported yet."); + } + + public PlayerInventory getInventory() { + throw new UnsupportedOperationException("Not supported yet."); + } + + public ItemStack getItemInHand() { + throw new UnsupportedOperationException("Not supported yet."); + } + + public void setItemInHand(ItemStack item) { + throw new UnsupportedOperationException("Not supported yet."); + } + + public boolean isSleeping() { + throw new UnsupportedOperationException("Not supported yet."); + } + + public int getSleepTicks() { + throw new UnsupportedOperationException("Not supported yet."); + } + + public GameMode getGameMode() { + throw new UnsupportedOperationException("Not supported yet."); + } + + public void setGameMode(GameMode mode) { + throw new UnsupportedOperationException("Not supported yet."); + } + + public int getHealth() { + throw new UnsupportedOperationException("Not supported yet."); + } + + public void setHealth(int health) { + throw new UnsupportedOperationException("Not supported yet."); + } + + public int getMaxHealth() { + throw new UnsupportedOperationException("Not supported yet."); + } + + public double getEyeHeight() { + throw new UnsupportedOperationException("Not supported yet."); + } + + public double getEyeHeight(boolean ignoreSneaking) { + throw new UnsupportedOperationException("Not supported yet."); + } + + public Location getEyeLocation() { + throw new UnsupportedOperationException("Not supported yet."); + } + + public List getLineOfSight(HashSet transparent, int maxDistance) { + throw new UnsupportedOperationException("Not supported yet."); + } + + public Block getTargetBlock(HashSet transparent, int maxDistance) { + throw new UnsupportedOperationException("Not supported yet."); + } + + public List getLastTwoTargetBlocks(HashSet transparent, int maxDistance) { + throw new UnsupportedOperationException("Not supported yet."); + } + + public Egg throwEgg() { + throw new UnsupportedOperationException("Not supported yet."); + } + + public Snowball throwSnowball() { + throw new UnsupportedOperationException("Not supported yet."); + } + + public Arrow shootArrow() { + throw new UnsupportedOperationException("Not supported yet."); + } + + public boolean isInsideVehicle() { + throw new UnsupportedOperationException("Not supported yet."); + } + + public boolean leaveVehicle() { + throw new UnsupportedOperationException("Not supported yet."); + } + + public Vehicle getVehicle() { + throw new UnsupportedOperationException("Not supported yet."); + } + + public int getRemainingAir() { + throw new UnsupportedOperationException("Not supported yet."); + } + + public void setRemainingAir(int ticks) { + throw new UnsupportedOperationException("Not supported yet."); + } + + public int getMaximumAir() { + throw new UnsupportedOperationException("Not supported yet."); + } + + public void setMaximumAir(int ticks) { + throw new UnsupportedOperationException("Not supported yet."); + } + + public void damage(int amount) { + throw new UnsupportedOperationException("Not supported yet."); + } + + public void damage(int amount, Entity source) { + throw new UnsupportedOperationException("Not supported yet."); + } + + public int getMaximumNoDamageTicks() { + throw new UnsupportedOperationException("Not supported yet."); + } + + public void setMaximumNoDamageTicks(int ticks) { + throw new UnsupportedOperationException("Not supported yet."); + } + + public int getLastDamage() { + throw new UnsupportedOperationException("Not supported yet."); + } + + public void setLastDamage(int damage) { + throw new UnsupportedOperationException("Not supported yet."); + } + + public int getNoDamageTicks() { + throw new UnsupportedOperationException("Not supported yet."); + } + + public void setNoDamageTicks(int ticks) { + throw new UnsupportedOperationException("Not supported yet."); + } + + public Player getKiller() { + throw new UnsupportedOperationException("Not supported yet."); + } + + public Location getLocation() { + throw new UnsupportedOperationException("Not supported yet."); + } + + public void setVelocity(Vector velocity) { + throw new UnsupportedOperationException("Not supported yet."); + } + + public Vector getVelocity() { + throw new UnsupportedOperationException("Not supported yet."); + } + + public World getWorld() { + throw new UnsupportedOperationException("Not supported yet."); + } + + public boolean teleport(Location location) { + throw new UnsupportedOperationException("Not supported yet."); + } + + public boolean teleport(Location location, TeleportCause cause) { + throw new UnsupportedOperationException("Not supported yet."); + } + + public boolean teleport(Entity destination) { + throw new UnsupportedOperationException("Not supported yet."); + } + + public boolean teleport(Entity destination, TeleportCause cause) { + throw new UnsupportedOperationException("Not supported yet."); + } + + public List getNearbyEntities(double x, double y, double z) { + throw new UnsupportedOperationException("Not supported yet."); + } + + public int getEntityId() { + throw new UnsupportedOperationException("Not supported yet."); + } + + public int getFireTicks() { + throw new UnsupportedOperationException("Not supported yet."); + } + + public int getMaxFireTicks() { + throw new UnsupportedOperationException("Not supported yet."); + } + + public void setFireTicks(int ticks) { + throw new UnsupportedOperationException("Not supported yet."); + } + + public void remove() { + throw new UnsupportedOperationException("Not supported yet."); + } + + public boolean isDead() { + throw new UnsupportedOperationException("Not supported yet."); + } + + public Server getServer() { + throw new UnsupportedOperationException("Not supported yet."); + } + + public Entity getPassenger() { + throw new UnsupportedOperationException("Not supported yet."); + } + + public boolean setPassenger(Entity passenger) { + throw new UnsupportedOperationException("Not supported yet."); + } + + public boolean isEmpty() { + throw new UnsupportedOperationException("Not supported yet."); + } + + public boolean eject() { + throw new UnsupportedOperationException("Not supported yet."); + } + + public float getFallDistance() { + throw new UnsupportedOperationException("Not supported yet."); + } + + public void setFallDistance(float distance) { + throw new UnsupportedOperationException("Not supported yet."); + } + + public void setLastDamageCause(EntityDamageEvent event) { + throw new UnsupportedOperationException("Not supported yet."); + } + + public EntityDamageEvent getLastDamageCause() { + throw new UnsupportedOperationException("Not supported yet."); + } + + public UUID getUniqueId() { + throw new UnsupportedOperationException("Not supported yet."); + } + + public int getTicksLived() { + throw new UnsupportedOperationException("Not supported yet."); + } + + public void setTicksLived(int value) { + throw new UnsupportedOperationException("Not supported yet."); + } + + public boolean isPermissionSet(String name) { + throw new UnsupportedOperationException("Not supported yet."); + } + + public boolean isPermissionSet(Permission perm) { + throw new UnsupportedOperationException("Not supported yet."); + } + + public boolean hasPermission(String name) { + throw new UnsupportedOperationException("Not supported yet."); + } + + public boolean hasPermission(Permission perm) { + throw new UnsupportedOperationException("Not supported yet."); + } + + public PermissionAttachment addAttachment(Plugin plugin, String name, boolean value) { + throw new UnsupportedOperationException("Not supported yet."); + } + + public PermissionAttachment addAttachment(Plugin plugin) { + throw new UnsupportedOperationException("Not supported yet."); + } + + public PermissionAttachment addAttachment(Plugin plugin, String name, boolean value, int ticks) { + throw new UnsupportedOperationException("Not supported yet."); + } + + public PermissionAttachment addAttachment(Plugin plugin, int ticks) { + throw new UnsupportedOperationException("Not supported yet."); + } + + public void removeAttachment(PermissionAttachment attachment) { + throw new UnsupportedOperationException("Not supported yet."); + } + + public void recalculatePermissions() { + throw new UnsupportedOperationException("Not supported yet."); + } + + public Set getEffectivePermissions() { + throw new UnsupportedOperationException("Not supported yet."); + } + + public boolean isOp() { + throw new UnsupportedOperationException("Not supported yet."); + } + + public void setOp(boolean value) { + throw new UnsupportedOperationException("Not supported yet."); + } + + public void sendMessage(String message) { + throw new UnsupportedOperationException("Not supported yet."); + } + + public boolean isOnline() { + throw new UnsupportedOperationException("Not supported yet."); + } + + public boolean isBanned() { + throw new UnsupportedOperationException("Not supported yet."); + } + + public void setBanned(boolean banned) { + throw new UnsupportedOperationException("Not supported yet."); + } + + public boolean isWhitelisted() { + throw new UnsupportedOperationException("Not supported yet."); + } + + public void setWhitelisted(boolean value) { + throw new UnsupportedOperationException("Not supported yet."); + } + + public Player getPlayer() { + throw new UnsupportedOperationException("Not supported yet."); + } + + public long getFirstPlayed() { + throw new UnsupportedOperationException("Not supported yet."); + } + + public long getLastPlayed() { + throw new UnsupportedOperationException("Not supported yet."); + } + + public boolean hasPlayedBefore() { + throw new UnsupportedOperationException("Not supported yet."); + } + + public Map serialize() { + throw new UnsupportedOperationException("Not supported yet."); + } + + public void sendPluginMessage(Plugin source, String channel, byte[] message) { + throw new UnsupportedOperationException("Not supported yet."); + } + + public Set getListeningPluginChannels() { + throw new UnsupportedOperationException("Not supported yet."); + } +} diff --git a/src/test/java/org/bukkit/plugin/messaging/TestPlugin.java b/src/test/java/org/bukkit/plugin/messaging/TestPlugin.java new file mode 100644 index 00000000..060b742a --- /dev/null +++ b/src/test/java/org/bukkit/plugin/messaging/TestPlugin.java @@ -0,0 +1,116 @@ +package org.bukkit.plugin.messaging; + +import com.avaje.ebean.EbeanServer; +import java.io.File; +import java.io.InputStream; +import org.bukkit.Server; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.event.Event.Type; +import org.bukkit.generator.ChunkGenerator; +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.PluginDescriptionFile; +import org.bukkit.plugin.PluginLoader; +import org.bukkit.util.config.Configuration; + +public class TestPlugin implements Plugin { + private boolean enabled = true; + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + public File getDataFolder() { + throw new UnsupportedOperationException("Not supported."); + } + + public PluginDescriptionFile getDescription() { + throw new UnsupportedOperationException("Not supported."); + } + + public Configuration getConfiguration() { + throw new UnsupportedOperationException("Not supported."); + } + + public FileConfiguration getConfig() { + throw new UnsupportedOperationException("Not supported."); + } + + public InputStream getResource(String filename) { + throw new UnsupportedOperationException("Not supported."); + } + + public void saveConfig() { + throw new UnsupportedOperationException("Not supported."); + } + + public void saveDefaultConfig() { + throw new UnsupportedOperationException("Not supported."); + } + + public void saveResource(String resourcePath, boolean replace) { + throw new UnsupportedOperationException("Not supported."); + } + + public void reloadConfig() { + throw new UnsupportedOperationException("Not supported."); + } + + public PluginLoader getPluginLoader() { + throw new UnsupportedOperationException("Not supported."); + } + + public Server getServer() { + throw new UnsupportedOperationException("Not supported."); + } + + public boolean isEnabled() { + return enabled; + } + + public void onDisable() { + throw new UnsupportedOperationException("Not supported."); + } + + public void onLoad() { + throw new UnsupportedOperationException("Not supported."); + } + + public void onEnable() { + throw new UnsupportedOperationException("Not supported."); + } + + public boolean isNaggable() { + throw new UnsupportedOperationException("Not supported."); + } + + public void setNaggable(boolean canNag) { + throw new UnsupportedOperationException("Not supported."); + } + + public EbeanServer getDatabase() { + throw new UnsupportedOperationException("Not supported."); + } + + public ChunkGenerator getDefaultWorldGenerator(String worldName, String id) { + throw new UnsupportedOperationException("Not supported."); + } + + public long getTiming(Type type) { + throw new UnsupportedOperationException("Not supported."); + } + + public void incTiming(Type type, long delta) { + throw new UnsupportedOperationException("Not supported."); + } + + public void resetTimings() { + throw new UnsupportedOperationException("Not supported."); + } + + public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { + throw new UnsupportedOperationException("Not supported."); + } + +} -- cgit v1.2.3