Merge API into master. This marks the dawn of a new Bungee era, but must be regarded as UNSTABLE.

This commit is contained in:
md_5
2013-01-22 13:27:55 +11:00
99 changed files with 3100 additions and 2034 deletions

View File

@@ -0,0 +1,19 @@
package net.md_5.bungee.api;
/**
* Represents a method which may be called once a result has been computed
* asynchronously.
*
* @param <V> the type of result
*/
public interface Callback<V>
{
/**
* Called when the result is done.
*
* @param result the result of the computation
* @param error the error(s) that occurred, if any
*/
public void done(V result, Throwable error);
}

View File

@@ -0,0 +1,156 @@
package net.md_5.bungee.api;
import java.util.regex.Pattern;
/**
* Simplistic enumeration of all supported color values for chat.
*/
public enum ChatColor
{
/**
* Represents black.
*/
BLACK('0'),
/**
* Represents dark blue.
*/
DARK_BLUE('1'),
/**
* Represents dark green.
*/
DARK_GREEN('2'),
/**
* Represents dark blue (aqua).
*/
DARK_AQUA('3'),
/**
* Represents dark red.
*/
DARK_RED('4'),
/**
* Represents dark purple.
*/
DARK_PURPLE('5'),
/**
* Represents gold.
*/
GOLD('6'),
/**
* Represents gray.
*/
GRAY('7'),
/**
* Represents dark gray.
*/
DARK_GRAY('8'),
/**
* Represents blue.
*/
BLUE('9'),
/**
* Represents green.
*/
GREEN('a'),
/**
* Represents aqua.
*/
AQUA('b'),
/**
* Represents red.
*/
RED('c'),
/**
* Represents light purple.
*/
LIGHT_PURPLE('d'),
/**
* Represents yellow.
*/
YELLOW('e'),
/**
* Represents white.
*/
WHITE('f'),
/**
* Represents magical characters that change around randomly.
*/
MAGIC('k'),
/**
* Makes the text bold.
*/
BOLD('l'),
/**
* Makes a line appear through the text.
*/
STRIKETHROUGH('m'),
/**
* Makes the text appear underlined.
*/
UNDERLINE('n'),
/**
* Makes the text italic.
*/
ITALIC('o'),
/**
* Resets all previous chat colors or formats.
*/
RESET('r');
/**
* The special character which prefixes all chat colour codes. Use this if
* you need to dynamically convert colour codes from your custom format.
*/
public static final char COLOR_CHAR = '\u00A7';
/**
* Pattern to remove all colour codes.
*/
private static final Pattern STRIP_COLOR_PATTERN = Pattern.compile("(?i)" + String.valueOf(COLOR_CHAR) + "[0-9A-FK-OR]");
/**
* This colour's colour char prefixed by the {@link #COLOR_CHAR}.
*/
private final String toString;
private ChatColor(char code)
{
this.toString = new String(new char[]
{
COLOR_CHAR, code
});
}
@Override
public String toString()
{
return toString;
}
/**
* Strips the given message of all color codes
*
* @param input String to strip of color
* @return A copy of the input string, without any coloring
*/
public static String stripColor(final String input)
{
if (input == null)
{
return null;
}
return STRIP_COLOR_PATTERN.matcher(input).replaceAll("");
}
public static String translateAlternateColorCodes(char altColorChar, String textToTranslate)
{
char[] b = textToTranslate.toCharArray();
for (int i = 0; i < b.length - 1; i++)
{
if (b[i] == altColorChar && "0123456789AaBbCcDdEeFfKkLlMmNnOoRr".indexOf(b[i + 1]) > -1)
{
b[i] = ChatColor.COLOR_CHAR;
b[i + 1] = Character.toLowerCase(b[i + 1]);
}
}
return new String(b);
}
}

View File

@@ -0,0 +1,59 @@
package net.md_5.bungee.api;
import java.util.Collection;
public interface CommandSender
{
/**
* Get the unique name of this command sender.
*
* @return the senders username
*/
public String getName();
/**
* Send a message to this sender.
*
* @param message the message to send
*/
public void sendMessage(String message);
/**
* Get all groups this user is part of. This returns an unmodifiable
* collection.
*
* @return the users groups
*/
public Collection<String> getGroups();
/**
* Adds groups to a this user for the current session only.
*
* @param groups the groups to add
*/
public void addGroups(String... groups);
/**
* Remove groups from this user for the current session only.
*
* @param groups the groups to remove
*/
public void removeGroups(String... groups);
/**
* Checks if this user has the specified permission node.
*
* @param permission the node to check
* @return whether they have this node
*/
public boolean hasPermission(String permission);
/**
* Set a permission node for this user.
*
* @param permission the node to set
* @param value the value of the node
*/
public void setPermission(String permission, boolean value);
}

View File

@@ -0,0 +1,177 @@
package net.md_5.bungee.api;
import net.md_5.bungee.api.plugin.PluginManager;
import com.google.common.base.Preconditions;
import java.util.Collection;
import java.util.Map;
import java.util.logging.Logger;
import lombok.Getter;
import net.md_5.bungee.api.config.ConfigurationAdapter;
import net.md_5.bungee.api.config.ServerInfo;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.api.connection.Server;
import net.md_5.bungee.api.plugin.Plugin;
public abstract class ProxyServer
{
@Getter
private static ProxyServer instance;
/**
* Sets the proxy instance. This method may only be called once per an
* application.
*
* @param instance the new instance to set
*/
public static void setInstance(ProxyServer instance)
{
Preconditions.checkNotNull(instance, "instance");
Preconditions.checkArgument(ProxyServer.instance == null, "Instance already set");
ProxyServer.instance = instance;
}
/**
* Gets the name of the currently running proxy software.
*
* @return the name of this instance
*/
public abstract String getName();
/**
* Gets the version of the currently running proxy software.
*
* @return the version of this instance
*/
public abstract String getVersion();
/**
* Gets the main logger which can be used as a suitable replacement for
* {@link System#out} and {@link System#err}.
*
* @return the {@link Logger} instance
*/
public abstract Logger getLogger();
/**
* Return all players currently connected.
*
* @return all connected players
*/
public abstract Collection<ProxiedPlayer> getPlayers();
/**
* Gets a connected player via their unique username.
*
* @param name of the player
* @return their player instance
*/
public abstract ProxiedPlayer getPlayer(String name);
/**
* Get a server by its name. The instance returned will be taken from a
* player currently on that server to facilitate abstract proxy -> server
* actions.
*
* @param name the name to lookup
* @return the associated server
*/
public abstract Server getServer(String name);
/**
* Return all servers registered to this proxy, keyed by name. Unlike the
* methods in {@link ConfigurationAdapter#getServers()}, this will not
* return a fresh map each time.
*
* @return all registered remote server destinations
*/
public abstract Map<String, ServerInfo> getServers();
/**
* Get the {@link PluginManager} associated with loading plugins and
* dispatching events. It is recommended that implementations use the
* provided PluginManager class.
*
* @return the plugin manager
*/
public abstract PluginManager getPluginManager();
/**
* Returns the currently in use configuration adapter.
*
* @return the used configuration adapter
*/
public abstract ConfigurationAdapter getConfigurationAdapter();
/**
* Set the configuration adapter to be used. Must be called from
* {@link Plugin#onLoad()}.
*
* @param adapter the adapter to use
*/
public abstract void setConfigurationAdapter(ConfigurationAdapter adapter);
/**
* Get the currently in use tab list handle.
*
* @return the tab list handler
*/
public abstract TabListHandler getTabListHandler();
/**
* Set the used tab list handler, should not be changed once players have
* connected
*
* @param handler the tab list handler to set
*/
public abstract void setTabListHandler(TabListHandler handler);
/**
* Get the currently in use reconnect handler.
*
* @return the in use reconnect handler
*/
public abstract ReconnectHandler getReconnectHandler();
/**
* Sets the reconnect handler to be used for subsequent connections.
*
* @param handler the new handler
*/
public abstract void setReconnectHandler(ReconnectHandler handler);
/**
* Gracefully mark this instance for shutdown.
*/
public abstract void stop();
/**
* Start this instance so that it may accept connections.
*
* @throws Exception any exception thrown during startup causing the
* instance to fail to boot
*/
public abstract void start() throws Exception;
/**
* Register a channel for use with plugin messages. This is required by some
* server / client implementations.
*
* @param channel the channel to register
*/
public abstract void registerChannel(String channel);
/**
* Unregister a previously registered channel.
*
* @param channel the channel to unregister
*/
public abstract void unregisterChannel(String channel);
/**
* Get an immutable set of all registered plugin channels.
*
* @return registered plugin channels
*/
public abstract Collection<String> getChannels();
}

View File

@@ -0,0 +1,30 @@
package net.md_5.bungee.api;
import net.md_5.bungee.api.connection.ProxiedPlayer;
public interface ReconnectHandler
{
/**
* Gets the initial server name for a connecting player.
*
* @param player the connecting player
* @return the server name
*/
public String getServer(ProxiedPlayer player);
/**
* Save the server of this player before they disconnect so it can be
* retrieved later.
*
* @param player the player to save
*/
public void setServer(ProxiedPlayer player);
/**
* Save all pending reconnect locations. Whilst not used for database
* connections, this method will be called at a predefined interval to allow
* the saving of reconnect files.
*/
public void save();
}

View File

@@ -0,0 +1,33 @@
package net.md_5.bungee.api;
import lombok.Data;
/**
* Represents the standard list data returned by opening a server in the
* Minecraft client server list, or hitting it with a packet 0xFE.
*/
@Data
public class ServerPing
{
/**
* Numeric protocol version supported by the server.
*/
private final byte protocolVersion;
/**
* Human readable game version.
*/
private final String gameVersion;
/**
* Server MOTD.
*/
private final String motd;
/**
* Current amount of players on the server.
*/
private final String currentPlayers;
/**
* Max amount of players the server will allow.
*/
private final String maxPlayers;
}

View File

@@ -0,0 +1,48 @@
package net.md_5.bungee.api;
import net.md_5.bungee.api.connection.ProxiedPlayer;
public interface TabListHandler
{
/**
* Called when a player first connects to the proxy.
*
* @param player the connecting player
*/
public void onConnect(ProxiedPlayer player);
/**
* Called when a player changes their connected server.
*
* @param player the player who changed servers
*/
public void onServerChange(ProxiedPlayer player);
/**
* Called when a players ping changes. The new ping will have not updated in
* the player instance until this method returns.
*
* @param player the player who's ping changed
* @param ping the player's new ping.
*/
public void onPingChange(ProxiedPlayer player, int ping);
/**
* Called when a player disconnects.
*
* @param player the disconnected player
*/
public void onDisconnect(ProxiedPlayer player);
/**
* Called when a list update packet is sent from server to client.
*
* @param player receiving this packet
* @param name the player which this packet is relevant to
* @param online whether the subject player is online
* @param ping ping of the subject player
* @return whether to send the packet to the client
*/
public boolean onListUpdate(ProxiedPlayer player, String name, boolean online, int ping);
}

View File

@@ -0,0 +1,61 @@
package net.md_5.bungee.api.config;
import java.util.Collection;
import java.util.Map;
/**
* This class allows plugins to set their own configuration adapter to load
* settings from a different place.
*/
public interface ConfigurationAdapter
{
/**
* Gets an integer from the specified path.
*
* @param path the path to retrieve the integer from
* @param def the default value
* @return the retrieved integer
*/
public int getInt(String path, int def);
/**
* Gets a string from the specified path.
*
* @param path the path to retrieve the string from.
* @param def the default value
* @return the retrieved string
*/
public String getString(String path, String def);
/**
* Get the configuration all servers which may be accessible via the proxy.
*
* @return all accessible servers, keyed by name
*/
public Map<String, ServerInfo> getServers();
/**
* Get information about all hosts to bind the proxy to.
*
* @return a list of all hosts to bind to
*/
public Collection<ListenerInfo> getListeners();
/**
* Get all groups this player is in.
*
* @param player the player to check
* @return all the player's groups.
*/
public Collection<String> getGroups(String player);
/**
* Get all permission corresponding to the specified group. The result of
* this method may or may not be cached, depending on the implementation.
*
* @param group the group to check
* @return all true permissions for this group
*/
public Collection<String> getPermissions(String group);
}

View File

@@ -0,0 +1,41 @@
package net.md_5.bungee.api.config;
import java.net.InetSocketAddress;
import java.util.Map;
import lombok.Data;
/**
* Class representing the configuration of a server listener. Used for allowing
* multiple listeners on different ports.
*/
@Data
public class ListenerInfo
{
/**
* Host to bind to.
*/
private final InetSocketAddress host;
/**
* Displayed MOTD.
*/
private final String motd;
/**
* Max amount of slots displayed on the ping page.
*/
private final int maxPlayers;
/**
* Name of the server which users will be taken to by default.
*/
private final String defaultServer;
/**
* Whether reconnect locations will be used, or else the user is simply
* transferred to the default server on connect.
*/
private final boolean forceDefault;
/**
* A list of host to server name mappings which will force a user to be
* transferred depending on the host they connect to.
*/
private final Map<String, String> forcedHosts;
}

View File

@@ -0,0 +1,65 @@
package net.md_5.bungee.api.config;
import java.net.InetSocketAddress;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.Synchronized;
import net.md_5.bungee.api.connection.ProxiedPlayer;
/**
* Class used to represent a server to connect to.
*/
@Data
@AllArgsConstructor
public class ServerInfo
{
/**
* Name this server displays as.
*/
private final String name;
/**
* Connectable address of this server.
*/
private final InetSocketAddress address;
/**
* Players connected to a server defined by these properties.
*/
private final Collection<ProxiedPlayer> players = new ArrayList<>();
/**
* Add a player to the internal set of this server.
*
* @param player the player to add
*/
@Synchronized("players")
public void addPlayer(ProxiedPlayer player)
{
players.add(player);
}
/**
* Remove a player form the internal set of this server.
*
* @param player the player to remove
*/
@Synchronized("players")
public void removePlayer(ProxiedPlayer player)
{
players.remove(player);
}
/**
* Get the set of all players on this server.
*
* @return an unmodifiable collection of all players on this server
*/
@Synchronized("players")
public Collection<ProxiedPlayer> getPlayers()
{
return Collections.unmodifiableCollection(players);
}
}

View File

@@ -0,0 +1,8 @@
package net.md_5.bungee.api.connection;
/**
* Represents a player physically connected to the world hosted on this server.
*/
public interface ConnectedPlayer extends ProxiedPlayer
{
}

View File

@@ -0,0 +1,19 @@
package net.md_5.bungee.api.connection;
import java.net.InetSocketAddress;
/**
* A proxy connection is defined as a connection directly connected to a socket.
* It should expose information about the remote peer, however not be specific
* to a type of connection, whether server or player.
*/
public interface Connection
{
/**
* Gets the remote address of this connection.
*
* @return the remote address
*/
public InetSocketAddress getAddress();
}

View File

@@ -0,0 +1,47 @@
package net.md_5.bungee.api.connection;
import java.net.InetSocketAddress;
import net.md_5.bungee.api.config.ListenerInfo;
/**
* Represents a user attempting to log into the proxy.
*/
public interface PendingConnection extends Connection
{
/**
* Get the requested username.
*
* @return the requested username, or null if not set
*/
public String getName();
/**
* Get the numerical client version of the player attempting to log in.
*
* @return the protocol version of the remote client
*/
public byte getVersion();
/**
* Get the requested virtual host that the client tried to connect to.
*
* @return request virtual host or null if invalid / not specified.
*/
public InetSocketAddress getVirtualHost();
/**
* Completely kick this user from the proxy and all of its child
* connections.
*
* @param reason the disconnect reason displayed to the player
*/
public void disconnect(String reason);
/**
* Get the listener that accepted this connection.
*
* @return the accepting listener
*/
public ListenerInfo getListener();
}

View File

@@ -0,0 +1,72 @@
package net.md_5.bungee.api.connection;
import net.md_5.bungee.api.CommandSender;
import net.md_5.bungee.api.config.ServerInfo;
/**
* Represents a player who's connection is being connected to somewhere else,
* whether it be a remote or embedded server.
*/
public interface ProxiedPlayer extends Connection, CommandSender
{
/**
* Gets this player's display name.
*
* @return the players current display name
*/
public String getDisplayName();
/**
* Sets this players display name to be used as their nametag and tab list
* name.
*
* @param name the name to set
*/
public void setDisplayName(String name);
/**
* Connects / transfers this user to the specified connection, gracefully
* closing the current one. Depending on the implementation, this method
* might return before the user has been connected.
*
* @param target the new server to connect to
*/
public void connect(ServerInfo target);
/**
* Gets the server this player is connected to.
*
* @return the server this player is connected to
*/
public Server getServer();
/**
* Gets the ping time between the proxy and this connection.
*
* @return the current ping time
*/
public int getPing();
/**
* Disconnect (remove) this player from the proxy with the specified reason.
*
* @param reason the reason displayed to the player
*/
public void disconnect(String reason);
/**
* Send a plugin message to this player.
*
* @param channel the channel to send this data via
* @param data the data to send
*/
public void sendData(String channel, byte[] data);
/**
* Get the pending connection that belongs to this player.
*
* @return the pending connection that this player used
*/
public PendingConnection getPendingConnection();
}

View File

@@ -0,0 +1,34 @@
package net.md_5.bungee.api.connection;
import net.md_5.bungee.api.Callback;
import net.md_5.bungee.api.config.ServerInfo;
import net.md_5.bungee.api.ServerPing;
/**
* Represents a destination which this proxy might connect to.
*/
public interface Server extends Connection
{
/**
* Returns the basic information about this server.
*
* @return the {@link ServerInfo} for this server
*/
public ServerInfo getInfo();
/**
* Send data by any available means to this server.
*
* @param channel the channel to send this data via
* @param data the data to send
*/
public abstract void sendData(String channel, byte[] data);
/**
* Asynchronously gets the current player count on this server.
*
* @param callback the callback to call when the count has been retrieved.
*/
public abstract void ping(Callback<ServerPing> callback);
}

View File

@@ -0,0 +1,33 @@
package net.md_5.bungee.api.event;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import net.md_5.bungee.api.connection.Connection;
import net.md_5.bungee.api.plugin.Cancellable;
/**
* Event called when a player sends a message to a server, or a server sends a
* message to a player.
*/
@Data
@ToString(callSuper = true)
@EqualsAndHashCode(callSuper = true)
public class ChatEvent extends TargetedEvent implements Cancellable
{
/**
* Cancelled state.
*/
private boolean cancelled;
/**
* Text contained in this chat.
*/
private String message;
public ChatEvent(Connection sender, Connection receiver, String message)
{
super(sender, receiver);
this.message = message;
}
}

View File

@@ -0,0 +1,31 @@
package net.md_5.bungee.api.event;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import net.md_5.bungee.api.connection.PendingConnection;
import net.md_5.bungee.api.plugin.Cancellable;
import net.md_5.bungee.api.plugin.Event;
/**
* Event called to represent a player logging in.
*/
@Data
@ToString(callSuper = true)
@EqualsAndHashCode(callSuper = true)
public class LoginEvent extends Event implements Cancellable
{
/**
* Cancelled state.
*/
private boolean cancelled;
/**
* Message to use when kicking if this event is canceled.
*/
private String cancelReason;
/**
* Connection attempting to login.
*/
private final PendingConnection connection;
}

View File

@@ -0,0 +1,37 @@
package net.md_5.bungee.api.event;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import net.md_5.bungee.api.connection.Connection;
import net.md_5.bungee.api.plugin.Cancellable;
/**
* Event called when a plugin message is sent to the client or server.
*/
@Data
@ToString(callSuper = true)
@EqualsAndHashCode(callSuper = true)
public class PluginMessageEvent extends TargetedEvent implements Cancellable
{
/**
* Cancelled state.
*/
private boolean cancelled;
/**
* Tag specified for this plugin message.
*/
private final String tag;
/**
* Data contained in this plugin message.
*/
private final byte[] data;
public PluginMessageEvent(Connection sender, Connection receiver, String tag, byte[] data)
{
super(sender, receiver);
this.tag = tag;
this.data = data;
}
}

View File

@@ -0,0 +1,32 @@
package net.md_5.bungee.api.event;
import java.net.InetSocketAddress;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import net.md_5.bungee.api.ServerPing;
import net.md_5.bungee.api.config.ListenerInfo;
import net.md_5.bungee.api.plugin.Event;
/**
* Called when the proxy is pinged with packet 0xFE from the server list.
*/
@Data
@ToString(callSuper = true)
@EqualsAndHashCode(callSuper = true)
public class ProxyPingEvent extends Event
{
/**
* The address of the user pinging.
*/
private final InetSocketAddress remoteAddress;
/**
* The data corresponding to the server which received this ping.
*/
private final ListenerInfo server;
/**
* The data to respond with.
*/
private ServerPing response;
}

View File

@@ -0,0 +1,26 @@
package net.md_5.bungee.api.event;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import net.md_5.bungee.api.config.ServerInfo;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.api.plugin.Event;
@Data
@AllArgsConstructor
@ToString(callSuper = true)
@EqualsAndHashCode(callSuper = true)
public class ServerConnectEvent extends Event
{
/**
* Player connecting to a new server.
*/
private final ProxiedPlayer player;
/**
* Server the player will be connected to.
*/
private ServerInfo target;
}

View File

@@ -0,0 +1,30 @@
package net.md_5.bungee.api.event;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.api.connection.Server;
import net.md_5.bungee.api.plugin.Event;
/**
* Not to be confused with {@link ServerConnectEvent}, this event is called once
* a connection to a server is fully operational, and is about to hand over
* control of the session to the player. It is useful if you wish to send
* information to the server before the player logs in.
*/
@Data
@ToString(callSuper = true)
@EqualsAndHashCode(callSuper = true)
public class ServerConnectedEvent extends Event
{
/**
* Player whom the server is for.
*/
private final ProxiedPlayer player;
/**
* The server itself.
*/
private final Server server;
}

View File

@@ -0,0 +1,29 @@
package net.md_5.bungee.api.event;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import net.md_5.bungee.api.connection.Connection;
import net.md_5.bungee.api.plugin.Event;
/**
* An event which occurs in the communication between two nodes. It is not
* recommended to call {@link #setSender(net.md_5.bungee.api.Connection)} or
* {@link #setReceiver(net.md_5.bungee.api.Connection)} and the results of doing
* so are undefined.
*/
@Data
@AllArgsConstructor
@EqualsAndHashCode(callSuper = false)
public abstract class TargetedEvent extends Event
{
/**
* Creator of the action.
*/
private Connection sender;
/**
* Receiver of the action.
*/
private Connection receiver;
}

View File

@@ -0,0 +1,23 @@
package net.md_5.bungee.api.plugin;
/**
* Events that implement this indicate that they may be cancelled and thus
* prevented from happening.
*/
public interface Cancellable
{
/**
* Get whether or not this event is cancelled.
*
* @return the cancelled state of this event
*/
public boolean isCancelled();
/**
* Sets the cancelled state of this event.
*
* @param cancel the state to set
*/
public void setCancelled(boolean cancel);
}

View File

@@ -0,0 +1,52 @@
package net.md_5.bungee.api.plugin;
import lombok.AccessLevel;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import net.md_5.bungee.api.CommandSender;
/**
* A command that can be executed by a {@link CommandSender}.
*/
@Data
@RequiredArgsConstructor(access = AccessLevel.NONE)
public abstract class Command
{
private final String name;
private final String permission;
private final String[] aliases;
/**
* Construct a new command with no permissions or aliases.
*
* @param name the name of this command
*/
public Command(String name)
{
this(name, null);
}
/**
* Construct a new command.
*
* @param name primary name of this command
* @param permission the permission node required to execute this command,
* null or empty string allows it to be executed by everyone
* @param aliases aliases which map back to this command
*/
public Command(String name, String permission, String... aliases)
{
this.name = name;
this.permission = permission;
this.aliases = aliases;
}
/**
* Execute this command with the specified sender and arguments.
*
* @param sender the executor of this command
* @param args arguments used to invoke this command
*/
public abstract void execute(CommandSender sender, String[] args);
}

View File

@@ -0,0 +1,8 @@
package net.md_5.bungee.api.plugin;
/**
* Dummy class which all callable events must extend.
*/
public abstract class Event
{
}

View File

@@ -0,0 +1,8 @@
package net.md_5.bungee.api.plugin;
/**
* Dummy interface which all event subscribers and listeners must implement.
*/
public interface Listener
{
}

View File

@@ -0,0 +1,48 @@
package net.md_5.bungee.api.plugin;
import lombok.Getter;
import net.md_5.bungee.api.config.ConfigurationAdapter;
/**
* Represents any Plugin that may be loaded at runtime to enhance existing
* functionality.
*/
public class Plugin
{
@Getter
private PluginDescription description;
/**
* Called when the plugin has just been loaded. Most of the proxy will not
* be initialized, so only use it for registering
* {@link ConfigurationAdapter}'s and other predefined behavior.
*/
public void onLoad()
{
}
/**
* Called when this plugin is enabled.
*/
public void onEnable()
{
}
/**
* Called when this plugin is disabled.
*/
public void onDisable()
{
}
/**
* Called by the loader to initialize the fields in this plugin.
*
* @param description the description that describes this plugin
*/
final void init(PluginDescription description)
{
this.description = description;
}
}

View File

@@ -0,0 +1,28 @@
package net.md_5.bungee.api.plugin;
import lombok.Data;
/**
* POJO representing the plugin.yml file.
*/
@Data
public class PluginDescription
{
/**
* Friendly name of the plugin.
*/
private final String name;
/**
* Plugin main class. Needs to extend {@link Plugin}.
*/
private final String main;
/**
* Plugin version.
*/
private final String version;
/**
* Plugin author.
*/
private final String author;
}

View File

@@ -0,0 +1,218 @@
package net.md_5.bungee.api.plugin;
import com.google.common.base.Preconditions;
import com.google.common.eventbus.EventBus;
import com.google.common.eventbus.Subscribe;
import java.io.File;
import java.io.InputStream;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.logging.Level;
import java.util.regex.Pattern;
import net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.api.CommandSender;
import net.md_5.bungee.api.ProxyServer;
import org.yaml.snakeyaml.Yaml;
/**
* Class to manage bridging between plugin duties and implementation duties, for
* example event handling and plugin management.
*/
public class PluginManager
{
private static final Pattern argsSplit = Pattern.compile(" ");
/*========================================================================*/
private final Yaml yaml = new Yaml();
private final EventBus eventBus = new EventBus();
private final Map<String, Plugin> plugins = new HashMap<>();
private final Map<String, Command> commandMap = new HashMap<>();
/**
* Register a command so that it may be executed.
*
* @param command the command to register
*/
public void registerCommand(Command command)
{
commandMap.put(command.getName(), command);
for (String alias : command.getAliases())
{
commandMap.put(alias, command);
}
}
/**
* Unregister a command so it will no longer be executed.
*
* @param command the command to unregister
*/
public void unregisterCommand(Command command)
{
commandMap.values().remove(command);
}
/**
* Execute a command if it is registered, else return false.
*
* @param sender the sender executing the command
* @param commandLine the complete command line including command name and
* arguments
* @return whether the command was handled
*/
public boolean dispatchCommand(CommandSender sender, String commandLine)
{
String[] split = argsSplit.split(commandLine);
Command command = commandMap.get(split[0]);
if (command == null)
{
return false;
}
String permission = command.getPermission();
if (permission != null && !permission.isEmpty() && !sender.hasPermission(permission))
{
sender.sendMessage(ChatColor.RED + "You do not have permission to execute this command!");
return true;
}
String[] args = Arrays.copyOfRange(split, 1, split.length);
try
{
command.execute(sender, args);
} catch (Exception ex)
{
sender.sendMessage(ChatColor.RED + "An internal error occurred whilst executing this command, please check the console log for details.");
ProxyServer.getInstance().getLogger().log(Level.WARNING, "Error in dispatching command", ex);
}
return true;
}
/**
* Returns the {@link Plugin} objects corresponding to all loaded plugins.
*
* @return the set of loaded plugins
*/
public Collection<Plugin> getPlugins()
{
return plugins.values();
}
/**
* Returns a loaded plugin identified by the specified name.
*
* @param name of the plugin to retrieve
* @return the retrieved plugin or null if not loaded
*/
public Plugin getPlugin(String name)
{
return plugins.get(name);
}
/**
* Enable all plugins by calling the {@link Plugin#onEnable()} method.
*/
public void enablePlugins()
{
for (Map.Entry<String, Plugin> plugin : plugins.entrySet())
{
try
{
plugin.getValue().onEnable();
} catch (Exception ex)
{
ProxyServer.getInstance().getLogger().log(Level.WARNING, "Exception encountered when loading plugin: " + plugin.getKey(), ex);
}
}
}
/**
* Load a plugin from the specified file. This file must be in jar format.
* This will not enable plugins, {@link #enablePlugins()} must be called.
*
* @param file the file to load from
* @throws Exception Any exceptions encountered when loading a plugin from
* this file.
*/
public void loadPlugin(File file) throws Exception
{
Preconditions.checkNotNull(file, "file");
Preconditions.checkArgument(file.isFile(), "Must load from file");
try (JarFile jar = new JarFile(file))
{
JarEntry pdf = jar.getJarEntry("plugin.yml");
try (InputStream in = jar.getInputStream(pdf))
{
PluginDescription desc = yaml.loadAs(in, PluginDescription.class);
URLClassLoader loader = new URLClassLoader(new URL[]
{
file.toURI().toURL()
});
Class<?> main = loader.loadClass(desc.getMain());
Plugin plugin = (Plugin) main.getDeclaredConstructor().newInstance();
plugin.init(desc);
plugins.put(pdf.getName(), plugin);
plugin.onLoad();
}
}
}
/**
* Load all plugins from the specified folder.
*
* @param folder the folder to search for plugins in
*/
public void loadPlugins(File folder)
{
Preconditions.checkNotNull(folder, "folder");
Preconditions.checkArgument(folder.isDirectory(), "Must load from a directory");
for (File file : folder.listFiles())
{
if (file.isFile() && file.getName().endsWith(".jar"))
{
try
{
loadPlugin(file);
} catch (Exception ex)
{
ProxyServer.getInstance().getLogger().log(Level.WARNING, "Could not load plugin from file " + file, ex);
}
}
}
}
/**
* Dispatch an event to all subscribed listeners and return the event once
* it has been handled by these listeners.
*
* @param <T> the type bounds, must be a class which extends event
* @param event the event to call
* @return the called event
*/
public <T extends Event> T callEvent(T event)
{
eventBus.post(event);
return event;
}
/**
* Register a {@link Listener} for receiving called events. Methods in this
* Object which wish to receive events must be annotated with the
* {@link Subscribe} annotation.
*
* @param listener the listener to register events for
*/
public void registerListener(Listener listener)
{
eventBus.register(listener);
}
}