Compare commits
128 Commits
Minecraft-
...
Minecraft-
Author | SHA1 | Date | |
---|---|---|---|
![]() |
db5510cc4e | ||
![]() |
5ed5c71aea | ||
![]() |
38a8469ab4 | ||
![]() |
9538dcf4d4 | ||
![]() |
33f654ce6f | ||
![]() |
c108e4e1ce | ||
![]() |
e998faeec1 | ||
![]() |
d67acd7bc9 | ||
![]() |
702f434db1 | ||
![]() |
47b5631562 | ||
![]() |
80e23d6646 | ||
![]() |
1dca12cffb | ||
![]() |
29c897c9cf | ||
![]() |
042f47cbb9 | ||
![]() |
422e97f495 | ||
![]() |
08789d8f9f | ||
![]() |
96444ae304 | ||
![]() |
af58db8a67 | ||
![]() |
49cffebd9b | ||
![]() |
ffdb917f2c | ||
![]() |
9c9657e36d | ||
![]() |
1342baed47 | ||
![]() |
e3a7490bcd | ||
![]() |
3e8693793c | ||
![]() |
bb47aba682 | ||
![]() |
d0e5ee4e09 | ||
![]() |
07e330b005 | ||
![]() |
024288e587 | ||
![]() |
53a6bb1dee | ||
![]() |
0f06b2c4e0 | ||
![]() |
d3c1acce83 | ||
![]() |
eaea090d37 | ||
![]() |
7384e797fc | ||
![]() |
d4d93ddbb9 | ||
![]() |
ccdf2a89d8 | ||
![]() |
89edb00c05 | ||
![]() |
00a0277a13 | ||
![]() |
68f11e46f7 | ||
![]() |
c352e854ee | ||
![]() |
d8c92cd311 | ||
![]() |
99f361ca77 | ||
![]() |
738ed99d54 | ||
![]() |
ad0da59267 | ||
![]() |
1dcc8d6a4b | ||
![]() |
0840a77153 | ||
![]() |
61a93a54a9 | ||
![]() |
da0281508e | ||
![]() |
51e92de2dd | ||
![]() |
f948acd634 | ||
![]() |
773ce089c1 | ||
![]() |
a07eba7965 | ||
![]() |
b68b6a76c7 | ||
![]() |
332033bb02 | ||
![]() |
172b8bc75b | ||
![]() |
db5a147491 | ||
![]() |
f083e27649 | ||
![]() |
b64a7be19b | ||
![]() |
c4d60a8fa9 | ||
![]() |
f07cfe0cf7 | ||
![]() |
4463b0c1b2 | ||
![]() |
ee8f33c196 | ||
![]() |
14ac2dd308 | ||
![]() |
fb94612315 | ||
![]() |
4c96880580 | ||
![]() |
4c4cdd51a1 | ||
![]() |
1f38152530 | ||
![]() |
911f08d52c | ||
![]() |
8f961c9d4e | ||
![]() |
8a5d8a57f7 | ||
![]() |
c54553d0f9 | ||
![]() |
600a1b4ff5 | ||
![]() |
09e592295f | ||
![]() |
fa0ee02beb | ||
![]() |
b23b54d9e4 | ||
![]() |
b3e8feb4cb | ||
![]() |
d0d1562155 | ||
![]() |
f510ab2a0b | ||
![]() |
fb1cab499d | ||
![]() |
58ca63e2b1 | ||
![]() |
499337c98e | ||
![]() |
526137be7b | ||
![]() |
47839cb11c | ||
![]() |
55a6cc56ef | ||
![]() |
8c2bea5be2 | ||
![]() |
daa58ffe58 | ||
![]() |
0189ad9c17 | ||
![]() |
9adcb05d45 | ||
![]() |
10e81041b2 | ||
![]() |
0c56945ffd | ||
![]() |
0a36cbd5bc | ||
![]() |
61b4777177 | ||
![]() |
7d1904584b | ||
![]() |
475571986c | ||
![]() |
55c2bcd634 | ||
![]() |
db4abfe486 | ||
![]() |
9424bdedca | ||
![]() |
52b3c6b77c | ||
![]() |
be29799f5a | ||
![]() |
c0d581d41f | ||
![]() |
6b50c7c599 | ||
![]() |
b4101874cc | ||
![]() |
66de4c95ef | ||
![]() |
927a295add | ||
![]() |
2cbea83c02 | ||
![]() |
87884ad084 | ||
![]() |
94cc2412e7 | ||
![]() |
924b90e325 | ||
![]() |
3f476a30b4 | ||
![]() |
f579b31bca | ||
![]() |
cac35116c3 | ||
![]() |
bd42fb23a0 | ||
![]() |
ffbebaff69 | ||
![]() |
b741722e5d | ||
![]() |
07288c722c | ||
![]() |
85e82a2e34 | ||
![]() |
3aef35ccbb | ||
![]() |
d1760dad93 | ||
![]() |
d3d11cf283 | ||
![]() |
d3bada58d4 | ||
![]() |
23517a9a97 | ||
![]() |
fdc87e88f5 | ||
![]() |
12941ffe62 | ||
![]() |
06e732d8c7 | ||
![]() |
5c4ea3c7a0 | ||
![]() |
632fa8bd94 | ||
![]() |
8732904bfd | ||
![]() |
788b96dc0a | ||
![]() |
1296783d9b |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,7 +1,7 @@
|
||||
# Eclipse stuff
|
||||
.classpath
|
||||
.project
|
||||
.settings
|
||||
.settings/
|
||||
|
||||
# netbeans
|
||||
nbproject/
|
||||
|
10
api/pom.xml
10
api/pom.xml
@@ -6,13 +6,13 @@
|
||||
<parent>
|
||||
<groupId>net.md-5</groupId>
|
||||
<artifactId>bungeecord-parent</artifactId>
|
||||
<version>1.5-SNAPSHOT</version>
|
||||
<version>1.6.2-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<groupId>net.md-5</groupId>
|
||||
<artifactId>bungeecord-api</artifactId>
|
||||
<version>1.5-SNAPSHOT</version>
|
||||
<version>1.6.2-SNAPSHOT</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>BungeeCord-API</name>
|
||||
@@ -25,12 +25,6 @@
|
||||
<version>14.0.1</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.ning</groupId>
|
||||
<artifactId>async-http-client</artifactId>
|
||||
<version>1.7.17</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.md-5</groupId>
|
||||
<artifactId>bungeecord-event</artifactId>
|
||||
|
@@ -0,0 +1,46 @@
|
||||
package net.md_5.bungee.api;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import net.md_5.bungee.api.config.ServerInfo;
|
||||
import net.md_5.bungee.api.connection.PendingConnection;
|
||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||
|
||||
public abstract class AbstractReconnectHandler implements ReconnectHandler
|
||||
{
|
||||
|
||||
@Override
|
||||
public ServerInfo getServer(ProxiedPlayer player)
|
||||
{
|
||||
ServerInfo server = getForcedHost( player.getPendingConnection() );
|
||||
if ( server == null )
|
||||
{
|
||||
server = getStoredServer( player );
|
||||
if ( server == null )
|
||||
{
|
||||
server = ProxyServer.getInstance().getServerInfo( player.getPendingConnection().getListener().getDefaultServer() );
|
||||
}
|
||||
|
||||
Preconditions.checkState( server != null, "Default server not defined" );
|
||||
}
|
||||
|
||||
return server;
|
||||
}
|
||||
|
||||
public static ServerInfo getForcedHost(PendingConnection con)
|
||||
{
|
||||
if ( con.getVirtualHost() == null )
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
String forced = con.getListener().getForcedHosts().get( con.getVirtualHost().getHostString() );
|
||||
|
||||
if ( forced == null && con.getListener().isForceDefault() )
|
||||
{
|
||||
forced = con.getListener().getDefaultServer();
|
||||
}
|
||||
return ProxyServer.getInstance().getServerInfo( forced );
|
||||
}
|
||||
|
||||
protected abstract ServerInfo getStoredServer(ProxiedPlayer player);
|
||||
}
|
@@ -2,7 +2,6 @@ package net.md_5.bungee.api;
|
||||
|
||||
import net.md_5.bungee.api.plugin.PluginManager;
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.ning.http.client.AsyncHttpClient;
|
||||
import java.io.File;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.Collection;
|
||||
@@ -189,10 +188,11 @@ public abstract class ProxyServer
|
||||
*
|
||||
* @param name name of the server
|
||||
* @param address connectable Minecraft address + port of the server
|
||||
* @param motd the motd when used as a forced server
|
||||
* @param restricted whether the server info restricted property will be set
|
||||
* @return the constructed instance
|
||||
*/
|
||||
public abstract ServerInfo constructServerInfo(String name, InetSocketAddress address, boolean restricted);
|
||||
public abstract ServerInfo constructServerInfo(String name, InetSocketAddress address, String motd, boolean restricted);
|
||||
|
||||
/**
|
||||
* Returns the console overlord for this proxy. Being the console, this
|
||||
@@ -217,15 +217,6 @@ public abstract class ProxyServer
|
||||
*/
|
||||
public abstract TaskScheduler getScheduler();
|
||||
|
||||
/**
|
||||
* Gets the the web client used by this proxy to facilitate making web
|
||||
* requests. Care should be taken to ensure that all operations are non
|
||||
* blocking where applicable.
|
||||
*
|
||||
* @return the server's {@link AsyncHttpClient} instance
|
||||
*/
|
||||
public abstract AsyncHttpClient getHttpClient();
|
||||
|
||||
/**
|
||||
* Get the current number of connected users. The default implementation is
|
||||
* more efficient than {@link #getPlayers()} as it does not take a lock or
|
||||
@@ -249,4 +240,11 @@ public abstract class ProxyServer
|
||||
* @return a new {@link CustomTabList} instance
|
||||
*/
|
||||
public abstract CustomTabList customTabList(ProxiedPlayer player);
|
||||
|
||||
/**
|
||||
* Gets the commands which are disabled and will not be run on this proxy.
|
||||
*
|
||||
* @return the set of disabled commands
|
||||
*/
|
||||
public abstract Collection<String> getDisabledCommands();
|
||||
}
|
||||
|
@@ -43,6 +43,15 @@ public interface ConfigurationAdapter
|
||||
*/
|
||||
public boolean getBoolean(String path, boolean def);
|
||||
|
||||
/**
|
||||
* Get a list from the specified path.
|
||||
*
|
||||
* @param path the path to retrieve the list form.
|
||||
* @param def the default value
|
||||
* @return the retrieved list
|
||||
*/
|
||||
public Collection<?> getList(String path, Collection<?> def);
|
||||
|
||||
/**
|
||||
* Get the configuration all servers which may be accessible via the proxy.
|
||||
*
|
||||
|
@@ -48,13 +48,17 @@ public class ListenerInfo
|
||||
* transferred depending on the host they connect to.
|
||||
*/
|
||||
private final Map<String, String> forcedHosts;
|
||||
/**
|
||||
* Get the texture pack used for servers connected to this proxy. May be
|
||||
* null.
|
||||
*/
|
||||
private final TexturePackInfo texturePack;
|
||||
/**
|
||||
* Class used to build tab lists for this player.
|
||||
*/
|
||||
private final Class<? extends TabListHandler> tabList;
|
||||
/**
|
||||
* Whether to set the local address when connecting to servers.
|
||||
*/
|
||||
private final boolean setLocalAddress;
|
||||
/**
|
||||
* Whether to pass the ping through when we can reliably get the target
|
||||
* server (force default server).
|
||||
*/
|
||||
private final boolean pingPassthrough;
|
||||
}
|
||||
|
@@ -36,6 +36,13 @@ public interface ServerInfo
|
||||
*/
|
||||
Collection<ProxiedPlayer> getPlayers();
|
||||
|
||||
/**
|
||||
* Returns the MOTD which should be used when this server is a forced host.
|
||||
*
|
||||
* @return the motd
|
||||
*/
|
||||
String getMotd();
|
||||
|
||||
/**
|
||||
* Whether the player can access this server. It will only return false when
|
||||
* the player has no permission and this server is restricted.
|
||||
|
@@ -1,17 +0,0 @@
|
||||
package net.md_5.bungee.api.config;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class TexturePackInfo
|
||||
{
|
||||
|
||||
/**
|
||||
* The URL of the texture pack.
|
||||
*/
|
||||
private final String url;
|
||||
/**
|
||||
* The square dimension of this texture pack.
|
||||
*/
|
||||
private final int size;
|
||||
}
|
@@ -2,7 +2,6 @@ package net.md_5.bungee.api.connection;
|
||||
|
||||
import net.md_5.bungee.api.CommandSender;
|
||||
import net.md_5.bungee.api.config.ServerInfo;
|
||||
import net.md_5.bungee.api.config.TexturePackInfo;
|
||||
import net.md_5.bungee.api.tab.TabListHandler;
|
||||
|
||||
/**
|
||||
@@ -72,13 +71,6 @@ public interface ProxiedPlayer extends Connection, CommandSender
|
||||
*/
|
||||
void chat(String message);
|
||||
|
||||
/**
|
||||
* Send a request to change the players texture pack.
|
||||
*
|
||||
* @param pack the pack to request
|
||||
*/
|
||||
void setTexturePack(TexturePackInfo pack);
|
||||
|
||||
/**
|
||||
* Sets the new tab list for the user. At this stage it is not advisable to
|
||||
* change after the user has logged in!
|
||||
@@ -93,4 +85,18 @@ public interface ProxiedPlayer extends Connection, CommandSender
|
||||
* @return the tab list in use by this user
|
||||
*/
|
||||
TabListHandler getTabList();
|
||||
|
||||
/**
|
||||
* Get the server which this player will be sent to next time the log in.
|
||||
*
|
||||
* @return the server, or null if default
|
||||
*/
|
||||
ServerInfo getReconnectServer();
|
||||
|
||||
/**
|
||||
* Set the server which this player will be sent to next time the log in.
|
||||
*
|
||||
* @param server the server to set
|
||||
*/
|
||||
void setReconnectServer(ServerInfo server);
|
||||
}
|
||||
|
@@ -33,11 +33,27 @@ public class ServerKickEvent extends Event implements Cancellable
|
||||
* Server to send player to if this event is cancelled.
|
||||
*/
|
||||
private ServerInfo cancelServer;
|
||||
/**
|
||||
* State in which the kick occured.
|
||||
*/
|
||||
private State state;
|
||||
|
||||
public enum State
|
||||
{
|
||||
|
||||
CONNECTING, CONNECTED, UNKNOWN;
|
||||
}
|
||||
|
||||
public ServerKickEvent(ProxiedPlayer player, String kickReason, ServerInfo cancelServer)
|
||||
{
|
||||
this( player, kickReason, cancelServer, State.UNKNOWN );
|
||||
}
|
||||
|
||||
public ServerKickEvent(ProxiedPlayer player, String kickReason, ServerInfo cancelServer, State state)
|
||||
{
|
||||
this.player = player;
|
||||
this.kickReason = kickReason;
|
||||
this.cancelServer = cancelServer;
|
||||
this.state = state;
|
||||
}
|
||||
}
|
||||
|
@@ -2,13 +2,18 @@ package net.md_5.bungee.api.plugin;
|
||||
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CopyOnWriteArraySet;
|
||||
|
||||
public class PluginClassloader extends URLClassLoader
|
||||
{
|
||||
|
||||
private static final Set<PluginClassloader> allLoaders = new HashSet<>();
|
||||
private static final Set<PluginClassloader> allLoaders = new CopyOnWriteArraySet<>();
|
||||
|
||||
static
|
||||
{
|
||||
ClassLoader.registerAsParallelCapable();
|
||||
}
|
||||
|
||||
public PluginClassloader(URL[] urls)
|
||||
{
|
||||
|
@@ -13,13 +13,12 @@ public class PluginLogger extends Logger
|
||||
{
|
||||
super( plugin.getClass().getCanonicalName(), null );
|
||||
pluginName = "[" + plugin.getDescription().getName() + "] ";
|
||||
setParent( ProxyServer.getInstance().getLogger() );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void log(LogRecord logRecord)
|
||||
{
|
||||
logRecord.setMessage( pluginName + logRecord.getMessage() );
|
||||
super.log( logRecord );
|
||||
ProxyServer.getInstance().getLogger().log( logRecord );
|
||||
}
|
||||
}
|
||||
|
@@ -1,6 +1,8 @@
|
||||
package net.md_5.bungee.api.plugin;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.collect.ArrayListMultimap;
|
||||
import com.google.common.collect.Multimap;
|
||||
import com.google.common.eventbus.Subscribe;
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
@@ -10,7 +12,9 @@ import java.net.URLClassLoader;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Stack;
|
||||
import java.util.jar.JarEntry;
|
||||
@@ -42,12 +46,14 @@ public class PluginManager
|
||||
private final Map<String, Plugin> plugins = new LinkedHashMap<>();
|
||||
private final Map<String, Command> commandMap = new HashMap<>();
|
||||
private Map<String, PluginDescription> toLoad = new HashMap<>();
|
||||
private Multimap<Plugin, Command> commandsByPlugin = ArrayListMultimap.create();
|
||||
private Multimap<Plugin, Listener> listenersByPlugin = ArrayListMultimap.create();
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public PluginManager(ProxyServer proxy)
|
||||
{
|
||||
this.proxy = proxy;
|
||||
eventBus = new EventBus( proxy.getLogger(), Subscribe.class, EventHandler.class );
|
||||
eventBus = new EventBus( proxy.getLogger() );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -63,6 +69,7 @@ public class PluginManager
|
||||
{
|
||||
commandMap.put( alias.toLowerCase(), command );
|
||||
}
|
||||
commandsByPlugin.put( plugin, command );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -73,6 +80,26 @@ public class PluginManager
|
||||
public void unregisterCommand(Command command)
|
||||
{
|
||||
commandMap.values().remove( command );
|
||||
commandsByPlugin.values().remove( command );
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregister all commands owned by a {@link Plugin}
|
||||
*
|
||||
* @param plugin the plugin to register the commands of
|
||||
*/
|
||||
public void unregisterCommands(Plugin plugin)
|
||||
{
|
||||
for ( Iterator<Command> it = commandsByPlugin.get( plugin ).iterator(); it.hasNext(); )
|
||||
{
|
||||
commandMap.values().remove( it.next() );
|
||||
it.remove();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean dispatchCommand(CommandSender sender, String commandLine)
|
||||
{
|
||||
return dispatchCommand( sender, commandLine, null );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -83,10 +110,21 @@ public class PluginManager
|
||||
* arguments
|
||||
* @return whether the command was handled
|
||||
*/
|
||||
public boolean dispatchCommand(CommandSender sender, String commandLine)
|
||||
public boolean dispatchCommand(CommandSender sender, String commandLine, List<String> tabResults)
|
||||
{
|
||||
String[] split = argsSplit.split( commandLine );
|
||||
Command command = commandMap.get( split[0].toLowerCase() );
|
||||
// Check for chat that only contains " "
|
||||
if ( split.length == 0 )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
String commandName = split[0].toLowerCase();
|
||||
if ( proxy.getDisabledCommands().contains( commandName ) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
Command command = commandMap.get( commandName );
|
||||
if ( command == null )
|
||||
{
|
||||
return false;
|
||||
@@ -102,7 +140,16 @@ public class PluginManager
|
||||
String[] args = Arrays.copyOfRange( split, 1, split.length );
|
||||
try
|
||||
{
|
||||
command.execute( sender, args );
|
||||
if ( tabResults == null )
|
||||
{
|
||||
command.execute( sender, args );
|
||||
} else if ( command instanceof TabExecutor )
|
||||
{
|
||||
for ( String s : ( (TabExecutor) command ).onTabComplete( sender, args ) )
|
||||
{
|
||||
tabResults.add( s );
|
||||
}
|
||||
}
|
||||
} catch ( Exception ex )
|
||||
{
|
||||
sender.sendMessage( ChatColor.RED + "An internal error occurred whilst executing this command, please check the console log for details." );
|
||||
@@ -176,7 +223,7 @@ public class PluginManager
|
||||
for ( String dependName : plugin.getDepends() )
|
||||
{
|
||||
PluginDescription depend = toLoad.get( dependName );
|
||||
Boolean dependStatus = depend != null ? pluginStatuses.get( depend ) : Boolean.FALSE;
|
||||
Boolean dependStatus = ( depend != null ) ? pluginStatuses.get( depend ) : Boolean.FALSE;
|
||||
|
||||
if ( dependStatus == null )
|
||||
{
|
||||
@@ -202,7 +249,7 @@ public class PluginManager
|
||||
{
|
||||
ProxyServer.getInstance().getLogger().log( Level.WARNING, "{0} (required by {1}) is unavailable", new Object[]
|
||||
{
|
||||
depend.getName(), plugin.getName()
|
||||
String.valueOf( depend.getName() ), plugin.getName()
|
||||
} );
|
||||
status = false;
|
||||
}
|
||||
@@ -305,7 +352,7 @@ public class PluginManager
|
||||
/**
|
||||
* 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.
|
||||
* {@link EventHandler} annotation.
|
||||
*
|
||||
* @param plugin the owning plugin
|
||||
* @param listener the listener to register events for
|
||||
@@ -314,14 +361,35 @@ public class PluginManager
|
||||
{
|
||||
for ( Method method : listener.getClass().getDeclaredMethods() )
|
||||
{
|
||||
if ( method.isAnnotationPresent( Subscribe.class ) )
|
||||
{
|
||||
proxy.getLogger().log( Level.WARNING, "Listener " + listener + " has registered using depreceated subscribe annotation!"
|
||||
+ " Please advice author to update to @EventHandler."
|
||||
+ " As a server owner you may safely ignore this.", new Exception() );
|
||||
}
|
||||
Preconditions.checkArgument( !method.isAnnotationPresent( Subscribe.class ),
|
||||
"Listener %s has registered using deprecated subscribe annotation! Please update to @EventHandler.", listener );
|
||||
}
|
||||
|
||||
eventBus.register( listener );
|
||||
listenersByPlugin.put( plugin, listener );
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregister a {@link Listener} so that the events do not reach it anymore.
|
||||
*
|
||||
* @param listener the listener to unregister
|
||||
*/
|
||||
public void unregisterListener(Listener listener)
|
||||
{
|
||||
eventBus.unregister( listener );
|
||||
listenersByPlugin.values().remove( listener );
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregister all of a Plugin's listener.
|
||||
*
|
||||
* @param plugin
|
||||
*/
|
||||
public void unregisterListeners(Plugin plugin)
|
||||
{
|
||||
for ( Iterator<Listener> it = listenersByPlugin.get( plugin ).iterator(); it.hasNext(); )
|
||||
{
|
||||
eventBus.unregister( it.next() );
|
||||
it.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,10 @@
|
||||
package net.md_5.bungee.api.plugin;
|
||||
|
||||
import net.md_5.bungee.api.CommandSender;
|
||||
|
||||
|
||||
public interface TabExecutor
|
||||
{
|
||||
|
||||
public Iterable<String> onTabComplete(CommandSender sender, String[] args);
|
||||
}
|
@@ -1,6 +1,5 @@
|
||||
package net.md_5.bungee.api.scheduler;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import net.md_5.bungee.api.plugin.Plugin;
|
||||
|
||||
/**
|
||||
@@ -31,11 +30,7 @@ public interface ScheduledTask
|
||||
Runnable getTask();
|
||||
|
||||
/**
|
||||
* Get the delay in the specified unit before this task will next be
|
||||
* executed.
|
||||
*
|
||||
* @param unit the unit to get the delay in
|
||||
* @return the time before the next execution of this task
|
||||
* Cancel this task to suppress subsequent executions.
|
||||
*/
|
||||
long getDelay(TimeUnit unit);
|
||||
void cancel();
|
||||
}
|
||||
|
@@ -6,13 +6,13 @@
|
||||
<parent>
|
||||
<groupId>net.md-5</groupId>
|
||||
<artifactId>bungeecord-parent</artifactId>
|
||||
<version>1.4.7-SNAPSHOT</version>
|
||||
<version>1.6.2-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<groupId>net.md-5</groupId>
|
||||
<artifactId>bungeecord-config</artifactId>
|
||||
<version>1.4.7-SNAPSHOT</version>
|
||||
<version>1.6.2-SNAPSHOT</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>BungeeCord-Config</name>
|
||||
|
@@ -6,24 +6,15 @@
|
||||
<parent>
|
||||
<groupId>net.md-5</groupId>
|
||||
<artifactId>bungeecord-parent</artifactId>
|
||||
<version>1.5-SNAPSHOT</version>
|
||||
<version>1.6.2-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<groupId>net.md-5</groupId>
|
||||
<artifactId>bungeecord-event</artifactId>
|
||||
<version>1.5-SNAPSHOT</version>
|
||||
<version>1.6.2-SNAPSHOT</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>BungeeCord-Event</name>
|
||||
<description>Generic java event dispatching API intended for use with BungeeCord</description>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>4.11</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
|
@@ -1,11 +1,12 @@
|
||||
package net.md_5.bungee.event;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.locks.ReadWriteLock;
|
||||
@@ -16,35 +17,19 @@ import java.util.logging.Logger;
|
||||
public class EventBus
|
||||
{
|
||||
|
||||
private final Map<Class<?>, Map<Object, Method[]>> eventToHandler = new HashMap<>();
|
||||
private final Map<Class<?>, Map<Byte, Map<Object, Method[]>>> byListenerAndPriority = new HashMap<>();
|
||||
private final Map<Class<?>, EventHandlerMethod[]> byEventBaked = new HashMap<>();
|
||||
private final ReadWriteLock lock = new ReentrantReadWriteLock();
|
||||
private final Logger logger;
|
||||
private final Class<? extends Annotation>[] annotations;
|
||||
|
||||
public EventBus()
|
||||
{
|
||||
this( null, (Class<? extends Annotation>[]) null );
|
||||
this( null );
|
||||
}
|
||||
|
||||
public EventBus(Logger logger)
|
||||
{
|
||||
this( logger, (Class<? extends Annotation>[]) null );
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public EventBus(Class<? extends Annotation>... annotations)
|
||||
{
|
||||
this( null, annotations );
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public EventBus(Logger logger, Class<? extends Annotation>... annotations)
|
||||
{
|
||||
this.logger = ( logger == null ) ? Logger.getGlobal() : logger;
|
||||
this.annotations = ( annotations == null || annotations.length == 0 ) ? new Class[]
|
||||
{
|
||||
EventHandler.class
|
||||
} : annotations;
|
||||
}
|
||||
|
||||
public void post(Object event)
|
||||
@@ -52,26 +37,23 @@ public class EventBus
|
||||
lock.readLock().lock();
|
||||
try
|
||||
{
|
||||
Map<Object, Method[]> handlers = eventToHandler.get( event.getClass() );
|
||||
EventHandlerMethod[] handlers = byEventBaked.get( event.getClass() );
|
||||
if ( handlers != null )
|
||||
{
|
||||
for ( Map.Entry<Object, Method[]> handler : handlers.entrySet() )
|
||||
for ( EventHandlerMethod method : handlers )
|
||||
{
|
||||
for ( Method method : handler.getValue() )
|
||||
try
|
||||
{
|
||||
try
|
||||
{
|
||||
method.invoke( handler.getKey(), event );
|
||||
} catch ( IllegalAccessException ex )
|
||||
{
|
||||
throw new Error( "Method became inaccessible: " + event, ex );
|
||||
} catch ( IllegalArgumentException ex )
|
||||
{
|
||||
throw new Error( "Method rejected target/argument: " + event, ex );
|
||||
} catch ( InvocationTargetException ex )
|
||||
{
|
||||
logger.log( Level.WARNING, MessageFormat.format( "Error dispatching event {0} to listener {1}", event, handler.getKey() ), ex.getCause() );
|
||||
}
|
||||
method.invoke( event );
|
||||
} catch ( IllegalAccessException ex )
|
||||
{
|
||||
throw new Error( "Method became inaccessible: " + event, ex );
|
||||
} catch ( IllegalArgumentException ex )
|
||||
{
|
||||
throw new Error( "Method rejected target/argument: " + event, ex );
|
||||
} catch ( InvocationTargetException ex )
|
||||
{
|
||||
logger.log( Level.WARNING, MessageFormat.format( "Error dispatching event {0} to listener {1}", event, method.getListener() ), ex.getCause() );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -81,34 +63,36 @@ public class EventBus
|
||||
}
|
||||
}
|
||||
|
||||
private Map<Class<?>, Set<Method>> findHandlers(Object listener)
|
||||
private Map<Class<?>, Map<Byte, Set<Method>>> findHandlers(Object listener)
|
||||
{
|
||||
Map<Class<?>, Set<Method>> handler = new HashMap<>();
|
||||
Map<Class<?>, Map<Byte, Set<Method>>> handler = new HashMap<>();
|
||||
for ( Method m : listener.getClass().getDeclaredMethods() )
|
||||
{
|
||||
for ( Class<? extends Annotation> annotation : annotations )
|
||||
EventHandler annotation = m.getAnnotation( EventHandler.class );
|
||||
if ( annotation != null )
|
||||
{
|
||||
if ( m.isAnnotationPresent( annotation ) )
|
||||
Class<?>[] params = m.getParameterTypes();
|
||||
if ( params.length != 1 )
|
||||
{
|
||||
Class<?>[] params = m.getParameterTypes();
|
||||
if ( params.length != 1 )
|
||||
logger.log( Level.INFO, "Method {0} in class {1} annotated with {2} does not have single argument", new Object[]
|
||||
{
|
||||
logger.log( Level.INFO, "Method {0} in class {1} annotated with {2} does not have single argument", new Object[]
|
||||
{
|
||||
m, listener.getClass(), annotation
|
||||
} );
|
||||
continue;
|
||||
}
|
||||
|
||||
Set<Method> existing = handler.get( params[0] );
|
||||
if ( existing == null )
|
||||
{
|
||||
existing = new HashSet<>();
|
||||
handler.put( params[0], existing );
|
||||
}
|
||||
existing.add( m );
|
||||
break;
|
||||
m, listener.getClass(), annotation
|
||||
} );
|
||||
continue;
|
||||
}
|
||||
Map<Byte, Set<Method>> prioritiesMap = handler.get( params[0] );
|
||||
if ( prioritiesMap == null )
|
||||
{
|
||||
prioritiesMap = new HashMap<>();
|
||||
handler.put( params[0], prioritiesMap );
|
||||
}
|
||||
Set<Method> priority = prioritiesMap.get( annotation.priority() );
|
||||
if ( priority == null )
|
||||
{
|
||||
priority = new HashSet<>();
|
||||
prioritiesMap.put( annotation.priority(), priority );
|
||||
}
|
||||
priority.add( m );
|
||||
}
|
||||
}
|
||||
return handler;
|
||||
@@ -116,20 +100,30 @@ public class EventBus
|
||||
|
||||
public void register(Object listener)
|
||||
{
|
||||
Map<Class<?>, Set<Method>> handler = findHandlers( listener );
|
||||
Map<Class<?>, Map<Byte, Set<Method>>> handler = findHandlers( listener );
|
||||
lock.writeLock().lock();
|
||||
try
|
||||
{
|
||||
for ( Map.Entry<Class<?>, Set<Method>> e : handler.entrySet() )
|
||||
for ( Map.Entry<Class<?>, Map<Byte, Set<Method>>> e : handler.entrySet() )
|
||||
{
|
||||
Map<Object, Method[]> a = eventToHandler.get( e.getKey() );
|
||||
if ( a == null )
|
||||
Map<Byte, Map<Object, Method[]>> prioritiesMap = byListenerAndPriority.get( e.getKey() );
|
||||
if ( prioritiesMap == null )
|
||||
{
|
||||
a = new HashMap<>();
|
||||
eventToHandler.put( e.getKey(), a );
|
||||
prioritiesMap = new HashMap<>();
|
||||
byListenerAndPriority.put( e.getKey(), prioritiesMap );
|
||||
}
|
||||
Method[] baked = new Method[ e.getValue().size() ];
|
||||
a.put( listener, e.getValue().toArray( baked ) );
|
||||
for ( Map.Entry<Byte, Set<Method>> entry : e.getValue().entrySet() )
|
||||
{
|
||||
Map<Object, Method[]> currentPriorityMap = prioritiesMap.get( entry.getKey() );
|
||||
if ( currentPriorityMap == null )
|
||||
{
|
||||
currentPriorityMap = new HashMap<>();
|
||||
prioritiesMap.put( entry.getKey(), currentPriorityMap );
|
||||
}
|
||||
Method[] baked = new Method[ entry.getValue().size() ];
|
||||
currentPriorityMap.put( listener, entry.getValue().toArray( baked ) );
|
||||
}
|
||||
bakeHandlers( e.getKey() );
|
||||
}
|
||||
} finally
|
||||
{
|
||||
@@ -139,25 +133,70 @@ public class EventBus
|
||||
|
||||
public void unregister(Object listener)
|
||||
{
|
||||
Map<Class<?>, Set<Method>> handler = findHandlers( listener );
|
||||
Map<Class<?>, Map<Byte, Set<Method>>> handler = findHandlers( listener );
|
||||
lock.writeLock().lock();
|
||||
try
|
||||
{
|
||||
for ( Map.Entry<Class<?>, Set<Method>> e : handler.entrySet() )
|
||||
for ( Map.Entry<Class<?>, Map<Byte, Set<Method>>> e : handler.entrySet() )
|
||||
{
|
||||
Map<Object, Method[]> a = eventToHandler.get( e.getKey() );
|
||||
if ( a != null )
|
||||
Map<Byte, Map<Object, Method[]>> prioritiesMap = byListenerAndPriority.get( e.getKey() );
|
||||
if ( prioritiesMap != null )
|
||||
{
|
||||
a.remove( listener );
|
||||
if ( a.isEmpty() )
|
||||
for ( Byte priority : e.getValue().keySet() )
|
||||
{
|
||||
eventToHandler.remove( e.getKey() );
|
||||
Map<Object, Method[]> currentPriority = prioritiesMap.get( priority );
|
||||
if ( currentPriority != null )
|
||||
{
|
||||
currentPriority.remove( listener );
|
||||
if ( currentPriority.isEmpty() )
|
||||
{
|
||||
prioritiesMap.remove( priority );
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( prioritiesMap.isEmpty() )
|
||||
{
|
||||
byListenerAndPriority.remove( e.getKey() );
|
||||
}
|
||||
}
|
||||
bakeHandlers( e.getKey() );
|
||||
}
|
||||
} finally
|
||||
{
|
||||
lock.writeLock().unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Shouldn't be called without first locking the writeLock; intended for use
|
||||
* only inside {@link #register(java.lang.Object) register(Object)} or
|
||||
* {@link #unregister(java.lang.Object) unregister(Object)}.
|
||||
*/
|
||||
private void bakeHandlers(Class<?> eventClass)
|
||||
{
|
||||
Map<Byte, Map<Object, Method[]>> handlersByPriority = byListenerAndPriority.get( eventClass );
|
||||
if ( handlersByPriority != null )
|
||||
{
|
||||
List<EventHandlerMethod> handlersList = new ArrayList<>( handlersByPriority.size() * 2 );
|
||||
for ( byte value = Byte.MIN_VALUE; value < Byte.MAX_VALUE; value++ )
|
||||
{
|
||||
Map<Object, Method[]> handlersByListener = handlersByPriority.get( value );
|
||||
if ( handlersByListener != null )
|
||||
{
|
||||
for ( Map.Entry<Object, Method[]> listenerHandlers : handlersByListener.entrySet() )
|
||||
{
|
||||
for ( Method method : listenerHandlers.getValue() )
|
||||
{
|
||||
EventHandlerMethod ehm = new EventHandlerMethod( listenerHandlers.getKey(), method );
|
||||
handlersList.add( ehm );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
byEventBaked.put( eventClass, handlersList.toArray( new EventHandlerMethod[ handlersList.size() ] ) );
|
||||
} else
|
||||
{
|
||||
byEventBaked.put( eventClass, null );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -9,4 +9,18 @@ import java.lang.annotation.Target;
|
||||
@Target(ElementType.METHOD)
|
||||
public @interface EventHandler
|
||||
{
|
||||
|
||||
/**
|
||||
* Define the priority of the event handler.
|
||||
* <p>
|
||||
* Event handlers are called in order of priority:
|
||||
* <ol>
|
||||
* <li>LOWEST</li>
|
||||
* <li>LOW</li>
|
||||
* <li>NORMAL</li>
|
||||
* <li>HIGH</li>
|
||||
* <li>HIGHEST</li>
|
||||
* </ol>
|
||||
*/
|
||||
byte priority() default EventPriority.NORMAL;
|
||||
}
|
||||
|
@@ -0,0 +1,21 @@
|
||||
package net.md_5.bungee.event;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
@AllArgsConstructor
|
||||
public class EventHandlerMethod
|
||||
{
|
||||
|
||||
@Getter
|
||||
private final Object listener;
|
||||
@Getter
|
||||
private final Method method;
|
||||
|
||||
public void invoke(Object event) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException
|
||||
{
|
||||
method.invoke( listener, event );
|
||||
}
|
||||
}
|
19
event/src/main/java/net/md_5/bungee/event/EventPriority.java
Normal file
19
event/src/main/java/net/md_5/bungee/event/EventPriority.java
Normal file
@@ -0,0 +1,19 @@
|
||||
package net.md_5.bungee.event;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* Importance of the {@link EventHandler}. When executing an Event, the handlers
|
||||
* are called in order of their Priority.
|
||||
*/
|
||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
public class EventPriority
|
||||
{
|
||||
|
||||
public static final byte LOWEST = -64;
|
||||
public static final byte LOW = -32;
|
||||
public static final byte NORMAL = 0;
|
||||
public static final byte HIGH = 32;
|
||||
public static final byte HIGHEST = 64;
|
||||
}
|
@@ -15,14 +15,14 @@ public class EventBusTest
|
||||
{
|
||||
bus.register( this );
|
||||
bus.post( new FirstEvent() );
|
||||
Assert.assertEquals( latch.getCount(), 0 );
|
||||
Assert.assertEquals( 0, latch.getCount() );
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void firstListener(FirstEvent event)
|
||||
{
|
||||
bus.post( new SecondEvent() );
|
||||
Assert.assertEquals( latch.getCount(), 1 );
|
||||
Assert.assertEquals( 1, latch.getCount() );
|
||||
latch.countDown();
|
||||
}
|
||||
|
||||
|
@@ -0,0 +1,64 @@
|
||||
package net.md_5.bungee.event;
|
||||
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
public class EventPriorityTest
|
||||
{
|
||||
|
||||
private final EventBus bus = new EventBus();
|
||||
private final CountDownLatch latch = new CountDownLatch( 5 );
|
||||
|
||||
@Test
|
||||
public void testPriority()
|
||||
{
|
||||
bus.register( this );
|
||||
bus.register( new EventPriorityListenerPartner() );
|
||||
bus.post( new PriorityTestEvent() );
|
||||
Assert.assertEquals( 0, latch.getCount() );
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.LOWEST)
|
||||
public void onLowestPriority(PriorityTestEvent event)
|
||||
{
|
||||
Assert.assertEquals( 5, latch.getCount() );
|
||||
latch.countDown();
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onNormalPriority(PriorityTestEvent event)
|
||||
{
|
||||
Assert.assertEquals( 3, latch.getCount() );
|
||||
latch.countDown();
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.HIGHEST)
|
||||
public void onHighestPriority(PriorityTestEvent event)
|
||||
{
|
||||
Assert.assertEquals( 1, latch.getCount() );
|
||||
latch.countDown();
|
||||
}
|
||||
|
||||
public static class PriorityTestEvent
|
||||
{
|
||||
}
|
||||
|
||||
public class EventPriorityListenerPartner
|
||||
{
|
||||
|
||||
@EventHandler(priority = EventPriority.HIGH)
|
||||
public void onHighPriority(PriorityTestEvent event)
|
||||
{
|
||||
Assert.assertEquals( 2, latch.getCount() );
|
||||
latch.countDown();
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.LOW)
|
||||
public void onLowPriority(PriorityTestEvent event)
|
||||
{
|
||||
Assert.assertEquals( 4, latch.getCount() );
|
||||
latch.countDown();
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,28 @@
|
||||
package net.md_5.bungee.event;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
public class UnregisteringListenerTest
|
||||
{
|
||||
|
||||
private final EventBus bus = new EventBus();
|
||||
|
||||
@Test
|
||||
public void testPriority()
|
||||
{
|
||||
bus.register( this );
|
||||
bus.unregister( this );
|
||||
bus.post( new TestEvent() );
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onEvent(TestEvent evt)
|
||||
{
|
||||
Assert.fail( "Event listener wasn't unregistered" );
|
||||
}
|
||||
|
||||
public static class TestEvent
|
||||
{
|
||||
}
|
||||
}
|
18
pom.xml
18
pom.xml
@@ -11,16 +11,16 @@
|
||||
|
||||
<groupId>net.md-5</groupId>
|
||||
<artifactId>bungeecord-parent</artifactId>
|
||||
<version>1.5-SNAPSHOT</version>
|
||||
<version>1.6.2-SNAPSHOT</version>
|
||||
<packaging>pom</packaging>
|
||||
|
||||
<name>BungeeCord</name>
|
||||
<description>Parent project for all BungeeCord modules.</description>
|
||||
<url>https://github.com/ElasticPortalSuite/BungeeCord</url>
|
||||
<url>https://github.com/SpigotMC/BungeeCord</url>
|
||||
<inceptionYear>2012</inceptionYear>
|
||||
<organization>
|
||||
<name>Elastic Portal Suite</name>
|
||||
<url>https://github.com/ElasticPortalSuite</url>
|
||||
<url>https://github.com/SpigotMC</url>
|
||||
</organization>
|
||||
<licenses>
|
||||
<license>
|
||||
@@ -44,13 +44,13 @@
|
||||
</modules>
|
||||
|
||||
<scm>
|
||||
<connection>scm:git:git@github.com:ElasticPortalSuite/BungeeCord.git</connection>
|
||||
<developerConnection>scm:git:git@github.com:ElasticPortalSuite/BungeeCord.git</developerConnection>
|
||||
<url>git@github.com:ElasticPortalSuite/BungeeCord.git</url>
|
||||
<connection>scm:git:git@github.com:SpigotMC/BungeeCord.git</connection>
|
||||
<developerConnection>scm:git:git@github.com:SpigotMC/BungeeCord.git</developerConnection>
|
||||
<url>git@github.com:SpigotMC/BungeeCord.git</url>
|
||||
</scm>
|
||||
<issueManagement>
|
||||
<system>GitHub</system>
|
||||
<url>https://github.com/ElasticPortalSuite/BungeeCord/issues</url>
|
||||
<url>https://github.com/SpigotMC/BungeeCord/issues</url>
|
||||
</issueManagement>
|
||||
<ciManagement>
|
||||
<system>jenkins</system>
|
||||
@@ -59,7 +59,7 @@
|
||||
|
||||
<properties>
|
||||
<build.number>unknown</build.number>
|
||||
<netty.version>4.0.0.CR7</netty.version>
|
||||
<netty.version>4.0.9.Final</netty.version>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
||||
@@ -73,7 +73,7 @@
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>0.11.8</version>
|
||||
<version>0.12.0</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
@@ -6,13 +6,13 @@
|
||||
<parent>
|
||||
<groupId>net.md-5</groupId>
|
||||
<artifactId>bungeecord-parent</artifactId>
|
||||
<version>1.5-SNAPSHOT</version>
|
||||
<version>1.6.2-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<groupId>net.md-5</groupId>
|
||||
<artifactId>bungeecord-protocol</artifactId>
|
||||
<version>1.5-SNAPSHOT</version>
|
||||
<version>1.6.2-SNAPSHOT</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>BungeeCord-Protocol</name>
|
||||
@@ -23,7 +23,7 @@
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-buffer</artifactId>
|
||||
<version>${netty.version}</version>
|
||||
<scope>provided</scope>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
|
@@ -0,0 +1,38 @@
|
||||
package net.md_5.bungee.protocol;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class MinecraftInput
|
||||
{
|
||||
|
||||
private final ByteBuf buf;
|
||||
|
||||
public byte readByte()
|
||||
{
|
||||
return buf.readByte();
|
||||
}
|
||||
|
||||
public short readUnisgnedByte()
|
||||
{
|
||||
return buf.readUnsignedByte();
|
||||
}
|
||||
|
||||
public int readInt()
|
||||
{
|
||||
return buf.readInt();
|
||||
}
|
||||
|
||||
public String readString()
|
||||
{
|
||||
short len = buf.readShort();
|
||||
char[] c = new char[ len ];
|
||||
for ( int i = 0; i < c.length; i++ )
|
||||
{
|
||||
c[i] = buf.readChar();
|
||||
}
|
||||
|
||||
return new String( c );
|
||||
}
|
||||
}
|
@@ -0,0 +1,56 @@
|
||||
package net.md_5.bungee.protocol;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Arrays;
|
||||
|
||||
public class MinecraftOutput
|
||||
{
|
||||
|
||||
private final ByteBuf buf;
|
||||
|
||||
public MinecraftOutput()
|
||||
{
|
||||
buf = Unpooled.buffer();
|
||||
}
|
||||
|
||||
public byte[] toArray()
|
||||
{
|
||||
if ( buf.hasArray() )
|
||||
{
|
||||
return Arrays.copyOfRange( buf.array(), buf.arrayOffset(), buf.arrayOffset() + buf.writerIndex() );
|
||||
} else
|
||||
{
|
||||
byte[] b = new byte[ buf.writerIndex() ];
|
||||
buf.readBytes( b );
|
||||
return b;
|
||||
}
|
||||
}
|
||||
|
||||
public MinecraftOutput writeByte(byte b)
|
||||
{
|
||||
buf.writeByte( b );
|
||||
return this;
|
||||
}
|
||||
|
||||
public void writeInt(int i)
|
||||
{
|
||||
buf.writeInt( i );
|
||||
}
|
||||
|
||||
public void writeString(String s)
|
||||
{
|
||||
char[] cc = s.toCharArray();
|
||||
buf.writeShort( cc.length );
|
||||
for ( char c : cc )
|
||||
{
|
||||
buf.writeChar( c );
|
||||
}
|
||||
}
|
||||
|
||||
public void writeStringUTF8WithoutLengthHeaderBecauseDinnerboneStuffedUpTheMCBrandPacket(String s)
|
||||
{
|
||||
buf.writeBytes( s.getBytes( Charset.forName( "UTF-8" ) ) );
|
||||
}
|
||||
}
|
@@ -3,5 +3,5 @@ package net.md_5.bungee.protocol;
|
||||
public enum OpCode
|
||||
{
|
||||
|
||||
BOOLEAN, BULK_CHUNK, BYTE, BYTE_INT, DOUBLE, FLOAT, INT, INT_3, INT_BYTE, ITEM, LONG, METADATA, OPTIONAL_MOTION, SHORT, SHORT_BYTE, SHORT_ITEM, STRING, USHORT_BYTE
|
||||
BOOLEAN, BULK_CHUNK, BYTE, BYTE_INT, DOUBLE, FLOAT, INT, INT_3, INT_BYTE, ITEM, LONG, METADATA, OPTIONAL_MOTION, SHORT, SHORT_BYTE, SHORT_ITEM, STRING, USHORT_BYTE, OPTIONAL_WINDOW
|
||||
}
|
||||
|
@@ -1,7 +1,6 @@
|
||||
package net.md_5.bungee.protocol;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import lombok.Getter;
|
||||
@@ -9,10 +8,12 @@ import static net.md_5.bungee.protocol.OpCode.*;
|
||||
import net.md_5.bungee.protocol.packet.DefinedPacket;
|
||||
import net.md_5.bungee.protocol.packet.Packet0KeepAlive;
|
||||
import net.md_5.bungee.protocol.packet.Packet1Login;
|
||||
import net.md_5.bungee.protocol.packet.Packet2CEntityProperties;
|
||||
import net.md_5.bungee.protocol.packet.Packet2Handshake;
|
||||
import net.md_5.bungee.protocol.packet.Packet3Chat;
|
||||
import net.md_5.bungee.protocol.packet.Packet9Respawn;
|
||||
import net.md_5.bungee.protocol.packet.PacketC9PlayerListItem;
|
||||
import net.md_5.bungee.protocol.packet.PacketCBTabComplete;
|
||||
import net.md_5.bungee.protocol.packet.PacketCCSettings;
|
||||
import net.md_5.bungee.protocol.packet.PacketCDClientStatus;
|
||||
import net.md_5.bungee.protocol.packet.PacketCEScoreboardObjective;
|
||||
@@ -29,8 +30,8 @@ import net.md_5.bungee.protocol.skip.PacketReader;
|
||||
public class Vanilla implements Protocol
|
||||
{
|
||||
|
||||
public static final byte PROTOCOL_VERSION = 61;
|
||||
public static final String GAME_VERSION = "1.5.2";
|
||||
public static final byte PROTOCOL_VERSION = 74;
|
||||
public static final String GAME_VERSION = "1.6.2";
|
||||
@Getter
|
||||
private static final Vanilla instance = new Vanilla();
|
||||
/*========================================================================*/
|
||||
@@ -54,7 +55,9 @@ public class Vanilla implements Protocol
|
||||
classes[0x03] = Packet3Chat.class;
|
||||
classes[0x09] = Packet9Respawn.class;
|
||||
classes[0xC9] = PacketC9PlayerListItem.class;
|
||||
classes[0x2C] = Packet2CEntityProperties.class;
|
||||
classes[0xCC] = PacketCCSettings.class;
|
||||
classes[0xCB] = PacketCBTabComplete.class;
|
||||
classes[0xCD] = PacketCDClientStatus.class;
|
||||
classes[0xCE] = PacketCEScoreboardObjective.class;
|
||||
classes[0xCF] = PacketCFScoreboardScore.class;
|
||||
@@ -141,7 +144,7 @@ public class Vanilla implements Protocol
|
||||
};
|
||||
opCodes[0x08] = new OpCode[]
|
||||
{
|
||||
SHORT, SHORT, FLOAT
|
||||
FLOAT, SHORT, FLOAT
|
||||
};
|
||||
opCodes[0x0A] = new OpCode[]
|
||||
{
|
||||
@@ -181,7 +184,7 @@ public class Vanilla implements Protocol
|
||||
};
|
||||
opCodes[0x13] = new OpCode[]
|
||||
{
|
||||
INT, BYTE
|
||||
INT, BYTE, INT
|
||||
};
|
||||
opCodes[0x14] = new OpCode[]
|
||||
{
|
||||
@@ -207,6 +210,10 @@ public class Vanilla implements Protocol
|
||||
{
|
||||
INT, INT, INT, INT, SHORT
|
||||
};
|
||||
opCodes[0x1B] = new OpCode[]
|
||||
{
|
||||
FLOAT, FLOAT, BOOLEAN, BOOLEAN
|
||||
};
|
||||
opCodes[0x1C] = new OpCode[]
|
||||
{
|
||||
INT, SHORT, SHORT, SHORT
|
||||
@@ -245,7 +252,7 @@ public class Vanilla implements Protocol
|
||||
};
|
||||
opCodes[0x27] = new OpCode[]
|
||||
{
|
||||
INT, INT
|
||||
INT, INT, BOOLEAN
|
||||
};
|
||||
opCodes[0x28] = new OpCode[]
|
||||
{
|
||||
@@ -313,7 +320,7 @@ public class Vanilla implements Protocol
|
||||
};
|
||||
opCodes[0x64] = new OpCode[]
|
||||
{
|
||||
BYTE, BYTE, STRING, BYTE, BOOLEAN
|
||||
OPTIONAL_WINDOW
|
||||
};
|
||||
opCodes[0x65] = new OpCode[]
|
||||
{
|
||||
@@ -359,17 +366,21 @@ public class Vanilla implements Protocol
|
||||
{
|
||||
INT, SHORT, INT, BYTE, SHORT_BYTE
|
||||
};
|
||||
opCodes[0x85] = new OpCode[]
|
||||
{
|
||||
BYTE, INT, INT, INT
|
||||
};
|
||||
opCodes[0xC3] = new OpCode[]
|
||||
{
|
||||
SHORT, SHORT, INT_BYTE
|
||||
};
|
||||
opCodes[0xC8] = new OpCode[]
|
||||
{
|
||||
INT, BYTE
|
||||
INT, INT
|
||||
};
|
||||
opCodes[0xCA] = new OpCode[]
|
||||
{
|
||||
BYTE, BYTE, BYTE
|
||||
BYTE, FLOAT, FLOAT
|
||||
};
|
||||
opCodes[0xCB] = new OpCode[]
|
||||
{
|
||||
|
@@ -23,6 +23,10 @@ public abstract class AbstractPacketHandler
|
||||
{
|
||||
}
|
||||
|
||||
public void handle(Packet2CEntityProperties properties) throws Exception
|
||||
{
|
||||
}
|
||||
|
||||
public void handle(PacketC9PlayerListItem playerList) throws Exception
|
||||
{
|
||||
}
|
||||
@@ -70,4 +74,8 @@ public abstract class AbstractPacketHandler
|
||||
public void handle(PacketFFKick kick) throws Exception
|
||||
{
|
||||
}
|
||||
|
||||
public void handle(PacketCBTabComplete tabComplete) throws Exception
|
||||
{
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,49 @@
|
||||
package net.md_5.bungee.protocol.packet;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
import lombok.ToString;
|
||||
|
||||
@Getter
|
||||
@ToString
|
||||
@EqualsAndHashCode(callSuper = false)
|
||||
public class Packet2CEntityProperties extends DefinedPacket
|
||||
{
|
||||
|
||||
public Packet2CEntityProperties()
|
||||
{
|
||||
super( 0x2C );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(ByteBuf buf)
|
||||
{
|
||||
buf.readInt();
|
||||
int recordCount = buf.readInt();
|
||||
for ( int i = 0; i < recordCount; i++ )
|
||||
{
|
||||
readString( buf );
|
||||
buf.readDouble();
|
||||
short size = buf.readShort();
|
||||
for ( short s = 0; s < size; s++ )
|
||||
{
|
||||
buf.skipBytes( 25 ); // long, long, double, byte
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(ByteBuf buf)
|
||||
{
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(AbstractPacketHandler handler) throws Exception
|
||||
{
|
||||
handler.handle( this );
|
||||
}
|
||||
}
|
@@ -3,9 +3,11 @@ package net.md_5.bungee.protocol.packet;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.ToString;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@ToString
|
||||
@EqualsAndHashCode(callSuper = false)
|
||||
public class Packet3Chat extends DefinedPacket
|
||||
|
@@ -41,7 +41,7 @@ public class PacketC9PlayerListItem extends DefinedPacket
|
||||
{
|
||||
writeString( username, buf );
|
||||
buf.writeBoolean( online );
|
||||
buf.writeShort(ping );
|
||||
buf.writeShort( ping );
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@@ -0,0 +1,56 @@
|
||||
package net.md_5.bungee.protocol.packet;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
import lombok.ToString;
|
||||
|
||||
@Getter
|
||||
@ToString
|
||||
@EqualsAndHashCode(callSuper = false)
|
||||
public class PacketCBTabComplete extends DefinedPacket
|
||||
{
|
||||
|
||||
private String cursor;
|
||||
private String[] commands;
|
||||
|
||||
private PacketCBTabComplete()
|
||||
{
|
||||
super( 0xCB );
|
||||
}
|
||||
|
||||
public PacketCBTabComplete(String[] alternatives)
|
||||
{
|
||||
this();
|
||||
commands = alternatives;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(ByteBuf buf)
|
||||
{
|
||||
cursor = readString( buf );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(ByteBuf buf)
|
||||
{
|
||||
String tab = "";
|
||||
for ( String alternative : commands )
|
||||
{
|
||||
if ( tab.isEmpty() )
|
||||
{
|
||||
tab = alternative + " ";
|
||||
} else
|
||||
{
|
||||
tab += "\0" + alternative + " ";
|
||||
}
|
||||
}
|
||||
writeString( tab, buf );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(AbstractPacketHandler handler) throws Exception
|
||||
{
|
||||
handler.handle( this );
|
||||
}
|
||||
}
|
@@ -1,9 +1,15 @@
|
||||
package net.md_5.bungee.protocol.packet;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.DataInput;
|
||||
import java.io.DataInputStream;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
import lombok.ToString;
|
||||
import net.md_5.bungee.protocol.MinecraftInput;
|
||||
import net.md_5.bungee.protocol.MinecraftOutput;
|
||||
|
||||
@Getter
|
||||
@ToString
|
||||
@@ -45,4 +51,14 @@ public class PacketFAPluginMessage extends DefinedPacket
|
||||
{
|
||||
handler.handle( this );
|
||||
}
|
||||
|
||||
public DataInput getStream()
|
||||
{
|
||||
return new DataInputStream( new ByteArrayInputStream( data ) );
|
||||
}
|
||||
|
||||
public MinecraftInput getMCStream()
|
||||
{
|
||||
return new MinecraftInput( Unpooled.wrappedBuffer( data ) );
|
||||
}
|
||||
}
|
||||
|
@@ -23,6 +23,7 @@ abstract class Instruction
|
||||
static final Instruction SHORT_ITEM = new ShortHeader( ITEM );
|
||||
static final Instruction STRING = new ShortHeader( new Jump( 2 ) );
|
||||
static final Instruction USHORT_BYTE = new UnsignedShortByte();
|
||||
static final Instruction OPTIONAL_WINDOW = new OptionalWindow();
|
||||
// Illegal forward references below this line
|
||||
static final Instruction BYTE_INT = new ByteHeader( INT );
|
||||
// Custom instructions
|
||||
|
@@ -0,0 +1,21 @@
|
||||
package net.md_5.bungee.protocol.skip;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
|
||||
public class OptionalWindow extends Instruction
|
||||
{
|
||||
|
||||
@Override
|
||||
void read(ByteBuf in)
|
||||
{
|
||||
BYTE.read( in );
|
||||
byte type = in.readByte();
|
||||
STRING.read( in );
|
||||
BYTE.read( in );
|
||||
BOOLEAN.read( in );
|
||||
if ( type == 11 )
|
||||
{
|
||||
INT.read( in );
|
||||
}
|
||||
}
|
||||
}
|
@@ -6,28 +6,32 @@
|
||||
<parent>
|
||||
<groupId>net.md-5</groupId>
|
||||
<artifactId>bungeecord-parent</artifactId>
|
||||
<version>1.5-SNAPSHOT</version>
|
||||
<version>1.6.2-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<groupId>net.md-5</groupId>
|
||||
<artifactId>bungeecord-proxy</artifactId>
|
||||
<version>1.5-SNAPSHOT</version>
|
||||
<version>1.6.2-SNAPSHOT</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>BungeeCord-Proxy</name>
|
||||
<description>Proxy component of the Elastic Portal Suite</description>
|
||||
|
||||
<properties>
|
||||
<maven.build.timestamp.format>yyyyMMdd</maven.build.timestamp.format>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-codec</artifactId>
|
||||
<version>${netty.version}</version>
|
||||
<groupId>com.google.code.gson</groupId>
|
||||
<artifactId>gson</artifactId>
|
||||
<version>2.2.4</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-handler</artifactId>
|
||||
<artifactId>netty-codec-http</artifactId>
|
||||
<version>${netty.version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
@@ -58,7 +62,7 @@
|
||||
<dependency>
|
||||
<groupId>mysql</groupId>
|
||||
<artifactId>mysql-connector-java</artifactId>
|
||||
<version>5.1.24</version>
|
||||
<version>5.1.25</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
@@ -67,16 +71,16 @@
|
||||
<version>3.18.0-GA</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.xerial</groupId>
|
||||
<artifactId>sqlite-jdbc</artifactId>
|
||||
<version>3.7.2</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<finalName>BungeeCord</finalName>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>src/main/resources</directory>
|
||||
<filtering>true</filtering>
|
||||
</resource>
|
||||
</resources>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<!-- Don't deploy proxy to maven repo, only APIs -->
|
||||
@@ -95,6 +99,7 @@
|
||||
<manifestEntries>
|
||||
<Main-Class>net.md_5.bungee.BungeeCord</Main-Class>
|
||||
<Implementation-Version>${describe}</Implementation-Version>
|
||||
<Specification-Version>${maven.build.timestamp}</Specification-Version>
|
||||
</manifestEntries>
|
||||
</archive>
|
||||
</configuration>
|
||||
@@ -102,7 +107,7 @@
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>2.0</version>
|
||||
<version>2.1</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
|
@@ -2,13 +2,10 @@ package net.md_5.bungee;
|
||||
|
||||
import com.google.common.io.ByteStreams;
|
||||
import net.md_5.bungee.log.BungeeLogger;
|
||||
import net.md_5.bungee.reconnect.SQLReconnectHandler;
|
||||
import net.md_5.bungee.reconnect.YamlReconnectHandler;
|
||||
import net.md_5.bungee.scheduler.BungeeScheduler;
|
||||
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||
import com.ning.http.client.AsyncHttpClient;
|
||||
import com.ning.http.client.AsyncHttpClientConfig;
|
||||
import com.ning.http.client.providers.netty.NettyAsyncHttpProvider;
|
||||
import com.ning.http.client.providers.netty.NettyAsyncHttpProviderConfig;
|
||||
import com.google.gson.Gson;
|
||||
import io.netty.bootstrap.ServerBootstrap;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelException;
|
||||
@@ -17,11 +14,13 @@ import io.netty.channel.ChannelFutureListener;
|
||||
import io.netty.channel.MultithreadEventLoopGroup;
|
||||
import io.netty.channel.nio.NioEventLoopGroup;
|
||||
import io.netty.channel.socket.nio.NioServerSocketChannel;
|
||||
import io.netty.util.ResourceLeakDetector;
|
||||
import net.md_5.bungee.config.Configuration;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintStream;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Calendar;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
@@ -31,7 +30,6 @@ import java.util.MissingResourceException;
|
||||
import java.util.ResourceBundle;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.locks.ReadWriteLock;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
@@ -53,7 +51,6 @@ import net.md_5.bungee.api.config.ServerInfo;
|
||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||
import net.md_5.bungee.api.plugin.Plugin;
|
||||
import net.md_5.bungee.api.plugin.PluginManager;
|
||||
import net.md_5.bungee.api.scheduler.TaskScheduler;
|
||||
import net.md_5.bungee.api.tab.CustomTabList;
|
||||
import net.md_5.bungee.command.*;
|
||||
import net.md_5.bungee.config.YamlConfig;
|
||||
@@ -63,7 +60,6 @@ import net.md_5.bungee.protocol.packet.DefinedPacket;
|
||||
import net.md_5.bungee.protocol.packet.Packet3Chat;
|
||||
import net.md_5.bungee.protocol.packet.PacketFAPluginMessage;
|
||||
import net.md_5.bungee.protocol.Vanilla;
|
||||
import net.md_5.bungee.scheduler.BungeeThreadPool;
|
||||
import net.md_5.bungee.tab.Custom;
|
||||
import net.md_5.bungee.util.CaseInsensitiveMap;
|
||||
import org.fusesource.jansi.AnsiConsole;
|
||||
@@ -86,11 +82,7 @@ public class BungeeCord extends ProxyServer
|
||||
* Localization bundle.
|
||||
*/
|
||||
public final ResourceBundle bundle = ResourceBundle.getBundle( "messages_en" );
|
||||
/**
|
||||
* Thread pools.
|
||||
*/
|
||||
public final ScheduledThreadPoolExecutor executors = new BungeeThreadPool( new ThreadFactoryBuilder().setNameFormat( "Bungee Pool Thread #%1$d" ).build() );
|
||||
public final MultithreadEventLoopGroup eventLoops = new NioEventLoopGroup( Runtime.getRuntime().availableProcessors(), new ThreadFactoryBuilder().setNameFormat( "Netty IO Thread #%1$d" ).build() );
|
||||
public final MultithreadEventLoopGroup eventLoops = new NioEventLoopGroup( 0, new ThreadFactoryBuilder().setNameFormat( "Netty IO Thread #%1$d" ).build() );
|
||||
/**
|
||||
* locations.yml save thread.
|
||||
*/
|
||||
@@ -120,16 +112,14 @@ public class BungeeCord extends ProxyServer
|
||||
@Getter
|
||||
private final File pluginsFolder = new File( "plugins" );
|
||||
@Getter
|
||||
private final TaskScheduler scheduler = new BungeeScheduler();
|
||||
@Getter
|
||||
private final AsyncHttpClient httpClient = new AsyncHttpClient(
|
||||
new NettyAsyncHttpProvider(
|
||||
new AsyncHttpClientConfig.Builder().setAsyncHttpClientProviderConfig(
|
||||
new NettyAsyncHttpProviderConfig().addProperty( NettyAsyncHttpProviderConfig.BOSS_EXECUTOR_SERVICE, executors ) ).setExecutorService( executors ).build() ) );
|
||||
private final BungeeScheduler scheduler = new BungeeScheduler();
|
||||
@Getter
|
||||
private ConsoleReader consoleReader;
|
||||
@Getter
|
||||
private final Logger logger;
|
||||
public final Gson gson = new Gson();
|
||||
@Getter
|
||||
private ConnectionThrottle connectionThrottle;
|
||||
|
||||
|
||||
{
|
||||
@@ -178,15 +168,18 @@ public class BungeeCord extends ProxyServer
|
||||
*/
|
||||
public static void main(String[] args) throws Exception
|
||||
{
|
||||
Calendar deadline = Calendar.getInstance();
|
||||
deadline.set( 2013, 7, 1 ); // year, month, date
|
||||
if ( Calendar.getInstance().after( deadline ) )
|
||||
if ( BungeeCord.class.getPackage().getSpecificationVersion() != null )
|
||||
{
|
||||
System.err.println( "*** Warning, this build is outdated ***" );
|
||||
System.err.println( "*** Please download a new build from http://ci.md-5.net/job/BungeeCord ***" );
|
||||
System.err.println( "*** You will get NO support regarding this build ***" );
|
||||
System.err.println( "*** Server will start in 15 seconds ***" );
|
||||
Thread.sleep( TimeUnit.SECONDS.toMillis( 15 ) );
|
||||
Calendar deadline = Calendar.getInstance();
|
||||
deadline.add( Calendar.WEEK_OF_YEAR, 2 );
|
||||
if ( Calendar.getInstance().after( new SimpleDateFormat( "yyyyMMdd" ).parse( BungeeCord.class.getPackage().getSpecificationVersion() ) ) )
|
||||
{
|
||||
System.err.println( "*** Warning, this build is outdated ***" );
|
||||
System.err.println( "*** Please download a new build from http://ci.md-5.net/job/BungeeCord ***" );
|
||||
System.err.println( "*** You will get NO support regarding this build ***" );
|
||||
System.err.println( "*** Server will start in 30 seconds ***" );
|
||||
Thread.sleep( TimeUnit.SECONDS.toMillis( 30 ) );
|
||||
}
|
||||
}
|
||||
|
||||
BungeeCord bungee = new BungeeCord();
|
||||
@@ -216,17 +209,24 @@ public class BungeeCord extends ProxyServer
|
||||
@Override
|
||||
public void start() throws Exception
|
||||
{
|
||||
ResourceLeakDetector.setEnabled( false ); // Eats performance
|
||||
|
||||
pluginsFolder.mkdir();
|
||||
pluginManager.detectPlugins( pluginsFolder );
|
||||
config.load();
|
||||
if ( reconnectHandler == null )
|
||||
for ( ListenerInfo info : config.getListeners() )
|
||||
{
|
||||
reconnectHandler = new SQLReconnectHandler();
|
||||
if ( !info.isForceDefault() && reconnectHandler == null )
|
||||
{
|
||||
reconnectHandler = new YamlReconnectHandler();
|
||||
break;
|
||||
}
|
||||
}
|
||||
isRunning = true;
|
||||
|
||||
pluginManager.loadAndEnablePlugins();
|
||||
|
||||
connectionThrottle = new ConnectionThrottle( config.getThrottle() );
|
||||
startListeners();
|
||||
|
||||
saveThread.scheduleAtFixedRate( new TimerTask()
|
||||
@@ -234,7 +234,10 @@ public class BungeeCord extends ProxyServer
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
getReconnectHandler().save();
|
||||
if ( getReconnectHandler() != null )
|
||||
{
|
||||
getReconnectHandler().save();
|
||||
}
|
||||
}
|
||||
}, 0, TimeUnit.MINUTES.toMillis( 5 ) );
|
||||
metricsThread.scheduleAtFixedRate( new Metrics(), 0, TimeUnit.MINUTES.toMillis( Metrics.PING_INTERVAL ) );
|
||||
@@ -295,9 +298,6 @@ public class BungeeCord extends ProxyServer
|
||||
{
|
||||
BungeeCord.this.isRunning = false;
|
||||
|
||||
httpClient.close();
|
||||
executors.shutdown();
|
||||
|
||||
stopListeners();
|
||||
getLogger().info( "Closing pending connections" );
|
||||
|
||||
@@ -323,9 +323,12 @@ public class BungeeCord extends ProxyServer
|
||||
{
|
||||
}
|
||||
|
||||
getLogger().info( "Saving reconnect locations" );
|
||||
reconnectHandler.save();
|
||||
reconnectHandler.close();
|
||||
if ( reconnectHandler != null )
|
||||
{
|
||||
getLogger().info( "Saving reconnect locations" );
|
||||
reconnectHandler.save();
|
||||
reconnectHandler.close();
|
||||
}
|
||||
saveThread.cancel();
|
||||
metricsThread.cancel();
|
||||
|
||||
@@ -333,10 +336,18 @@ public class BungeeCord extends ProxyServer
|
||||
getLogger().info( "Disabling plugins" );
|
||||
for ( Plugin plugin : pluginManager.getPlugins() )
|
||||
{
|
||||
plugin.onDisable();
|
||||
try
|
||||
{
|
||||
plugin.onDisable();
|
||||
} catch ( Throwable t )
|
||||
{
|
||||
getLogger().severe( "Exception disabling plugin " + plugin.getDescription().getName() );
|
||||
t.printStackTrace();
|
||||
}
|
||||
getScheduler().cancel( plugin );
|
||||
}
|
||||
|
||||
scheduler.shutdown();
|
||||
getLogger().info( "Thankyou and goodbye" );
|
||||
System.exit( 0 );
|
||||
}
|
||||
@@ -472,9 +483,9 @@ public class BungeeCord extends ProxyServer
|
||||
}
|
||||
|
||||
@Override
|
||||
public ServerInfo constructServerInfo(String name, InetSocketAddress address, boolean restricted)
|
||||
public ServerInfo constructServerInfo(String name, InetSocketAddress address, String motd, boolean restricted)
|
||||
{
|
||||
return new BungeeServerInfo( name, address, restricted );
|
||||
return new BungeeServerInfo( name, address, motd, restricted );
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -487,7 +498,9 @@ public class BungeeCord extends ProxyServer
|
||||
public void broadcast(String message)
|
||||
{
|
||||
getConsole().sendMessage( message );
|
||||
broadcast( new Packet3Chat( message ) );
|
||||
// TODO: Here too
|
||||
String encoded = BungeeCord.getInstance().gson.toJson( message );
|
||||
broadcast( new Packet3Chat( "{\"text\":" + encoded + "}" ) );
|
||||
}
|
||||
|
||||
public void addConnection(UserConnection con)
|
||||
@@ -519,4 +532,9 @@ public class BungeeCord extends ProxyServer
|
||||
{
|
||||
return new Custom( player );
|
||||
}
|
||||
|
||||
public Collection<String> getDisabledCommands()
|
||||
{
|
||||
return config.getDisabledCommands();
|
||||
}
|
||||
}
|
||||
|
@@ -38,6 +38,8 @@ public class BungeeServerInfo implements ServerInfo
|
||||
private final InetSocketAddress address;
|
||||
private final Collection<ProxiedPlayer> players = new ArrayList<>();
|
||||
@Getter
|
||||
private final String motd;
|
||||
@Getter
|
||||
private final boolean restricted;
|
||||
@Getter
|
||||
private final Queue<DefinedPacket> packetQueue = new LinkedList<>();
|
||||
|
27
proxy/src/main/java/net/md_5/bungee/ConnectionThrottle.java
Normal file
27
proxy/src/main/java/net/md_5/bungee/ConnectionThrottle.java
Normal file
@@ -0,0 +1,27 @@
|
||||
package net.md_5.bungee;
|
||||
|
||||
import gnu.trove.map.hash.TObjectLongHashMap;
|
||||
import java.net.InetAddress;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class ConnectionThrottle
|
||||
{
|
||||
|
||||
private final TObjectLongHashMap<InetAddress> throttle = new TObjectLongHashMap<>();
|
||||
private final int throttleTime;
|
||||
|
||||
public void unthrottle(InetAddress address)
|
||||
{
|
||||
throttle.remove( address );
|
||||
}
|
||||
|
||||
public boolean throttle(InetAddress address)
|
||||
{
|
||||
long value = throttle.get( address );
|
||||
long currentTime = System.currentTimeMillis();
|
||||
|
||||
throttle.put( address, currentTime );
|
||||
return value != 0 && currentTime - value < throttleTime;
|
||||
}
|
||||
}
|
@@ -1,5 +1,7 @@
|
||||
package net.md_5.bungee;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
|
||||
/**
|
||||
* Class to rewrite integers within packets.
|
||||
*/
|
||||
@@ -102,6 +104,10 @@ public class EntityMap
|
||||
{
|
||||
1
|
||||
};
|
||||
entityIds[0x2C] = new int[]
|
||||
{
|
||||
1
|
||||
};
|
||||
entityIds[0x37] = new int[]
|
||||
{
|
||||
1
|
||||
@@ -113,20 +119,20 @@ public class EntityMap
|
||||
};
|
||||
}
|
||||
|
||||
public static void rewrite(byte[] packet, int oldId, int newId)
|
||||
public static void rewrite(ByteBuf packet, int oldId, int newId)
|
||||
{
|
||||
int packetId = packet[0] & 0xFF;
|
||||
int packetId = packet.getUnsignedByte( 0 );
|
||||
if ( packetId == 0x1D )
|
||||
{ // bulk entity
|
||||
for ( int pos = 2; pos < packet.length; pos += 4 )
|
||||
for ( int pos = 2; pos < packet.readableBytes(); pos += 4 )
|
||||
{
|
||||
int readId = readInt( packet, pos );
|
||||
int readId = packet.getInt( pos );
|
||||
if ( readId == oldId )
|
||||
{
|
||||
setInt( packet, pos, newId );
|
||||
packet.setInt( pos, newId );
|
||||
} else if ( readId == newId )
|
||||
{
|
||||
setInt( packet, pos, oldId );
|
||||
packet.setInt( pos, oldId );
|
||||
}
|
||||
}
|
||||
} else
|
||||
@@ -136,41 +142,28 @@ public class EntityMap
|
||||
{
|
||||
for ( int pos : idArray )
|
||||
{
|
||||
int readId = readInt( packet, pos );
|
||||
int readId = packet.getInt( pos );
|
||||
if ( readId == oldId )
|
||||
{
|
||||
setInt( packet, pos, newId );
|
||||
packet.setInt( pos, newId );
|
||||
} else if ( readId == newId )
|
||||
{
|
||||
setInt( packet, pos, oldId );
|
||||
packet.setInt( pos, oldId );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( packetId == 0x17 )
|
||||
{
|
||||
int type = packet[5] & 0xFF;
|
||||
int type = packet.getUnsignedByte( 5 );
|
||||
if ( type == 60 || type == 90 )
|
||||
{
|
||||
int index20 = readInt( packet, 20 );
|
||||
if ( packet.length > 24 && index20 == oldId )
|
||||
int index20 = packet.getInt( 20 );
|
||||
if ( packet.readableBytes() > 24 && index20 == oldId )
|
||||
{
|
||||
setInt( packet, 20, newId );
|
||||
packet.setInt( 20, newId );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void setInt(byte[] buf, int pos, int i)
|
||||
{
|
||||
buf[pos] = (byte) ( i >> 24 );
|
||||
buf[pos + 1] = (byte) ( i >> 16 );
|
||||
buf[pos + 2] = (byte) ( i >> 8 );
|
||||
buf[pos + 3] = (byte) i;
|
||||
}
|
||||
|
||||
private static int readInt(byte[] buf, int pos)
|
||||
{
|
||||
return ( ( ( buf[pos] & 0xFF ) << 24 ) | ( ( buf[pos + 1] & 0xFF ) << 16 ) | ( ( buf[pos + 2] & 0xFF ) << 8 ) | buf[pos + 3] & 0xFF );
|
||||
}
|
||||
}
|
||||
|
@@ -42,6 +42,7 @@ public class ServerConnection implements Server
|
||||
{
|
||||
if ( !ch.isClosed() )
|
||||
{
|
||||
// TODO: Can we just use a future here?
|
||||
unsafe().sendPacket( new PacketFFKick( reason ) );
|
||||
ch.getHandle().eventLoop().schedule( new Runnable()
|
||||
{
|
||||
|
@@ -1,9 +1,9 @@
|
||||
package net.md_5.bungee;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.io.ByteArrayDataInput;
|
||||
import com.google.common.io.ByteArrayDataOutput;
|
||||
import com.google.common.io.ByteStreams;
|
||||
import java.io.DataInput;
|
||||
import java.security.PublicKey;
|
||||
import java.util.Objects;
|
||||
import java.util.Queue;
|
||||
@@ -29,6 +29,7 @@ import net.md_5.bungee.netty.PacketDecoder;
|
||||
import net.md_5.bungee.netty.PacketHandler;
|
||||
import net.md_5.bungee.netty.PipelineUtils;
|
||||
import net.md_5.bungee.protocol.Forge;
|
||||
import net.md_5.bungee.protocol.MinecraftOutput;
|
||||
import net.md_5.bungee.protocol.packet.DefinedPacket;
|
||||
import net.md_5.bungee.protocol.packet.Packet1Login;
|
||||
import net.md_5.bungee.protocol.packet.Packet9Respawn;
|
||||
@@ -153,6 +154,10 @@ public class ServerConnector extends PacketHandler
|
||||
(byte) user.getPendingConnection().getListener().getTabListSize() );
|
||||
}
|
||||
user.unsafe().sendPacket( modLogin );
|
||||
|
||||
MinecraftOutput out = new MinecraftOutput();
|
||||
out.writeStringUTF8WithoutLengthHeaderBecauseDinnerboneStuffedUpTheMCBrandPacket( ProxyServer.getInstance().getName() + " (" + ProxyServer.getInstance().getVersion() + ")" );
|
||||
user.unsafe().sendPacket( new PacketFAPluginMessage( "MC|Brand", out.toArray() ) );
|
||||
} else
|
||||
{
|
||||
user.getTabList().onServerChange();
|
||||
@@ -220,7 +225,7 @@ public class ServerConnector extends PacketHandler
|
||||
ch.write( new PacketFCEncryptionResponse( shared, token ) );
|
||||
|
||||
Cipher encrypt = EncryptionUtil.getCipher( Cipher.ENCRYPT_MODE, secretkey );
|
||||
ch.getHandle().pipeline().addBefore( PipelineUtils.PACKET_DECODE_HANDLER, PipelineUtils.ENCRYPT_HANDLER, new CipherEncoder( encrypt ) );
|
||||
ch.addBefore( PipelineUtils.PACKET_DECODE_HANDLER, PipelineUtils.ENCRYPT_HANDLER, new CipherEncoder( encrypt ) );
|
||||
|
||||
thisState = State.ENCRYPT_RESPONSE;
|
||||
} else
|
||||
@@ -235,7 +240,7 @@ public class ServerConnector extends PacketHandler
|
||||
Preconditions.checkState( thisState == State.ENCRYPT_RESPONSE, "Not expecting ENCRYPT_RESPONSE" );
|
||||
|
||||
Cipher decrypt = EncryptionUtil.getCipher( Cipher.DECRYPT_MODE, secretkey );
|
||||
ch.getHandle().pipeline().addBefore( PipelineUtils.PACKET_DECODE_HANDLER, PipelineUtils.DECRYPT_HANDLER, new CipherDecoder( decrypt ) );
|
||||
ch.addBefore( PipelineUtils.PACKET_DECODE_HANDLER, PipelineUtils.DECRYPT_HANDLER, new CipherDecoder( decrypt ) );
|
||||
|
||||
ch.write( user.getPendingConnection().getForgeLogin() );
|
||||
|
||||
@@ -251,14 +256,14 @@ public class ServerConnector extends PacketHandler
|
||||
{
|
||||
def = null;
|
||||
}
|
||||
ServerKickEvent event = bungee.getPluginManager().callEvent( new ServerKickEvent( user, kick.getMessage(), def ) );
|
||||
ServerKickEvent event = bungee.getPluginManager().callEvent( new ServerKickEvent( user, kick.getMessage(), def, ServerKickEvent.State.CONNECTING ) );
|
||||
if ( event.isCancelled() && event.getCancelServer() != null )
|
||||
{
|
||||
user.connect( event.getCancelServer() );
|
||||
return;
|
||||
}
|
||||
|
||||
String message = bungee.getTranslation( "connect_kick" ) + target.getName() + ": " + kick.getMessage();
|
||||
String message = bungee.getTranslation( "connect_kick" ) + target.getName() + ": " + event.getKickReason();
|
||||
if ( user.getServer() == null )
|
||||
{
|
||||
user.disconnect( message );
|
||||
@@ -276,10 +281,9 @@ public class ServerConnector extends PacketHandler
|
||||
throw new IllegalStateException( "May not connect to another BungeCord!" );
|
||||
}
|
||||
|
||||
if ( pluginMessage.getTag().equals( "FML" ) && ( pluginMessage.getData()[0] & 0xFF ) == 0 )
|
||||
DataInput in = pluginMessage.getStream();
|
||||
if ( pluginMessage.getTag().equals( "FML" ) && in.readUnsignedByte() == 0 )
|
||||
{
|
||||
ByteArrayDataInput in = ByteStreams.newDataInput( pluginMessage.getData() );
|
||||
in.readUnsignedByte();
|
||||
int count = in.readInt();
|
||||
for ( int i = 0; i < count; i++ )
|
||||
{
|
||||
|
@@ -22,7 +22,6 @@ import lombok.Setter;
|
||||
import net.md_5.bungee.api.ChatColor;
|
||||
import net.md_5.bungee.api.ProxyServer;
|
||||
import net.md_5.bungee.api.config.ServerInfo;
|
||||
import net.md_5.bungee.api.config.TexturePackInfo;
|
||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||
import net.md_5.bungee.api.event.PermissionCheckEvent;
|
||||
import net.md_5.bungee.api.event.ServerConnectEvent;
|
||||
@@ -31,6 +30,7 @@ import net.md_5.bungee.api.tab.TabListHandler;
|
||||
import net.md_5.bungee.connection.InitialHandler;
|
||||
import net.md_5.bungee.netty.ChannelWrapper;
|
||||
import net.md_5.bungee.netty.HandlerBoss;
|
||||
import net.md_5.bungee.netty.PacketWrapper;
|
||||
import net.md_5.bungee.netty.PipelineUtils;
|
||||
import net.md_5.bungee.protocol.packet.DefinedPacket;
|
||||
import net.md_5.bungee.protocol.packet.Packet3Chat;
|
||||
@@ -73,6 +73,9 @@ public final class UserConnection implements ProxiedPlayer
|
||||
@Getter
|
||||
@Setter
|
||||
private int ping = 100;
|
||||
@Getter
|
||||
@Setter
|
||||
private ServerInfo reconnectServer;
|
||||
/*========================================================================*/
|
||||
private final Collection<String> groups = new CaseInsensitiveSet();
|
||||
private final Collection<String> permissions = new CaseInsensitiveSet();
|
||||
@@ -127,9 +130,9 @@ public final class UserConnection implements ProxiedPlayer
|
||||
this.tabList = tabList;
|
||||
}
|
||||
|
||||
public void sendPacket(byte[] b)
|
||||
public void sendPacket(PacketWrapper packet)
|
||||
{
|
||||
ch.write( b );
|
||||
ch.write( packet );
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
@@ -178,12 +181,12 @@ public final class UserConnection implements ProxiedPlayer
|
||||
|
||||
if ( getServer() != null && Objects.equals( getServer().getInfo(), target ) )
|
||||
{
|
||||
sendMessage( ChatColor.RED + "Cannot connect to server you are already on!" );
|
||||
sendMessage( bungee.getTranslation( "already_connected" ) );
|
||||
return;
|
||||
}
|
||||
if ( pendingConnects.contains( target ) )
|
||||
{
|
||||
sendMessage( ChatColor.RED + "Already connecting to this server!" );
|
||||
sendMessage( bungee.getTranslation( "already_connecting" ) );
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -209,7 +212,7 @@ public final class UserConnection implements ProxiedPlayer
|
||||
pendingConnects.remove( target );
|
||||
|
||||
ServerInfo def = ProxyServer.getInstance().getServers().get( getPendingConnection().getListener().getFallbackServer() );
|
||||
if ( retry & target != def && ( getServer() == null || def != getServer().getInfo() ) )
|
||||
if ( retry && target != def && ( getServer() == null || def != getServer().getInfo() ) )
|
||||
{
|
||||
sendMessage( bungee.getTranslation( "fallback_lobby" ) );
|
||||
connect( def, false );
|
||||
@@ -233,7 +236,7 @@ public final class UserConnection implements ProxiedPlayer
|
||||
.option( ChannelOption.CONNECT_TIMEOUT_MILLIS, 5000 ) // TODO: Configurable
|
||||
.remoteAddress( target.getAddress() );
|
||||
// Windows is bugged, multi homed users will just have to live with random connecting IPs
|
||||
if ( !PlatformDependent.isWindows() )
|
||||
if ( getPendingConnection().getListener().isSetLocalAddress() && !PlatformDependent.isWindows() )
|
||||
{
|
||||
b.localAddress( getPendingConnection().getListener().getHost().getHostString(), 0 );
|
||||
}
|
||||
@@ -265,7 +268,9 @@ public final class UserConnection implements ProxiedPlayer
|
||||
@Override
|
||||
public void sendMessage(String message)
|
||||
{
|
||||
unsafe().sendPacket( new Packet3Chat( message ) );
|
||||
// TODO: Fix this
|
||||
String encoded = BungeeCord.getInstance().gson.toJson( message );
|
||||
unsafe().sendPacket( new Packet3Chat( "{\"text\":" + encoded + "}" ) );
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -345,12 +350,6 @@ public final class UserConnection implements ProxiedPlayer
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTexturePack(TexturePackInfo pack)
|
||||
{
|
||||
unsafe().sendPacket( new PacketFAPluginMessage( "MC|TPack", ( pack.getUrl() + "\00" + pack.getSize() ).getBytes() ) );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Unsafe unsafe()
|
||||
{
|
||||
|
@@ -41,7 +41,6 @@ public class CommandAlert extends Command
|
||||
String message = builder.substring( 0, builder.length() - 1 );
|
||||
|
||||
ProxyServer.getInstance().broadcast( message );
|
||||
ProxyServer.getInstance().getConsole().sendMessage( message );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,12 +1,12 @@
|
||||
package net.md_5.bungee.command;
|
||||
|
||||
import java.util.Collections;
|
||||
import net.md_5.bungee.api.ChatColor;
|
||||
import net.md_5.bungee.api.CommandSender;
|
||||
import net.md_5.bungee.api.ProxyServer;
|
||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||
import net.md_5.bungee.api.plugin.Command;
|
||||
|
||||
public class CommandFind extends Command
|
||||
public class CommandFind extends PlayerCommand
|
||||
{
|
||||
|
||||
public CommandFind()
|
||||
@@ -32,4 +32,10 @@ public class CommandFind extends Command
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<String> onTabComplete(CommandSender sender, String[] args)
|
||||
{
|
||||
return ( args.length == 0 ) ? super.onTabComplete( sender, args ) : Collections.EMPTY_LIST;
|
||||
}
|
||||
}
|
||||
|
@@ -2,6 +2,7 @@ package net.md_5.bungee.command;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import net.md_5.bungee.Util;
|
||||
import net.md_5.bungee.api.ChatColor;
|
||||
import net.md_5.bungee.api.CommandSender;
|
||||
import net.md_5.bungee.api.ProxyServer;
|
||||
@@ -18,15 +19,12 @@ public class CommandPerms extends Command
|
||||
@Override
|
||||
public void execute(CommandSender sender, String[] args)
|
||||
{
|
||||
StringBuilder groups = new StringBuilder();
|
||||
Set<String> permissions = new HashSet<>();
|
||||
for ( String group : sender.getGroups() )
|
||||
{
|
||||
groups.append( group );
|
||||
groups.append( ", " );
|
||||
permissions.addAll( ProxyServer.getInstance().getConfigurationAdapter().getPermissions( group ) );
|
||||
}
|
||||
sender.sendMessage( ChatColor.GOLD + "You have the following groups: " + groups.substring( 0, groups.length() - 2 ) );
|
||||
sender.sendMessage( ChatColor.GOLD + "You have the following groups: " + Util.csv( sender.getGroups() ) );
|
||||
|
||||
for ( String permission : permissions )
|
||||
{
|
||||
|
@@ -1,16 +1,21 @@
|
||||
package net.md_5.bungee.command;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.collect.Iterables;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import net.md_5.bungee.api.CommandSender;
|
||||
import net.md_5.bungee.api.ProxyServer;
|
||||
import net.md_5.bungee.api.config.ServerInfo;
|
||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||
import net.md_5.bungee.api.plugin.Command;
|
||||
import net.md_5.bungee.api.plugin.TabExecutor;
|
||||
|
||||
/**
|
||||
* Command to list and switch a player between available servers.
|
||||
*/
|
||||
public class CommandServer extends Command
|
||||
public class CommandServer extends Command implements TabExecutor
|
||||
{
|
||||
|
||||
public CommandServer()
|
||||
@@ -60,4 +65,24 @@ public class CommandServer extends Command
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<String> onTabComplete(final CommandSender sender, String[] args)
|
||||
{
|
||||
return ( args.length != 0 ) ? Collections.EMPTY_LIST : Iterables.transform( Iterables.filter( ProxyServer.getInstance().getServers().values(), new Predicate<ServerInfo>()
|
||||
{
|
||||
@Override
|
||||
public boolean apply(ServerInfo input)
|
||||
{
|
||||
return input.canAccess( sender );
|
||||
}
|
||||
} ), new Function<ServerInfo, String>()
|
||||
{
|
||||
@Override
|
||||
public String apply(ServerInfo input)
|
||||
{
|
||||
return input.getName();
|
||||
}
|
||||
} );
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,36 @@
|
||||
package net.md_5.bungee.command;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.collect.Iterables;
|
||||
import net.md_5.bungee.api.CommandSender;
|
||||
import net.md_5.bungee.api.ProxyServer;
|
||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||
import net.md_5.bungee.api.plugin.Command;
|
||||
import net.md_5.bungee.api.plugin.TabExecutor;
|
||||
|
||||
public abstract class PlayerCommand extends Command implements TabExecutor
|
||||
{
|
||||
|
||||
public PlayerCommand(String name)
|
||||
{
|
||||
super( name );
|
||||
}
|
||||
|
||||
public PlayerCommand(String name, String permission, String... aliases)
|
||||
{
|
||||
super( name, permission, aliases );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<String> onTabComplete(CommandSender sender, String[] args)
|
||||
{
|
||||
return Iterables.transform( ProxyServer.getInstance().getPlayers(), new Function<ProxiedPlayer, String>()
|
||||
{
|
||||
@Override
|
||||
public String apply(ProxiedPlayer input)
|
||||
{
|
||||
return input.getDisplayName();
|
||||
}
|
||||
} );
|
||||
}
|
||||
}
|
@@ -2,6 +2,7 @@ package net.md_5.bungee.config;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import gnu.trove.map.TMap;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
@@ -10,10 +11,8 @@ import net.md_5.bungee.api.ProxyServer;
|
||||
import net.md_5.bungee.api.config.ConfigurationAdapter;
|
||||
import net.md_5.bungee.api.config.ListenerInfo;
|
||||
import net.md_5.bungee.api.config.ServerInfo;
|
||||
import net.md_5.bungee.tab.GlobalPing;
|
||||
import net.md_5.bungee.tab.Global;
|
||||
import net.md_5.bungee.tab.ServerUnique;
|
||||
import net.md_5.bungee.util.CaseInsensitiveMap;
|
||||
import net.md_5.bungee.util.CaseInsensitiveSet;
|
||||
|
||||
/**
|
||||
* Core configuration for the proxy.
|
||||
@@ -43,6 +42,8 @@ public class Configuration
|
||||
*/
|
||||
private boolean onlineMode = true;
|
||||
private int playerLimit = -1;
|
||||
private Collection<String> disabledCommands;
|
||||
private int throttle = 4000;
|
||||
|
||||
public void load()
|
||||
{
|
||||
@@ -54,6 +55,9 @@ public class Configuration
|
||||
uuid = adapter.getString( "stats", uuid );
|
||||
onlineMode = adapter.getBoolean( "online_mode", onlineMode );
|
||||
playerLimit = adapter.getInt( "player_limit", playerLimit );
|
||||
throttle = adapter.getInt( "connection_throttle", throttle );
|
||||
|
||||
disabledCommands = new CaseInsensitiveSet( (Collection<String>) adapter.getList( "disabled_commands", Arrays.asList( "find" ) ) );
|
||||
|
||||
Preconditions.checkArgument( listeners != null && !listeners.isEmpty(), "No listeners defined." );
|
||||
|
||||
@@ -84,6 +88,7 @@ public class Configuration
|
||||
for ( ListenerInfo listener : listeners )
|
||||
{
|
||||
Preconditions.checkArgument( servers.containsKey( listener.getDefaultServer() ), "Default server %s is not defined", listener.getDefaultServer() );
|
||||
Preconditions.checkArgument( servers.containsKey( listener.getFallbackServer() ), "Fallback server %s is not defined", listener.getFallbackServer() );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -21,7 +21,6 @@ import net.md_5.bungee.api.ProxyServer;
|
||||
import net.md_5.bungee.api.config.ConfigurationAdapter;
|
||||
import net.md_5.bungee.api.config.ListenerInfo;
|
||||
import net.md_5.bungee.api.config.ServerInfo;
|
||||
import net.md_5.bungee.api.config.TexturePackInfo;
|
||||
import net.md_5.bungee.api.tab.TabListHandler;
|
||||
import net.md_5.bungee.tab.Global;
|
||||
import net.md_5.bungee.tab.GlobalPing;
|
||||
@@ -171,9 +170,10 @@ public class YamlConfig implements ConfigurationAdapter
|
||||
Map<String, Object> val = entry.getValue();
|
||||
String name = entry.getKey();
|
||||
String addr = get( "address", "localhost:25565", val );
|
||||
String motd = ChatColor.translateAlternateColorCodes( '&', get( "motd", "&1Just another BungeeCord - Forced Host", val ) );
|
||||
boolean restricted = get( "restricted", false, val );
|
||||
InetSocketAddress address = Util.getAddr( addr );
|
||||
ServerInfo info = ProxyServer.getInstance().constructServerInfo( name, address, restricted );
|
||||
ServerInfo info = ProxyServer.getInstance().constructServerInfo( name, address, motd, restricted );
|
||||
ret.put( name, info );
|
||||
}
|
||||
|
||||
@@ -195,7 +195,7 @@ public class YamlConfig implements ConfigurationAdapter
|
||||
|
||||
for ( Map<String, Object> val : base )
|
||||
{
|
||||
String motd = get( "motd", "Another Bungee server", val );
|
||||
String motd = get( "motd", "&1Another Bungee server", val );
|
||||
motd = ChatColor.translateAlternateColorCodes( '&', motd );
|
||||
|
||||
int maxPlayers = get( "max_players", 1, val );
|
||||
@@ -206,17 +206,16 @@ public class YamlConfig implements ConfigurationAdapter
|
||||
int tabListSize = get( "tab_size", 60, val );
|
||||
InetSocketAddress address = Util.getAddr( host );
|
||||
Map<String, String> forced = new CaseInsensitiveMap<>( get( "forced_hosts", forcedDef, val ) );
|
||||
String textureURL = get( "texture_url", null, val );
|
||||
int textureSize = get( "texture_size", 16, val );
|
||||
TexturePackInfo texture = ( textureURL == null ) ? null : new TexturePackInfo( textureURL, textureSize );
|
||||
String tabListName = get( "tab_list", "GLOBAL_PING", val );
|
||||
DefaultTabList value = DefaultTabList.valueOf( tabListName.toUpperCase() );
|
||||
if ( value == null )
|
||||
{
|
||||
value = DefaultTabList.GLOBAL_PING;
|
||||
}
|
||||
boolean setLocalAddress = get( "bind_local_address", true, val );
|
||||
boolean pingPassthrough = get( "ping_passthrough", false, val );
|
||||
|
||||
ListenerInfo info = new ListenerInfo( address, motd, maxPlayers, tabListSize, defaultServer, fallbackServer, forceDefault, forced, texture, value.clazz );
|
||||
ListenerInfo info = new ListenerInfo( address, motd, maxPlayers, tabListSize, defaultServer, fallbackServer, forceDefault, forced, value.clazz, setLocalAddress, pingPassthrough );
|
||||
ret.add( info );
|
||||
}
|
||||
|
||||
@@ -233,6 +232,12 @@ public class YamlConfig implements ConfigurationAdapter
|
||||
return ret;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<?> getList(String path, Collection<?> def)
|
||||
{
|
||||
return get( path, def );
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public Collection<String> getPermissions(String group)
|
||||
|
@@ -1,15 +1,14 @@
|
||||
package net.md_5.bungee.connection;
|
||||
|
||||
import com.google.common.io.ByteArrayDataInput;
|
||||
import com.google.common.io.ByteArrayDataOutput;
|
||||
import com.google.common.io.ByteStreams;
|
||||
import java.io.DataInput;
|
||||
import java.util.Objects;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import net.md_5.bungee.EntityMap;
|
||||
import net.md_5.bungee.ServerConnection;
|
||||
import net.md_5.bungee.UserConnection;
|
||||
import net.md_5.bungee.Util;
|
||||
import net.md_5.bungee.api.ChatColor;
|
||||
import net.md_5.bungee.api.ProxyServer;
|
||||
import net.md_5.bungee.api.config.ServerInfo;
|
||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||
@@ -22,6 +21,7 @@ import net.md_5.bungee.api.score.Scoreboard;
|
||||
import net.md_5.bungee.api.score.Team;
|
||||
import net.md_5.bungee.netty.ChannelWrapper;
|
||||
import net.md_5.bungee.netty.PacketHandler;
|
||||
import net.md_5.bungee.netty.PacketWrapper;
|
||||
import net.md_5.bungee.protocol.packet.Packet0KeepAlive;
|
||||
import net.md_5.bungee.protocol.packet.PacketC9PlayerListItem;
|
||||
import net.md_5.bungee.protocol.packet.PacketCEScoreboardObjective;
|
||||
@@ -31,8 +31,6 @@ import net.md_5.bungee.protocol.packet.PacketD1Team;
|
||||
import net.md_5.bungee.protocol.packet.PacketFAPluginMessage;
|
||||
import net.md_5.bungee.protocol.packet.PacketFFKick;
|
||||
|
||||
;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class DownstreamBridge extends PacketHandler
|
||||
{
|
||||
@@ -47,8 +45,9 @@ public class DownstreamBridge extends PacketHandler
|
||||
ServerInfo def = bungee.getServerInfo( con.getPendingConnection().getListener().getFallbackServer() );
|
||||
if ( server.getInfo() != def )
|
||||
{
|
||||
server.setObsolete( true );
|
||||
con.connectNow( def );
|
||||
con.sendMessage( ChatColor.RED + "The server you were previously on went down, you have been connected to the lobby" );
|
||||
con.sendMessage( bungee.getTranslation( "server_went_down" ) );
|
||||
} else
|
||||
{
|
||||
con.disconnect( Util.exception( t ) );
|
||||
@@ -60,7 +59,10 @@ public class DownstreamBridge extends PacketHandler
|
||||
{
|
||||
// We lost connection to the server
|
||||
server.getInfo().removePlayer( con );
|
||||
bungee.getReconnectHandler().setServer( con );
|
||||
if ( bungee.getReconnectHandler() != null )
|
||||
{
|
||||
bungee.getReconnectHandler().setServer( con );
|
||||
}
|
||||
|
||||
if ( !server.isObsolete() )
|
||||
{
|
||||
@@ -69,12 +71,12 @@ public class DownstreamBridge extends PacketHandler
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(byte[] buf) throws Exception
|
||||
public void handle(PacketWrapper packet) throws Exception
|
||||
{
|
||||
if ( !server.isObsolete() )
|
||||
{
|
||||
EntityMap.rewrite( buf, con.getServerEntityId(), con.getClientEntityId() );
|
||||
con.sendPacket( buf );
|
||||
EntityMap.rewrite( packet.buf, con.getServerEntityId(), con.getClientEntityId() );
|
||||
con.sendPacket( packet );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -185,7 +187,7 @@ public class DownstreamBridge extends PacketHandler
|
||||
@Override
|
||||
public void handle(PacketFAPluginMessage pluginMessage) throws Exception
|
||||
{
|
||||
ByteArrayDataInput in = ByteStreams.newDataInput( pluginMessage.getData() );
|
||||
DataInput in = pluginMessage.getStream();
|
||||
PluginMessageEvent event = new PluginMessageEvent( con.getServer(), con, pluginMessage.getTag(), pluginMessage.getData().clone() );
|
||||
|
||||
if ( bungee.getPluginManager().callEvent( event ).isCancelled() )
|
||||
@@ -193,11 +195,6 @@ public class DownstreamBridge extends PacketHandler
|
||||
throw new CancelSendSignal();
|
||||
}
|
||||
|
||||
if ( pluginMessage.getTag().equals( "MC|TPack" ) && con.getPendingConnection().getListener().getTexturePack() != null )
|
||||
{
|
||||
throw new CancelSendSignal();
|
||||
}
|
||||
|
||||
if ( pluginMessage.getTag().equals( "BungeeCord" ) )
|
||||
{
|
||||
ByteArrayDataOutput out = ByteStreams.newDataOutput();
|
||||
@@ -247,6 +244,18 @@ public class DownstreamBridge extends PacketHandler
|
||||
con.connect( server );
|
||||
}
|
||||
}
|
||||
if ( subChannel.equals( "ConnectOther" ) )
|
||||
{
|
||||
ProxiedPlayer player = bungee.getPlayer( in.readUTF() );
|
||||
if ( player != null )
|
||||
{
|
||||
ServerInfo server = bungee.getServerInfo( in.readUTF() );
|
||||
if ( server != null )
|
||||
{
|
||||
player.connect( server );
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( subChannel.equals( "IP" ) )
|
||||
{
|
||||
out.writeUTF( "IP" );
|
||||
@@ -328,7 +337,7 @@ public class DownstreamBridge extends PacketHandler
|
||||
{
|
||||
def = null;
|
||||
}
|
||||
ServerKickEvent event = bungee.getPluginManager().callEvent( new ServerKickEvent( con, kick.getMessage(), def ) );
|
||||
ServerKickEvent event = bungee.getPluginManager().callEvent( new ServerKickEvent( con, kick.getMessage(), def, ServerKickEvent.State.CONNECTED ) );
|
||||
if ( event.isCancelled() && event.getCancelServer() != null )
|
||||
{
|
||||
con.connectNow( event.getCancelServer() );
|
||||
|
@@ -1,8 +1,7 @@
|
||||
package net.md_5.bungee.connection;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.ning.http.client.AsyncCompletionHandler;
|
||||
import com.ning.http.client.Response;
|
||||
import io.netty.util.concurrent.ScheduledFuture;
|
||||
import java.math.BigInteger;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.URLEncoder;
|
||||
@@ -10,6 +9,7 @@ import java.security.GeneralSecurityException;
|
||||
import java.security.MessageDigest;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.logging.Level;
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.SecretKey;
|
||||
@@ -31,6 +31,7 @@ import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||
import net.md_5.bungee.api.event.LoginEvent;
|
||||
import net.md_5.bungee.api.event.PostLoginEvent;
|
||||
import net.md_5.bungee.api.event.ProxyPingEvent;
|
||||
import net.md_5.bungee.http.HttpClient;
|
||||
import net.md_5.bungee.netty.HandlerBoss;
|
||||
import net.md_5.bungee.netty.ChannelWrapper;
|
||||
import net.md_5.bungee.netty.CipherDecoder;
|
||||
@@ -39,6 +40,7 @@ import net.md_5.bungee.netty.PacketDecoder;
|
||||
import net.md_5.bungee.netty.PacketHandler;
|
||||
import net.md_5.bungee.netty.PipelineUtils;
|
||||
import net.md_5.bungee.protocol.Forge;
|
||||
import net.md_5.bungee.protocol.MinecraftInput;
|
||||
import net.md_5.bungee.protocol.Vanilla;
|
||||
import net.md_5.bungee.protocol.packet.DefinedPacket;
|
||||
import net.md_5.bungee.protocol.packet.Packet1Login;
|
||||
@@ -49,6 +51,7 @@ import net.md_5.bungee.protocol.packet.PacketFCEncryptionResponse;
|
||||
import net.md_5.bungee.protocol.packet.PacketFDEncryptionRequest;
|
||||
import net.md_5.bungee.protocol.packet.PacketFEPing;
|
||||
import net.md_5.bungee.protocol.packet.PacketFFKick;
|
||||
import net.md_5.bungee.api.AbstractReconnectHandler;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class InitialHandler extends PacketHandler implements PendingConnection
|
||||
@@ -77,6 +80,9 @@ public class InitialHandler extends PacketHandler implements PendingConnection
|
||||
ch.write( packet );
|
||||
}
|
||||
};
|
||||
private ScheduledFuture<?> pingFuture;
|
||||
private InetSocketAddress vHost;
|
||||
private byte version = -1;
|
||||
|
||||
private enum State
|
||||
{
|
||||
@@ -99,6 +105,22 @@ public class InitialHandler extends PacketHandler implements PendingConnection
|
||||
@Override
|
||||
public void handle(PacketFAPluginMessage pluginMessage) throws Exception
|
||||
{
|
||||
if ( pluginMessage.getTag().equals( "MC|PingHost" ) )
|
||||
{
|
||||
if ( pingFuture.cancel( false ) )
|
||||
{
|
||||
MinecraftInput in = pluginMessage.getMCStream();
|
||||
version = in.readByte();
|
||||
String connectHost = in.readString();
|
||||
int connectPort = in.readInt();
|
||||
this.vHost = new InetSocketAddress( connectHost, connectPort );
|
||||
|
||||
respondToPing();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: Unregister?
|
||||
if ( pluginMessage.getTag().equals( "REGISTER" ) )
|
||||
{
|
||||
@@ -109,21 +131,53 @@ public class InitialHandler extends PacketHandler implements PendingConnection
|
||||
}
|
||||
}
|
||||
|
||||
private void respondToPing()
|
||||
{
|
||||
ServerInfo forced = AbstractReconnectHandler.getForcedHost( this );
|
||||
final String motd = ( forced != null ) ? forced.getMotd() : listener.getMotd();
|
||||
|
||||
Callback<ServerPing> pingBack = new Callback<ServerPing>()
|
||||
{
|
||||
@Override
|
||||
public void done(ServerPing result, Throwable error)
|
||||
{
|
||||
if ( error != null )
|
||||
{
|
||||
result = new ServerPing( (byte) -1, "-1", "Error pinging remote server: " + Util.exception( error ), -1, -1 );
|
||||
}
|
||||
result = bungee.getPluginManager().callEvent( new ProxyPingEvent( InitialHandler.this, result ) ).getResponse();
|
||||
|
||||
String kickMessage = ChatColor.DARK_BLUE
|
||||
+ "\00" + result.getProtocolVersion()
|
||||
+ "\00" + result.getGameVersion()
|
||||
+ "\00" + result.getMotd()
|
||||
+ "\00" + result.getCurrentPlayers()
|
||||
+ "\00" + result.getMaxPlayers();
|
||||
BungeeCord.getInstance().getConnectionThrottle().unthrottle( getAddress().getAddress() );
|
||||
disconnect( kickMessage );
|
||||
}
|
||||
};
|
||||
|
||||
if ( forced != null && listener.isPingPassthrough() )
|
||||
{
|
||||
forced.ping( pingBack );
|
||||
} else
|
||||
{
|
||||
pingBack.done( new ServerPing( bungee.getProtocolVersion(), bungee.getGameVersion(), motd, bungee.getOnlineCount(), listener.getMaxPlayers() ), null );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(PacketFEPing ping) throws Exception
|
||||
{
|
||||
ServerPing response = new ServerPing( bungee.getProtocolVersion(), bungee.getGameVersion(),
|
||||
listener.getMotd(), bungee.getOnlineCount(), listener.getMaxPlayers() );
|
||||
|
||||
response = bungee.getPluginManager().callEvent( new ProxyPingEvent( this, response ) ).getResponse();
|
||||
|
||||
String kickMessage = ChatColor.DARK_BLUE
|
||||
+ "\00" + response.getProtocolVersion()
|
||||
+ "\00" + response.getGameVersion()
|
||||
+ "\00" + response.getMotd()
|
||||
+ "\00" + response.getCurrentPlayers()
|
||||
+ "\00" + response.getMaxPlayers();
|
||||
disconnect( kickMessage );
|
||||
pingFuture = ch.getHandle().eventLoop().schedule( new Runnable()
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
respondToPing();
|
||||
}
|
||||
}, 500, TimeUnit.MILLISECONDS );
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -141,14 +195,15 @@ public class InitialHandler extends PacketHandler implements PendingConnection
|
||||
{
|
||||
Preconditions.checkState( thisState == State.HANDSHAKE, "Not expecting HANDSHAKE" );
|
||||
this.handshake = handshake;
|
||||
this.vHost = new InetSocketAddress( handshake.getHost(), handshake.getPort() );
|
||||
bungee.getLogger().log( Level.INFO, "{0} has connected", this );
|
||||
|
||||
if ( handshake.getProcolVersion() > Vanilla.PROTOCOL_VERSION )
|
||||
{
|
||||
disconnect( "Outdated server!" );
|
||||
disconnect( bungee.getTranslation( "outdated_server" ) );
|
||||
} else if ( handshake.getProcolVersion() < Vanilla.PROTOCOL_VERSION )
|
||||
{
|
||||
disconnect( "Outdated client!" );
|
||||
disconnect( bungee.getTranslation( "outdated_client" ) );
|
||||
}
|
||||
|
||||
if ( handshake.getUsername().length() > 16 )
|
||||
@@ -184,7 +239,7 @@ public class InitialHandler extends PacketHandler implements PendingConnection
|
||||
|
||||
sharedKey = EncryptionUtil.getSecret( encryptResponse, request );
|
||||
Cipher decrypt = EncryptionUtil.getCipher( Cipher.DECRYPT_MODE, sharedKey );
|
||||
ch.getHandle().pipeline().addBefore( PipelineUtils.PACKET_DECODE_HANDLER, PipelineUtils.DECRYPT_HANDLER, new CipherDecoder( decrypt ) );
|
||||
ch.addBefore( PipelineUtils.PACKET_DECODE_HANDLER, PipelineUtils.DECRYPT_HANDLER, new CipherDecoder( decrypt ) );
|
||||
|
||||
if ( BungeeCord.getInstance().config.isOnlineMode() )
|
||||
{
|
||||
@@ -201,35 +256,37 @@ public class InitialHandler extends PacketHandler implements PendingConnection
|
||||
|
||||
String encodedHash = URLEncoder.encode( new BigInteger( sha.digest() ).toString( 16 ), "UTF-8" );
|
||||
String authURL = "http://session.minecraft.net/game/checkserver.jsp?user=" + encName + "&serverId=" + encodedHash;
|
||||
bungee.getHttpClient().prepareGet( authURL ).execute( new AsyncCompletionHandler<Response>()
|
||||
|
||||
Callback<String> handler = new Callback<String>()
|
||||
{
|
||||
@Override
|
||||
public Response onCompleted(Response response) throws Exception
|
||||
public void done(String result, Throwable error)
|
||||
{
|
||||
if ( "YES".equals( response.getResponseBody() ) )
|
||||
if ( error == null )
|
||||
{
|
||||
finish();
|
||||
if ( "YES".equals( result ) )
|
||||
{
|
||||
finish();
|
||||
} else
|
||||
{
|
||||
disconnect( "Not authenticated with Minecraft.net" );
|
||||
}
|
||||
} else
|
||||
{
|
||||
disconnect( "Not authenticated with Minecraft.net" );
|
||||
disconnect( bungee.getTranslation( "mojang_fail" ) );
|
||||
bungee.getLogger().log( Level.SEVERE, "Error authenticating " + getName() + " with minecraft.net", error );
|
||||
}
|
||||
return response;
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public void onThrowable(Throwable t)
|
||||
{
|
||||
disconnect( bungee.getTranslation( "mojang_fail" ) );
|
||||
bungee.getLogger().log( Level.SEVERE, "Error authenticating " + getName() + " with minecraft.net", t );
|
||||
}
|
||||
} );
|
||||
HttpClient.get( authURL, ch.getHandle().eventLoop(), handler );
|
||||
} else
|
||||
{
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
||||
private void finish() throws GeneralSecurityException
|
||||
private void finish()
|
||||
{
|
||||
// Check for multiple connections
|
||||
ProxiedPlayer old = bungee.getPlayer( handshake.getUsername() );
|
||||
@@ -258,14 +315,17 @@ public class InitialHandler extends PacketHandler implements PendingConnection
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
unsafe().sendPacket( new PacketFCEncryptionResponse( new byte[ 0 ], new byte[ 0 ] ) );
|
||||
try
|
||||
if ( ch.getHandle().isActive() )
|
||||
{
|
||||
Cipher encrypt = EncryptionUtil.getCipher( Cipher.ENCRYPT_MODE, sharedKey );
|
||||
ch.getHandle().pipeline().addBefore( PipelineUtils.DECRYPT_HANDLER, PipelineUtils.ENCRYPT_HANDLER, new CipherEncoder( encrypt ) );
|
||||
} catch ( GeneralSecurityException ex )
|
||||
{
|
||||
disconnect( "Cipher error: " + Util.exception( ex ) );
|
||||
unsafe().sendPacket( new PacketFCEncryptionResponse( new byte[ 0 ], new byte[ 0 ] ) );
|
||||
try
|
||||
{
|
||||
Cipher encrypt = EncryptionUtil.getCipher( Cipher.ENCRYPT_MODE, sharedKey );
|
||||
ch.addBefore( PipelineUtils.DECRYPT_HANDLER, PipelineUtils.ENCRYPT_HANDLER, new CipherEncoder( encrypt ) );
|
||||
} catch ( GeneralSecurityException ex )
|
||||
{
|
||||
disconnect( "Cipher error: " + Util.exception( ex ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
} );
|
||||
@@ -281,14 +341,21 @@ public class InitialHandler extends PacketHandler implements PendingConnection
|
||||
{
|
||||
Preconditions.checkState( thisState == State.LOGIN, "Not expecting LOGIN" );
|
||||
|
||||
UserConnection userCon = new UserConnection( (BungeeCord) bungee, ch, getName(), this );
|
||||
UserConnection userCon = new UserConnection( bungee, ch, getName(), this );
|
||||
userCon.init();
|
||||
|
||||
bungee.getPluginManager().callEvent( new PostLoginEvent( userCon ) );
|
||||
|
||||
ch.getHandle().pipeline().get( HandlerBoss.class ).setHandler( new UpstreamBridge( bungee, userCon ) );
|
||||
|
||||
ServerInfo server = bungee.getReconnectHandler().getServer( userCon );
|
||||
ServerInfo server;
|
||||
if ( bungee.getReconnectHandler() != null )
|
||||
{
|
||||
server = bungee.getReconnectHandler().getServer( userCon );
|
||||
} else
|
||||
{
|
||||
server = AbstractReconnectHandler.getForcedHost( this );
|
||||
}
|
||||
userCon.connect( server, true );
|
||||
|
||||
thisState = State.FINISHED;
|
||||
@@ -314,13 +381,13 @@ public class InitialHandler extends PacketHandler implements PendingConnection
|
||||
@Override
|
||||
public byte getVersion()
|
||||
{
|
||||
return ( handshake == null ) ? -1 : handshake.getProcolVersion();
|
||||
return ( handshake == null ) ? version : handshake.getProcolVersion();
|
||||
}
|
||||
|
||||
@Override
|
||||
public InetSocketAddress getVirtualHost()
|
||||
{
|
||||
return ( handshake == null ) ? null : new InetSocketAddress( handshake.getHost(), handshake.getPort() );
|
||||
return vHost;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@@ -1,5 +1,7 @@
|
||||
package net.md_5.bungee.connection;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import net.md_5.bungee.api.Callback;
|
||||
import net.md_5.bungee.api.ServerPing;
|
||||
@@ -14,15 +16,15 @@ public class PingHandler extends PacketHandler
|
||||
|
||||
private final ServerInfo target;
|
||||
private final Callback<ServerPing> callback;
|
||||
private static final byte[] pingBuf = new byte[]
|
||||
{
|
||||
(byte) 0xFE, (byte) 0x01
|
||||
};
|
||||
|
||||
@Override
|
||||
public void connected(ChannelWrapper channel) throws Exception
|
||||
{
|
||||
channel.write( pingBuf );
|
||||
// TODO: Update this to 1.6.2 style!
|
||||
channel.write( Unpooled.wrappedBuffer( new byte[]
|
||||
{
|
||||
(byte) 0xFE, (byte) 0x01
|
||||
} ) );
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@@ -5,16 +5,19 @@ import net.md_5.bungee.EntityMap;
|
||||
import net.md_5.bungee.UserConnection;
|
||||
import net.md_5.bungee.Util;
|
||||
import net.md_5.bungee.api.ProxyServer;
|
||||
import net.md_5.bungee.api.config.TexturePackInfo;
|
||||
import net.md_5.bungee.api.event.ChatEvent;
|
||||
import net.md_5.bungee.api.event.PlayerDisconnectEvent;
|
||||
import net.md_5.bungee.api.event.PluginMessageEvent;
|
||||
import net.md_5.bungee.netty.ChannelWrapper;
|
||||
import net.md_5.bungee.netty.PacketHandler;
|
||||
import net.md_5.bungee.netty.PacketWrapper;
|
||||
import net.md_5.bungee.protocol.packet.Packet0KeepAlive;
|
||||
import net.md_5.bungee.protocol.packet.Packet3Chat;
|
||||
import net.md_5.bungee.protocol.packet.PacketCBTabComplete;
|
||||
import net.md_5.bungee.protocol.packet.PacketCCSettings;
|
||||
import net.md_5.bungee.protocol.packet.PacketFAPluginMessage;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class UpstreamBridge extends PacketHandler
|
||||
{
|
||||
@@ -30,12 +33,6 @@ public class UpstreamBridge extends PacketHandler
|
||||
BungeeCord.getInstance().addConnection( con );
|
||||
con.getTabList().onConnect();
|
||||
con.unsafe().sendPacket( BungeeCord.getInstance().registerChannels() );
|
||||
|
||||
TexturePackInfo texture = con.getPendingConnection().getListener().getTexturePack();
|
||||
if ( texture != null )
|
||||
{
|
||||
con.setTexturePack( texture );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -60,12 +57,12 @@ public class UpstreamBridge extends PacketHandler
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(byte[] buf) throws Exception
|
||||
public void handle(PacketWrapper packet) throws Exception
|
||||
{
|
||||
EntityMap.rewrite( buf, con.getClientEntityId(), con.getServerEntityId() );
|
||||
EntityMap.rewrite( packet.buf, con.getClientEntityId(), con.getServerEntityId() );
|
||||
if ( con.getServer() != null )
|
||||
{
|
||||
con.getServer().getCh().write( buf );
|
||||
con.getServer().getCh().write( packet );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,14 +81,28 @@ public class UpstreamBridge extends PacketHandler
|
||||
public void handle(Packet3Chat chat) throws Exception
|
||||
{
|
||||
ChatEvent chatEvent = new ChatEvent( con, con.getServer(), chat.getMessage() );
|
||||
if ( bungee.getPluginManager().callEvent( chatEvent ).isCancelled() )
|
||||
if ( !bungee.getPluginManager().callEvent( chatEvent ).isCancelled() )
|
||||
{
|
||||
throw new CancelSendSignal();
|
||||
}
|
||||
if ( chatEvent.isCommand() )
|
||||
{
|
||||
if ( bungee.getPluginManager().dispatchCommand( con, chat.getMessage().substring( 1 ) ) )
|
||||
chat.setMessage( chatEvent.getMessage() );
|
||||
if ( !chatEvent.isCommand() || !bungee.getPluginManager().dispatchCommand( con, chat.getMessage().substring( 1 ) ) )
|
||||
{
|
||||
con.getServer().unsafe().sendPacket( chat );
|
||||
}
|
||||
}
|
||||
throw new CancelSendSignal();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(PacketCBTabComplete tabComplete) throws Exception
|
||||
{
|
||||
if ( tabComplete.getCursor().startsWith( "/" ) )
|
||||
{
|
||||
List<String> results = new ArrayList<>();
|
||||
bungee.getPluginManager().dispatchCommand( con, tabComplete.getCursor().substring( 1, tabComplete.getCursor().length() ), results );
|
||||
|
||||
if ( !results.isEmpty() )
|
||||
{
|
||||
con.unsafe().sendPacket( new PacketCBTabComplete( results.toArray( new String[ results.size() ] ) ) );
|
||||
throw new CancelSendSignal();
|
||||
}
|
||||
}
|
||||
@@ -111,7 +122,7 @@ public class UpstreamBridge extends PacketHandler
|
||||
throw new CancelSendSignal();
|
||||
}
|
||||
// Hack around Forge race conditions
|
||||
if ( pluginMessage.getTag().equals( "FML" ) && ( pluginMessage.getData()[0] & 0xFF ) == 1 )
|
||||
if ( pluginMessage.getTag().equals( "FML" ) && pluginMessage.getStream().readUnsignedByte() == 1 )
|
||||
{
|
||||
throw new CancelSendSignal();
|
||||
}
|
||||
|
77
proxy/src/main/java/net/md_5/bungee/http/HttpClient.java
Normal file
77
proxy/src/main/java/net/md_5/bungee/http/HttpClient.java
Normal file
@@ -0,0 +1,77 @@
|
||||
package net.md_5.bungee.http;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import io.netty.bootstrap.Bootstrap;
|
||||
import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.ChannelFutureListener;
|
||||
import io.netty.channel.ChannelOption;
|
||||
import io.netty.channel.EventLoop;
|
||||
import io.netty.channel.nio.NioEventLoopGroup;
|
||||
import io.netty.channel.socket.nio.NioSocketChannel;
|
||||
import io.netty.handler.codec.http.DefaultHttpRequest;
|
||||
import io.netty.handler.codec.http.HttpHeaders;
|
||||
import io.netty.handler.codec.http.HttpMethod;
|
||||
import io.netty.handler.codec.http.HttpRequest;
|
||||
import io.netty.handler.codec.http.HttpVersion;
|
||||
import java.net.URI;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.NoArgsConstructor;
|
||||
import net.md_5.bungee.api.Callback;
|
||||
|
||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
public class HttpClient
|
||||
{
|
||||
|
||||
public static int TIMEOUT = 5000;
|
||||
|
||||
public static void get(String url, EventLoop eventLoop, final Callback<String> callback)
|
||||
{
|
||||
Preconditions.checkNotNull( url, "url" );
|
||||
Preconditions.checkNotNull( eventLoop, "eventLoop" );
|
||||
Preconditions.checkNotNull( callback, "callBack" );
|
||||
|
||||
final URI uri = URI.create( url );
|
||||
|
||||
Preconditions.checkNotNull( uri.getScheme(), "scheme" );
|
||||
Preconditions.checkNotNull( uri.getHost(), "host" );
|
||||
boolean ssl = uri.getScheme().equals( "https" );
|
||||
int port = uri.getPort();
|
||||
if ( port == -1 )
|
||||
{
|
||||
switch ( uri.getScheme() )
|
||||
{
|
||||
case "http":
|
||||
port = 80;
|
||||
break;
|
||||
case "https":
|
||||
port = 443;
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException( "Unknown scheme " + uri.getScheme() );
|
||||
}
|
||||
}
|
||||
|
||||
ChannelFutureListener future = new ChannelFutureListener()
|
||||
{
|
||||
@Override
|
||||
public void operationComplete(ChannelFuture future) throws Exception
|
||||
{
|
||||
if ( future.isSuccess() )
|
||||
{
|
||||
String path = uri.getRawPath() + ( ( uri.getRawQuery() == null ) ? "" : "?" + uri.getRawQuery() );
|
||||
|
||||
HttpRequest request = new DefaultHttpRequest( HttpVersion.HTTP_1_1, HttpMethod.GET, path );
|
||||
request.headers().set( HttpHeaders.Names.HOST, uri.getHost() );
|
||||
|
||||
future.channel().writeAndFlush( request );
|
||||
} else
|
||||
{
|
||||
callback.done( null, future.cause() );
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
new Bootstrap().channel( NioSocketChannel.class ).group( eventLoop ).handler( new HttpInitializer( callback, ssl ) ).
|
||||
option( ChannelOption.CONNECT_TIMEOUT_MILLIS, TIMEOUT ).remoteAddress( uri.getHost(), port ).connect().addListener( future );
|
||||
}
|
||||
}
|
61
proxy/src/main/java/net/md_5/bungee/http/HttpHandler.java
Normal file
61
proxy/src/main/java/net/md_5/bungee/http/HttpHandler.java
Normal file
@@ -0,0 +1,61 @@
|
||||
package net.md_5.bungee.http;
|
||||
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.SimpleChannelInboundHandler;
|
||||
import io.netty.handler.codec.http.HttpContent;
|
||||
import io.netty.handler.codec.http.HttpObject;
|
||||
import io.netty.handler.codec.http.HttpResponse;
|
||||
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||
import io.netty.handler.codec.http.LastHttpContent;
|
||||
import java.nio.charset.Charset;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import net.md_5.bungee.api.Callback;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class HttpHandler extends SimpleChannelInboundHandler<HttpObject>
|
||||
{
|
||||
|
||||
private final Callback<String> callback;
|
||||
private final StringBuilder buffer = new StringBuilder();
|
||||
|
||||
@Override
|
||||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception
|
||||
{
|
||||
try
|
||||
{
|
||||
callback.done( null, cause );
|
||||
} finally
|
||||
{
|
||||
ctx.channel().close();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void channelRead0(ChannelHandlerContext ctx, HttpObject msg) throws Exception
|
||||
{
|
||||
if ( msg instanceof HttpResponse )
|
||||
{
|
||||
HttpResponse response = (HttpResponse) msg;
|
||||
if ( response.getStatus().code() != 200 )
|
||||
{
|
||||
throw new IllegalStateException( "Expected HTTP response 200 OK, got " + response.getStatus() );
|
||||
}
|
||||
}
|
||||
if ( msg instanceof HttpContent )
|
||||
{
|
||||
HttpContent content = (HttpContent) msg;
|
||||
buffer.append( content.content().toString( Charset.forName( "UTF-8" ) ) );
|
||||
|
||||
if ( msg instanceof LastHttpContent )
|
||||
{
|
||||
try
|
||||
{
|
||||
callback.done( buffer.toString(), null );
|
||||
} finally
|
||||
{
|
||||
ctx.channel().close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,42 @@
|
||||
package net.md_5.bungee.http;
|
||||
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelInitializer;
|
||||
import io.netty.handler.codec.http.HttpClientCodec;
|
||||
import io.netty.handler.ssl.SslHandler;
|
||||
import io.netty.handler.timeout.ReadTimeoutHandler;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.SSLEngine;
|
||||
import javax.net.ssl.TrustManager;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import net.md_5.bungee.api.Callback;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class HttpInitializer extends ChannelInitializer<Channel>
|
||||
{
|
||||
|
||||
private final Callback<String> callback;
|
||||
private final boolean ssl;
|
||||
|
||||
@Override
|
||||
protected void initChannel(Channel ch) throws Exception
|
||||
{
|
||||
ch.pipeline().addLast( "timeout", new ReadTimeoutHandler( HttpClient.TIMEOUT, TimeUnit.MILLISECONDS ) );
|
||||
if ( ssl )
|
||||
{
|
||||
SSLContext context = SSLContext.getInstance( "TLS" );
|
||||
context.init( null, new TrustManager[]
|
||||
{
|
||||
TrustingX509Manager.getInstance()
|
||||
}, null );
|
||||
|
||||
SSLEngine engine = context.createSSLEngine();
|
||||
engine.setUseClientMode( true );
|
||||
|
||||
ch.pipeline().addLast( "ssl", new SslHandler( engine ) );
|
||||
}
|
||||
ch.pipeline().addLast( "http", new HttpClientCodec() );
|
||||
ch.pipeline().addLast( "handler", new HttpHandler( callback ) );
|
||||
}
|
||||
}
|
@@ -0,0 +1,32 @@
|
||||
package net.md_5.bungee.http;
|
||||
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.cert.X509Certificate;
|
||||
import javax.net.ssl.X509TrustManager;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
public class TrustingX509Manager implements X509TrustManager
|
||||
{
|
||||
|
||||
@Getter
|
||||
private static final X509TrustManager instance = new TrustingX509Manager();
|
||||
|
||||
@Override
|
||||
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public X509Certificate[] getAcceptedIssuers()
|
||||
{
|
||||
return new X509Certificate[ 0 ];
|
||||
}
|
||||
}
|
@@ -12,7 +12,7 @@ public class LogDispatcher extends Thread
|
||||
|
||||
public LogDispatcher(BungeeLogger logger)
|
||||
{
|
||||
super( "BungeeCord Logger Thread - " + logger );
|
||||
super( "BungeeCord Logger Thread" );
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
|
@@ -1,6 +1,8 @@
|
||||
package net.md_5.bungee.netty;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelHandler;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import lombok.Getter;
|
||||
|
||||
@@ -20,7 +22,15 @@ public class ChannelWrapper
|
||||
{
|
||||
if ( !closed )
|
||||
{
|
||||
ch.write( packet );
|
||||
if ( packet instanceof PacketWrapper )
|
||||
{
|
||||
( (PacketWrapper) packet ).setReleased( true );
|
||||
ch.write( ( (PacketWrapper) packet ).buf );
|
||||
} else
|
||||
{
|
||||
ch.write( packet );
|
||||
}
|
||||
ch.flush();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,10 +39,18 @@ public class ChannelWrapper
|
||||
if ( !closed )
|
||||
{
|
||||
closed = true;
|
||||
ch.flush();
|
||||
ch.close();
|
||||
}
|
||||
}
|
||||
|
||||
public void addBefore(String baseName, String name, ChannelHandler handler)
|
||||
{
|
||||
Preconditions.checkState( ch.eventLoop().inEventLoop(), "cannot add handler outside of event loop" );
|
||||
ch.pipeline().flush();
|
||||
ch.pipeline().addBefore( baseName, name, handler );
|
||||
}
|
||||
|
||||
public Channel getHandle()
|
||||
{
|
||||
return ch;
|
||||
|
@@ -51,7 +51,7 @@ public class CipherBase
|
||||
byte[] heapIn = bufToByte( in );
|
||||
|
||||
ByteBuf heapOut = ctx.alloc().heapBuffer( cipher.getOutputSize( readableBytes ) );
|
||||
heapOut.writerIndex( cipher.update( heapIn, 0, readableBytes, heapOut.array(), heapOut.arrayOffset() ));
|
||||
heapOut.writerIndex( cipher.update( heapIn, 0, readableBytes, heapOut.array(), heapOut.arrayOffset() ) );
|
||||
|
||||
return heapOut;
|
||||
}
|
||||
|
@@ -2,8 +2,8 @@ package net.md_5.bungee.netty;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.MessageList;
|
||||
import io.netty.handler.codec.MessageToMessageDecoder;
|
||||
import java.util.List;
|
||||
import javax.crypto.Cipher;
|
||||
|
||||
public class CipherDecoder extends MessageToMessageDecoder<ByteBuf>
|
||||
@@ -17,7 +17,7 @@ public class CipherDecoder extends MessageToMessageDecoder<ByteBuf>
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void decode(ChannelHandlerContext ctx, ByteBuf msg, MessageList<Object> out) throws Exception
|
||||
protected void decode(ChannelHandlerContext ctx, ByteBuf msg, List<Object> out) throws Exception
|
||||
{
|
||||
out.add( cipher.cipher( ctx, msg ) );
|
||||
}
|
||||
|
@@ -3,7 +3,6 @@ package net.md_5.bungee.netty;
|
||||
import com.google.common.base.Preconditions;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelInboundHandlerAdapter;
|
||||
import io.netty.channel.MessageList;
|
||||
import io.netty.handler.timeout.ReadTimeoutException;
|
||||
import java.io.IOException;
|
||||
import java.util.logging.Level;
|
||||
@@ -59,30 +58,31 @@ public class HandlerBoss extends ChannelInboundHandlerAdapter
|
||||
}
|
||||
|
||||
@Override
|
||||
public void messageReceived(ChannelHandlerContext ctx, MessageList<Object> msgs) throws Exception
|
||||
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception
|
||||
{
|
||||
for ( Object msg : msgs )
|
||||
if ( handler != null )
|
||||
{
|
||||
if ( handler != null && ctx.channel().isActive() )
|
||||
PacketWrapper packet = (PacketWrapper) msg;
|
||||
boolean sendPacket = true;
|
||||
try
|
||||
{
|
||||
if ( msg instanceof PacketWrapper )
|
||||
if ( packet.packet != null )
|
||||
{
|
||||
boolean sendPacket = true;
|
||||
try
|
||||
{
|
||||
( (PacketWrapper) msg ).packet.handle( handler );
|
||||
packet.packet.handle( handler );
|
||||
} catch ( CancelSendSignal ex )
|
||||
{
|
||||
sendPacket = false;
|
||||
}
|
||||
if ( sendPacket )
|
||||
{
|
||||
handler.handle( ( (PacketWrapper) msg ).buf );
|
||||
}
|
||||
} else
|
||||
{
|
||||
handler.handle( (byte[]) msg );
|
||||
}
|
||||
if ( sendPacket )
|
||||
{
|
||||
handler.handle( packet );
|
||||
}
|
||||
} finally
|
||||
{
|
||||
packet.trySingleRelease();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -2,8 +2,8 @@ package net.md_5.bungee.netty;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.MessageList;
|
||||
import io.netty.handler.codec.ReplayingDecoder;
|
||||
import java.util.List;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
@@ -28,7 +28,7 @@ public class PacketDecoder extends ReplayingDecoder<Void>
|
||||
private Protocol protocol;
|
||||
|
||||
@Override
|
||||
protected void decode(ChannelHandlerContext ctx, ByteBuf in, MessageList<Object> out) throws Exception
|
||||
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception
|
||||
{
|
||||
// While we have enough data
|
||||
while ( true )
|
||||
@@ -40,24 +40,11 @@ public class PacketDecoder extends ReplayingDecoder<Void>
|
||||
// If we got this far, it means we have formed a packet, so lets grab the end index
|
||||
int endIndex = in.readerIndex();
|
||||
// Allocate a buffer big enough for all bytes we have read
|
||||
byte[] buf = new byte[ endIndex - startIndex ];
|
||||
// Go back to start index
|
||||
in.readerIndex( startIndex );
|
||||
// Drain all the bytes into our buffer
|
||||
in.readBytes( buf, 0, buf.length );
|
||||
// Jump back to the end of this packet
|
||||
in.readerIndex( endIndex );
|
||||
ByteBuf buf = in.copy( startIndex, endIndex - startIndex );
|
||||
// Checkpoint our state incase we don't have enough data for another packet
|
||||
checkpoint();
|
||||
|
||||
// Store our decoded message
|
||||
if ( packet != null )
|
||||
{
|
||||
out.add( new PacketWrapper( packet, buf ) );
|
||||
} else
|
||||
{
|
||||
out.add( buf );
|
||||
}
|
||||
out.add( new PacketWrapper( packet, buf ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -10,7 +10,7 @@ public abstract class PacketHandler extends net.md_5.bungee.protocol.packet.Abst
|
||||
{
|
||||
}
|
||||
|
||||
public void handle(byte[] buf) throws Exception
|
||||
public void handle(PacketWrapper packet) throws Exception
|
||||
{
|
||||
}
|
||||
|
||||
|
@@ -1,16 +1,25 @@
|
||||
package net.md_5.bungee.netty;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import net.md_5.bungee.protocol.packet.DefinedPacket;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class PacketWrapper
|
||||
{
|
||||
|
||||
DefinedPacket packet;
|
||||
byte[] buf;
|
||||
public final DefinedPacket packet;
|
||||
public final ByteBuf buf;
|
||||
@Setter
|
||||
private boolean released;
|
||||
|
||||
public PacketWrapper(DefinedPacket packet, byte[] buf)
|
||||
public void trySingleRelease()
|
||||
{
|
||||
this.packet = packet;
|
||||
this.buf = buf;
|
||||
if ( !released )
|
||||
{
|
||||
buf.release();
|
||||
released = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -4,9 +4,9 @@ import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelException;
|
||||
import io.netty.channel.ChannelInitializer;
|
||||
import io.netty.channel.ChannelOption;
|
||||
import io.netty.handler.codec.bytes.ByteArrayEncoder;
|
||||
import io.netty.handler.timeout.ReadTimeoutHandler;
|
||||
import io.netty.util.AttributeKey;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import net.md_5.bungee.BungeeCord;
|
||||
import net.md_5.bungee.BungeeServerInfo;
|
||||
@@ -28,6 +28,12 @@ public class PipelineUtils
|
||||
@Override
|
||||
protected void initChannel(Channel ch) throws Exception
|
||||
{
|
||||
if ( BungeeCord.getInstance().getConnectionThrottle().throttle( ( (InetSocketAddress) ch.remoteAddress() ).getAddress() ) )
|
||||
{
|
||||
ch.close();
|
||||
return;
|
||||
}
|
||||
|
||||
BASE.initChannel( ch );
|
||||
ch.pipeline().get( HandlerBoss.class ).setHandler( new InitialHandler( ProxyServer.getInstance(), ch.attr( LISTENER ).get() ) );
|
||||
}
|
||||
@@ -43,11 +49,9 @@ public class PipelineUtils
|
||||
};
|
||||
public static final Base BASE = new Base();
|
||||
private static final DefinedPacketEncoder packetEncoder = new DefinedPacketEncoder();
|
||||
private static final ByteArrayEncoder arrayEncoder = new ByteArrayEncoder();
|
||||
public static String TIMEOUT_HANDLER = "timeout";
|
||||
public static String PACKET_DECODE_HANDLER = "packet-decoder";
|
||||
public static String PACKET_ENCODE_HANDLER = "packet-encoder";
|
||||
public static String ARRAY_ENCODE_HANDLER = "array-encoder";
|
||||
public static String BOSS_HANDLER = "inbound-boss";
|
||||
public static String ENCRYPT_HANDLER = "encrypt";
|
||||
public static String DECRYPT_HANDLER = "decrypt";
|
||||
@@ -69,7 +73,6 @@ public class PipelineUtils
|
||||
ch.pipeline().addLast( TIMEOUT_HANDLER, new ReadTimeoutHandler( BungeeCord.getInstance().config.getTimeout(), TimeUnit.MILLISECONDS ) );
|
||||
ch.pipeline().addLast( PACKET_DECODE_HANDLER, new PacketDecoder( Vanilla.getInstance() ) );
|
||||
ch.pipeline().addLast( PACKET_ENCODE_HANDLER, packetEncoder );
|
||||
ch.pipeline().addLast( ARRAY_ENCODE_HANDLER, arrayEncoder );
|
||||
ch.pipeline().addLast( BOSS_HANDLER, new HandlerBoss() );
|
||||
}
|
||||
};
|
||||
|
@@ -1,36 +0,0 @@
|
||||
package net.md_5.bungee.reconnect;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import net.md_5.bungee.api.ProxyServer;
|
||||
import net.md_5.bungee.api.ReconnectHandler;
|
||||
import net.md_5.bungee.api.config.ListenerInfo;
|
||||
import net.md_5.bungee.api.config.ServerInfo;
|
||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||
|
||||
public abstract class AbstractReconnectManager implements ReconnectHandler
|
||||
{
|
||||
|
||||
@Override
|
||||
public ServerInfo getServer(ProxiedPlayer player)
|
||||
{
|
||||
ListenerInfo listener = player.getPendingConnection().getListener();
|
||||
String name;
|
||||
String forced = listener.getForcedHosts().get( player.getPendingConnection().getVirtualHost().getHostString() );
|
||||
if ( forced == null && listener.isForceDefault() )
|
||||
{
|
||||
forced = listener.getDefaultServer();
|
||||
}
|
||||
|
||||
String server = ( forced == null ) ? getStoredServer( player ) : forced;
|
||||
name = ( server != null ) ? server : listener.getDefaultServer();
|
||||
ServerInfo info = ProxyServer.getInstance().getServerInfo( name );
|
||||
if ( info == null )
|
||||
{
|
||||
info = ProxyServer.getInstance().getServerInfo( listener.getDefaultServer() );
|
||||
}
|
||||
Preconditions.checkState( info != null, "Default server not defined" );
|
||||
return info;
|
||||
}
|
||||
|
||||
protected abstract String getStoredServer(ProxiedPlayer player);
|
||||
}
|
@@ -1,94 +0,0 @@
|
||||
package net.md_5.bungee.reconnect;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.DriverManager;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.logging.Level;
|
||||
import net.md_5.bungee.api.ProxyServer;
|
||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||
|
||||
public class SQLReconnectHandler extends AbstractReconnectManager
|
||||
{
|
||||
|
||||
private final Connection connection;
|
||||
|
||||
public SQLReconnectHandler() throws ClassNotFoundException, SQLException
|
||||
{
|
||||
Class.forName( "org.sqlite.JDBC" );
|
||||
connection = DriverManager.getConnection( "jdbc:sqlite:bungee.sqlite" );
|
||||
|
||||
try ( PreparedStatement ps = connection.prepareStatement(
|
||||
"CREATE TABLE IF NOT EXISTS players ("
|
||||
+ "playerId INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,"
|
||||
+ "username TEXT NOT NULL UNIQUE COLLATE NOCASE,"
|
||||
+ "seen INTEGER,"
|
||||
+ "server TEXT"
|
||||
+ ");" ) )
|
||||
{
|
||||
ps.executeUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getStoredServer(ProxiedPlayer player)
|
||||
{
|
||||
String server = null;
|
||||
try ( PreparedStatement ps = connection.prepareStatement( "SELECT server FROM players WHERE username = ?" ) )
|
||||
{
|
||||
ps.setString( 1, player.getName() );
|
||||
try ( ResultSet rs = ps.executeQuery() )
|
||||
{
|
||||
if ( rs.next() )
|
||||
{
|
||||
server = rs.getString( 1 );
|
||||
} else
|
||||
{
|
||||
try ( PreparedStatement playerUpdate = connection.prepareStatement( "INSERT INTO players( username ) VALUES( ? )" ) )
|
||||
{
|
||||
playerUpdate.setString( 1, player.getName() );
|
||||
playerUpdate.executeUpdate();
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch ( SQLException ex )
|
||||
{
|
||||
ProxyServer.getInstance().getLogger().log( Level.WARNING, "Could not load location for player " + player.getName(), ex );
|
||||
}
|
||||
return server;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setServer(ProxiedPlayer player)
|
||||
{
|
||||
|
||||
try ( PreparedStatement ps = connection.prepareStatement( "UPDATE players SET server = ?, seen = ? WHERE username = ?" ) )
|
||||
{
|
||||
ps.setString( 1, player.getServer().getInfo().getName() );
|
||||
ps.setInt( 2, (int) ( System.currentTimeMillis() / 1000L ) );
|
||||
ps.setString( 3, player.getName() );
|
||||
ps.executeUpdate();
|
||||
} catch ( SQLException ex )
|
||||
{
|
||||
ProxyServer.getInstance().getLogger().log( Level.WARNING, "Could not save location for player " + player.getName() + " on server " + player.getServer().getInfo().getName(), ex );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save()
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close()
|
||||
{
|
||||
try
|
||||
{
|
||||
connection.close();
|
||||
} catch ( SQLException ex )
|
||||
{
|
||||
ProxyServer.getInstance().getLogger().log( Level.WARNING, "Error closing SQLite connection", ex );
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,114 @@
|
||||
package net.md_5.bungee.reconnect;
|
||||
|
||||
import net.md_5.bungee.api.AbstractReconnectHandler;
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.locks.ReadWriteLock;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
import java.util.logging.Level;
|
||||
import net.md_5.bungee.api.ProxyServer;
|
||||
import net.md_5.bungee.api.config.ServerInfo;
|
||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||
import org.yaml.snakeyaml.Yaml;
|
||||
import org.yaml.snakeyaml.error.YAMLException;
|
||||
|
||||
public class YamlReconnectHandler extends AbstractReconnectHandler
|
||||
{
|
||||
|
||||
private final Yaml yaml = new Yaml();
|
||||
private final File file = new File( "locations.yml" );
|
||||
private final ReadWriteLock lock = new ReentrantReadWriteLock();
|
||||
/*========================================================================*/
|
||||
private Map<String, String> data;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public YamlReconnectHandler()
|
||||
{
|
||||
try
|
||||
{
|
||||
file.createNewFile();
|
||||
try ( FileReader rd = new FileReader( file ) )
|
||||
{
|
||||
data = yaml.loadAs( rd, Map.class );
|
||||
}
|
||||
} catch ( YAMLException ex )
|
||||
{
|
||||
file.renameTo( new File( "locations.yml.old" ) );
|
||||
ProxyServer.getInstance().getLogger().log( Level.WARNING, "Could not load reconnect locations, resetting them" );
|
||||
} catch ( IOException ex )
|
||||
{
|
||||
ProxyServer.getInstance().getLogger().log( Level.WARNING, "Could not load reconnect locations", ex );
|
||||
}
|
||||
|
||||
if ( data == null )
|
||||
{
|
||||
data = new HashMap<>();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ServerInfo getStoredServer(ProxiedPlayer player)
|
||||
{
|
||||
ServerInfo server = null;
|
||||
lock.readLock().lock();
|
||||
try
|
||||
{
|
||||
server = ProxyServer.getInstance().getServerInfo( data.get( key( player ) ) );
|
||||
} finally
|
||||
{
|
||||
lock.readLock().unlock();
|
||||
}
|
||||
return server;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setServer(ProxiedPlayer player)
|
||||
{
|
||||
lock.writeLock().lock();
|
||||
try
|
||||
{
|
||||
data.put( key( player ), ( player.getReconnectServer() != null ) ? player.getReconnectServer().getName() : player.getServer().getInfo().getName() );
|
||||
} finally
|
||||
{
|
||||
lock.writeLock().unlock();
|
||||
}
|
||||
}
|
||||
|
||||
private String key(ProxiedPlayer player)
|
||||
{
|
||||
InetSocketAddress host = player.getPendingConnection().getVirtualHost();
|
||||
return player.getName() + ";" + host.getHostString() + ":" + host.getPort();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save()
|
||||
{
|
||||
Map<String, String> copy = new HashMap<>();
|
||||
lock.readLock().lock();
|
||||
try
|
||||
{
|
||||
copy.putAll( data );
|
||||
} finally
|
||||
{
|
||||
lock.readLock().unlock();
|
||||
}
|
||||
|
||||
try ( FileWriter wr = new FileWriter( file ) )
|
||||
{
|
||||
yaml.dump( copy, wr );
|
||||
} catch ( IOException ex )
|
||||
{
|
||||
ProxyServer.getInstance().getLogger().log( Level.WARNING, "Could not save reconnect locations", ex );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close()
|
||||
{
|
||||
}
|
||||
}
|
@@ -4,14 +4,16 @@ import com.google.common.base.Preconditions;
|
||||
import com.google.common.collect.HashMultimap;
|
||||
import com.google.common.collect.Multimap;
|
||||
import com.google.common.collect.Multimaps;
|
||||
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||
import gnu.trove.TCollections;
|
||||
import gnu.trove.map.TIntObjectMap;
|
||||
import gnu.trove.map.hash.TIntObjectHashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import net.md_5.bungee.BungeeCord;
|
||||
import net.md_5.bungee.api.plugin.Plugin;
|
||||
import net.md_5.bungee.api.scheduler.ScheduledTask;
|
||||
import net.md_5.bungee.api.scheduler.TaskScheduler;
|
||||
@@ -19,16 +21,22 @@ import net.md_5.bungee.api.scheduler.TaskScheduler;
|
||||
public class BungeeScheduler implements TaskScheduler
|
||||
{
|
||||
|
||||
private final ExecutorService s = Executors.newCachedThreadPool( new ThreadFactoryBuilder().setNameFormat( "Bungee Pool Thread #%1$d" ).build() );
|
||||
private final AtomicInteger taskCounter = new AtomicInteger();
|
||||
private final TIntObjectMap<BungeeTask> tasks = TCollections.synchronizedMap( new TIntObjectHashMap<BungeeTask>() );
|
||||
private final Multimap<Plugin, BungeeTask> tasksByPlugin = Multimaps.synchronizedMultimap( HashMultimap.<Plugin, BungeeTask>create() );
|
||||
|
||||
public void shutdown()
|
||||
{
|
||||
s.shutdown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancel(int id)
|
||||
{
|
||||
BungeeTask task = tasks.remove( id );
|
||||
task.cancel();
|
||||
tasksByPlugin.values().remove( task );
|
||||
task.getFuture().cancel( false );
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -61,21 +69,17 @@ public class BungeeScheduler implements TaskScheduler
|
||||
@Override
|
||||
public ScheduledTask schedule(Plugin owner, Runnable task, long delay, TimeUnit unit)
|
||||
{
|
||||
return prepare( owner, task ).setFuture( BungeeCord.getInstance().executors.schedule( task, delay, unit ) );
|
||||
return schedule( owner, task, delay, 0, unit );
|
||||
}
|
||||
|
||||
@Override
|
||||
public ScheduledTask schedule(Plugin owner, Runnable task, long delay, long period, TimeUnit unit)
|
||||
{
|
||||
return prepare( owner, task ).setFuture( BungeeCord.getInstance().executors.scheduleWithFixedDelay( task, delay, period, unit ) );
|
||||
}
|
||||
|
||||
private BungeeTask prepare(Plugin owner, Runnable task)
|
||||
{
|
||||
Preconditions.checkNotNull( owner, "owner" );
|
||||
Preconditions.checkNotNull( task, "task" );
|
||||
BungeeTask prepared = new BungeeTask( taskCounter.getAndIncrement(), owner, task );
|
||||
BungeeTask prepared = new BungeeTask( this, taskCounter.getAndIncrement(), owner, task, delay, period, unit );
|
||||
tasks.put( prepared.getId(), prepared );
|
||||
s.execute( prepared );
|
||||
return prepared;
|
||||
}
|
||||
}
|
||||
|
@@ -1,32 +1,81 @@
|
||||
package net.md_5.bungee.scheduler;
|
||||
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import lombok.AccessLevel;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.logging.Level;
|
||||
import lombok.Data;
|
||||
import lombok.Setter;
|
||||
import net.md_5.bungee.api.ProxyServer;
|
||||
import net.md_5.bungee.api.plugin.Plugin;
|
||||
import net.md_5.bungee.api.scheduler.ScheduledTask;
|
||||
|
||||
@Data
|
||||
public class BungeeTask implements ScheduledTask
|
||||
public class BungeeTask implements Runnable, ScheduledTask
|
||||
{
|
||||
|
||||
private final BungeeScheduler sched;
|
||||
private final int id;
|
||||
private final Plugin owner;
|
||||
private final Runnable task;
|
||||
@Setter(AccessLevel.NONE)
|
||||
private ScheduledFuture<?> future;
|
||||
//
|
||||
private final long delay;
|
||||
private final long period;
|
||||
private final AtomicBoolean running = new AtomicBoolean( true );
|
||||
|
||||
public BungeeTask(BungeeScheduler sched, int id, Plugin owner, Runnable task, long delay, long period, TimeUnit unit)
|
||||
{
|
||||
this.sched = sched;
|
||||
this.id = id;
|
||||
this.owner = owner;
|
||||
this.task = task;
|
||||
this.delay = unit.toMillis( delay );
|
||||
this.period = unit.toMillis( period );
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getDelay(TimeUnit unit)
|
||||
public void cancel()
|
||||
{
|
||||
return future.getDelay( unit );
|
||||
running.set( false );
|
||||
}
|
||||
|
||||
BungeeTask setFuture(ScheduledFuture<?> future)
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
this.future = future;
|
||||
return this;
|
||||
if ( delay > 0 )
|
||||
{
|
||||
try
|
||||
{
|
||||
Thread.sleep( delay );
|
||||
} catch ( InterruptedException ex )
|
||||
{
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
|
||||
while ( running.get() )
|
||||
{
|
||||
try
|
||||
{
|
||||
task.run();
|
||||
} catch ( Throwable t )
|
||||
{
|
||||
ProxyServer.getInstance().getLogger().log( Level.SEVERE, String.format( "Task %s encountered an exception", this ), t );
|
||||
}
|
||||
|
||||
// If we have a period of 0 or less, only run once
|
||||
if ( period <= 0 )
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
Thread.sleep( period );
|
||||
} catch ( InterruptedException ex )
|
||||
{
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
|
||||
sched.cancel( this );
|
||||
}
|
||||
}
|
||||
|
@@ -1,28 +0,0 @@
|
||||
package net.md_5.bungee.scheduler;
|
||||
|
||||
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.logging.Level;
|
||||
import net.md_5.bungee.api.ProxyServer;
|
||||
|
||||
public class BungeeThreadPool extends ScheduledThreadPoolExecutor
|
||||
{
|
||||
|
||||
public BungeeThreadPool(ThreadFactory threadFactory)
|
||||
{
|
||||
super( Integer.MAX_VALUE, threadFactory );
|
||||
setKeepAliveTime( 5, TimeUnit.MINUTES );
|
||||
allowCoreThreadTimeOut( true );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void afterExecute(Runnable r, Throwable t)
|
||||
{
|
||||
super.afterExecute( r, t );
|
||||
if ( t != null )
|
||||
{
|
||||
ProxyServer.getInstance().getLogger().log( Level.SEVERE, "Task caused exception whilst running", t );
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,5 +1,6 @@
|
||||
alert: \u00a78[\u00a74Alert\u00a78]\u00a7r
|
||||
already_connected: You are already connected to the server
|
||||
already_connected: \u00a7cYou are already connected to this server
|
||||
already_connecting: \u00a7cAlready connecting to this server!
|
||||
connect_kick: \u00a7cKicked whilst connecting to
|
||||
current_server: \u00a76You are currently connected to
|
||||
fallback_kick: \u00a7cCould not connect to default server, please try again later:
|
||||
@@ -9,8 +10,11 @@ mojang_fail: Error occurred while contacting login servers, are they down?
|
||||
no_permission: \u00a7cYou do not have permission to execute this command!
|
||||
no_server: \u00a7cThe specified server does not exist
|
||||
no_server_permission: \u00a7cYou don't have permission to access this server
|
||||
outdated_client: Outdated Client!
|
||||
outdated_server: Outdated Server!
|
||||
proxy_full: Server is full
|
||||
restart: [Proxy] Proxy restarting.
|
||||
server_kick: [Kicked]
|
||||
server_list: \u00a76You may connect to the following servers at this time:
|
||||
server_went_down: \u00a7cThe server you were previously on went down, you have been connected to the lobby
|
||||
total_players: Total players online:
|
||||
|
26
proxy/src/test/java/net/md_5/bungee/ThrottleTest.java
Normal file
26
proxy/src/test/java/net/md_5/bungee/ThrottleTest.java
Normal file
@@ -0,0 +1,26 @@
|
||||
package net.md_5.bungee;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
public class ThrottleTest
|
||||
{
|
||||
|
||||
@Test
|
||||
public void testThrottle() throws InterruptedException, UnknownHostException
|
||||
{
|
||||
ConnectionThrottle throttle = new ConnectionThrottle( 5 );
|
||||
InetAddress address = InetAddress.getLocalHost();
|
||||
|
||||
Assert.assertFalse( "Address should not be throttled", throttle.throttle( address ) );
|
||||
Assert.assertTrue( "Address should be throttled", throttle.throttle( address ) );
|
||||
|
||||
throttle.unthrottle( address );
|
||||
Assert.assertFalse( "Address should not be throttled", throttle.throttle( address ) );
|
||||
|
||||
Thread.sleep( 15 );
|
||||
Assert.assertFalse( "Address should not be throttled", throttle.throttle( address ) );
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user