Compare commits
212 Commits
Minecraft-
...
Minecraft-
Author | SHA1 | Date | |
---|---|---|---|
![]() |
09d04c34cb | ||
![]() |
3132d2c7cf | ||
![]() |
3682e8ba3a | ||
![]() |
82d5954f2f | ||
![]() |
384228b751 | ||
![]() |
50f8bd2eb0 | ||
![]() |
ff32764f9a | ||
![]() |
a1f928b210 | ||
![]() |
5bf616dc4d | ||
![]() |
0d7759f50b | ||
![]() |
824bdc5491 | ||
![]() |
ebff48ff86 | ||
![]() |
6efba44e5a | ||
![]() |
9f8ad518e8 | ||
![]() |
885a96c0c6 | ||
![]() |
5d0ff24f70 | ||
![]() |
ca5db43f70 | ||
![]() |
49a22f188f | ||
![]() |
ce7c095243 | ||
![]() |
cbb08ec58b | ||
![]() |
349949d154 | ||
![]() |
92e7faa346 | ||
![]() |
6b21fdaaea | ||
![]() |
fa9dd7e27f | ||
![]() |
f44cf6c8e9 | ||
![]() |
6bf9df31f5 | ||
![]() |
c08764990d | ||
![]() |
a82e6f3eea | ||
![]() |
26cee397e6 | ||
![]() |
dc6835c21b | ||
![]() |
be30c8b89a | ||
![]() |
70e10c382e | ||
![]() |
93ea108acb | ||
![]() |
a63739277b | ||
![]() |
112d543c2a | ||
![]() |
7338e20e98 | ||
![]() |
18a5534499 | ||
![]() |
e540626a28 | ||
![]() |
5e2bcc2907 | ||
![]() |
a38b3ce9f2 | ||
![]() |
c615b2362f | ||
![]() |
5620c4679d | ||
![]() |
a9ad4889f7 | ||
![]() |
7108bd4deb | ||
![]() |
ac1119bef8 | ||
![]() |
dac259933b | ||
![]() |
a57ae83d62 | ||
![]() |
a00c91c03a | ||
![]() |
a67d4a1697 | ||
![]() |
b87fff2614 | ||
![]() |
0c144c38db | ||
![]() |
5592f81e97 | ||
![]() |
5d1a2c59a7 | ||
![]() |
88c99f071e | ||
![]() |
19c3c23b86 | ||
![]() |
5dfe83cf6d | ||
![]() |
fecaf76acf | ||
![]() |
c8c1028bd9 | ||
![]() |
51be33dbd7 | ||
![]() |
8cac038a07 | ||
![]() |
edceaf072c | ||
![]() |
f935f93d98 | ||
![]() |
e5c457df04 | ||
![]() |
d4f60e65c3 | ||
![]() |
548b2f2c60 | ||
![]() |
3733ecf628 | ||
![]() |
6e9cdb2e20 | ||
![]() |
d900a5eae7 | ||
![]() |
68712ab854 | ||
![]() |
d067662967 | ||
![]() |
88a52bc4e9 | ||
![]() |
d684f5de69 | ||
![]() |
a316d6ccdc | ||
![]() |
5de8ac89e5 | ||
![]() |
8707995503 | ||
![]() |
caea1e3fa5 | ||
![]() |
4096012d8e | ||
![]() |
acbf6d3137 | ||
![]() |
3a3fb27d9a | ||
![]() |
e506957d38 | ||
![]() |
8003dc50c4 | ||
![]() |
75bc2738b9 | ||
![]() |
cf4846baa9 | ||
![]() |
120a4dc401 | ||
![]() |
6881597692 | ||
![]() |
0ffb557557 | ||
![]() |
5f7ecf9d3b | ||
![]() |
e07f1e603a | ||
![]() |
430b5ff392 | ||
![]() |
81fe547a7c | ||
![]() |
04fb1df3e1 | ||
![]() |
73aaf58009 | ||
![]() |
fd062503e1 | ||
![]() |
c97f113497 | ||
![]() |
14fcb90395 | ||
![]() |
39009d8c96 | ||
![]() |
13f394b9a7 | ||
![]() |
9838a09a8c | ||
![]() |
ffddcf939f | ||
![]() |
3d8143c36e | ||
![]() |
5e31b158e9 | ||
![]() |
a59e0f0b6b | ||
![]() |
517655f54e | ||
![]() |
55ec76beee | ||
![]() |
a564d4c7f1 | ||
![]() |
86b864ce21 | ||
![]() |
6813b82b84 | ||
![]() |
27d454524f | ||
![]() |
8827feacfb | ||
![]() |
692610cd7e | ||
![]() |
1edd27963f | ||
![]() |
730c05aaad | ||
![]() |
2bae6cafc2 | ||
![]() |
5a15d5387c | ||
![]() |
3fed94fbf2 | ||
![]() |
1dd661f619 | ||
![]() |
e364cff44e | ||
![]() |
dfa47f740d | ||
![]() |
87fcef0658 | ||
![]() |
49f2f5f28b | ||
![]() |
a12debf6d0 | ||
![]() |
5d3cb452fe | ||
![]() |
b3c34815b0 | ||
![]() |
40768c1711 | ||
![]() |
b16da7d048 | ||
![]() |
e681c8906d | ||
![]() |
ce40391717 | ||
![]() |
2e51ec4fba | ||
![]() |
54098c8989 | ||
![]() |
156ea30c32 | ||
![]() |
34f4bae923 | ||
![]() |
6bb089074e | ||
![]() |
70c73211a0 | ||
![]() |
c059345802 | ||
![]() |
f5b4e1242d | ||
![]() |
5365e5fb92 | ||
![]() |
dc2ef1eac7 | ||
![]() |
113dada511 | ||
![]() |
8ea5205fef | ||
![]() |
05d76c3f67 | ||
![]() |
d6e29b3f29 | ||
![]() |
bd479ba083 | ||
![]() |
704fe11b05 | ||
![]() |
4811e7be4f | ||
![]() |
ac426f0c3f | ||
![]() |
0f30024040 | ||
![]() |
5bfab582df | ||
![]() |
ec1de0f636 | ||
![]() |
9bf6a58ba2 | ||
![]() |
9483c0228b | ||
![]() |
09bb7a93d2 | ||
![]() |
8935e77118 | ||
![]() |
b71d253de2 | ||
![]() |
d54f2462a8 | ||
![]() |
c1ff4ffb89 | ||
![]() |
58f1ab208b | ||
![]() |
d5f25b07e5 | ||
![]() |
30b381853c | ||
![]() |
b0820208e6 | ||
![]() |
bc0a076e4b | ||
![]() |
1763dd3078 | ||
![]() |
373ec187fb | ||
![]() |
ac2c96c2ea | ||
![]() |
5688099605 | ||
![]() |
1d2afae98e | ||
![]() |
4805087e38 | ||
![]() |
e2f134ec08 | ||
![]() |
92c1450909 | ||
![]() |
fea3642550 | ||
![]() |
f02d17c979 | ||
![]() |
3dac86a94c | ||
![]() |
758e196479 | ||
![]() |
5dd3384b31 | ||
![]() |
59efec128d | ||
![]() |
3b90737273 | ||
![]() |
f9f664f9b6 | ||
![]() |
9edcda7ace | ||
![]() |
cd1420d9cd | ||
![]() |
a3e1493ce1 | ||
![]() |
9fb814003b | ||
![]() |
76319371f7 | ||
![]() |
2e33ab1460 | ||
![]() |
cfd10f5714 | ||
![]() |
6ae5feee60 | ||
![]() |
0b7b87d9c3 | ||
![]() |
fa5804ec86 | ||
![]() |
6e18403cd9 | ||
![]() |
f67ad024f5 | ||
![]() |
a0989a8932 | ||
![]() |
3be83cb5a4 | ||
![]() |
0ce02251d4 | ||
![]() |
b4d104d258 | ||
![]() |
0c69814db7 | ||
![]() |
f0766ebcfb | ||
![]() |
f0a19e0f45 | ||
![]() |
6525502836 | ||
![]() |
504f703fbb | ||
![]() |
4fb85721a9 | ||
![]() |
e12bc1d92e | ||
![]() |
bcaafc206f | ||
![]() |
8a96555cc7 | ||
![]() |
45c848a4fd | ||
![]() |
9e0ae0a70d | ||
![]() |
9ad9003974 | ||
![]() |
e18fe49cf9 | ||
![]() |
c3d702a5b3 | ||
![]() |
cdf26f7950 | ||
![]() |
644deee3c6 | ||
![]() |
b6e76f4054 | ||
![]() |
0f9cc76633 | ||
![]() |
0afc52c130 | ||
![]() |
0077af58d0 |
14
api/pom.xml
14
api/pom.xml
@@ -6,13 +6,13 @@
|
||||
<parent>
|
||||
<groupId>net.md-5</groupId>
|
||||
<artifactId>bungeecord-parent</artifactId>
|
||||
<version>1.4.7-SNAPSHOT</version>
|
||||
<version>1.5-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<groupId>net.md-5</groupId>
|
||||
<artifactId>bungeecord-api</artifactId>
|
||||
<version>1.4.7-SNAPSHOT</version>
|
||||
<version>1.5-SNAPSHOT</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>BungeeCord-API</name>
|
||||
@@ -22,13 +22,19 @@
|
||||
<dependency>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
<version>13.0.1</version>
|
||||
<version>14.0.1</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.ning</groupId>
|
||||
<artifactId>async-http-client</artifactId>
|
||||
<version>1.7.14</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.yaml</groupId>
|
||||
<artifactId>snakeyaml</artifactId>
|
||||
<version>1.11</version>
|
||||
<version>1.12</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* Copyright (C) 2007 The Guava Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.google.common.eventbus;
|
||||
|
||||
import com.google.common.base.Throwables;
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import com.google.common.cache.CacheLoader;
|
||||
import com.google.common.cache.LoadingCache;
|
||||
import com.google.common.collect.HashMultimap;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.Multimap;
|
||||
import com.google.common.reflect.TypeToken;
|
||||
import com.google.common.util.concurrent.UncheckedExecutionException;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* A {@link HandlerFindingStrategy} for collecting all event handler methods that are marked with
|
||||
* the {@link Subscribe} annotation.
|
||||
*
|
||||
* @author Cliff Biffle
|
||||
* @author Louis Wasserman
|
||||
*/
|
||||
class AnnotatedHandlerFinder implements HandlerFindingStrategy {
|
||||
/**
|
||||
* A thread-safe cache that contains the mapping from each class to all methods in that class and
|
||||
* all super-classes, that are annotated with {@code @Subscribe}. The cache is shared across all
|
||||
* instances of this class; this greatly improves performance if multiple EventBus instances are
|
||||
* created and objects of the same class are registered on all of them.
|
||||
*/
|
||||
private static final LoadingCache<Class<?>, ImmutableList<Method>> handlerMethodsCache =
|
||||
CacheBuilder.newBuilder()
|
||||
.weakKeys()
|
||||
.build(new CacheLoader<Class<?>, ImmutableList<Method>>() {
|
||||
@Override
|
||||
public ImmutableList<Method> load(Class<?> concreteClass) throws Exception {
|
||||
return getAnnotatedMethodsInternal(concreteClass);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* This implementation finds all methods marked with a {@link Subscribe} annotation.
|
||||
*/
|
||||
@Override
|
||||
public Multimap<Class<?>, EventHandler> findAllHandlers(Object listener) {
|
||||
Multimap<Class<?>, EventHandler> methodsInListener = HashMultimap.create();
|
||||
Class<?> clazz = listener.getClass();
|
||||
for (Method method : getAnnotatedMethods(clazz)) {
|
||||
Class<?>[] parameterTypes = method.getParameterTypes();
|
||||
Class<?> eventType = parameterTypes[0];
|
||||
EventHandler handler = new EventHandler(listener, method);
|
||||
methodsInListener.put(eventType, handler);
|
||||
}
|
||||
return methodsInListener;
|
||||
}
|
||||
|
||||
private static ImmutableList<Method> getAnnotatedMethods(Class<?> clazz) {
|
||||
try {
|
||||
return handlerMethodsCache.getUnchecked(clazz);
|
||||
} catch (UncheckedExecutionException e) {
|
||||
throw Throwables.propagate(e.getCause());
|
||||
}
|
||||
}
|
||||
|
||||
private static ImmutableList<Method> getAnnotatedMethodsInternal(Class<?> clazz) {
|
||||
Set<? extends Class<?>> supers = TypeToken.of(clazz).getTypes().rawTypes();
|
||||
ImmutableList.Builder<Method> result = ImmutableList.builder();
|
||||
for (Method method : clazz.getMethods()) {
|
||||
/*
|
||||
* Iterate over each distinct method of {@code clazz}, checking if it is annotated with
|
||||
* @Subscribe by any of the superclasses or superinterfaces that declare it.
|
||||
*/
|
||||
for (Class<?> c : supers) {
|
||||
try {
|
||||
Method m = c.getMethod(method.getName(), method.getParameterTypes());
|
||||
if (m.isAnnotationPresent(Subscribe.class)) {
|
||||
Class<?>[] parameterTypes = method.getParameterTypes();
|
||||
if (parameterTypes.length != 1) {
|
||||
throw new IllegalArgumentException("Method " + method
|
||||
+ " has @Subscribe annotation, but requires " + parameterTypes.length
|
||||
+ " arguments. Event handler methods must require a single argument.");
|
||||
}
|
||||
Class<?> eventType = parameterTypes[0];
|
||||
result.add(method);
|
||||
break;
|
||||
}
|
||||
} catch (NoSuchMethodException ignored) {
|
||||
// Move on.
|
||||
}
|
||||
}
|
||||
}
|
||||
return result.build();
|
||||
}
|
||||
}
|
@@ -1,5 +1,7 @@
|
||||
package net.md_5.bungee.api;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
@@ -105,13 +107,30 @@ public enum ChatColor
|
||||
* Pattern to remove all colour codes.
|
||||
*/
|
||||
private static final Pattern STRIP_COLOR_PATTERN = Pattern.compile( "(?i)" + String.valueOf( COLOR_CHAR ) + "[0-9A-FK-OR]" );
|
||||
/**
|
||||
* Colour instances keyed by their active character.
|
||||
*/
|
||||
private static final Map<Character, ChatColor> BY_CHAR = new HashMap<>();
|
||||
/**
|
||||
* The code appended to {@link #COLOR_CHAR} to make usable colour.
|
||||
*/
|
||||
private final char code;
|
||||
/**
|
||||
* This colour's colour char prefixed by the {@link #COLOR_CHAR}.
|
||||
*/
|
||||
private final String toString;
|
||||
|
||||
static
|
||||
{
|
||||
for ( ChatColor colour : values() )
|
||||
{
|
||||
BY_CHAR.put( colour.code, colour );
|
||||
}
|
||||
}
|
||||
|
||||
private ChatColor(char code)
|
||||
{
|
||||
this.code = code;
|
||||
this.toString = new String( new char[]
|
||||
{
|
||||
COLOR_CHAR, code
|
||||
@@ -153,4 +172,15 @@ public enum ChatColor
|
||||
}
|
||||
return new String( b );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the colour represented by the specified code.
|
||||
*
|
||||
* @param code the code to search for
|
||||
* @return the mapped colour, or null if non exists
|
||||
*/
|
||||
public static ChatColor getByChar(char code)
|
||||
{
|
||||
return BY_CHAR.get( code );
|
||||
}
|
||||
}
|
||||
|
@@ -19,6 +19,14 @@ public interface CommandSender
|
||||
*/
|
||||
public void sendMessage(String message);
|
||||
|
||||
/**
|
||||
* Send several messages to this sender. Each message will be sent
|
||||
* separately.
|
||||
*
|
||||
* @param messages the messages to send
|
||||
*/
|
||||
public void sendMessages(String... messages);
|
||||
|
||||
/**
|
||||
* Get all groups this user is part of. This returns an unmodifiable
|
||||
* collection.
|
||||
|
@@ -2,6 +2,8 @@ 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;
|
||||
import java.util.Map;
|
||||
@@ -12,6 +14,7 @@ import net.md_5.bungee.api.config.ServerInfo;
|
||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||
import net.md_5.bungee.api.connection.Server;
|
||||
import net.md_5.bungee.api.plugin.Plugin;
|
||||
import net.md_5.bungee.api.scheduler.TaskScheduler;
|
||||
|
||||
public abstract class ProxyServer
|
||||
{
|
||||
@@ -208,7 +211,40 @@ public abstract class ProxyServer
|
||||
*
|
||||
* @param name name of the server
|
||||
* @param address connectable Minecraft address + port of the server
|
||||
* @param restricted whether the server info restricted property will be set
|
||||
* @return the constructed instance
|
||||
*/
|
||||
public abstract ServerInfo constructServerInfo(String name, InetSocketAddress address);
|
||||
public abstract ServerInfo constructServerInfo(String name, InetSocketAddress address, boolean restricted);
|
||||
|
||||
/**
|
||||
* Returns the console overlord for this proxy. Being the console, this
|
||||
* command server cannot have permissions or groups, and will be able to
|
||||
* execute anything.
|
||||
*
|
||||
* @return the console command sender of this proxy
|
||||
*/
|
||||
public abstract CommandSender getConsole();
|
||||
|
||||
/**
|
||||
* Return the folder used to load plugins from.
|
||||
*
|
||||
* @return the folder used to load plugin
|
||||
*/
|
||||
public abstract File getPluginsFolder();
|
||||
|
||||
/**
|
||||
* Get the scheduler instance for this proxy.
|
||||
*
|
||||
* @return the in use scheduler
|
||||
*/
|
||||
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();
|
||||
}
|
||||
|
@@ -32,6 +32,11 @@ public class ListenerInfo
|
||||
* Name of the server which users will be taken to by default.
|
||||
*/
|
||||
private final String defaultServer;
|
||||
/**
|
||||
* Name of the server which users will be taken when current server goes
|
||||
* down.
|
||||
*/
|
||||
private final String fallbackServer;
|
||||
/**
|
||||
* Whether reconnect locations will be used, or else the user is simply
|
||||
* transferred to the default server on connect.
|
||||
@@ -42,4 +47,9 @@ 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;
|
||||
}
|
||||
|
@@ -1,69 +1,49 @@
|
||||
package net.md_5.bungee.api.config;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.Synchronized;
|
||||
import net.md_5.bungee.api.Callback;
|
||||
import net.md_5.bungee.api.CommandSender;
|
||||
import net.md_5.bungee.api.ServerPing;
|
||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||
|
||||
/**
|
||||
* Class used to represent a server to connect to.
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
public abstract class ServerInfo
|
||||
public interface ServerInfo
|
||||
{
|
||||
|
||||
/**
|
||||
* Name this server displays as.
|
||||
*/
|
||||
private final String name;
|
||||
/**
|
||||
* Connectable address of this server.
|
||||
*/
|
||||
private final InetSocketAddress address;
|
||||
/**
|
||||
* Players connected to a server defined by these properties.
|
||||
*/
|
||||
private final Collection<ProxiedPlayer> players = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Add a player to the internal set of this server.
|
||||
* Get the name of this server.
|
||||
*
|
||||
* @param player the player to add
|
||||
* @return the configured name for this server address
|
||||
*/
|
||||
@Synchronized("players")
|
||||
public void addPlayer(ProxiedPlayer player)
|
||||
{
|
||||
players.add( player );
|
||||
}
|
||||
String getName();
|
||||
|
||||
/**
|
||||
* Remove a player form the internal set of this server.
|
||||
* Gets the connectable host + port pair for this server. Implementations
|
||||
* expect this to be used as the unique identifier per each instance of this
|
||||
* class.
|
||||
*
|
||||
* @param player the player to remove
|
||||
* @return the IP and port pair for this server
|
||||
*/
|
||||
@Synchronized("players")
|
||||
public void removePlayer(ProxiedPlayer player)
|
||||
{
|
||||
players.remove( player );
|
||||
}
|
||||
InetSocketAddress getAddress();
|
||||
|
||||
/**
|
||||
* Get the set of all players on this server.
|
||||
*
|
||||
* @return an unmodifiable collection of all players on this server
|
||||
*/
|
||||
@Synchronized("players")
|
||||
public Collection<ProxiedPlayer> getPlayers()
|
||||
{
|
||||
return Collections.unmodifiableCollection( players );
|
||||
}
|
||||
Collection<ProxiedPlayer> getPlayers();
|
||||
|
||||
/**
|
||||
* Whether the player can access this server. It will only return false when
|
||||
* the player has no permission and this server is restricted.
|
||||
*
|
||||
* @param sender the player to check access for
|
||||
* @return whether access is granted to this server
|
||||
*/
|
||||
boolean canAccess(CommandSender sender);
|
||||
|
||||
/**
|
||||
* Send data by any available means to this server.
|
||||
@@ -71,12 +51,12 @@ public abstract class ServerInfo
|
||||
* @param channel the channel to send this data via
|
||||
* @param data the data to send
|
||||
*/
|
||||
public abstract void sendData(String channel, byte[] data);
|
||||
void sendData(String channel, byte[] data);
|
||||
|
||||
/**
|
||||
* Asynchronously gets the current player count on this server.
|
||||
*
|
||||
* @param callback the callback to call when the count has been retrieved.
|
||||
*/
|
||||
public abstract void ping(Callback<ServerPing> callback);
|
||||
void ping(Callback<ServerPing> callback);
|
||||
}
|
||||
|
@@ -0,0 +1,17 @@
|
||||
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;
|
||||
}
|
@@ -16,4 +16,14 @@ public interface Connection
|
||||
* @return the remote address
|
||||
*/
|
||||
public InetSocketAddress getAddress();
|
||||
|
||||
/**
|
||||
* Disconnects this end of the connection for the specified reason. If this
|
||||
* is an {@link ProxiedPlayer} the respective server connection will be
|
||||
* closed too.
|
||||
*
|
||||
* @param reason the reason shown to the player / sent to the server on
|
||||
* disconnect
|
||||
*/
|
||||
public void disconnect(String reason);
|
||||
}
|
||||
|
@@ -30,14 +30,6 @@ public interface PendingConnection extends Connection
|
||||
*/
|
||||
public InetSocketAddress getVirtualHost();
|
||||
|
||||
/**
|
||||
* Completely kick this user from the proxy and all of its child
|
||||
* connections.
|
||||
*
|
||||
* @param reason the disconnect reason displayed to the player
|
||||
*/
|
||||
public void disconnect(String reason);
|
||||
|
||||
/**
|
||||
* Get the listener that accepted this connection.
|
||||
*
|
||||
|
@@ -48,13 +48,6 @@ public interface ProxiedPlayer extends Connection, CommandSender
|
||||
*/
|
||||
public int getPing();
|
||||
|
||||
/**
|
||||
* Disconnect (remove) this player from the proxy with the specified reason.
|
||||
*
|
||||
* @param reason the reason displayed to the player
|
||||
*/
|
||||
public void disconnect(String reason);
|
||||
|
||||
/**
|
||||
* Send a plugin message to this player.
|
||||
*
|
||||
@@ -69,4 +62,11 @@ public interface ProxiedPlayer extends Connection, CommandSender
|
||||
* @return the pending connection that this player used
|
||||
*/
|
||||
public PendingConnection getPendingConnection();
|
||||
|
||||
/**
|
||||
* Make this player chat (say something), to the server he is currently on.
|
||||
*
|
||||
* @param message the message to say
|
||||
*/
|
||||
public void chat(String message);
|
||||
}
|
||||
|
@@ -1,7 +1,5 @@
|
||||
package net.md_5.bungee.api.connection;
|
||||
|
||||
import net.md_5.bungee.api.Callback;
|
||||
import net.md_5.bungee.api.ServerPing;
|
||||
import net.md_5.bungee.api.config.ServerInfo;
|
||||
|
||||
/**
|
||||
@@ -24,14 +22,4 @@ public interface Server extends Connection
|
||||
* @param data the data to send
|
||||
*/
|
||||
public abstract void sendData(String channel, byte[] data);
|
||||
|
||||
/**
|
||||
* Asynchronously gets the current player count on this server.
|
||||
*
|
||||
* @param callback the callback to call when the count has been retrieved.
|
||||
* @deprecated use the corresponding method in {@link ServerInfo} for
|
||||
* clarity
|
||||
*/
|
||||
@Deprecated
|
||||
public abstract void ping(Callback<ServerPing> callback);
|
||||
}
|
||||
|
75
api/src/main/java/net/md_5/bungee/api/event/AsyncEvent.java
Normal file
75
api/src/main/java/net/md_5/bungee/api/event/AsyncEvent.java
Normal file
@@ -0,0 +1,75 @@
|
||||
package net.md_5.bungee.api.event;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
import net.md_5.bungee.api.Callback;
|
||||
import net.md_5.bungee.api.plugin.Event;
|
||||
import net.md_5.bungee.api.plugin.Plugin;
|
||||
|
||||
/**
|
||||
* Represents an event which depends on the result of asynchronous operations.
|
||||
*
|
||||
* @param <T> Type of this event
|
||||
*/
|
||||
@Data
|
||||
@ToString(callSuper = true)
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class AsyncEvent<T> extends Event
|
||||
{
|
||||
|
||||
private final Callback<T> done;
|
||||
private final Set<Plugin> intents = Collections.newSetFromMap( new ConcurrentHashMap<Plugin, Boolean>() );
|
||||
private final AtomicBoolean fired = new AtomicBoolean();
|
||||
private final AtomicInteger latch = new AtomicInteger();
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public void postCall()
|
||||
{
|
||||
fired.set( true );
|
||||
if ( latch.get() == 0 )
|
||||
{
|
||||
done.done( (T) this, null );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register an intent that this plugin will continue to perform work on a
|
||||
* background task, and wishes to let the event proceed once the registered
|
||||
* background task has completed.
|
||||
*
|
||||
* @param plugin the plugin registering this intent
|
||||
*/
|
||||
public void registerIntent(Plugin plugin)
|
||||
{
|
||||
Preconditions.checkState( !fired.get(), "Event %s has already been fired", this );
|
||||
Preconditions.checkState( !intents.contains( plugin ), "Plugin %s already registered intent for event %s", plugin, this );
|
||||
|
||||
intents.add( plugin );
|
||||
latch.incrementAndGet();
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies this event that this plugin has done all its required processing
|
||||
* and wishes to let the event proceed.
|
||||
*
|
||||
* @param plugin a plugin which has an intent registered for this event
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public void completeIntent(Plugin plugin)
|
||||
{
|
||||
Preconditions.checkState( intents.contains( plugin ), "Plugin %s has not registered intent for event %s", plugin, this );
|
||||
intents.remove( plugin );
|
||||
if ( latch.decrementAndGet() == 0 )
|
||||
{
|
||||
done.done( (T) this, null );
|
||||
}
|
||||
}
|
||||
}
|
@@ -30,4 +30,14 @@ public class ChatEvent extends TargetedEvent implements Cancellable
|
||||
super( sender, receiver );
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether this message is valid as a command
|
||||
*
|
||||
* @return if this message is a command
|
||||
*/
|
||||
public boolean isCommand()
|
||||
{
|
||||
return message.length() > 0 && message.charAt( 0 ) == '/';
|
||||
}
|
||||
}
|
||||
|
@@ -3,9 +3,9 @@ package net.md_5.bungee.api.event;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
import net.md_5.bungee.api.Callback;
|
||||
import net.md_5.bungee.api.connection.PendingConnection;
|
||||
import net.md_5.bungee.api.plugin.Cancellable;
|
||||
import net.md_5.bungee.api.plugin.Event;
|
||||
|
||||
/**
|
||||
* Event called to represent a player logging in.
|
||||
@@ -13,7 +13,7 @@ import net.md_5.bungee.api.plugin.Event;
|
||||
@Data
|
||||
@ToString(callSuper = false)
|
||||
@EqualsAndHashCode(callSuper = false)
|
||||
public class LoginEvent extends Event implements Cancellable
|
||||
public class LoginEvent extends AsyncEvent<LoginEvent> implements Cancellable
|
||||
{
|
||||
|
||||
/**
|
||||
@@ -28,4 +28,10 @@ public class LoginEvent extends Event implements Cancellable
|
||||
* Connection attempting to login.
|
||||
*/
|
||||
private final PendingConnection connection;
|
||||
|
||||
public LoginEvent(PendingConnection connection, Callback<LoginEvent> done)
|
||||
{
|
||||
super( done );
|
||||
this.connection = connection;
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,40 @@
|
||||
package net.md_5.bungee.api.event;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
import lombok.ToString;
|
||||
import net.md_5.bungee.api.CommandSender;
|
||||
import net.md_5.bungee.api.plugin.Event;
|
||||
|
||||
/**
|
||||
* Called when the permission of a CommandSender is checked.
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@ToString(callSuper = false)
|
||||
@EqualsAndHashCode(callSuper = false)
|
||||
public class PermissionCheckEvent extends Event
|
||||
{
|
||||
|
||||
/**
|
||||
* The command sender being checked for a permission.
|
||||
*/
|
||||
private final CommandSender sender;
|
||||
/**
|
||||
* The permission to check.
|
||||
*/
|
||||
private final String permission;
|
||||
/**
|
||||
* The outcome of this permission check.
|
||||
*/
|
||||
@Getter(AccessLevel.NONE)
|
||||
private boolean hasPermission;
|
||||
|
||||
public boolean hasPermission()
|
||||
{
|
||||
return hasPermission;
|
||||
}
|
||||
}
|
@@ -0,0 +1,23 @@
|
||||
package net.md_5.bungee.api.event;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||
import net.md_5.bungee.api.plugin.Event;
|
||||
|
||||
/**
|
||||
* Event called as soon as a connection has an {@link ProxiedPlayer} and is
|
||||
* ready to be connected to a server.
|
||||
*/
|
||||
@Data
|
||||
@ToString(callSuper = false)
|
||||
@EqualsAndHashCode(callSuper = false)
|
||||
public class PostLoginEvent extends Event
|
||||
{
|
||||
|
||||
/**
|
||||
* The player involved with this event.
|
||||
*/
|
||||
private final ProxiedPlayer player;
|
||||
}
|
@@ -0,0 +1,43 @@
|
||||
package net.md_5.bungee.api.event;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
import net.md_5.bungee.api.config.ServerInfo;
|
||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||
import net.md_5.bungee.api.plugin.Cancellable;
|
||||
import net.md_5.bungee.api.plugin.Event;
|
||||
|
||||
/**
|
||||
* Represents a player getting kicked from a server.
|
||||
*/
|
||||
@Data
|
||||
@ToString(callSuper = false)
|
||||
@EqualsAndHashCode(callSuper = false)
|
||||
public class ServerKickEvent extends Event implements Cancellable
|
||||
{
|
||||
|
||||
/**
|
||||
* Cancelled status.
|
||||
*/
|
||||
private boolean cancelled;
|
||||
/**
|
||||
* Player being kicked.
|
||||
*/
|
||||
private final ProxiedPlayer player;
|
||||
/**
|
||||
* Kick reason.
|
||||
*/
|
||||
private String kickReason;
|
||||
/**
|
||||
* Server to send player to if this event is cancelled.
|
||||
*/
|
||||
private ServerInfo cancelServer;
|
||||
|
||||
public ServerKickEvent(ProxiedPlayer player, String kickReason, ServerInfo cancelServer)
|
||||
{
|
||||
this.player = player;
|
||||
this.kickReason = kickReason;
|
||||
this.cancelServer = cancelServer;
|
||||
}
|
||||
}
|
@@ -4,5 +4,12 @@ package net.md_5.bungee.api.plugin;
|
||||
* Dummy class which all callable events must extend.
|
||||
*/
|
||||
public abstract class Event
|
||||
{
|
||||
|
||||
/**
|
||||
* Method called after this event has been dispatched to all handlers.
|
||||
*/
|
||||
public void postCall()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
@@ -1,6 +1,9 @@
|
||||
package net.md_5.bungee.api.plugin;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
import lombok.Getter;
|
||||
import net.md_5.bungee.api.ProxyServer;
|
||||
import net.md_5.bungee.api.config.ConfigurationAdapter;
|
||||
|
||||
/**
|
||||
@@ -12,6 +15,10 @@ public class Plugin
|
||||
|
||||
@Getter
|
||||
private PluginDescription description;
|
||||
@Getter
|
||||
private ProxyServer proxy;
|
||||
@Getter
|
||||
private File file;
|
||||
|
||||
/**
|
||||
* Called when the plugin has just been loaded. Most of the proxy will not
|
||||
@@ -36,13 +43,40 @@ public class Plugin
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the data folder where this plugin may store arbitrary data. It will
|
||||
* be a child of {@link ProxyServer#getPluginsFolder()}.
|
||||
*
|
||||
* @return the data folder of this plugin
|
||||
*/
|
||||
public final File getDataFolder()
|
||||
{
|
||||
return new File( getProxy().getPluginsFolder(), getDescription().getName() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a resource from within this plugins jar or container. Care must be
|
||||
* taken to close the returned stream.
|
||||
*
|
||||
* @param name the full path name of this resource
|
||||
* @return the stream for getting this resource, or null if it does not
|
||||
* exist
|
||||
*/
|
||||
public final InputStream getResourceAsStream(String name)
|
||||
{
|
||||
return getClass().getClassLoader().getResourceAsStream( name );
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by the loader to initialize the fields in this plugin.
|
||||
*
|
||||
* @param description the description that describes this plugin
|
||||
* @param jarfile this plugins jar or container
|
||||
*/
|
||||
final void init(PluginDescription description)
|
||||
final void init(ProxyServer proxy, PluginDescription description, File file)
|
||||
{
|
||||
this.proxy = proxy;
|
||||
this.description = description;
|
||||
this.file = file;
|
||||
}
|
||||
}
|
||||
|
@@ -1,12 +1,16 @@
|
||||
package net.md_5.bungee.api.plugin;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* POJO representing the plugin.yml file.
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class PluginDescription
|
||||
{
|
||||
@@ -27,8 +31,8 @@ public class PluginDescription
|
||||
* Plugin author.
|
||||
*/
|
||||
private String author;
|
||||
|
||||
public PluginDescription()
|
||||
{
|
||||
}
|
||||
/**
|
||||
* Plugin hard dependencies.
|
||||
*/
|
||||
private Set<String> depends = new HashSet<>();
|
||||
}
|
||||
|
@@ -11,24 +11,30 @@ import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Stack;
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.jar.JarFile;
|
||||
import java.util.logging.Level;
|
||||
import java.util.regex.Pattern;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
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.event.LoginEvent;
|
||||
import org.yaml.snakeyaml.Yaml;
|
||||
|
||||
/**
|
||||
* Class to manage bridging between plugin duties and implementation duties, for
|
||||
* example event handling and plugin management.
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
public class PluginManager
|
||||
{
|
||||
|
||||
private static final Pattern argsSplit = Pattern.compile( " " );
|
||||
/*========================================================================*/
|
||||
private final ProxyServer proxy;
|
||||
/*========================================================================*/
|
||||
private final Yaml yaml = new Yaml();
|
||||
private final EventBus eventBus = new EventBus();
|
||||
private final Map<String, Plugin> plugins = new HashMap<>();
|
||||
@@ -37,9 +43,10 @@ public class PluginManager
|
||||
/**
|
||||
* Register a command so that it may be executed.
|
||||
*
|
||||
* @param plugin the plugin owning this command
|
||||
* @param command the command to register
|
||||
*/
|
||||
public void registerCommand(Command command)
|
||||
public void registerCommand(Plugin plugin, Command command)
|
||||
{
|
||||
commandMap.put( command.getName().toLowerCase(), command );
|
||||
for ( String alias : command.getAliases() )
|
||||
@@ -120,21 +127,87 @@ public class PluginManager
|
||||
*/
|
||||
public void enablePlugins()
|
||||
{
|
||||
Map<Plugin, Boolean> pluginStatuses = new HashMap<>();
|
||||
for ( Map.Entry<String, Plugin> entry : plugins.entrySet() )
|
||||
{
|
||||
Plugin plugin = entry.getValue();
|
||||
if ( !this.enablePlugin( pluginStatuses, new Stack<Plugin>(), plugin ) )
|
||||
{
|
||||
ProxyServer.getInstance().getLogger().warning( "Failed to enable " + entry.getKey() );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean enablePlugin(Map<Plugin, Boolean> pluginStatuses, Stack<Plugin> dependStack, Plugin plugin)
|
||||
{
|
||||
if ( pluginStatuses.containsKey( plugin ) )
|
||||
{
|
||||
return pluginStatuses.get( plugin );
|
||||
}
|
||||
|
||||
// success status
|
||||
boolean status = true;
|
||||
|
||||
// try to load dependencies first
|
||||
for ( String dependName : plugin.getDescription().getDepends() )
|
||||
{
|
||||
Plugin depend = this.plugins.get( dependName );
|
||||
Boolean dependStatus = depend != null ? pluginStatuses.get( depend ) : Boolean.FALSE;
|
||||
|
||||
if ( dependStatus == null )
|
||||
{
|
||||
if ( dependStack.contains( depend ) )
|
||||
{
|
||||
StringBuilder dependencyGraph = new StringBuilder();
|
||||
for ( Plugin element : dependStack )
|
||||
{
|
||||
dependencyGraph.append( element.getDescription().getName() ).append( " -> " );
|
||||
}
|
||||
dependencyGraph.append( plugin.getDescription().getName() ).append( " -> " ).append( dependName );
|
||||
ProxyServer.getInstance().getLogger().log( Level.WARNING, "Circular dependency detected: " + dependencyGraph );
|
||||
status = false;
|
||||
} else
|
||||
{
|
||||
dependStack.push( plugin );
|
||||
dependStatus = this.enablePlugin( pluginStatuses, dependStack, depend );
|
||||
dependStack.pop();
|
||||
}
|
||||
}
|
||||
|
||||
if ( dependStatus == Boolean.FALSE )
|
||||
{
|
||||
ProxyServer.getInstance().getLogger().log( Level.WARNING, "{0} (required by {1}) is unavailable", new Object[]
|
||||
{
|
||||
depend.getDescription().getName(), plugin.getDescription().getName()
|
||||
} );
|
||||
status = false;
|
||||
}
|
||||
|
||||
if ( !status )
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// do actual loading
|
||||
if ( status )
|
||||
{
|
||||
try
|
||||
{
|
||||
plugin.onEnable();
|
||||
ProxyServer.getInstance().getLogger().log( Level.INFO, "Enabled plugin {0} version {1} by {2}", new Object[]
|
||||
{
|
||||
entry.getKey(), plugin.getDescription().getVersion(), plugin.getDescription().getAuthor()
|
||||
plugin.getDescription().getName(), plugin.getDescription().getVersion(), plugin.getDescription().getAuthor()
|
||||
} );
|
||||
} catch ( Exception ex )
|
||||
} catch ( Throwable t )
|
||||
{
|
||||
ProxyServer.getInstance().getLogger().log( Level.WARNING, "Exception encountered when loading plugin: " + entry.getKey(), ex );
|
||||
ProxyServer.getInstance().getLogger().log( Level.WARNING, "Exception encountered when loading plugin: " + plugin.getDescription().getName(), t );
|
||||
status = false;
|
||||
}
|
||||
}
|
||||
|
||||
pluginStatuses.put( plugin, status );
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -165,7 +238,7 @@ public class PluginManager
|
||||
Class<?> main = loader.loadClass( desc.getMain() );
|
||||
Plugin plugin = (Plugin) main.getDeclaredConstructor().newInstance();
|
||||
|
||||
plugin.init( desc );
|
||||
plugin.init( proxy, desc, file );
|
||||
plugins.put( desc.getName(), plugin );
|
||||
plugin.onLoad();
|
||||
ProxyServer.getInstance().getLogger().log( Level.INFO, "Loaded plugin {0} version {1} by {2}", new Object[]
|
||||
@@ -211,7 +284,20 @@ public class PluginManager
|
||||
*/
|
||||
public <T extends Event> T callEvent(T event)
|
||||
{
|
||||
Preconditions.checkNotNull( event, "event" );
|
||||
|
||||
long start = System.nanoTime();
|
||||
eventBus.post( event );
|
||||
event.postCall();
|
||||
|
||||
long elapsed = start - System.nanoTime();
|
||||
if ( elapsed > 250000 )
|
||||
{
|
||||
ProxyServer.getInstance().getLogger().log( Level.WARNING, "Event {0} took more {1}ns to process!", new Object[]
|
||||
{
|
||||
event, elapsed
|
||||
} );
|
||||
}
|
||||
return event;
|
||||
}
|
||||
|
||||
@@ -220,9 +306,10 @@ public class PluginManager
|
||||
* Object which wish to receive events must be annotated with the
|
||||
* {@link Subscribe} annotation.
|
||||
*
|
||||
* @param plugin the owning plugin
|
||||
* @param listener the listener to register events for
|
||||
*/
|
||||
public void registerListener(Listener listener)
|
||||
public void registerListener(Plugin plugin, Listener listener)
|
||||
{
|
||||
eventBus.register( listener );
|
||||
}
|
||||
|
@@ -0,0 +1,41 @@
|
||||
package net.md_5.bungee.api.scheduler;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import net.md_5.bungee.api.plugin.Plugin;
|
||||
|
||||
/**
|
||||
* Represents a task scheduled for execution by the {@link TaskScheduler}.
|
||||
*/
|
||||
public interface ScheduledTask
|
||||
{
|
||||
|
||||
/**
|
||||
* Gets the unique ID of this task.
|
||||
*
|
||||
* @return this tasks ID
|
||||
*/
|
||||
int getId();
|
||||
|
||||
/**
|
||||
* Return the plugin which scheduled this task for execution.
|
||||
*
|
||||
* @return the owning plugin
|
||||
*/
|
||||
Plugin getOwner();
|
||||
|
||||
/**
|
||||
* Get the actual method which will be executed by this task.
|
||||
*
|
||||
* @return the {@link Runnable} behind this task
|
||||
*/
|
||||
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
|
||||
*/
|
||||
long getDelay(TimeUnit unit);
|
||||
}
|
@@ -0,0 +1,74 @@
|
||||
package net.md_5.bungee.api.scheduler;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import net.md_5.bungee.api.plugin.Plugin;
|
||||
|
||||
/**
|
||||
* This interface represents a scheduler which may be used to queue, delay and
|
||||
* execute tasks in an asynchronous fashion.
|
||||
*/
|
||||
public interface TaskScheduler
|
||||
{
|
||||
|
||||
/**
|
||||
* Cancel a task to prevent it from executing, or if its a repeating task,
|
||||
* prevent its further execution.
|
||||
*
|
||||
* @param id the id of the task to cancel
|
||||
*/
|
||||
void cancel(int id);
|
||||
|
||||
/**
|
||||
* Cancel a task to prevent it from executing, or if its a repeating task,
|
||||
* prevent its further execution.
|
||||
*
|
||||
* @param task the task to cancel
|
||||
*/
|
||||
void cancel(ScheduledTask task);
|
||||
|
||||
/**
|
||||
* Cancel all tasks owned by this plugin, this preventing them from being
|
||||
* executed hereon in.
|
||||
*
|
||||
* @param plugin the plugin owning the tasks to be cancelled
|
||||
* @return the number of tasks cancelled by this method
|
||||
*/
|
||||
int cancel(Plugin plugin);
|
||||
|
||||
/**
|
||||
* Schedule a task to be executed asynchronously. The task will commence
|
||||
* running as soon as this method returns.
|
||||
*
|
||||
* @param owner the plugin owning this task
|
||||
* @param task the task to run
|
||||
* @return the scheduled task
|
||||
*/
|
||||
ScheduledTask runAsync(Plugin owner, Runnable task);
|
||||
|
||||
/**
|
||||
* Schedules a task to be executed asynchronously after the specified delay
|
||||
* is up.
|
||||
*
|
||||
* @param owner the plugin owning this task
|
||||
* @param task the task to run
|
||||
* @param delay the delay before this task will be executed
|
||||
* @param unit the unit in which the delay will be measured
|
||||
* @return the scheduled task
|
||||
*/
|
||||
ScheduledTask schedule(Plugin owner, Runnable task, long delay, TimeUnit unit);
|
||||
|
||||
/**
|
||||
* Schedules a task to be executed asynchronously after the specified delay
|
||||
* is up. The scheduled task will continue running at the specified
|
||||
* interval. The interval will not begin to count down until the last task
|
||||
* invocation is complete.
|
||||
*
|
||||
* @param owner the plugin owning this task
|
||||
* @param task the task to run
|
||||
* @param delay the delay in milliseconds before this task will be executed
|
||||
* @param period the interval before subsequent executions of this task
|
||||
* @param unit the unit in which the delay and period will be measured
|
||||
* @return the scheduled task
|
||||
*/
|
||||
ScheduledTask schedule(Plugin owner, Runnable task, long delay, long period, TimeUnit unit);
|
||||
}
|
@@ -0,0 +1,20 @@
|
||||
package net.md_5.bungee.api.scoreboard;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* Represents an objective entry.
|
||||
*/
|
||||
@Data
|
||||
public class Objective
|
||||
{
|
||||
|
||||
/**
|
||||
* Name of the objective.
|
||||
*/
|
||||
private final String name;
|
||||
/**
|
||||
* Value of the objective.
|
||||
*/
|
||||
private final String value; // displayName
|
||||
}
|
@@ -0,0 +1,10 @@
|
||||
package net.md_5.bungee.api.scoreboard;
|
||||
|
||||
/**
|
||||
* Represents locations for a scoreboard to be displayed.
|
||||
*/
|
||||
public enum Position
|
||||
{
|
||||
|
||||
LIST, SIDEBAR, BELOW;
|
||||
}
|
24
api/src/main/java/net/md_5/bungee/api/scoreboard/Score.java
Normal file
24
api/src/main/java/net/md_5/bungee/api/scoreboard/Score.java
Normal file
@@ -0,0 +1,24 @@
|
||||
package net.md_5.bungee.api.scoreboard;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* Represents a scoreboard score entry.
|
||||
*/
|
||||
@Data
|
||||
public class Score
|
||||
{
|
||||
|
||||
/**
|
||||
* Name to be displayed in the list.
|
||||
*/
|
||||
private final String itemName; // Player
|
||||
/**
|
||||
* Unique name of the score.
|
||||
*/
|
||||
private final String scoreName; // Score
|
||||
/**
|
||||
* Value of the score.
|
||||
*/
|
||||
private final int value;
|
||||
}
|
101
api/src/main/java/net/md_5/bungee/api/scoreboard/Scoreboard.java
Normal file
101
api/src/main/java/net/md_5/bungee/api/scoreboard/Scoreboard.java
Normal file
@@ -0,0 +1,101 @@
|
||||
package net.md_5.bungee.api.scoreboard;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
public class Scoreboard
|
||||
{
|
||||
|
||||
/**
|
||||
* Unique name for this scoreboard.
|
||||
*/
|
||||
private String name;
|
||||
/**
|
||||
* Position of this scoreboard.
|
||||
*/
|
||||
private Position position;
|
||||
/**
|
||||
* Objectives for this scoreboard.
|
||||
*/
|
||||
private final Map<String, Objective> objectives = new HashMap<>();
|
||||
/**
|
||||
* Scores for this scoreboard.
|
||||
*/
|
||||
private final Map<String, Score> scores = new HashMap<>();
|
||||
/**
|
||||
* Teams on this board.
|
||||
*/
|
||||
private final Map<String, Team> teams = new HashMap<>();
|
||||
|
||||
public Collection<Objective> getObjectives()
|
||||
{
|
||||
return Collections.unmodifiableCollection( objectives.values() );
|
||||
}
|
||||
|
||||
public Collection<Score> getScores()
|
||||
{
|
||||
return Collections.unmodifiableCollection( scores.values() );
|
||||
}
|
||||
|
||||
public Collection<Team> getTeams()
|
||||
{
|
||||
return Collections.unmodifiableCollection( teams.values() );
|
||||
}
|
||||
|
||||
public void addObjective(Objective objective)
|
||||
{
|
||||
Preconditions.checkNotNull( objective, "objective" );
|
||||
Preconditions.checkArgument( !objectives.containsKey( objective.getName() ), "Objective %s already exists in this scoreboard", objective.getName() );
|
||||
objectives.put( objective.getName(), objective );
|
||||
}
|
||||
|
||||
public void addScore(Score score)
|
||||
{
|
||||
Preconditions.checkNotNull( score, "score" );
|
||||
Preconditions.checkArgument( !scores.containsKey( score.getItemName() ), "Score %s already exists in this scoreboard", score.getItemName() );
|
||||
scores.put( score.getItemName(), score );
|
||||
}
|
||||
|
||||
public void addTeam(Team team)
|
||||
{
|
||||
Preconditions.checkNotNull( team, "team" );
|
||||
Preconditions.checkArgument( !teams.containsKey( team.getName() ), "Team %s already exists in this scoreboard", team.getName() );
|
||||
teams.put( team.getName(), team );
|
||||
}
|
||||
|
||||
public Team getTeam(String name)
|
||||
{
|
||||
return teams.get( name );
|
||||
}
|
||||
|
||||
public void removeObjective(String objectiveName)
|
||||
{
|
||||
objectives.remove( objectiveName );
|
||||
}
|
||||
|
||||
public void removeScore(String scoreName)
|
||||
{
|
||||
scores.remove( scoreName );
|
||||
}
|
||||
|
||||
public void removeTeam(String teamName)
|
||||
{
|
||||
teams.remove( teamName );
|
||||
}
|
||||
|
||||
public void clear()
|
||||
{
|
||||
name = null;
|
||||
position = null;
|
||||
objectives.clear();
|
||||
scores.clear();
|
||||
teams.clear();
|
||||
}
|
||||
}
|
34
api/src/main/java/net/md_5/bungee/api/scoreboard/Team.java
Normal file
34
api/src/main/java/net/md_5/bungee/api/scoreboard/Team.java
Normal file
@@ -0,0 +1,34 @@
|
||||
package net.md_5.bungee.api.scoreboard;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class Team
|
||||
{
|
||||
|
||||
private final String name;
|
||||
private String displayName;
|
||||
private String prefix;
|
||||
private String suffix;
|
||||
private byte friendlyMode;
|
||||
private Set<String> players = new HashSet<>();
|
||||
|
||||
public Collection<String> getPlayers()
|
||||
{
|
||||
return Collections.unmodifiableSet( players );
|
||||
}
|
||||
|
||||
public void addPlayer(String name)
|
||||
{
|
||||
players.add( name );
|
||||
}
|
||||
|
||||
public void removePlayer(String name)
|
||||
{
|
||||
players.remove( name );
|
||||
}
|
||||
}
|
9
pom.xml
9
pom.xml
@@ -11,7 +11,7 @@
|
||||
|
||||
<groupId>net.md-5</groupId>
|
||||
<artifactId>bungeecord-parent</artifactId>
|
||||
<version>1.4.7-SNAPSHOT</version>
|
||||
<version>1.5-SNAPSHOT</version>
|
||||
<packaging>pom</packaging>
|
||||
|
||||
<name>BungeeCord</name>
|
||||
@@ -57,15 +57,16 @@
|
||||
</ciManagement>
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<build.number>unknown</build.number>
|
||||
<netty.version>4.0.0.CR1</netty.version>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>0.11.6</version>
|
||||
<version>0.11.8</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
@@ -92,7 +93,7 @@
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.0</version>
|
||||
<version>2.5.1</version>
|
||||
<configuration>
|
||||
<source>1.7</source>
|
||||
<target>1.7</target>
|
||||
|
@@ -6,15 +6,24 @@
|
||||
<parent>
|
||||
<groupId>net.md-5</groupId>
|
||||
<artifactId>bungeecord-parent</artifactId>
|
||||
<version>1.4.7-SNAPSHOT</version>
|
||||
<version>1.5-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<groupId>net.md-5</groupId>
|
||||
<artifactId>bungeecord-protocol</artifactId>
|
||||
<version>1.4.7-SNAPSHOT</version>
|
||||
<version>1.5-SNAPSHOT</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>BungeeCord-Protocol</name>
|
||||
<description>Minimal implementation of the Minecraft protocol for use in BungeeCord</description>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-buffer</artifactId>
|
||||
<version>${netty.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
|
@@ -1,19 +1,18 @@
|
||||
package net.md_5.mendax;
|
||||
package net.md_5.bungee.protocol;
|
||||
|
||||
import static net.md_5.mendax.PacketDefinitions.OpCode.*;
|
||||
import static net.md_5.bungee.protocol.PacketDefinitions.OpCode.*;
|
||||
|
||||
public class PacketDefinitions
|
||||
{
|
||||
|
||||
private static final int MAX_PACKET = 256;
|
||||
public static final OpCode[][] opCodes = new OpCode[ MAX_PACKET * 2 ][];
|
||||
public static final OpCode[][] opCodes = new OpCode[ 512 ][];
|
||||
public static final int VANILLA_PROTOCOL = 0;
|
||||
public static final int FORGE_PROTOCOL = MAX_PACKET;
|
||||
public static final int FORGE_PROTOCOL = 256;
|
||||
|
||||
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, SCORE, SHORT, SHORT_BYTE, SHORT_ITEM, STRING, TEAM, USHORT_BYTE
|
||||
}
|
||||
|
||||
static
|
||||
@@ -214,6 +213,10 @@ public class PacketDefinitions
|
||||
{
|
||||
STRING, INT, INT, INT, FLOAT, BYTE
|
||||
};
|
||||
opCodes[0x3F] = new OpCode[]
|
||||
{
|
||||
STRING, FLOAT, FLOAT, FLOAT, FLOAT, FLOAT, FLOAT, FLOAT, INT
|
||||
};
|
||||
opCodes[0x46] = new OpCode[]
|
||||
{
|
||||
BYTE, BYTE
|
||||
@@ -224,7 +227,7 @@ public class PacketDefinitions
|
||||
};
|
||||
opCodes[0x64] = new OpCode[]
|
||||
{
|
||||
BYTE, BYTE, STRING, BYTE
|
||||
BYTE, BYTE, STRING, BYTE, BOOLEAN
|
||||
};
|
||||
opCodes[0x65] = new OpCode[]
|
||||
{
|
||||
@@ -298,6 +301,22 @@ public class PacketDefinitions
|
||||
{
|
||||
BYTE
|
||||
};
|
||||
opCodes[0xCE] = new OpCode[]
|
||||
{
|
||||
STRING, STRING, BYTE
|
||||
};
|
||||
opCodes[0xCF] = new OpCode[]
|
||||
{
|
||||
SCORE
|
||||
};
|
||||
opCodes[0xD0] = new OpCode[]
|
||||
{
|
||||
BYTE, STRING
|
||||
};
|
||||
opCodes[0xD1] = new OpCode[]
|
||||
{
|
||||
TEAM
|
||||
};
|
||||
opCodes[0xFA] = new OpCode[]
|
||||
{
|
||||
STRING, SHORT_BYTE
|
||||
@@ -312,7 +331,8 @@ public class PacketDefinitions
|
||||
};
|
||||
opCodes[0xFE] = new OpCode[]
|
||||
{
|
||||
}; // Should be byte, screw you too bitchy server admins!
|
||||
BYTE
|
||||
};
|
||||
opCodes[0xFF] = new OpCode[]
|
||||
{
|
||||
STRING
|
@@ -1,17 +1,17 @@
|
||||
package net.md_5.mendax.datainput;
|
||||
package net.md_5.bungee.protocol.netty;
|
||||
|
||||
import java.io.DataInput;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import java.io.IOException;
|
||||
|
||||
public class BulkChunk extends Instruction
|
||||
{
|
||||
|
||||
@Override
|
||||
void read(DataInput in, byte[] buffer) throws IOException
|
||||
void read(ByteBuf in) throws IOException
|
||||
{
|
||||
short count = in.readShort();
|
||||
int size = in.readInt();
|
||||
in.readBoolean();
|
||||
skip( in, buffer, size + count * 12 );
|
||||
in.skipBytes( size + count * 12 );
|
||||
}
|
||||
}
|
@@ -1,6 +1,6 @@
|
||||
package net.md_5.mendax.datainput;
|
||||
package net.md_5.bungee.protocol.netty;
|
||||
|
||||
import java.io.DataInput;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import java.io.IOException;
|
||||
|
||||
class ByteHeader extends Instruction
|
||||
@@ -14,12 +14,12 @@ class ByteHeader extends Instruction
|
||||
}
|
||||
|
||||
@Override
|
||||
void read(DataInput in, byte[] buffer) throws IOException
|
||||
void read(ByteBuf in) throws IOException
|
||||
{
|
||||
byte size = in.readByte();
|
||||
for ( byte b = 0; b < size; b++ )
|
||||
{
|
||||
child.read( in, buffer );
|
||||
child.read( in );
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,6 +1,6 @@
|
||||
package net.md_5.mendax.datainput;
|
||||
package net.md_5.bungee.protocol.netty;
|
||||
|
||||
import java.io.DataInput;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import java.io.IOException;
|
||||
|
||||
abstract class Instruction
|
||||
@@ -19,18 +19,17 @@ abstract class Instruction
|
||||
static final Instruction LONG = new Jump( 8 );
|
||||
static final Instruction METADATA = new MetaData();
|
||||
static final Instruction OPTIONAL_MOTION = new OptionalMotion();
|
||||
static final Instruction SCORE = new Score();
|
||||
static final Instruction SHORT = new Jump( 2 );
|
||||
static final Instruction SHORT_BYTE = new ShortHeader( BYTE );
|
||||
static final Instruction SHORT_ITEM = new ShortHeader( ITEM );
|
||||
static final Instruction STRING = new ShortHeader( new Jump( 2 ) );
|
||||
static final Instruction TEAM = new Team();
|
||||
static final Instruction USHORT_BYTE = new UnsignedShortByte();
|
||||
// Illegal forward references below this line
|
||||
static final Instruction BYTE_INT = new ByteHeader( INT );
|
||||
// Custom instructions
|
||||
static final Instruction STRING_ARRAY = new ShortHeader( STRING );
|
||||
|
||||
abstract void read(DataInput in, byte[] buffer) throws IOException;
|
||||
|
||||
final void skip(DataInput in, byte[] buffer, int len) throws IOException
|
||||
{
|
||||
in.readFully( buffer, 0, len );
|
||||
}
|
||||
abstract void read(ByteBuf in) throws IOException;
|
||||
}
|
@@ -1,6 +1,6 @@
|
||||
package net.md_5.mendax.datainput;
|
||||
package net.md_5.bungee.protocol.netty;
|
||||
|
||||
import java.io.DataInput;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import java.io.IOException;
|
||||
|
||||
class IntHeader extends Instruction
|
||||
@@ -14,12 +14,12 @@ class IntHeader extends Instruction
|
||||
}
|
||||
|
||||
@Override
|
||||
void read(DataInput in, byte[] buffer) throws IOException
|
||||
void read(ByteBuf in) throws IOException
|
||||
{
|
||||
int size = in.readInt();
|
||||
for ( int i = 0; i < size; i++ )
|
||||
{
|
||||
child.read( in, buffer );
|
||||
child.read( in );
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,19 @@
|
||||
package net.md_5.bungee.protocol.netty;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import java.io.IOException;
|
||||
|
||||
class Item extends Instruction
|
||||
{
|
||||
|
||||
@Override
|
||||
void read(ByteBuf in) throws IOException
|
||||
{
|
||||
short type = in.readShort();
|
||||
if ( type >= 0 )
|
||||
{
|
||||
in.skipBytes( 3 );
|
||||
SHORT_BYTE.read( in );
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,6 +1,6 @@
|
||||
package net.md_5.mendax.datainput;
|
||||
package net.md_5.bungee.protocol.netty;
|
||||
|
||||
import java.io.DataInput;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import java.io.IOException;
|
||||
|
||||
class Jump extends Instruction
|
||||
@@ -18,8 +18,8 @@ class Jump extends Instruction
|
||||
}
|
||||
|
||||
@Override
|
||||
void read(DataInput in, byte[] buffer) throws IOException
|
||||
void read(ByteBuf in) throws IOException
|
||||
{
|
||||
skip( in, buffer, len );
|
||||
in.skipBytes( len );
|
||||
}
|
||||
}
|
@@ -1,13 +1,13 @@
|
||||
package net.md_5.mendax.datainput;
|
||||
package net.md_5.bungee.protocol.netty;
|
||||
|
||||
import java.io.DataInput;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import java.io.IOException;
|
||||
|
||||
class MetaData extends Instruction
|
||||
{
|
||||
|
||||
@Override
|
||||
void read(DataInput in, byte[] buffer) throws IOException
|
||||
void read(ByteBuf in) throws IOException
|
||||
{
|
||||
int x = in.readUnsignedByte();
|
||||
while ( x != 127 )
|
||||
@@ -16,25 +16,25 @@ class MetaData extends Instruction
|
||||
switch ( type )
|
||||
{
|
||||
case 0:
|
||||
BYTE.read( in, buffer );
|
||||
BYTE.read( in );
|
||||
break;
|
||||
case 1:
|
||||
SHORT.read( in, buffer );
|
||||
SHORT.read( in );
|
||||
break;
|
||||
case 2:
|
||||
INT.read( in, buffer );
|
||||
INT.read( in );
|
||||
break;
|
||||
case 3:
|
||||
FLOAT.read( in, buffer );
|
||||
FLOAT.read( in );
|
||||
break;
|
||||
case 4:
|
||||
STRING.read( in, buffer );
|
||||
STRING.read( in );
|
||||
break;
|
||||
case 5:
|
||||
ITEM.read( in, buffer );
|
||||
ITEM.read( in );
|
||||
break;
|
||||
case 6:
|
||||
skip( in, buffer, 12 ); // int, int, int
|
||||
in.skipBytes( 12 ); // int, int, int
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException( "Unknown metadata type " + type );
|
@@ -0,0 +1,18 @@
|
||||
package net.md_5.bungee.protocol.netty;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import java.io.IOException;
|
||||
|
||||
class OptionalMotion extends Instruction
|
||||
{
|
||||
|
||||
@Override
|
||||
void read(ByteBuf in) throws IOException
|
||||
{
|
||||
int data = in.readInt();
|
||||
if ( data > 0 )
|
||||
{
|
||||
in.skipBytes( 6 );
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,16 +1,16 @@
|
||||
package net.md_5.mendax.datainput;
|
||||
package net.md_5.bungee.protocol.netty;
|
||||
|
||||
import java.io.DataInput;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import net.md_5.mendax.PacketDefinitions;
|
||||
import net.md_5.mendax.PacketDefinitions.OpCode;
|
||||
import net.md_5.bungee.protocol.PacketDefinitions;
|
||||
import net.md_5.bungee.protocol.PacketDefinitions.OpCode;
|
||||
|
||||
public class DataInputPacketReader
|
||||
public class PacketReader
|
||||
{
|
||||
|
||||
private static final Instruction[][] instructions = new Instruction[ 256 ][];
|
||||
private static final Instruction[][] instructions = new Instruction[ PacketDefinitions.opCodes.length ][];
|
||||
|
||||
static
|
||||
{
|
||||
@@ -59,7 +59,7 @@ public class DataInputPacketReader
|
||||
}
|
||||
}
|
||||
|
||||
private static void readPacket(int packetId, DataInput in, byte[] buffer, int protocol) throws IOException
|
||||
private static void readPacket(int packetId, ByteBuf in, int protocol) throws IOException
|
||||
{
|
||||
Instruction[] packetDef = null;
|
||||
if ( packetId + protocol < instructions.length )
|
||||
@@ -74,20 +74,20 @@ public class DataInputPacketReader
|
||||
throw new IOException( "Unknown packet id " + packetId );
|
||||
} else
|
||||
{
|
||||
readPacket( packetId, in, buffer, PacketDefinitions.VANILLA_PROTOCOL );
|
||||
readPacket( packetId, in, PacketDefinitions.VANILLA_PROTOCOL );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
for ( Instruction instruction : packetDef )
|
||||
{
|
||||
instruction.read( in, buffer );
|
||||
instruction.read( in );
|
||||
}
|
||||
}
|
||||
|
||||
public static void readPacket(DataInput in, byte[] buffer, int protocol) throws IOException
|
||||
public static void readPacket(ByteBuf in, int protocol) throws IOException
|
||||
{
|
||||
int packetId = in.readUnsignedByte();
|
||||
readPacket( packetId, in, buffer, protocol );
|
||||
readPacket( packetId, in, protocol );
|
||||
}
|
||||
}
|
@@ -0,0 +1,19 @@
|
||||
package net.md_5.bungee.protocol.netty;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import java.io.IOException;
|
||||
|
||||
public class Score extends Instruction
|
||||
{
|
||||
|
||||
@Override
|
||||
void read(ByteBuf in) throws IOException
|
||||
{
|
||||
STRING.read( in );
|
||||
if ( in.readByte() == 0 )
|
||||
{
|
||||
STRING.read( in );
|
||||
INT.read( in );
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,6 +1,6 @@
|
||||
package net.md_5.mendax.datainput;
|
||||
package net.md_5.bungee.protocol.netty;
|
||||
|
||||
import java.io.DataInput;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import java.io.IOException;
|
||||
|
||||
class ShortHeader extends Instruction
|
||||
@@ -14,12 +14,12 @@ class ShortHeader extends Instruction
|
||||
}
|
||||
|
||||
@Override
|
||||
void read(DataInput in, byte[] buffer) throws IOException
|
||||
void read(ByteBuf in) throws IOException
|
||||
{
|
||||
short size = in.readShort();
|
||||
for ( short s = 0; s < size; s++ )
|
||||
{
|
||||
child.read( in, buffer );
|
||||
child.read( in );
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,26 @@
|
||||
package net.md_5.bungee.protocol.netty;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import java.io.IOException;
|
||||
|
||||
class Team extends Instruction
|
||||
{
|
||||
|
||||
@Override
|
||||
void read(ByteBuf in) throws IOException
|
||||
{
|
||||
STRING.read( in );
|
||||
byte mode = in.readByte();
|
||||
if ( mode == 0 || mode == 2 )
|
||||
{
|
||||
STRING.read( in );
|
||||
STRING.read( in );
|
||||
STRING.read( in );
|
||||
BYTE.read( in );
|
||||
}
|
||||
if ( mode == 0 || mode == 3 || mode == 4 )
|
||||
{
|
||||
STRING_ARRAY.read( in );
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,15 @@
|
||||
package net.md_5.bungee.protocol.netty;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import java.io.IOException;
|
||||
|
||||
class UnsignedShortByte extends Instruction
|
||||
{
|
||||
|
||||
@Override
|
||||
void read(ByteBuf in) throws IOException
|
||||
{
|
||||
int size = in.readUnsignedShort();
|
||||
in.skipBytes( size );
|
||||
}
|
||||
}
|
@@ -1,19 +0,0 @@
|
||||
package net.md_5.mendax.datainput;
|
||||
|
||||
import java.io.DataInput;
|
||||
import java.io.IOException;
|
||||
|
||||
class Item extends Instruction
|
||||
{
|
||||
|
||||
@Override
|
||||
void read(DataInput in, byte[] buffer) throws IOException
|
||||
{
|
||||
short type = in.readShort();
|
||||
if ( type >= 0 )
|
||||
{
|
||||
skip( in, buffer, 3 );
|
||||
SHORT_BYTE.read( in, buffer );
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,18 +0,0 @@
|
||||
package net.md_5.mendax.datainput;
|
||||
|
||||
import java.io.DataInput;
|
||||
import java.io.IOException;
|
||||
|
||||
public class OptionalMotion extends Instruction
|
||||
{
|
||||
|
||||
@Override
|
||||
void read(DataInput in, byte[] buffer) throws IOException
|
||||
{
|
||||
int data = in.readInt();
|
||||
if ( data > 0 )
|
||||
{
|
||||
skip( in, buffer, 6 );
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,15 +0,0 @@
|
||||
package net.md_5.mendax.datainput;
|
||||
|
||||
import java.io.DataInput;
|
||||
import java.io.IOException;
|
||||
|
||||
public class UnsignedShortByte extends Instruction
|
||||
{
|
||||
|
||||
@Override
|
||||
void read(DataInput in, byte[] buffer) throws IOException
|
||||
{
|
||||
int size = in.readUnsignedShort();
|
||||
skip( in, buffer, size );
|
||||
}
|
||||
}
|
@@ -6,39 +6,68 @@
|
||||
<parent>
|
||||
<groupId>net.md-5</groupId>
|
||||
<artifactId>bungeecord-parent</artifactId>
|
||||
<version>1.4.7-SNAPSHOT</version>
|
||||
<version>1.5-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<groupId>net.md-5</groupId>
|
||||
<artifactId>bungeecord-proxy</artifactId>
|
||||
<version>1.4.7-SNAPSHOT</version>
|
||||
<version>1.5-SNAPSHOT</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>BungeeCord-Proxy</name>
|
||||
<description>Proxy component of the Elastic Portal Suite</description>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-codec</artifactId>
|
||||
<version>${netty.version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-handler</artifactId>
|
||||
<version>${netty.version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.md-5</groupId>
|
||||
<artifactId>bungeecord-protocol</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.md-5</groupId>
|
||||
<artifactId>bungeecord-api</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.sf.trove4j</groupId>
|
||||
<artifactId>trove4j</artifactId>
|
||||
<version>3.0.3</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>mysql</groupId>
|
||||
<artifactId>mysql-connector-java</artifactId>
|
||||
<version>5.1.22</version>
|
||||
<version>5.1.24</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<finalName>BungeeCord</finalName>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<!-- Don't deploy proxy to maven repo, only APIs -->
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-deploy-plugin</artifactId>
|
||||
<configuration>
|
||||
<skip>true</skip>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
@@ -75,7 +104,6 @@
|
||||
</excludes>
|
||||
</filter>
|
||||
</filters>
|
||||
<finalName>${project.build.finalName}-shaded</finalName>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
|
@@ -1,29 +1,40 @@
|
||||
package net.md_5.bungee;
|
||||
|
||||
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 io.netty.bootstrap.ServerBootstrap;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelException;
|
||||
import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.ChannelFutureListener;
|
||||
import io.netty.channel.MultithreadEventLoopGroup;
|
||||
import io.netty.channel.nio.NioEventLoopGroup;
|
||||
import io.netty.channel.socket.nio.NioServerSocketChannel;
|
||||
import net.md_5.bungee.config.Configuration;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.Socket;
|
||||
import java.util.Calendar;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.Synchronized;
|
||||
import static net.md_5.bungee.Logger.$;
|
||||
import net.md_5.bungee.api.CommandSender;
|
||||
import net.md_5.bungee.api.ProxyServer;
|
||||
import net.md_5.bungee.api.ReconnectHandler;
|
||||
import net.md_5.bungee.api.TabListHandler;
|
||||
@@ -34,10 +45,13 @@ import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||
import net.md_5.bungee.api.connection.Server;
|
||||
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.command.*;
|
||||
import net.md_5.bungee.config.YamlConfig;
|
||||
import net.md_5.bungee.netty.PipelineUtils;
|
||||
import net.md_5.bungee.packet.DefinedPacket;
|
||||
import net.md_5.bungee.packet.PacketFAPluginMessage;
|
||||
import net.md_5.bungee.scheduler.BungeeThreadPool;
|
||||
|
||||
/**
|
||||
* Main BungeeCord proxy class.
|
||||
@@ -48,11 +62,11 @@ public class BungeeCord extends ProxyServer
|
||||
/**
|
||||
* Server protocol version.
|
||||
*/
|
||||
public static final byte PROTOCOL_VERSION = 51;
|
||||
public static final byte PROTOCOL_VERSION = 60;
|
||||
/**
|
||||
* Server game version.
|
||||
*/
|
||||
public static final String GAME_VERSION = "1.4.6";
|
||||
public static final String GAME_VERSION = "1.5";
|
||||
/**
|
||||
* Current operation state.
|
||||
*/
|
||||
@@ -62,9 +76,10 @@ public class BungeeCord extends ProxyServer
|
||||
*/
|
||||
public final Configuration config = new Configuration();
|
||||
/**
|
||||
* Thread pool.
|
||||
* Thread pools.
|
||||
*/
|
||||
public final ExecutorService threadPool = Executors.newCachedThreadPool();
|
||||
public final ScheduledThreadPoolExecutor executors = new BungeeThreadPool( new ThreadFactoryBuilder().setNameFormat( "Bungee Pool Thread #%1$d" ).build() );
|
||||
public final MultithreadEventLoopGroup eventLoops = new NioEventLoopGroup( 0, new ThreadFactoryBuilder().setNameFormat( "Netty IO Thread #%1$d" ).build() );
|
||||
/**
|
||||
* locations.yml save thread.
|
||||
*/
|
||||
@@ -72,7 +87,7 @@ public class BungeeCord extends ProxyServer
|
||||
/**
|
||||
* Server socket listener.
|
||||
*/
|
||||
private Collection<ListenThread> listeners = new HashSet<>();
|
||||
private Collection<Channel> listeners = new HashSet<>();
|
||||
/**
|
||||
* Fully qualified connections.
|
||||
*/
|
||||
@@ -87,7 +102,7 @@ public class BungeeCord extends ProxyServer
|
||||
* Plugin manager.
|
||||
*/
|
||||
@Getter
|
||||
public final PluginManager pluginManager = new PluginManager();
|
||||
public final PluginManager pluginManager = new PluginManager( this );
|
||||
@Getter
|
||||
@Setter
|
||||
private ReconnectHandler reconnectHandler;
|
||||
@@ -95,17 +110,28 @@ public class BungeeCord extends ProxyServer
|
||||
@Setter
|
||||
private ConfigurationAdapter configurationAdapter = new YamlConfig();
|
||||
private final Collection<String> pluginChannels = new HashSet<>();
|
||||
@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() ) );
|
||||
|
||||
|
||||
{
|
||||
getPluginManager().registerCommand( new CommandReload() );
|
||||
getPluginManager().registerCommand( new CommandEnd() );
|
||||
getPluginManager().registerCommand( new CommandList() );
|
||||
getPluginManager().registerCommand( new CommandServer() );
|
||||
getPluginManager().registerCommand( new CommandIP() );
|
||||
getPluginManager().registerCommand( new CommandAlert() );
|
||||
getPluginManager().registerCommand( new CommandBungee() );
|
||||
getPluginManager().registerCommand( new CommandPerms() );
|
||||
// TODO: Proper fallback when we interface the manager
|
||||
getPluginManager().registerCommand( null, new CommandReload() );
|
||||
getPluginManager().registerCommand( null, new CommandEnd() );
|
||||
getPluginManager().registerCommand( null, new CommandList() );
|
||||
getPluginManager().registerCommand( null, new CommandServer() );
|
||||
getPluginManager().registerCommand( null, new CommandIP() );
|
||||
getPluginManager().registerCommand( null, new CommandAlert() );
|
||||
getPluginManager().registerCommand( null, new CommandBungee() );
|
||||
getPluginManager().registerCommand( null, new CommandPerms() );
|
||||
getPluginManager().registerCommand( null, new CommandSend() );
|
||||
|
||||
registerChannel( "BungeeCord" );
|
||||
}
|
||||
@@ -119,13 +145,24 @@ public class BungeeCord extends ProxyServer
|
||||
* Starts a new instance of BungeeCord.
|
||||
*
|
||||
* @param args command line arguments, currently none are used
|
||||
* @throws IOException when the server cannot be started
|
||||
* @throws Exception when the server cannot be started
|
||||
*/
|
||||
public static void main(String[] args) throws IOException
|
||||
public static void main(String[] args) throws Exception
|
||||
{
|
||||
Calendar deadline = Calendar.getInstance();
|
||||
deadline.set( 2013, 5, 26 ); // year, month, date
|
||||
if ( Calendar.getInstance().after( deadline ) )
|
||||
{
|
||||
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 ) );
|
||||
}
|
||||
|
||||
BungeeCord bungee = new BungeeCord();
|
||||
ProxyServer.setInstance( bungee );
|
||||
$().info( "Enabled BungeeCord version " + bungee.getVersion() );
|
||||
bungee.getLogger().info( "Enabled BungeeCord version " + bungee.getVersion() );
|
||||
bungee.start();
|
||||
|
||||
BufferedReader br = new BufferedReader( new InputStreamReader( System.in ) );
|
||||
@@ -147,13 +184,13 @@ public class BungeeCord extends ProxyServer
|
||||
* Start this proxy instance by loading the configuration, plugins and
|
||||
* starting the connect thread.
|
||||
*
|
||||
* @throws IOException
|
||||
* @throws Exception
|
||||
*/
|
||||
public void start() throws IOException
|
||||
@Override
|
||||
public void start() throws Exception
|
||||
{
|
||||
File plugins = new File( "plugins" );
|
||||
plugins.mkdir();
|
||||
pluginManager.loadPlugins( plugins );
|
||||
pluginsFolder.mkdir();
|
||||
pluginManager.loadPlugins( pluginsFolder );
|
||||
config.load();
|
||||
if ( reconnectHandler == null )
|
||||
{
|
||||
@@ -179,34 +216,43 @@ public class BungeeCord extends ProxyServer
|
||||
|
||||
public void startListeners()
|
||||
{
|
||||
for ( ListenerInfo info : config.getListeners() )
|
||||
for ( final ListenerInfo info : config.getListeners() )
|
||||
{
|
||||
try
|
||||
new ServerBootstrap()
|
||||
.channel( NioServerSocketChannel.class )
|
||||
.childAttr( PipelineUtils.LISTENER, info )
|
||||
.childHandler( PipelineUtils.SERVER_CHILD )
|
||||
.group( eventLoops )
|
||||
.localAddress( info.getHost() )
|
||||
.bind().addListener( new ChannelFutureListener()
|
||||
{
|
||||
ListenThread listener = new ListenThread( info );
|
||||
listener.start();
|
||||
listeners.add( listener );
|
||||
$().info( "Listening on " + info.getHost() );
|
||||
} catch ( IOException ex )
|
||||
@Override
|
||||
public void operationComplete(ChannelFuture future) throws Exception
|
||||
{
|
||||
$().log( Level.SEVERE, "Could not start listener " + info, ex );
|
||||
if ( future.isSuccess() )
|
||||
{
|
||||
listeners.add( future.channel() );
|
||||
getLogger().info( "Listening on " + info.getHost() );
|
||||
} else
|
||||
{
|
||||
getLogger().log( Level.WARNING, "Could not bind to host " + info.getHost(), future.cause() );
|
||||
}
|
||||
}
|
||||
} );
|
||||
}
|
||||
}
|
||||
|
||||
public void stopListeners()
|
||||
{
|
||||
for ( ListenThread listener : listeners )
|
||||
for ( Channel listener : listeners )
|
||||
{
|
||||
$().log( Level.INFO, "Closing listen thread {0}", listener.socket );
|
||||
getLogger().log( Level.INFO, "Closing listener {0}", listener );
|
||||
try
|
||||
{
|
||||
listener.interrupt();
|
||||
listener.socket.close();
|
||||
listener.join();
|
||||
} catch ( InterruptedException | IOException ex )
|
||||
listener.close().syncUninterruptibly();
|
||||
} catch ( ChannelException ex )
|
||||
{
|
||||
$().severe( "Could not close listen thread" );
|
||||
getLogger().severe( "Could not close listen thread" );
|
||||
}
|
||||
}
|
||||
listeners.clear();
|
||||
@@ -217,44 +263,37 @@ public class BungeeCord extends ProxyServer
|
||||
{
|
||||
this.isRunning = false;
|
||||
|
||||
stopListeners();
|
||||
$().info( "Closing pending connections" );
|
||||
threadPool.shutdown();
|
||||
httpClient.close();
|
||||
executors.shutdown();
|
||||
|
||||
$().info( "Disconnecting " + connections.size() + " connections" );
|
||||
stopListeners();
|
||||
getLogger().info( "Closing pending connections" );
|
||||
|
||||
getLogger().info( "Disconnecting " + connections.size() + " connections" );
|
||||
for ( UserConnection user : connections.values() )
|
||||
{
|
||||
user.disconnect( "Proxy restarting, brb." );
|
||||
}
|
||||
|
||||
$().info( "Saving reconnect locations" );
|
||||
getLogger().info( "Closing IO threads" );
|
||||
eventLoops.shutdown();
|
||||
|
||||
getLogger().info( "Saving reconnect locations" );
|
||||
reconnectHandler.save();
|
||||
saveThread.cancel();
|
||||
|
||||
$().info( "Disabling plugins" );
|
||||
// TODO: Fix this shit
|
||||
getLogger().info( "Disabling plugins" );
|
||||
for ( Plugin plugin : pluginManager.getPlugins() )
|
||||
{
|
||||
plugin.onDisable();
|
||||
getScheduler().cancel( plugin );
|
||||
}
|
||||
|
||||
$().info( "Thank you and goodbye" );
|
||||
getLogger().info( "Thank you and goodbye" );
|
||||
System.exit( 0 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Miscellaneous method to set options on a socket based on those in the
|
||||
* configuration.
|
||||
*
|
||||
* @param socket to set the options on
|
||||
* @throws IOException when the underlying set methods thrown an exception
|
||||
*/
|
||||
public void setSocketOptions(Socket socket) throws IOException
|
||||
{
|
||||
socket.setSoTimeout( config.getTimeout() );
|
||||
socket.setTrafficClass( 0x18 );
|
||||
socket.setTcpNoDelay( true );
|
||||
}
|
||||
|
||||
/**
|
||||
* Broadcasts a packet to all clients that is connected to this instance.
|
||||
*
|
||||
@@ -264,7 +303,7 @@ public class BungeeCord extends ProxyServer
|
||||
{
|
||||
for ( UserConnection con : connections.values() )
|
||||
{
|
||||
con.packetQueue.add( packet );
|
||||
con.sendPacket( packet );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -283,7 +322,7 @@ public class BungeeCord extends ProxyServer
|
||||
@Override
|
||||
public Logger getLogger()
|
||||
{
|
||||
return $();
|
||||
return BungeeLogger.instance;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -364,8 +403,14 @@ public class BungeeCord extends ProxyServer
|
||||
}
|
||||
|
||||
@Override
|
||||
public ServerInfo constructServerInfo(String name, InetSocketAddress address)
|
||||
public ServerInfo constructServerInfo(String name, InetSocketAddress address, boolean restricted)
|
||||
{
|
||||
return new BungeeServerInfo( name, address );
|
||||
return new BungeeServerInfo( name, address, restricted );
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandSender getConsole()
|
||||
{
|
||||
return ConsoleCommandSender.getInstance();
|
||||
}
|
||||
}
|
||||
|
@@ -3,28 +3,28 @@ package net.md_5.bungee;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.text.MessageFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.logging.FileHandler;
|
||||
import java.util.logging.Formatter;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.LogRecord;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* Logger to handle formatting and storage of the proxy's logger.
|
||||
*/
|
||||
public class Logger extends java.util.logging.Logger
|
||||
public class BungeeLogger extends Logger
|
||||
{
|
||||
|
||||
private static final Formatter formatter = new ConsoleLogFormatter();
|
||||
private static final Logger instance = new Logger();
|
||||
static final BungeeLogger instance = new BungeeLogger();
|
||||
|
||||
public Logger()
|
||||
public BungeeLogger()
|
||||
{
|
||||
super( "RubberBand", null );
|
||||
super( "BungeeCord", null );
|
||||
try
|
||||
{
|
||||
FileHandler handler = new FileHandler( "proxy.log", 1 << 14, 1, true );
|
||||
FileHandler handler = new FileHandler( "proxy.log", 1 << 24, 8, true );
|
||||
handler.setFormatter( formatter );
|
||||
addHandler( handler );
|
||||
} catch ( IOException ex )
|
||||
@@ -48,16 +48,6 @@ public class Logger extends java.util.logging.Logger
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current logger instance.
|
||||
*
|
||||
* @return the current logger instance
|
||||
*/
|
||||
public static Logger $()
|
||||
{
|
||||
return instance;
|
||||
}
|
||||
|
||||
public static class ConsoleLogFormatter extends Formatter
|
||||
{
|
||||
|
@@ -1,31 +1,82 @@
|
||||
package net.md_5.bungee;
|
||||
|
||||
import java.io.DataOutputStream;
|
||||
import io.netty.bootstrap.Bootstrap;
|
||||
import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.ChannelFutureListener;
|
||||
import io.netty.channel.ChannelOption;
|
||||
import io.netty.channel.socket.nio.NioSocketChannel;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.Socket;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Objects;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.Synchronized;
|
||||
import net.md_5.bungee.api.Callback;
|
||||
import net.md_5.bungee.api.CommandSender;
|
||||
import net.md_5.bungee.api.ProxyServer;
|
||||
import net.md_5.bungee.api.ServerPing;
|
||||
import net.md_5.bungee.api.config.ServerInfo;
|
||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||
import net.md_5.bungee.api.connection.Server;
|
||||
import net.md_5.bungee.connection.PingHandler;
|
||||
import net.md_5.bungee.netty.HandlerBoss;
|
||||
import net.md_5.bungee.netty.PipelineUtils;
|
||||
import net.md_5.bungee.packet.DefinedPacket;
|
||||
import net.md_5.bungee.packet.PacketFAPluginMessage;
|
||||
import net.md_5.bungee.packet.PacketFFKick;
|
||||
import net.md_5.bungee.packet.PacketStream;
|
||||
import net.md_5.mendax.PacketDefinitions;
|
||||
|
||||
public class BungeeServerInfo extends ServerInfo
|
||||
@RequiredArgsConstructor
|
||||
public class BungeeServerInfo implements ServerInfo
|
||||
{
|
||||
|
||||
@Getter
|
||||
private final String name;
|
||||
@Getter
|
||||
private final InetSocketAddress address;
|
||||
private final Collection<ProxiedPlayer> players = new ArrayList<>();
|
||||
@Getter
|
||||
private final boolean restricted;
|
||||
@Getter
|
||||
private final Queue<DefinedPacket> packetQueue = new ConcurrentLinkedQueue<>();
|
||||
|
||||
public BungeeServerInfo(String name, InetSocketAddress address)
|
||||
@Synchronized("players")
|
||||
public void addPlayer(ProxiedPlayer player)
|
||||
{
|
||||
super( name, address );
|
||||
players.add( player );
|
||||
}
|
||||
|
||||
@Synchronized("players")
|
||||
public void removePlayer(ProxiedPlayer player)
|
||||
{
|
||||
players.remove( player );
|
||||
}
|
||||
|
||||
@Synchronized("players")
|
||||
@Override
|
||||
public Collection<ProxiedPlayer> getPlayers()
|
||||
{
|
||||
return Collections.unmodifiableCollection( players );
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canAccess(CommandSender player)
|
||||
{
|
||||
return !restricted || player.hasPermission( "bungeecord.server." + name );
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj)
|
||||
{
|
||||
return ( obj instanceof ServerInfo ) && Objects.equals( getAddress(), ( (ServerInfo) obj ).getAddress() );
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
return address.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -44,31 +95,26 @@ public class BungeeServerInfo extends ServerInfo
|
||||
@Override
|
||||
public void ping(final Callback<ServerPing> callback)
|
||||
{
|
||||
new Thread()
|
||||
new Bootstrap()
|
||||
.channel( NioSocketChannel.class )
|
||||
.group( BungeeCord.getInstance().eventLoops )
|
||||
.handler( PipelineUtils.BASE )
|
||||
.option( ChannelOption.CONNECT_TIMEOUT_MILLIS, 5000 ) // TODO: Configurable
|
||||
.remoteAddress( getAddress() )
|
||||
.connect()
|
||||
.addListener( new ChannelFutureListener()
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
public void operationComplete(ChannelFuture future) throws Exception
|
||||
{
|
||||
try ( Socket socket = new Socket(); )
|
||||
if ( future.isSuccess() )
|
||||
{
|
||||
socket.connect( getAddress() );
|
||||
|
||||
DataOutputStream out = new DataOutputStream( socket.getOutputStream() );
|
||||
out.write( 0xFE );
|
||||
out.write( 0x01 );
|
||||
|
||||
PacketStream in = new PacketStream( socket.getInputStream(), PacketDefinitions.VANILLA_PROTOCOL );
|
||||
PacketFFKick response = new PacketFFKick( in.readPacket() );
|
||||
|
||||
String[] split = response.message.split( "\00" );
|
||||
|
||||
ServerPing ping = new ServerPing( Byte.parseByte( split[1] ), split[2], split[3], Integer.parseInt( split[4] ), Integer.parseInt( split[5] ) );
|
||||
callback.done( ping, null );
|
||||
} catch ( Throwable t )
|
||||
future.channel().pipeline().get( HandlerBoss.class ).setHandler( new PingHandler( BungeeServerInfo.this, callback ) );
|
||||
} else
|
||||
{
|
||||
callback.done( null, t );
|
||||
callback.done( null, future.cause() );
|
||||
}
|
||||
}
|
||||
}.start();
|
||||
} );
|
||||
}
|
||||
}
|
||||
|
@@ -1,17 +1,11 @@
|
||||
package net.md_5.bungee;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.math.BigInteger;
|
||||
import java.net.URL;
|
||||
import java.net.URLEncoder;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.InvalidAlgorithmParameterException;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.Key;
|
||||
import java.security.KeyPair;
|
||||
import java.security.KeyPairGenerator;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Random;
|
||||
@@ -32,7 +26,7 @@ public class EncryptionUtil
|
||||
{
|
||||
|
||||
private static final Random random = new Random();
|
||||
private static KeyPair keys;
|
||||
public static KeyPair keys;
|
||||
|
||||
public static PacketFDEncryptionRequest encryptRequest() throws NoSuchAlgorithmException
|
||||
{
|
||||
@@ -66,31 +60,7 @@ public class EncryptionUtil
|
||||
return new SecretKeySpec( secret, "AES" );
|
||||
}
|
||||
|
||||
public static boolean isAuthenticated(String username, String connectionHash, SecretKey shared) throws NoSuchAlgorithmException, IOException
|
||||
{
|
||||
String encName = URLEncoder.encode( username, "UTF-8" );
|
||||
|
||||
MessageDigest sha = MessageDigest.getInstance( "SHA-1" );
|
||||
for ( byte[] bit : new byte[][]
|
||||
{
|
||||
connectionHash.getBytes( "ISO_8859_1" ), shared.getEncoded(), keys.getPublic().getEncoded()
|
||||
} )
|
||||
{
|
||||
sha.update( bit );
|
||||
}
|
||||
|
||||
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;
|
||||
String reply;
|
||||
try ( BufferedReader in = new BufferedReader( new InputStreamReader( new URL( authURL ).openStream() ) ) )
|
||||
{
|
||||
reply = in.readLine();
|
||||
}
|
||||
|
||||
return "YES".equals( reply );
|
||||
}
|
||||
|
||||
public static Cipher getCipher(int opMode, Key shared) throws InvalidAlgorithmParameterException, InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException
|
||||
public static Cipher getCipher(int opMode, Key shared) throws GeneralSecurityException
|
||||
{
|
||||
Cipher cip = Cipher.getInstance( "AES/CFB8/NoPadding" );
|
||||
cip.init( opMode, shared, new IvParameterSpec( shared.getEncoded() ) );
|
||||
|
@@ -40,7 +40,7 @@ public class EntityMap
|
||||
};
|
||||
entityIds[0x17] = new int[]
|
||||
{
|
||||
1, 20
|
||||
1 //, 20
|
||||
};
|
||||
entityIds[0x18] = new int[]
|
||||
{
|
||||
@@ -115,7 +115,7 @@ public class EntityMap
|
||||
|
||||
public static void rewrite(byte[] packet, int oldId, int newId)
|
||||
{
|
||||
int packetId = Util.getId( packet );
|
||||
int packetId = packet[0] & 0xFF;
|
||||
if ( packetId == 0x1D )
|
||||
{ // bulk entity
|
||||
for ( int pos = 2; pos < packet.length; pos += 4 )
|
||||
@@ -147,6 +147,17 @@ public class EntityMap
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( packetId == 0x17 )
|
||||
{
|
||||
int type = packet[5] & 0xFF;
|
||||
if ( type == 60 || type == 90 )
|
||||
{
|
||||
if ( readInt( packet, 20 ) == oldId )
|
||||
{
|
||||
setInt( packet, 20, newId );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void setInt(byte[] buf, int pos, int i)
|
||||
|
@@ -1,61 +0,0 @@
|
||||
package net.md_5.bungee;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.net.Socket;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import static net.md_5.bungee.Logger.$;
|
||||
import net.md_5.bungee.packet.PacketFFKick;
|
||||
import net.md_5.bungee.packet.PacketStream;
|
||||
|
||||
/**
|
||||
* Class to represent a Minecraft connection.
|
||||
*/
|
||||
@EqualsAndHashCode
|
||||
@RequiredArgsConstructor
|
||||
public class GenericConnection
|
||||
{
|
||||
|
||||
protected final Socket socket;
|
||||
protected final PacketStream stream;
|
||||
@Getter
|
||||
public String name;
|
||||
@Getter
|
||||
public String displayName;
|
||||
|
||||
/**
|
||||
* Close the socket with the specified reason.
|
||||
*
|
||||
* @param reason to disconnect
|
||||
*/
|
||||
public void disconnect(String reason)
|
||||
{
|
||||
if ( socket.isClosed() )
|
||||
{
|
||||
return;
|
||||
}
|
||||
log( "disconnected with " + reason );
|
||||
try
|
||||
{
|
||||
stream.write( new PacketFFKick( "[Proxy] " + reason ) );
|
||||
} catch ( IOException ex )
|
||||
{
|
||||
} finally
|
||||
{
|
||||
try
|
||||
{
|
||||
socket.shutdownOutput();
|
||||
socket.close();
|
||||
} catch ( IOException ioe )
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void log(String message)
|
||||
{
|
||||
$().info( socket.getInetAddress() + ( ( name == null ) ? " " : " [" + name + "] " ) + message );
|
||||
}
|
||||
}
|
@@ -1,232 +0,0 @@
|
||||
package net.md_5.bungee;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import java.io.EOFException;
|
||||
import java.io.IOException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.Socket;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.CipherInputStream;
|
||||
import javax.crypto.CipherOutputStream;
|
||||
import javax.crypto.SecretKey;
|
||||
import lombok.Getter;
|
||||
import net.md_5.bungee.api.ChatColor;
|
||||
import net.md_5.bungee.api.ProxyServer;
|
||||
import net.md_5.bungee.api.ServerPing;
|
||||
import net.md_5.bungee.api.config.ListenerInfo;
|
||||
import net.md_5.bungee.api.config.ServerInfo;
|
||||
import net.md_5.bungee.api.connection.PendingConnection;
|
||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||
import net.md_5.bungee.api.event.LoginEvent;
|
||||
import net.md_5.bungee.api.event.ProxyPingEvent;
|
||||
import net.md_5.bungee.packet.DefinedPacket;
|
||||
import net.md_5.bungee.packet.Packet1Login;
|
||||
import net.md_5.bungee.packet.Packet2Handshake;
|
||||
import net.md_5.bungee.packet.PacketCDClientStatus;
|
||||
import net.md_5.bungee.packet.PacketFAPluginMessage;
|
||||
import net.md_5.bungee.packet.PacketFCEncryptionResponse;
|
||||
import net.md_5.bungee.packet.PacketFDEncryptionRequest;
|
||||
import net.md_5.bungee.packet.PacketFEPing;
|
||||
import net.md_5.bungee.packet.PacketFFKick;
|
||||
import net.md_5.bungee.packet.PacketHandler;
|
||||
import net.md_5.bungee.packet.PacketStream;
|
||||
import net.md_5.mendax.PacketDefinitions;
|
||||
|
||||
public class InitialHandler extends PacketHandler implements Runnable, PendingConnection
|
||||
{
|
||||
|
||||
private final Socket socket;
|
||||
@Getter
|
||||
private final ListenerInfo listener;
|
||||
private PacketStream stream;
|
||||
private Packet1Login forgeLogin;
|
||||
private Packet2Handshake handshake;
|
||||
private PacketFDEncryptionRequest request;
|
||||
private List<PacketFAPluginMessage> loginMessages = new ArrayList<>();
|
||||
private State thisState = State.HANDSHAKE;
|
||||
private static final PacketFAPluginMessage forgeMods = new PacketFAPluginMessage( "FML", new byte[]
|
||||
{
|
||||
0, 0, 0, 0, 0, 2
|
||||
} );
|
||||
|
||||
public InitialHandler(Socket socket, ListenerInfo info) throws IOException
|
||||
{
|
||||
this.socket = socket;
|
||||
this.listener = info;
|
||||
stream = new PacketStream( socket.getInputStream(), socket.getOutputStream(), PacketDefinitions.VANILLA_PROTOCOL );
|
||||
}
|
||||
|
||||
private enum State
|
||||
{
|
||||
|
||||
HANDSHAKE, ENCRYPT, LOGIN, FINISHED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(Packet1Login login) throws Exception
|
||||
{
|
||||
Preconditions.checkState( thisState == State.LOGIN, "Not expecting FORGE LOGIN" );
|
||||
Preconditions.checkState( forgeLogin == null, "Already received FORGE LOGIN" );
|
||||
forgeLogin = login;
|
||||
stream.setProtocol( PacketDefinitions.FORGE_PROTOCOL );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(PacketFAPluginMessage pluginMessage) throws Exception
|
||||
{
|
||||
loginMessages.add( pluginMessage );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(PacketFEPing ping) throws Exception
|
||||
{
|
||||
socket.setSoTimeout( 100 );
|
||||
boolean newPing = false;
|
||||
try
|
||||
{
|
||||
socket.getInputStream().read();
|
||||
newPing = true;
|
||||
} catch ( IOException ex )
|
||||
{
|
||||
}
|
||||
|
||||
ServerPing pingevent = new ServerPing( BungeeCord.PROTOCOL_VERSION, BungeeCord.GAME_VERSION,
|
||||
listener.getMotd(), ProxyServer.getInstance().getPlayers().size(), listener.getMaxPlayers() );
|
||||
|
||||
pingevent = ProxyServer.getInstance().getPluginManager().callEvent( new ProxyPingEvent( this, pingevent ) ).getResponse();
|
||||
|
||||
String response = ( newPing ) ? ChatColor.COLOR_CHAR + "1"
|
||||
+ "\00" + pingevent.getProtocolVersion()
|
||||
+ "\00" + pingevent.getGameVersion()
|
||||
+ "\00" + pingevent.getMotd()
|
||||
+ "\00" + pingevent.getCurrentPlayers()
|
||||
+ "\00" + pingevent.getMaxPlayers()
|
||||
: pingevent.getMotd() + ChatColor.COLOR_CHAR + pingevent.getCurrentPlayers() + ChatColor.COLOR_CHAR + pingevent.getMaxPlayers();
|
||||
disconnect( response );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(Packet2Handshake handshake) throws Exception
|
||||
{
|
||||
Preconditions.checkState( thisState == State.HANDSHAKE, "Not expecting HANDSHAKE" );
|
||||
this.handshake = handshake;
|
||||
stream.write( forgeMods );
|
||||
stream.write( request = EncryptionUtil.encryptRequest() );
|
||||
thisState = State.ENCRYPT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(PacketFCEncryptionResponse encryptResponse) throws Exception
|
||||
{
|
||||
Preconditions.checkState( thisState == State.ENCRYPT, "Not expecting ENCRYPT" );
|
||||
|
||||
SecretKey shared = EncryptionUtil.getSecret( encryptResponse, request );
|
||||
if ( BungeeCord.getInstance().config.isOnlineMode() && !EncryptionUtil.isAuthenticated( handshake.username, request.serverId, shared ) )
|
||||
{
|
||||
throw new KickException( "Not authenticated with minecraft.net" );
|
||||
}
|
||||
|
||||
// Check for multiple connections
|
||||
ProxiedPlayer old = ProxyServer.getInstance().getPlayer( handshake.username );
|
||||
if ( old != null )
|
||||
{
|
||||
old.disconnect( "You are already connected to the server" );
|
||||
}
|
||||
|
||||
// fire login event
|
||||
LoginEvent event = new LoginEvent( this );
|
||||
ProxyServer.getInstance().getPluginManager().callEvent( event );
|
||||
if ( event.isCancelled() )
|
||||
{
|
||||
throw new KickException( event.getCancelReason() );
|
||||
}
|
||||
|
||||
stream.write( new PacketFCEncryptionResponse() );
|
||||
stream = new PacketStream( new CipherInputStream( socket.getInputStream(), EncryptionUtil.getCipher( Cipher.DECRYPT_MODE, shared ) ),
|
||||
new CipherOutputStream( socket.getOutputStream(), EncryptionUtil.getCipher( Cipher.ENCRYPT_MODE, shared ) ), stream.getProtocol() );
|
||||
|
||||
thisState = State.LOGIN;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(PacketCDClientStatus clientStatus) throws Exception
|
||||
{
|
||||
Preconditions.checkState( thisState == State.LOGIN, "Not expecting LOGIN" );
|
||||
|
||||
UserConnection userCon = new UserConnection( socket, this, stream, handshake, forgeLogin, loginMessages );
|
||||
ServerInfo server = ProxyServer.getInstance().getReconnectHandler().getServer( userCon );
|
||||
userCon.connect( server, true );
|
||||
|
||||
thisState = State.FINISHED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
try
|
||||
{
|
||||
while ( thisState != State.FINISHED )
|
||||
{
|
||||
byte[] buf = stream.readPacket();
|
||||
DefinedPacket packet = DefinedPacket.packet( buf );
|
||||
packet.handle( this );
|
||||
}
|
||||
} catch ( KickException ex )
|
||||
{
|
||||
disconnect( "[Proxy - Kicked] " + ex.getMessage() );
|
||||
} catch ( EOFException ex )
|
||||
{
|
||||
} catch ( Exception ex )
|
||||
{
|
||||
disconnect( "[Proxy Error] " + Util.exception( ex ) );
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disconnect(String reason)
|
||||
{
|
||||
thisState = State.FINISHED;
|
||||
try
|
||||
{
|
||||
stream.write( new PacketFFKick( reason ) );
|
||||
} catch ( IOException ioe )
|
||||
{
|
||||
} finally
|
||||
{
|
||||
try
|
||||
{
|
||||
socket.shutdownOutput();
|
||||
socket.close();
|
||||
} catch ( IOException ioe2 )
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName()
|
||||
{
|
||||
return ( handshake == null ) ? null : handshake.username;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte getVersion()
|
||||
{
|
||||
return ( handshake == null ) ? -1 : handshake.procolVersion;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InetSocketAddress getVirtualHost()
|
||||
{
|
||||
return ( handshake == null ) ? null : new InetSocketAddress( handshake.host, handshake.port );
|
||||
}
|
||||
|
||||
@Override
|
||||
public InetSocketAddress getAddress()
|
||||
{
|
||||
return (InetSocketAddress) socket.getRemoteSocketAddress();
|
||||
}
|
||||
}
|
@@ -1,14 +0,0 @@
|
||||
package net.md_5.bungee;
|
||||
|
||||
/**
|
||||
* Exception, which when thrown will disconnect the player from the proxy with
|
||||
* the specified message.
|
||||
*/
|
||||
public class KickException extends RuntimeException
|
||||
{
|
||||
|
||||
public KickException(String message)
|
||||
{
|
||||
super( message );
|
||||
}
|
||||
}
|
@@ -1,48 +0,0 @@
|
||||
package net.md_5.bungee;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.ServerSocket;
|
||||
import java.net.Socket;
|
||||
import java.net.SocketException;
|
||||
import static net.md_5.bungee.Logger.$;
|
||||
import net.md_5.bungee.api.config.ListenerInfo;
|
||||
|
||||
/**
|
||||
* Thread to listen and dispatch incoming connections to the proxy.
|
||||
*/
|
||||
public class ListenThread extends Thread
|
||||
{
|
||||
|
||||
public final ServerSocket socket;
|
||||
private final ListenerInfo info;
|
||||
|
||||
public ListenThread(ListenerInfo info) throws IOException
|
||||
{
|
||||
super( "Listen Thread - " + info );
|
||||
this.info = info;
|
||||
socket = new ServerSocket();
|
||||
socket.bind( info.getHost() );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
while ( !isInterrupted() )
|
||||
{
|
||||
try
|
||||
{
|
||||
Socket client = socket.accept();
|
||||
BungeeCord.getInstance().setSocketOptions( client );
|
||||
$().info( client.getInetAddress() + " has connected" );
|
||||
InitialHandler handler = new InitialHandler( client, info );
|
||||
BungeeCord.getInstance().threadPool.submit( handler );
|
||||
} catch ( SocketException ex )
|
||||
{
|
||||
ex.printStackTrace(); // Now people can see why their operating system is failing them and stop bitching at me!
|
||||
} catch ( IOException ex )
|
||||
{
|
||||
ex.printStackTrace(); // TODO
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -8,7 +8,6 @@ import java.io.UnsupportedEncodingException;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.net.URLEncoder;
|
||||
import static net.md_5.bungee.Logger.$;
|
||||
import net.md_5.bungee.api.ProxyServer;
|
||||
|
||||
public class Metrics extends Thread
|
||||
@@ -55,7 +54,7 @@ public class Metrics extends Thread
|
||||
firstPost = false;
|
||||
} catch ( IOException ex )
|
||||
{
|
||||
$().info( "[Metrics] " + ex.getMessage() );
|
||||
ProxyServer.getInstance().getLogger().info( "[Metrics] " + ex.getMessage() );
|
||||
}
|
||||
try
|
||||
{
|
||||
|
@@ -1,47 +1,52 @@
|
||||
package net.md_5.bungee;
|
||||
|
||||
import io.netty.channel.Channel;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.Socket;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import lombok.Getter;
|
||||
import net.md_5.bungee.api.Callback;
|
||||
import net.md_5.bungee.api.ServerPing;
|
||||
import net.md_5.bungee.api.config.ServerInfo;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import net.md_5.bungee.api.connection.Server;
|
||||
import net.md_5.bungee.packet.DefinedPacket;
|
||||
import net.md_5.bungee.netty.ChannelWrapper;
|
||||
import net.md_5.bungee.packet.Packet1Login;
|
||||
import net.md_5.bungee.packet.PacketFAPluginMessage;
|
||||
import net.md_5.bungee.packet.PacketStream;
|
||||
import net.md_5.bungee.packet.PacketFFKick;
|
||||
|
||||
/**
|
||||
* Class representing a connection from the proxy to the server; ie upstream.
|
||||
*/
|
||||
public class ServerConnection extends GenericConnection implements Server
|
||||
@RequiredArgsConstructor
|
||||
public class ServerConnection implements Server
|
||||
{
|
||||
|
||||
@Getter
|
||||
private final ServerInfo info;
|
||||
public final Packet1Login loginPacket;
|
||||
public Queue<DefinedPacket> packetQueue = new ConcurrentLinkedQueue<>();
|
||||
|
||||
public ServerConnection(Socket socket, ServerInfo info, PacketStream stream, Packet1Login loginPacket)
|
||||
{
|
||||
super( socket, stream );
|
||||
this.info = info;
|
||||
this.loginPacket = loginPacket;
|
||||
}
|
||||
private final ChannelWrapper ch;
|
||||
@Getter
|
||||
private final BungeeServerInfo info;
|
||||
@Getter
|
||||
private final Packet1Login loginPacket;
|
||||
@Getter
|
||||
@Setter
|
||||
private boolean isObsolete;
|
||||
|
||||
@Override
|
||||
public void sendData(String channel, byte[] data)
|
||||
{
|
||||
packetQueue.add( new PacketFAPluginMessage( channel, data ) );
|
||||
ch.write( new PacketFAPluginMessage( channel, data ) );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void ping(final Callback<ServerPing> callback)
|
||||
public synchronized void disconnect(String reason)
|
||||
{
|
||||
getInfo().ping( callback );
|
||||
if ( ch.getHandle().isActive() )
|
||||
{
|
||||
ch.write( new PacketFFKick( reason ) );
|
||||
ch.getHandle().eventLoop().schedule( new Runnable()
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
ch.getHandle().close();
|
||||
}
|
||||
}, 100, TimeUnit.MILLISECONDS );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@@ -1,45 +1,157 @@
|
||||
package net.md_5.bungee;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import java.io.IOException;
|
||||
import java.net.Socket;
|
||||
import com.google.common.io.ByteArrayDataOutput;
|
||||
import com.google.common.io.ByteStreams;
|
||||
import java.util.Objects;
|
||||
import java.util.Queue;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
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.event.ServerConnectedEvent;
|
||||
import net.md_5.bungee.api.event.ServerKickEvent;
|
||||
import net.md_5.bungee.api.scoreboard.Objective;
|
||||
import net.md_5.bungee.api.scoreboard.Scoreboard;
|
||||
import net.md_5.bungee.api.scoreboard.Team;
|
||||
import net.md_5.bungee.connection.CancelSendSignal;
|
||||
import net.md_5.bungee.connection.DownstreamBridge;
|
||||
import net.md_5.bungee.netty.HandlerBoss;
|
||||
import net.md_5.bungee.netty.ChannelWrapper;
|
||||
import net.md_5.bungee.packet.DefinedPacket;
|
||||
import net.md_5.bungee.packet.Packet1Login;
|
||||
import net.md_5.bungee.packet.Packet9Respawn;
|
||||
import net.md_5.bungee.packet.PacketCDClientStatus;
|
||||
import net.md_5.bungee.packet.PacketCEScoreboardObjective;
|
||||
import net.md_5.bungee.packet.PacketD1Team;
|
||||
import net.md_5.bungee.packet.PacketFAPluginMessage;
|
||||
import net.md_5.bungee.packet.PacketFDEncryptionRequest;
|
||||
import net.md_5.bungee.packet.PacketFFKick;
|
||||
import net.md_5.bungee.packet.PacketHandler;
|
||||
import net.md_5.bungee.packet.PacketStream;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class ServerConnector extends PacketHandler
|
||||
{
|
||||
|
||||
private final PacketStream stream;
|
||||
private Packet1Login loginPacket;
|
||||
private final ProxyServer bungee;
|
||||
private ChannelWrapper ch;
|
||||
private final UserConnection user;
|
||||
private final BungeeServerInfo target;
|
||||
private State thisState = State.ENCRYPT_REQUEST;
|
||||
|
||||
public ServerConnector(PacketStream stream)
|
||||
{
|
||||
this.stream = stream;
|
||||
}
|
||||
|
||||
private enum State
|
||||
{
|
||||
|
||||
ENCRYPT_REQUEST, LOGIN, FINISHED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void connected(ChannelWrapper channel) throws Exception
|
||||
{
|
||||
this.ch = channel;
|
||||
|
||||
ByteArrayDataOutput out = ByteStreams.newDataOutput();
|
||||
out.writeUTF( "Login" );
|
||||
out.writeUTF( user.getAddress().getAddress().getHostAddress() );
|
||||
out.writeInt( user.getAddress().getPort() );
|
||||
channel.write( new PacketFAPluginMessage( "BungeeCord", out.toByteArray() ) );
|
||||
|
||||
channel.write( user.getPendingConnection().getHandshake() );
|
||||
channel.write( PacketCDClientStatus.CLIENT_LOGIN );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disconnected(ChannelWrapper channel) throws Exception
|
||||
{
|
||||
user.getPendingConnects().remove( target );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(Packet1Login login) throws Exception
|
||||
{
|
||||
Preconditions.checkState( thisState == State.LOGIN, "Not exepcting LOGIN" );
|
||||
loginPacket = login;
|
||||
|
||||
ServerConnection server = new ServerConnection( ch, target, login );
|
||||
ServerConnectedEvent event = new ServerConnectedEvent( user, server );
|
||||
bungee.getPluginManager().callEvent( event );
|
||||
|
||||
ch.write( BungeeCord.getInstance().registerChannels() );
|
||||
|
||||
// TODO: Race conditions with many connects
|
||||
Queue<DefinedPacket> packetQueue = ( (BungeeServerInfo) target ).getPacketQueue();
|
||||
while ( !packetQueue.isEmpty() )
|
||||
{
|
||||
ch.write( packetQueue.poll() );
|
||||
}
|
||||
if ( user.getSettings() != null )
|
||||
{
|
||||
ch.write( user.getSettings() );
|
||||
}
|
||||
|
||||
synchronized ( user.getSwitchMutex() )
|
||||
{
|
||||
if ( user.getServer() == null )
|
||||
{
|
||||
// Once again, first connection
|
||||
user.setClientEntityId( login.entityId );
|
||||
user.setServerEntityId( login.entityId );
|
||||
// Set tab list size
|
||||
Packet1Login modLogin = new Packet1Login(
|
||||
login.entityId,
|
||||
login.levelType,
|
||||
login.gameMode,
|
||||
(byte) login.dimension,
|
||||
login.difficulty,
|
||||
login.unused,
|
||||
(byte) user.getPendingConnection().getListener().getTabListSize() );
|
||||
user.sendPacket( modLogin );
|
||||
} else
|
||||
{
|
||||
bungee.getTabListHandler().onServerChange( user );
|
||||
|
||||
Scoreboard serverScoreboard = user.getServerSentScoreboard();
|
||||
for ( Objective objective : serverScoreboard.getObjectives() )
|
||||
{
|
||||
user.sendPacket( new PacketCEScoreboardObjective( objective.getName(), objective.getValue(), (byte) 1 ) );
|
||||
}
|
||||
for ( Team team : serverScoreboard.getTeams() )
|
||||
{
|
||||
user.sendPacket( PacketD1Team.destroy( team.getName() ) );
|
||||
}
|
||||
serverScoreboard.clear();
|
||||
|
||||
user.sendPacket( Packet9Respawn.DIM1_SWITCH );
|
||||
user.sendPacket( Packet9Respawn.DIM2_SWITCH );
|
||||
|
||||
user.setServerEntityId( login.entityId );
|
||||
user.sendPacket( new Packet9Respawn( login.dimension, login.difficulty, login.gameMode, (short) 256, login.levelType ) );
|
||||
|
||||
// Remove from old servers
|
||||
user.getServer().setObsolete( true );
|
||||
user.getServer().disconnect( "Quitting" );
|
||||
}
|
||||
|
||||
// TODO: Fix this?
|
||||
if ( !user.isActive() )
|
||||
{
|
||||
server.disconnect( "Quitting" );
|
||||
// Silly server admins see stack trace and die
|
||||
bungee.getLogger().warning( "No client connected for pending server!" );
|
||||
return;
|
||||
}
|
||||
|
||||
// Add to new server
|
||||
// TODO: Move this to the connected() method of DownstreamBridge
|
||||
target.addPlayer( user );
|
||||
user.getPendingConnects().remove( target );
|
||||
|
||||
user.setServer( server );
|
||||
ch.getHandle().pipeline().get( HandlerBoss.class ).setHandler( new DownstreamBridge( bungee, user, server ) );
|
||||
}
|
||||
|
||||
thisState = State.FINISHED;
|
||||
|
||||
throw new CancelSendSignal();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -52,66 +164,31 @@ public class ServerConnector extends PacketHandler
|
||||
@Override
|
||||
public void handle(PacketFFKick kick) throws Exception
|
||||
{
|
||||
throw new KickException( kick.message );
|
||||
ServerInfo def = bungee.getServerInfo( user.getPendingConnection().getListener().getFallbackServer() );
|
||||
if ( Objects.equals( target, def ) )
|
||||
{
|
||||
def = null;
|
||||
}
|
||||
ServerKickEvent event = bungee.getPluginManager().callEvent( new ServerKickEvent( user, kick.message, def ) );
|
||||
if ( event.isCancelled() && event.getCancelServer() != null )
|
||||
{
|
||||
user.connect( event.getCancelServer() );
|
||||
return;
|
||||
}
|
||||
|
||||
public static ServerConnection connect(UserConnection user, ServerInfo info, boolean retry)
|
||||
String message = ChatColor.RED + "Kicked whilst connecting to " + target.getName() + ": " + kick.message;
|
||||
if ( user.getServer() == null )
|
||||
{
|
||||
Socket socket = null;
|
||||
try
|
||||
{
|
||||
socket = new Socket();
|
||||
socket.connect( info.getAddress(), BungeeCord.getInstance().config.getTimeout() );
|
||||
BungeeCord.getInstance().setSocketOptions( socket );
|
||||
PacketStream stream = new PacketStream( socket.getInputStream(), socket.getOutputStream(), user.stream.getProtocol() );
|
||||
|
||||
ServerConnector connector = new ServerConnector( stream );
|
||||
stream.write( user.handshake );
|
||||
stream.write( PacketCDClientStatus.CLIENT_LOGIN );
|
||||
|
||||
while ( connector.thisState != State.FINISHED )
|
||||
{
|
||||
byte[] buf = stream.readPacket();
|
||||
DefinedPacket packet = DefinedPacket.packet( buf );
|
||||
packet.handle( connector );
|
||||
}
|
||||
|
||||
ServerConnection server = new ServerConnection( socket, info, stream, connector.loginPacket );
|
||||
ServerConnectedEvent event = new ServerConnectedEvent( user, server );
|
||||
ProxyServer.getInstance().getPluginManager().callEvent( event );
|
||||
|
||||
stream.write( BungeeCord.getInstance().registerChannels() );
|
||||
|
||||
Queue<DefinedPacket> packetQueue = ( (BungeeServerInfo) info ).getPacketQueue();
|
||||
while ( !packetQueue.isEmpty() )
|
||||
{
|
||||
stream.write( packetQueue.poll() );
|
||||
}
|
||||
return server;
|
||||
} catch ( Exception ex )
|
||||
{
|
||||
if ( socket != null )
|
||||
{
|
||||
try
|
||||
{
|
||||
socket.close();
|
||||
} catch ( IOException ioe )
|
||||
{
|
||||
}
|
||||
}
|
||||
ServerInfo def = ProxyServer.getInstance().getServers().get( user.getPendingConnection().getListener().getDefaultServer() );
|
||||
if ( retry && !info.equals( def ) )
|
||||
{
|
||||
user.sendMessage( ChatColor.RED + "Could not connect to target server, you have been moved to the default server" );
|
||||
return connect( user, def, false );
|
||||
user.disconnect( message );
|
||||
} else
|
||||
{
|
||||
if ( ex instanceof RuntimeException )
|
||||
user.sendMessage( message );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
throw (RuntimeException) ex;
|
||||
}
|
||||
throw new RuntimeException( "Could not connect to target server " + Util.exception( ex ) );
|
||||
}
|
||||
}
|
||||
return "[" + user.getName() + "] <-> ServerConnector [" + target.getName() + "]";
|
||||
}
|
||||
}
|
||||
|
@@ -1,224 +1,270 @@
|
||||
package net.md_5.bungee;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import com.google.common.base.Preconditions;
|
||||
import io.netty.bootstrap.Bootstrap;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.ChannelFutureListener;
|
||||
import io.netty.channel.ChannelInitializer;
|
||||
import io.netty.channel.ChannelOption;
|
||||
import io.netty.channel.socket.nio.NioSocketChannel;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.Socket;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.Objects;
|
||||
import java.util.logging.Level;
|
||||
import lombok.Getter;
|
||||
import lombok.Synchronized;
|
||||
import lombok.NonNull;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
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.connection.PendingConnection;
|
||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||
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.api.event.PermissionCheckEvent;
|
||||
import net.md_5.bungee.api.event.ServerConnectEvent;
|
||||
import net.md_5.bungee.packet.*;
|
||||
import net.md_5.bungee.api.scoreboard.Scoreboard;
|
||||
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.PipelineUtils;
|
||||
import net.md_5.bungee.packet.DefinedPacket;
|
||||
import net.md_5.bungee.packet.Packet3Chat;
|
||||
import net.md_5.bungee.packet.Packet9Respawn;
|
||||
import net.md_5.bungee.packet.PacketCCSettings;
|
||||
import net.md_5.bungee.packet.PacketFAPluginMessage;
|
||||
import net.md_5.bungee.packet.PacketFFKick;
|
||||
|
||||
public final class UserConnection extends GenericConnection implements ProxiedPlayer
|
||||
@RequiredArgsConstructor
|
||||
public final class UserConnection implements ProxiedPlayer
|
||||
{
|
||||
|
||||
public final Packet2Handshake handshake;
|
||||
final Packet1Login forgeLogin;
|
||||
final List<PacketFAPluginMessage> loginMessages;
|
||||
public Queue<DefinedPacket> packetQueue = new ConcurrentLinkedQueue<>();
|
||||
/*========================================================================*/
|
||||
@NonNull
|
||||
private final ProxyServer bungee;
|
||||
@NonNull
|
||||
private final ChannelWrapper ch;
|
||||
@Getter
|
||||
private final PendingConnection pendingConnection;
|
||||
@NonNull
|
||||
private final String name;
|
||||
@Getter
|
||||
@NonNull
|
||||
private final InitialHandler pendingConnection;
|
||||
/*========================================================================*/
|
||||
@Getter
|
||||
@Setter
|
||||
private ServerConnection server;
|
||||
private UpstreamBridge upBridge;
|
||||
private DownstreamBridge downBridge;
|
||||
// reconnect stuff
|
||||
private int clientEntityId;
|
||||
private int serverEntityId;
|
||||
private volatile boolean reconnecting;
|
||||
// ping stuff
|
||||
private int trackingPingId;
|
||||
private long pingTime;
|
||||
@Getter
|
||||
private int ping = 1000;
|
||||
// Permissions
|
||||
private final Object switchMutex = new Object();
|
||||
@Getter
|
||||
private final Collection<ServerInfo> pendingConnects = new HashSet<>();
|
||||
/*========================================================================*/
|
||||
@Getter
|
||||
@Setter
|
||||
private int sentPingId;
|
||||
@Getter
|
||||
@Setter
|
||||
private long sentPingTime;
|
||||
@Getter
|
||||
@Setter
|
||||
private int ping = 100;
|
||||
/*========================================================================*/
|
||||
private final Collection<String> groups = new HashSet<>();
|
||||
private final Map<String, Boolean> permissions = new HashMap<>();
|
||||
private final Object permMutex = new Object();
|
||||
// Hack for connect timings
|
||||
private ServerInfo nextServer;
|
||||
private volatile boolean clientConnected = true;
|
||||
private final Collection<String> permissions = new HashSet<>();
|
||||
/*========================================================================*/
|
||||
@Getter
|
||||
private int clientEntityId;
|
||||
@Getter
|
||||
@Setter
|
||||
private int serverEntityId;
|
||||
@Getter
|
||||
@Setter
|
||||
private PacketCCSettings settings;
|
||||
@Getter
|
||||
private final Scoreboard serverSentScoreboard = new Scoreboard();
|
||||
/*========================================================================*/
|
||||
@Getter
|
||||
private String displayName;
|
||||
/*========================================================================*/
|
||||
|
||||
public UserConnection(Socket socket, PendingConnection pendingConnection, PacketStream stream, Packet2Handshake handshake, Packet1Login forgeLogin, List<PacketFAPluginMessage> loginMessages)
|
||||
public void init()
|
||||
{
|
||||
super( socket, stream );
|
||||
this.handshake = handshake;
|
||||
this.pendingConnection = pendingConnection;
|
||||
this.forgeLogin = forgeLogin;
|
||||
this.loginMessages = loginMessages;
|
||||
name = handshake.username.substring( 0, Math.min( handshake.username.length(), 16 ) );
|
||||
displayName = name;
|
||||
this.displayName = name;
|
||||
|
||||
Collection<String> g = ProxyServer.getInstance().getConfigurationAdapter().getGroups( name );
|
||||
Collection<String> g = bungee.getConfigurationAdapter().getGroups( name );
|
||||
for ( String s : g )
|
||||
{
|
||||
addGroups( s );
|
||||
}
|
||||
}
|
||||
|
||||
public void sendPacket(DefinedPacket p)
|
||||
{
|
||||
ch.write( p );
|
||||
}
|
||||
|
||||
public void sendPacket(byte[] b)
|
||||
{
|
||||
ch.write( b );
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public boolean isActive()
|
||||
{
|
||||
return ch.getHandle().isActive();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDisplayName(String name)
|
||||
{
|
||||
ProxyServer.getInstance().getTabListHandler().onDisconnect( this );
|
||||
Preconditions.checkNotNull( name, "displayName" );
|
||||
Preconditions.checkArgument( name.length() <= 16, "Display name cannot be longer than 16 characters" );
|
||||
bungee.getTabListHandler().onDisconnect( this );
|
||||
displayName = name;
|
||||
ProxyServer.getInstance().getTabListHandler().onConnect( this );
|
||||
bungee.getTabListHandler().onConnect( this );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void connect(ServerInfo target)
|
||||
{
|
||||
nextServer = target;
|
||||
connect( target, false );
|
||||
}
|
||||
|
||||
public void connect(ServerInfo target, boolean force)
|
||||
public void connectNow(ServerInfo target)
|
||||
{
|
||||
nextServer = null;
|
||||
if ( server == null )
|
||||
{
|
||||
// First join
|
||||
BungeeCord.getInstance().connections.put( name, this );
|
||||
ProxyServer.getInstance().getTabListHandler().onConnect( this );
|
||||
sendPacket( Packet9Respawn.DIM1_SWITCH );
|
||||
sendPacket( Packet9Respawn.DIM2_SWITCH );
|
||||
connect( target );
|
||||
}
|
||||
|
||||
ServerConnectEvent event = new ServerConnectEvent( this, target );
|
||||
BungeeCord.getInstance().getPluginManager().callEvent( event );
|
||||
target = event.getTarget(); // Update in case the event changed target
|
||||
|
||||
ProxyServer.getInstance().getTabListHandler().onServerChange( this );
|
||||
try
|
||||
public void connect(ServerInfo info, final boolean retry)
|
||||
{
|
||||
reconnecting = true;
|
||||
ServerConnectEvent event = new ServerConnectEvent( this, info );
|
||||
ProxyServer.getInstance().getPluginManager().callEvent( event );
|
||||
|
||||
if ( server != null )
|
||||
Preconditions.checkArgument( event.getTarget() instanceof BungeeServerInfo, "BungeeCord can only connect to BungeeServerInfo instances" );
|
||||
final BungeeServerInfo target = (BungeeServerInfo) event.getTarget(); // Update in case the event changed target
|
||||
|
||||
if ( getServer() != null && Objects.equals( getServer().getInfo(), target ) )
|
||||
{
|
||||
stream.write( new Packet9Respawn( (byte) 1, (byte) 0, (byte) 0, (short) 256, "DEFAULT" ) );
|
||||
stream.write( new Packet9Respawn( (byte) -1, (byte) 0, (byte) 0, (short) 256, "DEFAULT" ) );
|
||||
sendMessage( ChatColor.RED + "Cannot connect to server you are already on!" );
|
||||
return;
|
||||
}
|
||||
if ( pendingConnects.contains( target ) )
|
||||
{
|
||||
sendMessage( ChatColor.RED + "Already connecting to this server!" );
|
||||
return;
|
||||
}
|
||||
|
||||
ServerConnection newServer = ServerConnector.connect( this, target, true );
|
||||
if ( server == null )
|
||||
{
|
||||
// Once again, first connection
|
||||
clientEntityId = newServer.loginPacket.entityId;
|
||||
serverEntityId = newServer.loginPacket.entityId;
|
||||
// Set tab list size
|
||||
Packet1Login s = newServer.loginPacket;
|
||||
Packet1Login login = new Packet1Login( s.entityId, s.levelType, s.gameMode, (byte) s.dimension, s.difficulty, s.unused, (byte) pendingConnection.getListener().getTabListSize() );
|
||||
stream.write( login );
|
||||
stream.write( BungeeCord.getInstance().registerChannels() );
|
||||
pendingConnects.add( target );
|
||||
|
||||
upBridge = new UpstreamBridge();
|
||||
upBridge.start();
|
||||
new Bootstrap()
|
||||
.channel( NioSocketChannel.class )
|
||||
.group( BungeeCord.getInstance().eventLoops )
|
||||
.handler( new ChannelInitializer()
|
||||
{
|
||||
@Override
|
||||
protected void initChannel(Channel ch) throws Exception
|
||||
{
|
||||
PipelineUtils.BASE.initChannel( ch );
|
||||
ch.pipeline().get( HandlerBoss.class ).setHandler( new ServerConnector( bungee, UserConnection.this, target ) );
|
||||
}
|
||||
} )
|
||||
.option( ChannelOption.CONNECT_TIMEOUT_MILLIS, 5000 ) // TODO: Configurable
|
||||
.remoteAddress( target.getAddress() )
|
||||
.connect().addListener( new ChannelFutureListener()
|
||||
{
|
||||
@Override
|
||||
public void operationComplete(ChannelFuture future) throws Exception
|
||||
{
|
||||
if ( !future.isSuccess() )
|
||||
{
|
||||
future.channel().close();
|
||||
pendingConnects.remove( target );
|
||||
|
||||
ServerInfo def = ProxyServer.getInstance().getServers().get( getPendingConnection().getListener().getFallbackServer() );
|
||||
if ( retry & target != def && ( getServer() == null || def != getServer().getInfo() ) )
|
||||
{
|
||||
sendMessage( ChatColor.RED + "Could not connect to target server, you have been moved to the lobby server" );
|
||||
connect( def, false );
|
||||
} else
|
||||
{
|
||||
try
|
||||
if ( server == null )
|
||||
{
|
||||
downBridge.interrupt();
|
||||
downBridge.join();
|
||||
} catch ( InterruptedException ie )
|
||||
disconnect( "Could not connect to default server, please try again later: " + future.cause().getClass().getName() );
|
||||
} else
|
||||
{
|
||||
sendMessage( ChatColor.RED + "Could not connect to selected server, please try again later: " + future.cause().getClass().getName() );
|
||||
}
|
||||
|
||||
server.disconnect( "Quitting" );
|
||||
server.getInfo().removePlayer( this );
|
||||
|
||||
Packet1Login login = newServer.loginPacket;
|
||||
serverEntityId = login.entityId;
|
||||
stream.write( new Packet9Respawn( login.dimension, login.difficulty, login.gameMode, (short) 256, login.levelType ) );
|
||||
}
|
||||
|
||||
// Reconnect process has finished, lets get the player moving again
|
||||
reconnecting = false;
|
||||
|
||||
// Add to new
|
||||
target.addPlayer( this );
|
||||
|
||||
// Start the bridges and move on
|
||||
server = newServer;
|
||||
downBridge = new DownstreamBridge();
|
||||
downBridge.start();
|
||||
} catch ( KickException ex )
|
||||
{
|
||||
disconnect( ex.getMessage() );
|
||||
} catch ( Exception ex )
|
||||
{
|
||||
disconnect( "Could not connect to server - " + Util.exception( ex ) );
|
||||
}
|
||||
}
|
||||
} );
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void disconnect(String reason)
|
||||
{
|
||||
if ( clientConnected )
|
||||
if ( ch.getHandle().isActive() )
|
||||
{
|
||||
PlayerDisconnectEvent event = new PlayerDisconnectEvent( this );
|
||||
ProxyServer.getInstance().getPluginManager().callEvent( event );
|
||||
ProxyServer.getInstance().getTabListHandler().onDisconnect( this );
|
||||
ProxyServer.getInstance().getPlayers().remove( this );
|
||||
|
||||
super.disconnect( reason );
|
||||
bungee.getLogger().log( Level.INFO, "[" + getName() + "] disconnected with: " + reason );
|
||||
sendPacket( new PacketFFKick( reason ) );
|
||||
ch.getHandle().close();
|
||||
if ( server != null )
|
||||
{
|
||||
server.getInfo().removePlayer( this );
|
||||
server.disconnect( "Quitting" );
|
||||
ProxyServer.getInstance().getReconnectHandler().setServer( this );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
clientConnected = false;
|
||||
}
|
||||
@Override
|
||||
public void chat(String message)
|
||||
{
|
||||
Preconditions.checkState( server != null, "Not connected to server" );
|
||||
server.getCh().write( new Packet3Chat( message ) );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendMessage(String message)
|
||||
{
|
||||
packetQueue.add( new Packet3Chat( message ) );
|
||||
sendPacket( new Packet3Chat( message ) );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendMessages(String... messages)
|
||||
{
|
||||
for ( String message : messages )
|
||||
{
|
||||
sendMessage( message );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendData(String channel, byte[] data)
|
||||
{
|
||||
server.packetQueue.add( new PacketFAPluginMessage( channel, data ) );
|
||||
sendPacket( new PacketFAPluginMessage( channel, data ) );
|
||||
}
|
||||
|
||||
@Override
|
||||
public InetSocketAddress getAddress()
|
||||
{
|
||||
return (InetSocketAddress) socket.getRemoteSocketAddress();
|
||||
return (InetSocketAddress) ch.getHandle().remoteAddress();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Synchronized("permMutex")
|
||||
public Collection<String> getGroups()
|
||||
{
|
||||
return Collections.unmodifiableCollection( groups );
|
||||
}
|
||||
|
||||
@Override
|
||||
@Synchronized("permMutex")
|
||||
public void addGroups(String... groups)
|
||||
{
|
||||
for ( String group : groups )
|
||||
{
|
||||
this.groups.add( group );
|
||||
for ( String permission : ProxyServer.getInstance().getConfigurationAdapter().getPermissions( group ) )
|
||||
for ( String permission : bungee.getConfigurationAdapter().getPermissions( group ) )
|
||||
{
|
||||
setPermission( permission, true );
|
||||
}
|
||||
@@ -226,13 +272,12 @@ public final class UserConnection extends GenericConnection implements ProxiedPl
|
||||
}
|
||||
|
||||
@Override
|
||||
@Synchronized("permMutex")
|
||||
public void removeGroups(String... groups)
|
||||
{
|
||||
for ( String group : groups )
|
||||
{
|
||||
this.groups.remove( group );
|
||||
for ( String permission : ProxyServer.getInstance().getConfigurationAdapter().getPermissions( group ) )
|
||||
for ( String permission : bungee.getConfigurationAdapter().getPermissions( group ) )
|
||||
{
|
||||
setPermission( permission, false );
|
||||
}
|
||||
@@ -240,306 +285,32 @@ public final class UserConnection extends GenericConnection implements ProxiedPl
|
||||
}
|
||||
|
||||
@Override
|
||||
@Synchronized("permMutex")
|
||||
public boolean hasPermission(String permission)
|
||||
{
|
||||
Boolean val = permissions.get( permission );
|
||||
return ( val == null ) ? false : val;
|
||||
return bungee.getPluginManager().callEvent( new PermissionCheckEvent( this, permission, permissions.contains( permission ) ) ).hasPermission();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Synchronized("permMutex")
|
||||
public void setPermission(String permission, boolean value)
|
||||
{
|
||||
permissions.put( permission, value );
|
||||
if ( value )
|
||||
{
|
||||
permissions.add( permission );
|
||||
} else
|
||||
{
|
||||
permissions.remove( permission );
|
||||
}
|
||||
|
||||
private class UpstreamBridge extends Thread
|
||||
{
|
||||
|
||||
public UpstreamBridge()
|
||||
{
|
||||
super( "Upstream Bridge - " + name );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
public String toString()
|
||||
{
|
||||
while ( !socket.isClosed() )
|
||||
{
|
||||
try
|
||||
{
|
||||
byte[] packet = stream.readPacket();
|
||||
boolean sendPacket = true;
|
||||
int id = Util.getId( packet );
|
||||
|
||||
switch ( id )
|
||||
{
|
||||
case 0x00:
|
||||
if ( trackingPingId == new Packet0KeepAlive( packet ).id )
|
||||
{
|
||||
int newPing = (int) ( System.currentTimeMillis() - pingTime );
|
||||
ProxyServer.getInstance().getTabListHandler().onPingChange( UserConnection.this, newPing );
|
||||
ping = newPing;
|
||||
}
|
||||
break;
|
||||
case 0x03:
|
||||
Packet3Chat chat = new Packet3Chat( packet );
|
||||
if ( chat.message.startsWith( "/" ) )
|
||||
{
|
||||
sendPacket = !ProxyServer.getInstance().getPluginManager().dispatchCommand( UserConnection.this, chat.message.substring( 1 ) );
|
||||
} else
|
||||
{
|
||||
ChatEvent chatEvent = new ChatEvent( UserConnection.this, server, chat.message );
|
||||
ProxyServer.getInstance().getPluginManager().callEvent( chatEvent );
|
||||
sendPacket = !chatEvent.isCancelled();
|
||||
}
|
||||
break;
|
||||
case 0xFA:
|
||||
// Call the onPluginMessage event
|
||||
PacketFAPluginMessage message = new PacketFAPluginMessage( packet );
|
||||
|
||||
// Might matter in the future
|
||||
if ( message.tag.equals( "BungeeCord" ) )
|
||||
{
|
||||
continue;
|
||||
return name;
|
||||
}
|
||||
|
||||
PluginMessageEvent event = new PluginMessageEvent( UserConnection.this, server, message.tag, message.data );
|
||||
ProxyServer.getInstance().getPluginManager().callEvent( event );
|
||||
|
||||
if ( event.isCancelled() )
|
||||
public void setClientEntityId(int clientEntityId)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
while ( !server.packetQueue.isEmpty() )
|
||||
{
|
||||
DefinedPacket p = server.packetQueue.poll();
|
||||
if ( p != null )
|
||||
{
|
||||
server.stream.write( p );
|
||||
}
|
||||
}
|
||||
|
||||
EntityMap.rewrite( packet, clientEntityId, serverEntityId );
|
||||
if ( sendPacket && !server.socket.isClosed() )
|
||||
{
|
||||
server.stream.write( packet );
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
Thread.sleep( BungeeCord.getInstance().config.getSleepTime() );
|
||||
} catch ( InterruptedException ex )
|
||||
{
|
||||
}
|
||||
} catch ( IOException ex )
|
||||
{
|
||||
disconnect( "Reached end of stream" );
|
||||
} catch ( Exception ex )
|
||||
{
|
||||
disconnect( Util.exception( ex ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class DownstreamBridge extends Thread
|
||||
{
|
||||
|
||||
public DownstreamBridge()
|
||||
{
|
||||
super( "Downstream Bridge - " + name );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
try
|
||||
{
|
||||
outer:
|
||||
while ( !reconnecting )
|
||||
{
|
||||
byte[] packet = server.stream.readPacket();
|
||||
int id = Util.getId( packet );
|
||||
|
||||
switch ( id )
|
||||
{
|
||||
case 0x00:
|
||||
trackingPingId = new Packet0KeepAlive( packet ).id;
|
||||
pingTime = System.currentTimeMillis();
|
||||
break;
|
||||
case 0x03:
|
||||
Packet3Chat chat = new Packet3Chat( packet );
|
||||
ChatEvent chatEvent = new ChatEvent( server, UserConnection.this, chat.message );
|
||||
ProxyServer.getInstance().getPluginManager().callEvent( chatEvent );
|
||||
|
||||
if ( chatEvent.isCancelled() )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
case 0xC9:
|
||||
PacketC9PlayerListItem playerList = new PacketC9PlayerListItem( packet );
|
||||
if ( !ProxyServer.getInstance().getTabListHandler().onListUpdate( UserConnection.this, playerList.username, playerList.online, playerList.ping ) )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
case 0xFA:
|
||||
// Call the onPluginMessage event
|
||||
PacketFAPluginMessage message = new PacketFAPluginMessage( packet );
|
||||
DataInputStream in = new DataInputStream( new ByteArrayInputStream( message.data ) );
|
||||
PluginMessageEvent event = new PluginMessageEvent( server, UserConnection.this, message.tag, message.data );
|
||||
ProxyServer.getInstance().getPluginManager().callEvent( event );
|
||||
|
||||
if ( event.isCancelled() )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( message.tag.equals( "BungeeCord" ) )
|
||||
{
|
||||
String subChannel = in.readUTF();
|
||||
if ( subChannel.equals( "Forward" ) )
|
||||
{
|
||||
String target = in.readUTF();
|
||||
String channel = in.readUTF();
|
||||
short len = in.readShort();
|
||||
byte[] data = new byte[ len ];
|
||||
in.readFully( data );
|
||||
|
||||
|
||||
ByteArrayOutputStream b = new ByteArrayOutputStream();
|
||||
DataOutputStream out = new DataOutputStream( b );
|
||||
out.writeUTF( channel );
|
||||
out.writeShort( data.length );
|
||||
out.write( data );
|
||||
|
||||
if ( target.equals( "ALL" ) )
|
||||
{
|
||||
for ( ServerInfo server : BungeeCord.getInstance().getServers().values() )
|
||||
{
|
||||
server.sendData( "BungeeCord", b.toByteArray() );
|
||||
}
|
||||
} else
|
||||
{
|
||||
ServerInfo server = BungeeCord.getInstance().getServerInfo( target );
|
||||
if ( server != null )
|
||||
{
|
||||
server.sendData( "BungeeCord", b.toByteArray() );
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( subChannel.equals( "Connect" ) )
|
||||
{
|
||||
ServerInfo server = ProxyServer.getInstance().getServerInfo( in.readUTF() );
|
||||
if ( server != null )
|
||||
{
|
||||
connect( server, true );
|
||||
break outer;
|
||||
}
|
||||
}
|
||||
if ( subChannel.equals( "IP" ) )
|
||||
{
|
||||
ByteArrayOutputStream b = new ByteArrayOutputStream();
|
||||
DataOutputStream out = new DataOutputStream( b );
|
||||
out.writeUTF( "IP" );
|
||||
out.writeUTF( getAddress().getHostString() );
|
||||
out.writeInt( getAddress().getPort() );
|
||||
getServer().sendData( "BungeeCord", b.toByteArray() );
|
||||
}
|
||||
if ( subChannel.equals( "PlayerCount" ) )
|
||||
{
|
||||
ServerInfo server = ProxyServer.getInstance().getServerInfo( in.readUTF() );
|
||||
if ( server != null )
|
||||
{
|
||||
ByteArrayOutputStream b = new ByteArrayOutputStream();
|
||||
DataOutputStream out = new DataOutputStream( b );
|
||||
out.writeUTF( "PlayerCount" );
|
||||
out.writeUTF( server.getName() );
|
||||
out.writeInt( server.getPlayers().size() );
|
||||
getServer().sendData( "BungeeCord", b.toByteArray() );
|
||||
}
|
||||
}
|
||||
if ( subChannel.equals( "PlayerList" ) )
|
||||
{
|
||||
ServerInfo server = ProxyServer.getInstance().getServerInfo( in.readUTF() );
|
||||
if ( server != null )
|
||||
{
|
||||
ByteArrayOutputStream b = new ByteArrayOutputStream();
|
||||
DataOutputStream out = new DataOutputStream( b );
|
||||
out.writeUTF( "PlayerList" );
|
||||
out.writeUTF( server.getName() );
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for ( ProxiedPlayer p : server.getPlayers() )
|
||||
{
|
||||
sb.append( p.getName() );
|
||||
sb.append( "," );
|
||||
}
|
||||
out.writeUTF( sb.substring( 0, sb.length() - 1 ) );
|
||||
|
||||
getServer().sendData( "BungeeCord", b.toByteArray() );
|
||||
}
|
||||
}
|
||||
if ( subChannel.equals( "GetServers" ) )
|
||||
{
|
||||
ByteArrayOutputStream b = new ByteArrayOutputStream();
|
||||
DataOutputStream out = new DataOutputStream( b );
|
||||
out.writeUTF( "GetServers" );
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for ( String server : ProxyServer.getInstance().getServers().keySet() )
|
||||
{
|
||||
sb.append( server );
|
||||
sb.append( "," );
|
||||
}
|
||||
out.writeUTF( sb.substring( 0, sb.length() - 1 ) );
|
||||
|
||||
getServer().sendData( "BungeeCord", b.toByteArray() );
|
||||
}
|
||||
if ( subChannel.equals( "Message" ) )
|
||||
{
|
||||
ProxiedPlayer target = ProxyServer.getInstance().getPlayer( in.readUTF() );
|
||||
if ( target != null )
|
||||
{
|
||||
target.sendMessage( in.readUTF() );
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
case 0xFF:
|
||||
disconnect( new PacketFFKick( packet ).message );
|
||||
break outer;
|
||||
}
|
||||
|
||||
while ( !packetQueue.isEmpty() )
|
||||
{
|
||||
DefinedPacket p = packetQueue.poll();
|
||||
if ( p != null )
|
||||
{
|
||||
stream.write( p );
|
||||
}
|
||||
}
|
||||
|
||||
EntityMap.rewrite( packet, serverEntityId, clientEntityId );
|
||||
stream.write( packet );
|
||||
|
||||
if ( nextServer != null )
|
||||
{
|
||||
connect( nextServer, true );
|
||||
break outer;
|
||||
}
|
||||
}
|
||||
} catch ( Exception ex )
|
||||
{
|
||||
disconnect( Util.exception( ex ) );
|
||||
}
|
||||
}
|
||||
Preconditions.checkState( this.clientEntityId == 0, "Client entityId already set!" );
|
||||
this.clientEntityId = clientEntityId;
|
||||
}
|
||||
}
|
||||
|
@@ -1,6 +1,8 @@
|
||||
package net.md_5.bungee;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Series of utility classes to perform various operations.
|
||||
@@ -27,39 +29,6 @@ public class Util
|
||||
return new InetSocketAddress( split[0], port );
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of the first unsigned byte of the specified array. Useful
|
||||
* for getting the id of a packet array .
|
||||
*
|
||||
* @param b the array to read from
|
||||
* @return the unsigned value of the first byte
|
||||
*/
|
||||
public static int getId(byte[] b)
|
||||
{
|
||||
return b[0] & 0xFF;
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizes a config path by prefix upper case letters with '_' and
|
||||
* turning them to lowercase.
|
||||
*
|
||||
* @param s the string to normalize
|
||||
* @return the normalized path
|
||||
*/
|
||||
public static String normalize(String s)
|
||||
{
|
||||
StringBuilder result = new StringBuilder();
|
||||
for ( char c : s.toCharArray() )
|
||||
{
|
||||
if ( Character.isUpperCase( c ) )
|
||||
{
|
||||
result.append( "_" );
|
||||
}
|
||||
result.append( Character.toLowerCase( c ) );
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats an integer as a hex value.
|
||||
*
|
||||
@@ -80,6 +49,26 @@ public class Util
|
||||
*/
|
||||
public static String exception(Throwable t)
|
||||
{
|
||||
return t.getClass().getSimpleName() + " : " + t.getMessage() + " @ " + t.getStackTrace()[0].getClassName() + ":" + t.getStackTrace()[0].getLineNumber();
|
||||
// TODO: We should use clear manually written exceptions
|
||||
StackTraceElement[] trace = t.getStackTrace();
|
||||
return t.getClass().getSimpleName() + " : " + t.getMessage()
|
||||
+ ( ( trace.length > 0 ) ? " @ " + t.getStackTrace()[0].getClassName() + ":" + t.getStackTrace()[0].getLineNumber() : "" );
|
||||
}
|
||||
|
||||
public static String csv(Collection<?> objects)
|
||||
{
|
||||
return format( objects, ", " );
|
||||
}
|
||||
|
||||
public static String format(Collection<?> objects, String separators)
|
||||
{
|
||||
StringBuilder ret = new StringBuilder();
|
||||
for ( Object o : objects )
|
||||
{
|
||||
ret.append( o );
|
||||
ret.append( separators );
|
||||
}
|
||||
|
||||
return ( ret.length() == 0 ) ? "" : ret.substring( 0, ret.length() - separators.length() );
|
||||
}
|
||||
}
|
||||
|
@@ -6,8 +6,8 @@ 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.ConcurrentHashMap;
|
||||
import java.util.logging.Level;
|
||||
import net.md_5.bungee.api.ProxyServer;
|
||||
import net.md_5.bungee.api.ReconnectHandler;
|
||||
@@ -41,10 +41,7 @@ public class YamlReconnectHandler implements ReconnectHandler
|
||||
|
||||
if ( data == null )
|
||||
{
|
||||
data = new ConcurrentHashMap<>();
|
||||
} else
|
||||
{
|
||||
data = new ConcurrentHashMap<>( data );
|
||||
data = new HashMap<>();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,15 +50,14 @@ public class YamlReconnectHandler implements ReconnectHandler
|
||||
{
|
||||
ListenerInfo listener = player.getPendingConnection().getListener();
|
||||
String name;
|
||||
if ( listener.isForceDefault() )
|
||||
String forced = listener.getForcedHosts().get( player.getPendingConnection().getVirtualHost().getHostName().toLowerCase() );
|
||||
if ( forced == null && listener.isForceDefault() )
|
||||
{
|
||||
name = listener.getDefaultServer();
|
||||
} else
|
||||
{
|
||||
String forced = listener.getForcedHosts().get( player.getPendingConnection().getVirtualHost().getHostName() );
|
||||
forced = listener.getDefaultServer();
|
||||
}
|
||||
|
||||
String server = ( forced == null ) ? data.get( key( player ) ) : forced;
|
||||
name = ( server != null ) ? server : listener.getDefaultServer();
|
||||
}
|
||||
ServerInfo info = ProxyServer.getInstance().getServerInfo( name );
|
||||
if ( info == null )
|
||||
{
|
||||
@@ -84,7 +80,7 @@ public class YamlReconnectHandler implements ReconnectHandler
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save()
|
||||
public synchronized void save()
|
||||
{
|
||||
try ( FileWriter wr = new FileWriter( file ) )
|
||||
{
|
||||
|
@@ -44,6 +44,7 @@ public class CommandAlert extends Command
|
||||
{
|
||||
player.sendMessage( message );
|
||||
}
|
||||
ProxyServer.getInstance().getConsole().sendMessage( message );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,9 +1,14 @@
|
||||
package net.md_5.bungee.command;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
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;
|
||||
import net.md_5.bungee.api.config.ServerInfo;
|
||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||
import net.md_5.bungee.api.plugin.Command;
|
||||
|
||||
@@ -21,23 +26,38 @@ public class CommandList extends Command
|
||||
@Override
|
||||
public void execute(CommandSender sender, String[] args)
|
||||
{
|
||||
StringBuilder users = new StringBuilder();
|
||||
Collection<ProxiedPlayer> connections = ProxyServer.getInstance().getPlayers();
|
||||
|
||||
if ( connections.isEmpty() )
|
||||
for ( ServerInfo server : ProxyServer.getInstance().getServers().values() )
|
||||
{
|
||||
sender.sendMessage( ChatColor.BLUE + "Currently no players online." );
|
||||
return;
|
||||
}
|
||||
|
||||
for ( ProxiedPlayer player : connections )
|
||||
if ( !server.canAccess( sender ) )
|
||||
{
|
||||
users.append( player.getDisplayName() );
|
||||
users.append( ", " );
|
||||
users.append( ChatColor.RESET );
|
||||
continue;
|
||||
}
|
||||
|
||||
users.setLength( users.length() - 2 );
|
||||
sender.sendMessage( ChatColor.BLUE + "Currently online across all servers (" + connections.size() + "): " + ChatColor.RESET + users );
|
||||
Collection<ProxiedPlayer> serverPlayers = server.getPlayers();
|
||||
|
||||
StringBuilder message = new StringBuilder();
|
||||
message.append( ChatColor.GREEN );
|
||||
message.append( "[" );
|
||||
message.append( server.getName() );
|
||||
message.append( "] " );
|
||||
message.append( ChatColor.YELLOW );
|
||||
message.append( "(" );
|
||||
message.append( serverPlayers.size() );
|
||||
message.append( "): " );
|
||||
message.append( ChatColor.RESET );
|
||||
|
||||
List<String> players = new ArrayList<>();
|
||||
for ( ProxiedPlayer player : serverPlayers )
|
||||
{
|
||||
players.add( player.getDisplayName() );
|
||||
}
|
||||
Collections.sort( players, String.CASE_INSENSITIVE_ORDER );
|
||||
|
||||
message.append( Util.format( players, ChatColor.RESET + ", " ) );
|
||||
|
||||
sender.sendMessage( message.toString() );
|
||||
}
|
||||
|
||||
sender.sendMessage( "Total players online: " + ProxyServer.getInstance().getPlayers().size() );
|
||||
}
|
||||
}
|
||||
|
69
proxy/src/main/java/net/md_5/bungee/command/CommandSend.java
Normal file
69
proxy/src/main/java/net/md_5/bungee/command/CommandSend.java
Normal file
@@ -0,0 +1,69 @@
|
||||
package net.md_5.bungee.command;
|
||||
|
||||
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.config.ServerInfo;
|
||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||
import net.md_5.bungee.api.plugin.Command;
|
||||
|
||||
public class CommandSend extends Command
|
||||
{
|
||||
|
||||
public CommandSend()
|
||||
{
|
||||
super( "send", "bungeecord.command.send" );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(CommandSender sender, String[] args)
|
||||
{
|
||||
if ( args.length != 2 )
|
||||
{
|
||||
sender.sendMessage( ChatColor.RED + "Not enough arguments, usage: /send <player|all|current> <target>" );
|
||||
return;
|
||||
}
|
||||
ServerInfo target = ProxyServer.getInstance().getServerInfo( args[1] );
|
||||
if ( target == null )
|
||||
{
|
||||
sender.sendMessage( ChatColor.RED + "Target server does not exist" );
|
||||
}
|
||||
|
||||
if ( args[0].equalsIgnoreCase( "all" ) )
|
||||
{
|
||||
for ( ProxiedPlayer p : ProxyServer.getInstance().getPlayers() )
|
||||
{
|
||||
summon( p, target, sender );
|
||||
}
|
||||
} else if ( args[0].equalsIgnoreCase( "current" ) )
|
||||
{
|
||||
if ( !( sender instanceof ProxiedPlayer ) )
|
||||
{
|
||||
sender.sendMessage( ChatColor.RED + "Only in game players can use this command" );
|
||||
}
|
||||
ProxiedPlayer player = (ProxiedPlayer) sender;
|
||||
for ( ProxiedPlayer p : player.getServer().getInfo().getPlayers() )
|
||||
{
|
||||
summon( p, target, sender );
|
||||
}
|
||||
} else
|
||||
{
|
||||
ProxiedPlayer player = ProxyServer.getInstance().getPlayer( args[0] );
|
||||
if ( player == null )
|
||||
{
|
||||
sender.sendMessage( ChatColor.RED + "That player is not online" );
|
||||
}
|
||||
summon( player, target, sender );
|
||||
}
|
||||
sender.sendMessage( ChatColor.GREEN + "Successfully summoned player(s)" );
|
||||
}
|
||||
|
||||
private void summon(ProxiedPlayer player, ServerInfo target, CommandSender sender)
|
||||
{
|
||||
if ( player.getServer() != null && !player.getServer().getInfo().equals( target ) )
|
||||
{
|
||||
player.connect( target );
|
||||
player.sendMessage( ChatColor.GOLD + "Summoned to " + target.getName() + " by " + sender.getName() );
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,9 +1,9 @@
|
||||
package net.md_5.bungee.command;
|
||||
|
||||
import java.util.Map;
|
||||
import net.md_5.bungee.BungeeCord;
|
||||
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.config.ServerInfo;
|
||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||
import net.md_5.bungee.api.plugin.Command;
|
||||
@@ -27,16 +27,22 @@ public class CommandServer extends Command
|
||||
return;
|
||||
}
|
||||
ProxiedPlayer player = (ProxiedPlayer) sender;
|
||||
Map<String, ServerInfo> servers = BungeeCord.getInstance().config.getServers();
|
||||
Map<String, ServerInfo> servers = ProxyServer.getInstance().getServers();
|
||||
if ( args.length == 0 )
|
||||
{
|
||||
StringBuilder serverList = new StringBuilder();
|
||||
for ( String server : servers.keySet() )
|
||||
for ( ServerInfo server : servers.values() )
|
||||
{
|
||||
serverList.append( server );
|
||||
if ( server.canAccess( player ) )
|
||||
{
|
||||
serverList.append( server.getName() );
|
||||
serverList.append( ", " );
|
||||
}
|
||||
}
|
||||
if ( serverList.length() != 0 )
|
||||
{
|
||||
serverList.setLength( serverList.length() - 2 );
|
||||
}
|
||||
player.sendMessage( ChatColor.GOLD + "You may connect to the following servers at this time: " + serverList.toString() );
|
||||
} else
|
||||
{
|
||||
@@ -44,9 +50,9 @@ public class CommandServer extends Command
|
||||
if ( server == null )
|
||||
{
|
||||
player.sendMessage( ChatColor.RED + "The specified server does not exist" );
|
||||
} else if ( server.equals( player.getServer().getInfo() ) )
|
||||
} else if ( !server.canAccess( player ) )
|
||||
{
|
||||
player.sendMessage( ChatColor.RED + "You are already on this server." );
|
||||
player.sendMessage( ChatColor.RED + "You don't have permission to access this server" );
|
||||
} else
|
||||
{
|
||||
player.connect( server );
|
||||
|
@@ -25,6 +25,15 @@ public class ConsoleCommandSender implements CommandSender
|
||||
System.out.println( ChatColor.stripColor( message ) );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendMessages(String... messages)
|
||||
{
|
||||
for ( String message : messages )
|
||||
{
|
||||
sendMessage( message );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName()
|
||||
{
|
||||
|
@@ -9,9 +9,9 @@ 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.tablist.GlobalPingTabList;
|
||||
import net.md_5.bungee.tablist.GlobalTabList;
|
||||
import net.md_5.bungee.tablist.ServerUniqueTabList;
|
||||
import net.md_5.bungee.tablist.GlobalPing;
|
||||
import net.md_5.bungee.tablist.Global;
|
||||
import net.md_5.bungee.tablist.ServerUnique;
|
||||
|
||||
/**
|
||||
* Core configuration for the proxy.
|
||||
@@ -48,7 +48,7 @@ public class Configuration
|
||||
* Should we check minecraft.net auth.
|
||||
*/
|
||||
private boolean onlineMode = true;
|
||||
private int sleepTime = 1;
|
||||
private int playerLimit = -1;
|
||||
|
||||
public void load()
|
||||
{
|
||||
@@ -58,7 +58,7 @@ public class Configuration
|
||||
timeout = adapter.getInt( "timeout", timeout );
|
||||
uuid = adapter.getString( "stats", uuid );
|
||||
onlineMode = adapter.getBoolean( "online_mode", onlineMode );
|
||||
sleepTime = adapter.getInt( "sleep_time", sleepTime );
|
||||
playerLimit = adapter.getInt( "player_limit", playerLimit );
|
||||
|
||||
DefaultTabList tab = DefaultTabList.valueOf( adapter.getString( "tab_list", "GLOBAL_PING" ) );
|
||||
if ( tab == null )
|
||||
@@ -68,25 +68,46 @@ public class Configuration
|
||||
switch ( tab )
|
||||
{
|
||||
case GLOBAL:
|
||||
ProxyServer.getInstance().setTabListHandler( new GlobalTabList() );
|
||||
ProxyServer.getInstance().setTabListHandler( new Global() );
|
||||
break;
|
||||
case GLOBAL_PING:
|
||||
ProxyServer.getInstance().setTabListHandler( new GlobalPingTabList() );
|
||||
ProxyServer.getInstance().setTabListHandler( new GlobalPing() );
|
||||
break;
|
||||
case SERVER:
|
||||
ProxyServer.getInstance().setTabListHandler( new ServerUniqueTabList() );
|
||||
ProxyServer.getInstance().setTabListHandler( new ServerUnique() );
|
||||
break;
|
||||
}
|
||||
|
||||
listeners = adapter.getListeners();
|
||||
Preconditions.checkArgument( listeners != null && !listeners.isEmpty(), "No listeners defined." );
|
||||
|
||||
servers = adapter.getServers();
|
||||
Preconditions.checkArgument( servers != null && !servers.isEmpty(), "No servers defined" );
|
||||
Map<String, ServerInfo> newServers = adapter.getServers();
|
||||
Preconditions.checkArgument( newServers != null && !newServers.isEmpty(), "No servers defined" );
|
||||
|
||||
if ( servers == null )
|
||||
{
|
||||
servers = newServers;
|
||||
} else
|
||||
{
|
||||
for ( ServerInfo oldServer : servers.values() )
|
||||
{
|
||||
// Don't allow servers to be removed
|
||||
Preconditions.checkArgument( newServers.containsValue( oldServer ), "Server %s removed on reload!", oldServer.getName() );
|
||||
}
|
||||
|
||||
// Add new servers
|
||||
for ( Map.Entry<String, ServerInfo> newServer : newServers.entrySet() )
|
||||
{
|
||||
if ( !servers.containsValue( newServer.getValue() ) )
|
||||
{
|
||||
servers.put( newServer.getKey(), newServer.getValue() );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for ( ListenerInfo listener : listeners )
|
||||
{
|
||||
Preconditions.checkArgument( servers.containsKey( listener.getDefaultServer() ) );
|
||||
Preconditions.checkArgument( servers.containsKey( listener.getDefaultServer() ), "Default server %s is not defined", listener.getDefaultServer() );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -20,6 +20,7 @@ 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 org.yaml.snakeyaml.DumperOptions;
|
||||
import org.yaml.snakeyaml.Yaml;
|
||||
|
||||
@@ -151,8 +152,9 @@ public class YamlConfig implements ConfigurationAdapter
|
||||
Map<String, Object> val = entry.getValue();
|
||||
String name = entry.getKey();
|
||||
String addr = get( "address", "localhost:25565", val );
|
||||
boolean restricted = get( "restricted", false, val );
|
||||
InetSocketAddress address = Util.getAddr( addr );
|
||||
ServerInfo info = ProxyServer.getInstance().constructServerInfo( name, address );
|
||||
ServerInfo info = ProxyServer.getInstance().constructServerInfo( name, address, restricted );
|
||||
ret.put( name, info );
|
||||
}
|
||||
|
||||
@@ -179,12 +181,16 @@ public class YamlConfig implements ConfigurationAdapter
|
||||
|
||||
int maxPlayers = get( "max_players", 1, val );
|
||||
String defaultServer = get( "default_server", "lobby", val );
|
||||
String fallbackServer = get( "fallback_server", defaultServer, val );
|
||||
boolean forceDefault = get( "force_default_server", false, val );
|
||||
String host = get( "host", "0.0.0.0:25577", val );
|
||||
int tabListSize = get( "tab_size", 60, val );
|
||||
InetSocketAddress address = Util.getAddr( host );
|
||||
Map<String, String> forced = get( "forced_hosts", forcedDef, val );
|
||||
ListenerInfo info = new ListenerInfo( address, motd, maxPlayers, tabListSize, defaultServer, forceDefault, forced );
|
||||
String textureURL = get( "texture_url", null, val );
|
||||
int textureSize = get( "texture_size", 16, val );
|
||||
TexturePackInfo texture = ( textureURL == null ) ? null : new TexturePackInfo( textureURL, textureSize );
|
||||
ListenerInfo info = new ListenerInfo( address, motd, maxPlayers, tabListSize, defaultServer, fallbackServer, forceDefault, forced, texture );
|
||||
ret.add( info );
|
||||
}
|
||||
|
||||
|
@@ -0,0 +1,17 @@
|
||||
package net.md_5.bungee.connection;
|
||||
|
||||
public class CancelSendSignal extends Error
|
||||
{
|
||||
|
||||
@Override
|
||||
public Throwable initCause(Throwable cause)
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Throwable fillInStackTrace()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
}
|
@@ -0,0 +1,349 @@
|
||||
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 io.netty.channel.Channel;
|
||||
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;
|
||||
import net.md_5.bungee.api.event.ChatEvent;
|
||||
import net.md_5.bungee.api.event.PluginMessageEvent;
|
||||
import net.md_5.bungee.api.event.ServerKickEvent;
|
||||
import net.md_5.bungee.api.scoreboard.Objective;
|
||||
import net.md_5.bungee.api.scoreboard.Position;
|
||||
import net.md_5.bungee.api.scoreboard.Score;
|
||||
import net.md_5.bungee.api.scoreboard.Scoreboard;
|
||||
import net.md_5.bungee.api.scoreboard.Team;
|
||||
import net.md_5.bungee.netty.ChannelWrapper;
|
||||
import net.md_5.bungee.packet.Packet0KeepAlive;
|
||||
import net.md_5.bungee.packet.Packet3Chat;
|
||||
import net.md_5.bungee.packet.PacketC9PlayerListItem;
|
||||
import net.md_5.bungee.packet.PacketCEScoreboardObjective;
|
||||
import net.md_5.bungee.packet.PacketCFScoreboardScore;
|
||||
import net.md_5.bungee.packet.PacketD0DisplayScoreboard;
|
||||
import net.md_5.bungee.packet.PacketD1Team;
|
||||
import net.md_5.bungee.packet.PacketFAPluginMessage;
|
||||
import net.md_5.bungee.packet.PacketFFKick;
|
||||
import net.md_5.bungee.packet.PacketHandler;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class DownstreamBridge extends PacketHandler
|
||||
{
|
||||
|
||||
private final ProxyServer bungee;
|
||||
private final UserConnection con;
|
||||
private final ServerConnection server;
|
||||
|
||||
@Override
|
||||
public void exception(Throwable t) throws Exception
|
||||
{
|
||||
ServerInfo def = bungee.getServerInfo( con.getPendingConnection().getListener().getFallbackServer() );
|
||||
if ( server.getInfo() != def )
|
||||
{
|
||||
con.connectNow( def );
|
||||
con.sendMessage( ChatColor.RED + "The server you were previously on went down, you have been connected to the lobby" );
|
||||
} else
|
||||
{
|
||||
con.disconnect( Util.exception( t ) );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disconnected(ChannelWrapper channel) throws Exception
|
||||
{
|
||||
// We lost connection to the server
|
||||
server.getInfo().removePlayer( con );
|
||||
bungee.getReconnectHandler().setServer( con );
|
||||
|
||||
if ( !server.isObsolete() )
|
||||
{
|
||||
con.disconnect( "[Proxy] Lost connection to server D:" );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(byte[] buf) throws Exception
|
||||
{
|
||||
EntityMap.rewrite( buf, con.getServerEntityId(), con.getClientEntityId() );
|
||||
con.sendPacket( buf );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(Packet0KeepAlive alive) throws Exception
|
||||
{
|
||||
con.setSentPingId( alive.id );
|
||||
con.setSentPingTime( System.currentTimeMillis() );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(Packet3Chat chat) throws Exception
|
||||
{
|
||||
ChatEvent chatEvent = new ChatEvent( con.getServer(), con, chat.message );
|
||||
bungee.getPluginManager().callEvent( chatEvent );
|
||||
|
||||
if ( chatEvent.isCancelled() )
|
||||
{
|
||||
throw new CancelSendSignal();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(PacketC9PlayerListItem playerList) throws Exception
|
||||
{
|
||||
|
||||
if ( !bungee.getTabListHandler().onListUpdate( con, playerList.username, playerList.online, playerList.ping ) )
|
||||
{
|
||||
throw new CancelSendSignal();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(PacketCEScoreboardObjective objective) throws Exception
|
||||
{
|
||||
Scoreboard serverScoreboard = con.getServerSentScoreboard();
|
||||
switch ( objective.action )
|
||||
{
|
||||
case 0:
|
||||
serverScoreboard.addObjective( new Objective( objective.name, objective.text ) );
|
||||
break;
|
||||
case 1:
|
||||
serverScoreboard.removeObjective( objective.name );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(PacketCFScoreboardScore score) throws Exception
|
||||
{
|
||||
Scoreboard serverScoreboard = con.getServerSentScoreboard();
|
||||
switch ( score.action )
|
||||
{
|
||||
case 0:
|
||||
Score s = new Score( score.itemName, score.scoreName, score.value );
|
||||
serverScoreboard.removeScore( score.itemName );
|
||||
serverScoreboard.addScore( s );
|
||||
break;
|
||||
case 1:
|
||||
serverScoreboard.removeScore( score.itemName );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(PacketD0DisplayScoreboard displayScoreboard) throws Exception
|
||||
{
|
||||
Scoreboard serverScoreboard = con.getServerSentScoreboard();
|
||||
serverScoreboard.setName( displayScoreboard.name );
|
||||
serverScoreboard.setPosition( Position.values()[displayScoreboard.position] );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(PacketD1Team team) throws Exception
|
||||
{
|
||||
Scoreboard serverScoreboard = con.getServerSentScoreboard();
|
||||
// Remove team and move on
|
||||
if ( team.mode == 1 )
|
||||
{
|
||||
serverScoreboard.removeTeam( team.name );
|
||||
return;
|
||||
}
|
||||
|
||||
// Create or get old team
|
||||
Team t;
|
||||
if ( team.mode == 0 )
|
||||
{
|
||||
t = new Team( team.name );
|
||||
serverScoreboard.addTeam( t );
|
||||
} else
|
||||
{
|
||||
t = serverScoreboard.getTeam( team.name );
|
||||
}
|
||||
|
||||
if ( t != null )
|
||||
{
|
||||
if ( team.mode == 0 || team.mode == 2 )
|
||||
{
|
||||
t.setDisplayName( team.displayName );
|
||||
t.setPrefix( team.prefix );
|
||||
t.setSuffix( team.suffix );
|
||||
t.setFriendlyMode( team.friendlyFire );
|
||||
}
|
||||
if ( team.players != null )
|
||||
{
|
||||
for ( String s : team.players )
|
||||
{
|
||||
if ( team.mode == 0 || team.mode == 3 )
|
||||
{
|
||||
t.addPlayer( s );
|
||||
} else
|
||||
{
|
||||
t.removePlayer( s );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(PacketFAPluginMessage pluginMessage) throws Exception
|
||||
{
|
||||
ByteArrayDataInput in = ByteStreams.newDataInput( pluginMessage.data );
|
||||
PluginMessageEvent event = new PluginMessageEvent( con.getServer(), con, pluginMessage.tag, pluginMessage.data.clone() );
|
||||
|
||||
if ( bungee.getPluginManager().callEvent( event ).isCancelled() )
|
||||
{
|
||||
throw new CancelSendSignal();
|
||||
}
|
||||
|
||||
if ( pluginMessage.tag.equals( "MC|TPack" ) && con.getPendingConnection().getListener().getTexturePack() != null )
|
||||
{
|
||||
throw new CancelSendSignal();
|
||||
}
|
||||
|
||||
if ( pluginMessage.tag.equals( "BungeeCord" ) )
|
||||
{
|
||||
ByteArrayDataOutput out = ByteStreams.newDataOutput();
|
||||
String subChannel = in.readUTF();
|
||||
|
||||
if ( subChannel.equals( "Forward" ) )
|
||||
{
|
||||
// Read data from server
|
||||
String target = in.readUTF();
|
||||
String channel = in.readUTF();
|
||||
short len = in.readShort();
|
||||
byte[] data = new byte[ len ];
|
||||
in.readFully( data );
|
||||
|
||||
// Prepare new data to send
|
||||
out.writeUTF( channel );
|
||||
out.writeShort( data.length );
|
||||
out.write( data );
|
||||
byte[] payload = out.toByteArray();
|
||||
|
||||
// Null out stream, important as we don't want to send to ourselves
|
||||
out = null;
|
||||
|
||||
if ( target.equals( "ALL" ) )
|
||||
{
|
||||
for ( ServerInfo server : bungee.getServers().values() )
|
||||
{
|
||||
if ( server != con.getServer().getInfo() )
|
||||
{
|
||||
server.sendData( "BungeeCord", payload );
|
||||
}
|
||||
}
|
||||
} else
|
||||
{
|
||||
ServerInfo server = bungee.getServerInfo( target );
|
||||
if ( server != null )
|
||||
{
|
||||
server.sendData( "BungeeCord", payload );
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( subChannel.equals( "Connect" ) )
|
||||
{
|
||||
ServerInfo server = bungee.getServerInfo( in.readUTF() );
|
||||
if ( server != null )
|
||||
{
|
||||
con.connect( server );
|
||||
}
|
||||
}
|
||||
if ( subChannel.equals( "IP" ) )
|
||||
{
|
||||
out.writeUTF( "IP" );
|
||||
out.writeUTF( con.getAddress().getHostString() );
|
||||
out.writeInt( con.getAddress().getPort() );
|
||||
}
|
||||
if ( subChannel.equals( "PlayerCount" ) )
|
||||
{
|
||||
ServerInfo server = bungee.getServerInfo( in.readUTF() );
|
||||
if ( server != null )
|
||||
{
|
||||
out.writeUTF( "PlayerCount" );
|
||||
out.writeUTF( server.getName() );
|
||||
out.writeInt( server.getPlayers().size() );
|
||||
}
|
||||
}
|
||||
if ( subChannel.equals( "PlayerList" ) )
|
||||
{
|
||||
String target = in.readUTF();
|
||||
out.writeUTF( "PlayerList" );
|
||||
if ( target.equals( "ALL" ) )
|
||||
{
|
||||
out.writeUTF( Util.csv( bungee.getPlayers() ) );
|
||||
} else
|
||||
{
|
||||
ServerInfo server = bungee.getServerInfo( target );
|
||||
if ( server != null )
|
||||
{
|
||||
out.writeUTF( server.getName() );
|
||||
out.writeUTF( Util.csv( server.getPlayers() ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( subChannel.equals( "GetServers" ) )
|
||||
{
|
||||
out.writeUTF( "GetServers" );
|
||||
out.writeUTF( Util.csv( bungee.getServers().keySet() ) );
|
||||
}
|
||||
if ( subChannel.equals( "Message" ) )
|
||||
{
|
||||
ProxiedPlayer target = bungee.getPlayer( in.readUTF() );
|
||||
if ( target != null )
|
||||
{
|
||||
target.sendMessage( in.readUTF() );
|
||||
}
|
||||
}
|
||||
if ( subChannel.equals( "GetServer" ) )
|
||||
{
|
||||
out.writeUTF( "GetServer" );
|
||||
out.writeUTF( server.getInfo().getName() );
|
||||
}
|
||||
|
||||
// Check we haven't set out to null, and we have written data, if so reply back back along the BungeeCord channel
|
||||
if ( out != null )
|
||||
{
|
||||
byte[] b = out.toByteArray();
|
||||
if ( b.length != 0 )
|
||||
{
|
||||
con.getServer().sendData( "BungeeCord", b );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(PacketFFKick kick) throws Exception
|
||||
{
|
||||
ServerInfo def = bungee.getServerInfo( con.getPendingConnection().getListener().getFallbackServer() );
|
||||
if ( Objects.equals( server.getInfo(), def ) )
|
||||
{
|
||||
def = null;
|
||||
}
|
||||
ServerKickEvent event = bungee.getPluginManager().callEvent( new ServerKickEvent( con, kick.message, def ) );
|
||||
if ( event.isCancelled() && event.getCancelServer() != null )
|
||||
{
|
||||
con.connectNow( event.getCancelServer() );
|
||||
} else
|
||||
{
|
||||
con.disconnect( "[Kicked] " + event.getKickReason() );
|
||||
}
|
||||
server.setObsolete( true );
|
||||
throw new CancelSendSignal();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return "[" + con.getName() + "] <-> DownstreamBridge <-> [" + server.getInfo().getName() + "]";
|
||||
}
|
||||
}
|
@@ -0,0 +1,267 @@
|
||||
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 java.math.BigInteger;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.URLEncoder;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.MessageDigest;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.SecretKey;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import net.md_5.bungee.BungeeCord;
|
||||
import net.md_5.bungee.EncryptionUtil;
|
||||
import net.md_5.bungee.UserConnection;
|
||||
import net.md_5.bungee.Util;
|
||||
import net.md_5.bungee.api.Callback;
|
||||
import net.md_5.bungee.api.ChatColor;
|
||||
import net.md_5.bungee.api.ProxyServer;
|
||||
import net.md_5.bungee.api.ServerPing;
|
||||
import net.md_5.bungee.api.config.ListenerInfo;
|
||||
import net.md_5.bungee.api.config.ServerInfo;
|
||||
import net.md_5.bungee.api.connection.PendingConnection;
|
||||
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.netty.CipherCodec;
|
||||
import net.md_5.bungee.netty.HandlerBoss;
|
||||
import net.md_5.bungee.netty.ChannelWrapper;
|
||||
import net.md_5.bungee.packet.Packet2Handshake;
|
||||
import net.md_5.bungee.packet.PacketCDClientStatus;
|
||||
import net.md_5.bungee.packet.PacketFAPluginMessage;
|
||||
import net.md_5.bungee.packet.PacketFCEncryptionResponse;
|
||||
import net.md_5.bungee.packet.PacketFDEncryptionRequest;
|
||||
import net.md_5.bungee.packet.PacketFEPing;
|
||||
import net.md_5.bungee.packet.PacketFFKick;
|
||||
import net.md_5.bungee.packet.PacketHandler;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class InitialHandler extends PacketHandler implements PendingConnection
|
||||
{
|
||||
|
||||
private final ProxyServer bungee;
|
||||
private ChannelWrapper ch;
|
||||
@Getter
|
||||
private final ListenerInfo listener;
|
||||
@Getter
|
||||
private Packet2Handshake handshake;
|
||||
private PacketFDEncryptionRequest request;
|
||||
private List<PacketFAPluginMessage> loginMessages = new ArrayList<>();
|
||||
private State thisState = State.HANDSHAKE;
|
||||
private SecretKey sharedKey;
|
||||
private boolean disconnected;
|
||||
|
||||
private enum State
|
||||
{
|
||||
|
||||
HANDSHAKE, ENCRYPT, LOGIN, FINISHED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void connected(ChannelWrapper channel) throws Exception
|
||||
{
|
||||
this.ch = channel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exception(Throwable t) throws Exception
|
||||
{
|
||||
disconnect( ChatColor.RED + Util.exception( t ) );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(PacketFAPluginMessage pluginMessage) throws Exception
|
||||
{
|
||||
loginMessages.add( pluginMessage );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(PacketFEPing ping) throws Exception
|
||||
{
|
||||
ServerPing response = new ServerPing( bungee.getProtocolVersion(), bungee.getGameVersion(),
|
||||
listener.getMotd(), bungee.getPlayers().size(), 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 );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(Packet2Handshake handshake) throws Exception
|
||||
{
|
||||
Preconditions.checkState( thisState == State.HANDSHAKE, "Not expecting HANDSHAKE" );
|
||||
if ( handshake.username.length() > 16 )
|
||||
{
|
||||
disconnect( "Cannot have username longer than 16 characters" );
|
||||
return;
|
||||
}
|
||||
|
||||
int limit = BungeeCord.getInstance().config.getPlayerLimit();
|
||||
Preconditions.checkState( limit <= 0 || bungee.getPlayers().size() < limit, "Server is full!" );
|
||||
|
||||
this.handshake = handshake;
|
||||
ch.write( request = EncryptionUtil.encryptRequest() );
|
||||
thisState = State.ENCRYPT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(final PacketFCEncryptionResponse encryptResponse) throws Exception
|
||||
{
|
||||
Preconditions.checkState( thisState == State.ENCRYPT, "Not expecting ENCRYPT" );
|
||||
|
||||
sharedKey = EncryptionUtil.getSecret( encryptResponse, request );
|
||||
if ( BungeeCord.getInstance().config.isOnlineMode() )
|
||||
{
|
||||
String encName = URLEncoder.encode( InitialHandler.this.getName(), "UTF-8" );
|
||||
|
||||
MessageDigest sha = MessageDigest.getInstance( "SHA-1" );
|
||||
for ( byte[] bit : new byte[][]
|
||||
{
|
||||
request.serverId.getBytes( "ISO_8859_1" ), sharedKey.getEncoded(), EncryptionUtil.keys.getPublic().getEncoded()
|
||||
} )
|
||||
{
|
||||
sha.update( bit );
|
||||
}
|
||||
|
||||
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>()
|
||||
{
|
||||
@Override
|
||||
public Response onCompleted(Response response) throws Exception
|
||||
{
|
||||
if ( "YES".equals( response.getResponseBody() ) )
|
||||
{
|
||||
finish();
|
||||
} else
|
||||
{
|
||||
disconnect( "Not authenticated with Minecraft.net" );
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onThrowable(Throwable t)
|
||||
{
|
||||
disconnect( "Error occured while contacting login servers, are they down?" + Util.exception( t ) );
|
||||
}
|
||||
} );
|
||||
} else
|
||||
{
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
||||
private void finish() throws GeneralSecurityException
|
||||
{
|
||||
// Check for multiple connections
|
||||
ProxiedPlayer old = bungee.getPlayer( handshake.username );
|
||||
if ( old != null )
|
||||
{
|
||||
old.disconnect( "You are already connected to the server" );
|
||||
}
|
||||
|
||||
Callback<LoginEvent> complete = new Callback<LoginEvent>()
|
||||
{
|
||||
@Override
|
||||
public void done(LoginEvent result, Throwable error)
|
||||
{
|
||||
if ( result.isCancelled() )
|
||||
{
|
||||
disconnect( result.getCancelReason() );
|
||||
}
|
||||
if ( disconnected )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
Cipher encrypt = EncryptionUtil.getCipher( Cipher.ENCRYPT_MODE, sharedKey );
|
||||
Cipher decrypt = EncryptionUtil.getCipher( Cipher.DECRYPT_MODE, sharedKey );
|
||||
ch.write( new PacketFCEncryptionResponse() );
|
||||
ch.getHandle().pipeline().addBefore( "decoder", "cipher", new CipherCodec( encrypt, decrypt ) );
|
||||
thisState = InitialHandler.State.LOGIN;
|
||||
} catch ( GeneralSecurityException ex )
|
||||
{
|
||||
disconnect( "Cipher error: " + Util.exception( ex ) );
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// fire login event
|
||||
bungee.getPluginManager().callEvent( new LoginEvent( InitialHandler.this, complete ) );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(PacketCDClientStatus clientStatus) throws Exception
|
||||
{
|
||||
Preconditions.checkState( thisState == State.LOGIN, "Not expecting LOGIN" );
|
||||
|
||||
UserConnection userCon = new UserConnection( (BungeeCord) 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 );
|
||||
userCon.connect( server, true );
|
||||
|
||||
thisState = State.FINISHED;
|
||||
throw new CancelSendSignal();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void disconnect(String reason)
|
||||
{
|
||||
if ( ch.getHandle().isActive() )
|
||||
{
|
||||
ch.write( new PacketFFKick( reason ) );
|
||||
ch.getHandle().close();
|
||||
disconnected = true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName()
|
||||
{
|
||||
return ( handshake == null ) ? null : handshake.username;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte getVersion()
|
||||
{
|
||||
return ( handshake == null ) ? -1 : handshake.procolVersion;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InetSocketAddress getVirtualHost()
|
||||
{
|
||||
return ( handshake == null ) ? null : new InetSocketAddress( handshake.host, handshake.port );
|
||||
}
|
||||
|
||||
@Override
|
||||
public InetSocketAddress getAddress()
|
||||
{
|
||||
return (InetSocketAddress) ch.getHandle().remoteAddress();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return "[" + ( ( getName() != null ) ? getName() : getAddress() ) + "] <-> InitialHandler";
|
||||
}
|
||||
}
|
@@ -0,0 +1,47 @@
|
||||
package net.md_5.bungee.connection;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import net.md_5.bungee.api.Callback;
|
||||
import net.md_5.bungee.api.ServerPing;
|
||||
import net.md_5.bungee.api.config.ServerInfo;
|
||||
import net.md_5.bungee.netty.ChannelWrapper;
|
||||
import net.md_5.bungee.packet.PacketFFKick;
|
||||
import net.md_5.bungee.packet.PacketHandler;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
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 );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exception(Throwable t) throws Exception
|
||||
{
|
||||
callback.done( null, t );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(PacketFFKick kick) throws Exception
|
||||
{
|
||||
String[] split = kick.message.split( "\00" );
|
||||
ServerPing ping = new ServerPing( Byte.parseByte( split[1] ), split[2], split[3], Integer.parseInt( split[4] ), Integer.parseInt( split[5] ) );
|
||||
callback.done( ping, null );
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return "[Ping Handler] -> " + target.getName();
|
||||
}
|
||||
}
|
@@ -0,0 +1,128 @@
|
||||
package net.md_5.bungee.connection;
|
||||
|
||||
import io.netty.channel.Channel;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import net.md_5.bungee.BungeeCord;
|
||||
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.packet.Packet0KeepAlive;
|
||||
import net.md_5.bungee.packet.Packet3Chat;
|
||||
import net.md_5.bungee.packet.PacketCCSettings;
|
||||
import net.md_5.bungee.packet.PacketFAPluginMessage;
|
||||
import net.md_5.bungee.packet.PacketHandler;
|
||||
|
||||
public class UpstreamBridge extends PacketHandler
|
||||
{
|
||||
|
||||
private final ProxyServer bungee;
|
||||
private final UserConnection con;
|
||||
|
||||
public UpstreamBridge(ProxyServer bungee, UserConnection con)
|
||||
{
|
||||
this.bungee = bungee;
|
||||
this.con = con;
|
||||
|
||||
BungeeCord.getInstance().connections.put( con.getName(), con );
|
||||
bungee.getTabListHandler().onConnect( con );
|
||||
con.sendPacket( BungeeCord.getInstance().registerChannels() );
|
||||
|
||||
TexturePackInfo texture = con.getPendingConnection().getListener().getTexturePack();
|
||||
if ( texture != null )
|
||||
{
|
||||
con.sendPacket( new PacketFAPluginMessage( "MC|TPack", ( texture.getUrl() + "\00" + texture.getSize() ).getBytes() ) );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exception(Throwable t) throws Exception
|
||||
{
|
||||
con.disconnect( Util.exception( t ) );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disconnected(ChannelWrapper channel) throws Exception
|
||||
{
|
||||
// We lost connection to the client
|
||||
PlayerDisconnectEvent event = new PlayerDisconnectEvent( con );
|
||||
bungee.getPluginManager().callEvent( event );
|
||||
bungee.getTabListHandler().onDisconnect( con );
|
||||
BungeeCord.getInstance().connections.remove( con.getName() ); //TODO: Better way, why do we need to raw access?
|
||||
|
||||
if ( con.getServer() != null )
|
||||
{
|
||||
con.getServer().disconnect( "Quitting" );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(byte[] buf) throws Exception
|
||||
{
|
||||
EntityMap.rewrite( buf, con.getClientEntityId(), con.getServerEntityId() );
|
||||
if ( con.getServer() != null )
|
||||
{
|
||||
con.getServer().getCh().write( buf );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(Packet0KeepAlive alive) throws Exception
|
||||
{
|
||||
if ( alive.id == con.getSentPingId() )
|
||||
{
|
||||
int newPing = (int) ( System.currentTimeMillis() - con.getSentPingTime() );
|
||||
bungee.getTabListHandler().onPingChange( con, newPing );
|
||||
con.setPing( newPing );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(Packet3Chat chat) throws Exception
|
||||
{
|
||||
ChatEvent chatEvent = new ChatEvent( con, con.getServer(), chat.message );
|
||||
if ( bungee.getPluginManager().callEvent( chatEvent ).isCancelled() )
|
||||
{
|
||||
throw new CancelSendSignal();
|
||||
}
|
||||
if ( chatEvent.isCommand() )
|
||||
{
|
||||
if ( bungee.getPluginManager().dispatchCommand( con, chat.message.substring( 1 ) ) )
|
||||
{
|
||||
throw new CancelSendSignal();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(PacketCCSettings settings) throws Exception
|
||||
{
|
||||
con.setSettings( settings );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(PacketFAPluginMessage pluginMessage) throws Exception
|
||||
{
|
||||
if ( pluginMessage.tag.equals( "BungeeCord" ) )
|
||||
{
|
||||
throw new CancelSendSignal();
|
||||
}
|
||||
|
||||
PluginMessageEvent event = new PluginMessageEvent( con, con.getServer(), pluginMessage.tag, pluginMessage.data.clone() );
|
||||
if ( bungee.getPluginManager().callEvent( event ).isCancelled() )
|
||||
{
|
||||
throw new CancelSendSignal();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return "[" + con.getName() + "] -> UpstreamBridge";
|
||||
}
|
||||
}
|
@@ -0,0 +1,26 @@
|
||||
package net.md_5.bungee.netty;
|
||||
|
||||
import io.netty.channel.Channel;
|
||||
|
||||
public class ChannelWrapper
|
||||
{
|
||||
|
||||
private final Channel ch;
|
||||
private final ReusableChannelPromise promise;
|
||||
|
||||
public ChannelWrapper(Channel ch)
|
||||
{
|
||||
this.ch = ch;
|
||||
this.promise = new ReusableChannelPromise( ch );
|
||||
}
|
||||
|
||||
public void write(Object packet)
|
||||
{
|
||||
ch.write( packet, promise );
|
||||
}
|
||||
|
||||
public Channel getHandle()
|
||||
{
|
||||
return ch;
|
||||
}
|
||||
}
|
68
proxy/src/main/java/net/md_5/bungee/netty/CipherCodec.java
Normal file
68
proxy/src/main/java/net/md_5/bungee/netty/CipherCodec.java
Normal file
@@ -0,0 +1,68 @@
|
||||
package net.md_5.bungee.netty;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.ByteToByteCodec;
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.ShortBufferException;
|
||||
|
||||
/**
|
||||
* This class is a complete solution for encrypting and decoding bytes in a
|
||||
* Netty stream. It takes two {@link Cipher} instances, used for encryption and
|
||||
* decryption respectively.
|
||||
*/
|
||||
public class CipherCodec extends ByteToByteCodec
|
||||
{
|
||||
|
||||
private Cipher encrypt;
|
||||
private Cipher decrypt;
|
||||
private ThreadLocal<byte[]> heapInLocal = new EmptyByteThreadLocal();
|
||||
private ThreadLocal<byte[]> heapOutLocal = new EmptyByteThreadLocal();
|
||||
|
||||
private static class EmptyByteThreadLocal extends ThreadLocal<byte[]>
|
||||
{
|
||||
|
||||
@Override
|
||||
protected byte[] initialValue()
|
||||
{
|
||||
return new byte[ 0 ];
|
||||
}
|
||||
}
|
||||
|
||||
public CipherCodec(Cipher encrypt, Cipher decrypt)
|
||||
{
|
||||
this.encrypt = encrypt;
|
||||
this.decrypt = decrypt;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void encode(ChannelHandlerContext ctx, ByteBuf in, ByteBuf out) throws Exception
|
||||
{
|
||||
cipher( in, out, encrypt );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void decode(ChannelHandlerContext ctx, ByteBuf in, ByteBuf out) throws Exception
|
||||
{
|
||||
cipher( in, out, decrypt );
|
||||
}
|
||||
|
||||
private void cipher(ByteBuf in, ByteBuf out, Cipher cipher) throws ShortBufferException
|
||||
{
|
||||
byte[] heapIn = heapInLocal.get();
|
||||
int readableBytes = in.readableBytes();
|
||||
if ( heapIn.length < readableBytes )
|
||||
{
|
||||
heapIn = new byte[ readableBytes ];
|
||||
}
|
||||
in.readBytes( heapIn, 0, readableBytes );
|
||||
|
||||
byte[] heapOut = heapOutLocal.get();
|
||||
int outputSize = cipher.getOutputSize( readableBytes );
|
||||
if ( heapOut.length < outputSize )
|
||||
{
|
||||
heapOut = new byte[ outputSize ];
|
||||
}
|
||||
out.writeBytes( heapOut, 0, cipher.update( heapIn, 0, readableBytes, heapOut ) );
|
||||
}
|
||||
}
|
@@ -0,0 +1,18 @@
|
||||
package net.md_5.bungee.netty;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.ChannelHandler;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.MessageToByteEncoder;
|
||||
import net.md_5.bungee.packet.DefinedPacket;
|
||||
|
||||
@ChannelHandler.Sharable
|
||||
public class DefinedPacketEncoder extends MessageToByteEncoder<DefinedPacket>
|
||||
{
|
||||
|
||||
@Override
|
||||
protected void encode(ChannelHandlerContext ctx, DefinedPacket msg, ByteBuf out) throws Exception
|
||||
{
|
||||
out.writeBytes( msg.getPacket() );
|
||||
}
|
||||
}
|
104
proxy/src/main/java/net/md_5/bungee/netty/HandlerBoss.java
Normal file
104
proxy/src/main/java/net/md_5/bungee/netty/HandlerBoss.java
Normal file
@@ -0,0 +1,104 @@
|
||||
package net.md_5.bungee.netty;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelInboundMessageHandlerAdapter;
|
||||
import io.netty.handler.timeout.ReadTimeoutException;
|
||||
import java.io.IOException;
|
||||
import java.util.logging.Level;
|
||||
import net.md_5.bungee.api.ProxyServer;
|
||||
import net.md_5.bungee.connection.CancelSendSignal;
|
||||
import net.md_5.bungee.packet.DefinedPacket;
|
||||
import net.md_5.bungee.packet.PacketHandler;
|
||||
|
||||
/**
|
||||
* This class is a primitive wrapper for {@link PacketHandler} instances tied to
|
||||
* channels to maintain simple states, and only call the required, adapted
|
||||
* methods when the channel is connected.
|
||||
*/
|
||||
public class HandlerBoss extends ChannelInboundMessageHandlerAdapter<byte[]>
|
||||
{
|
||||
|
||||
private ChannelWrapper channel;
|
||||
private PacketHandler handler;
|
||||
|
||||
public void setHandler(PacketHandler handler)
|
||||
{
|
||||
Preconditions.checkArgument( handler != null, "handler" );
|
||||
this.handler = handler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelActive(ChannelHandlerContext ctx) throws Exception
|
||||
{
|
||||
if ( handler != null )
|
||||
{
|
||||
channel = new ChannelWrapper( ctx.channel() );
|
||||
handler.connected( channel );
|
||||
ProxyServer.getInstance().getLogger().log( Level.INFO, "{0} has connected", handler );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelInactive(ChannelHandlerContext ctx) throws Exception
|
||||
{
|
||||
if ( handler != null )
|
||||
{
|
||||
ProxyServer.getInstance().getLogger().log( Level.INFO, "{0} has disconnected", handler );
|
||||
handler.disconnected( channel );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void messageReceived(ChannelHandlerContext ctx, byte[] msg) throws Exception
|
||||
{
|
||||
if ( handler != null && ctx.channel().isActive() )
|
||||
{
|
||||
DefinedPacket packet = DefinedPacket.packet( msg );
|
||||
boolean sendPacket = true;
|
||||
if ( packet != null )
|
||||
{
|
||||
try
|
||||
{
|
||||
packet.handle( handler );
|
||||
} catch ( CancelSendSignal ex )
|
||||
{
|
||||
sendPacket = false;
|
||||
}
|
||||
}
|
||||
if ( sendPacket )
|
||||
{
|
||||
handler.handle( msg );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception
|
||||
{
|
||||
if ( ctx.channel().isActive() )
|
||||
{
|
||||
if ( cause instanceof ReadTimeoutException )
|
||||
{
|
||||
ProxyServer.getInstance().getLogger().log( Level.WARNING, handler + " - read timed out" );
|
||||
} else if ( cause instanceof IOException )
|
||||
{
|
||||
ProxyServer.getInstance().getLogger().log( Level.WARNING, handler + " - IOException: " + cause.getMessage() );
|
||||
} else
|
||||
{
|
||||
ProxyServer.getInstance().getLogger().log( Level.SEVERE, handler + " - encountered exception", cause );
|
||||
}
|
||||
if ( handler != null )
|
||||
{
|
||||
try
|
||||
{
|
||||
handler.exception( cause );
|
||||
} catch ( Exception ex )
|
||||
{
|
||||
ProxyServer.getInstance().getLogger().log( Level.SEVERE, handler + " - exception processing exception", ex );
|
||||
}
|
||||
}
|
||||
ctx.close();
|
||||
}
|
||||
}
|
||||
}
|
37
proxy/src/main/java/net/md_5/bungee/netty/PacketDecoder.java
Normal file
37
proxy/src/main/java/net/md_5/bungee/netty/PacketDecoder.java
Normal file
@@ -0,0 +1,37 @@
|
||||
package net.md_5.bungee.netty;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.ReplayingDecoder;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import net.md_5.bungee.protocol.netty.PacketReader;
|
||||
|
||||
/**
|
||||
* This class will attempt to read a packet from {@link PacketReader}, with the
|
||||
* specified {@link #protocol} before returning a new {@link ByteBuf} with the
|
||||
* copied contents of all bytes read in this frame.
|
||||
* <p/>
|
||||
* It is based on {@link ReplayingDecoder} so that packets will only be returned
|
||||
* when all needed data is present.
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
public class PacketDecoder extends ReplayingDecoder<Void>
|
||||
{
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
private int protocol;
|
||||
|
||||
@Override
|
||||
protected byte[] decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception
|
||||
{
|
||||
int startIndex = in.readerIndex();
|
||||
PacketReader.readPacket( in, protocol );
|
||||
byte[] buf = new byte[ in.readerIndex() - startIndex ];
|
||||
in.readerIndex( startIndex );
|
||||
in.readBytes( buf, 0, buf.length );
|
||||
return buf;
|
||||
}
|
||||
}
|
68
proxy/src/main/java/net/md_5/bungee/netty/PipelineUtils.java
Normal file
68
proxy/src/main/java/net/md_5/bungee/netty/PipelineUtils.java
Normal file
@@ -0,0 +1,68 @@
|
||||
package net.md_5.bungee.netty;
|
||||
|
||||
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.util.concurrent.TimeUnit;
|
||||
import net.md_5.bungee.BungeeCord;
|
||||
import net.md_5.bungee.BungeeServerInfo;
|
||||
import net.md_5.bungee.ServerConnector;
|
||||
import net.md_5.bungee.UserConnection;
|
||||
import net.md_5.bungee.connection.InitialHandler;
|
||||
import net.md_5.bungee.api.ProxyServer;
|
||||
import net.md_5.bungee.api.config.ListenerInfo;
|
||||
import net.md_5.bungee.protocol.PacketDefinitions;
|
||||
|
||||
public class PipelineUtils
|
||||
{
|
||||
|
||||
public static final AttributeKey<ListenerInfo> LISTENER = new AttributeKey<>( "ListerInfo" );
|
||||
public static final AttributeKey<UserConnection> USER = new AttributeKey<>( "User" );
|
||||
public static final AttributeKey<BungeeServerInfo> TARGET = new AttributeKey<>( "Target" );
|
||||
public static final ChannelInitializer<Channel> SERVER_CHILD = new ChannelInitializer<Channel>()
|
||||
{
|
||||
@Override
|
||||
protected void initChannel(Channel ch) throws Exception
|
||||
{
|
||||
BASE.initChannel( ch );
|
||||
ch.pipeline().get( HandlerBoss.class ).setHandler( new InitialHandler( ProxyServer.getInstance(), ch.attr( LISTENER ).get() ) );
|
||||
}
|
||||
};
|
||||
public static final ChannelInitializer<Channel> CLIENT = new ChannelInitializer<Channel>()
|
||||
{
|
||||
@Override
|
||||
protected void initChannel(Channel ch) throws Exception
|
||||
{
|
||||
BASE.initChannel( ch );
|
||||
ch.pipeline().get( HandlerBoss.class ).setHandler( new ServerConnector( ProxyServer.getInstance(), ch.attr( USER ).get(), ch.attr( TARGET ).get() ) );
|
||||
}
|
||||
};
|
||||
public static final Base BASE = new Base();
|
||||
private static final DefinedPacketEncoder packetEncoder = new DefinedPacketEncoder();
|
||||
private static final ByteArrayEncoder arrayEncoder = new ByteArrayEncoder();
|
||||
|
||||
public final static class Base extends ChannelInitializer<Channel>
|
||||
{
|
||||
|
||||
@Override
|
||||
public void initChannel(Channel ch) throws Exception
|
||||
{
|
||||
try
|
||||
{
|
||||
ch.config().setOption( ChannelOption.IP_TOS, 0x18 );
|
||||
} catch ( ChannelException ex )
|
||||
{
|
||||
// IP_TOS is not supported (Windows XP / Windows Server 2003)
|
||||
}
|
||||
ch.pipeline().addLast( "timer", new ReadTimeoutHandler( BungeeCord.getInstance().config.getTimeout(), TimeUnit.MILLISECONDS ) );
|
||||
ch.pipeline().addLast( "decoder", new PacketDecoder( PacketDefinitions.VANILLA_PROTOCOL ) );
|
||||
ch.pipeline().addLast( "packet-encoder", packetEncoder );
|
||||
ch.pipeline().addLast( "array-encoder", arrayEncoder );
|
||||
ch.pipeline().addLast( "handler", new HandlerBoss() );
|
||||
}
|
||||
};
|
||||
}
|
@@ -0,0 +1,179 @@
|
||||
package net.md_5.bungee.netty;
|
||||
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelPromise;
|
||||
import io.netty.util.concurrent.Future;
|
||||
import io.netty.util.concurrent.GenericFutureListener;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class ReusableChannelPromise implements ChannelPromise
|
||||
{
|
||||
|
||||
private final Channel ch;
|
||||
|
||||
@Override
|
||||
public Channel channel()
|
||||
{
|
||||
return ch;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelPromise setSuccess(Void result)
|
||||
{
|
||||
throw new UnsupportedOperationException( "Not supported yet." );
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelPromise setSuccess()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean trySuccess()
|
||||
{
|
||||
throw new UnsupportedOperationException( "Not supported yet." );
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelPromise setFailure(Throwable cause)
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelPromise addListener(GenericFutureListener<? extends Future<Void>> listener)
|
||||
{
|
||||
throw new UnsupportedOperationException( "Not supported yet." );
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelPromise addListeners(GenericFutureListener<? extends Future<Void>>... listeners)
|
||||
{
|
||||
throw new UnsupportedOperationException( "Not supported yet." );
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelPromise removeListener(GenericFutureListener<? extends Future<Void>> listener)
|
||||
{
|
||||
throw new UnsupportedOperationException( "Not supported yet." );
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelPromise removeListeners(GenericFutureListener<? extends Future<Void>>... listeners)
|
||||
{
|
||||
throw new UnsupportedOperationException( "Not supported yet." );
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelPromise sync() throws InterruptedException
|
||||
{
|
||||
throw new UnsupportedOperationException( "Not supported yet." );
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelPromise syncUninterruptibly()
|
||||
{
|
||||
throw new UnsupportedOperationException( "Not supported yet." );
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelPromise await() throws InterruptedException
|
||||
{
|
||||
throw new UnsupportedOperationException( "Not supported yet." );
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelPromise awaitUninterruptibly()
|
||||
{
|
||||
throw new UnsupportedOperationException( "Not supported yet." );
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSuccess()
|
||||
{
|
||||
throw new UnsupportedOperationException( "Not supported yet." );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Throwable cause()
|
||||
{
|
||||
throw new UnsupportedOperationException( "Not supported yet." );
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean await(long timeout, TimeUnit unit) throws InterruptedException
|
||||
{
|
||||
throw new UnsupportedOperationException( "Not supported yet." );
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean await(long timeoutMillis) throws InterruptedException
|
||||
{
|
||||
throw new UnsupportedOperationException( "Not supported yet." );
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean awaitUninterruptibly(long timeout, TimeUnit unit)
|
||||
{
|
||||
throw new UnsupportedOperationException( "Not supported yet." );
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean awaitUninterruptibly(long timeoutMillis)
|
||||
{
|
||||
throw new UnsupportedOperationException( "Not supported yet." );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void getNow()
|
||||
{
|
||||
throw new UnsupportedOperationException( "Not supported yet." );
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean cancel(boolean mayInterruptIfRunning)
|
||||
{
|
||||
throw new UnsupportedOperationException( "Not supported yet." );
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCancelled()
|
||||
{
|
||||
throw new UnsupportedOperationException( "Not supported yet." );
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDone()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void get() throws InterruptedException, ExecutionException
|
||||
{
|
||||
throw new UnsupportedOperationException( "Not supported yet." );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException
|
||||
{
|
||||
throw new UnsupportedOperationException( "Not supported yet." );
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean trySuccess(Void result)
|
||||
{
|
||||
throw new UnsupportedOperationException( "Not supported yet." );
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean tryFailure(Throwable cause)
|
||||
{
|
||||
throw new UnsupportedOperationException( "Not supported yet." );
|
||||
}
|
||||
}
|
@@ -1,6 +1,5 @@
|
||||
package net.md_5.bungee.packet;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.io.ByteArrayDataOutput;
|
||||
import com.google.common.io.ByteStreams;
|
||||
import java.io.ByteArrayInputStream;
|
||||
@@ -20,42 +19,33 @@ import net.md_5.bungee.Util;
|
||||
public abstract class DefinedPacket implements DataOutput
|
||||
{
|
||||
|
||||
private interface Overriden
|
||||
private static interface Overriden
|
||||
{
|
||||
|
||||
void readUTF();
|
||||
|
||||
void writeUTF(String s);
|
||||
}
|
||||
private ByteArrayInputStream bin;
|
||||
private DataInputStream input;
|
||||
private ByteArrayInputStream byteStream;
|
||||
private DataInputStream in;
|
||||
@Delegate(excludes = Overriden.class)
|
||||
private ByteArrayDataOutput out;
|
||||
/**
|
||||
* Packet id.
|
||||
*/
|
||||
public final int id;
|
||||
/**
|
||||
* Already constructed packet.
|
||||
*/
|
||||
private byte[] packet;
|
||||
private byte[] buf;
|
||||
|
||||
public DefinedPacket(int id, byte[] buf)
|
||||
{
|
||||
bin = new ByteArrayInputStream( buf );
|
||||
input = new DataInputStream( bin );
|
||||
byteStream = new ByteArrayInputStream( buf );
|
||||
in = new DataInputStream( byteStream );
|
||||
if ( readUnsignedByte() != id )
|
||||
{
|
||||
throw new IllegalArgumentException( "Wasn't expecting packet id " + Util.hex( id ) );
|
||||
}
|
||||
this.id = id;
|
||||
packet = buf;
|
||||
this.buf = buf;
|
||||
}
|
||||
|
||||
public DefinedPacket(int id)
|
||||
{
|
||||
out = ByteStreams.newDataOutput();
|
||||
this.id = id;
|
||||
writeByte( id );
|
||||
}
|
||||
|
||||
@@ -67,7 +57,7 @@ public abstract class DefinedPacket implements DataOutput
|
||||
*/
|
||||
public byte[] getPacket()
|
||||
{
|
||||
return packet == null ? packet = out.toByteArray() : packet;
|
||||
return buf == null ? buf = out.toByteArray() : buf;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -104,14 +94,14 @@ public abstract class DefinedPacket implements DataOutput
|
||||
|
||||
public final int available()
|
||||
{
|
||||
return bin.available();
|
||||
return byteStream.available();
|
||||
}
|
||||
|
||||
public final void readFully(byte b[])
|
||||
{
|
||||
try
|
||||
{
|
||||
input.readFully( b );
|
||||
in.readFully( b );
|
||||
} catch ( IOException e )
|
||||
{
|
||||
throw new IllegalStateException( e );
|
||||
@@ -122,7 +112,7 @@ public abstract class DefinedPacket implements DataOutput
|
||||
{
|
||||
try
|
||||
{
|
||||
return input.readBoolean();
|
||||
return in.readBoolean();
|
||||
} catch ( IOException e )
|
||||
{
|
||||
throw new IllegalStateException( e );
|
||||
@@ -133,7 +123,7 @@ public abstract class DefinedPacket implements DataOutput
|
||||
{
|
||||
try
|
||||
{
|
||||
return input.readByte();
|
||||
return in.readByte();
|
||||
} catch ( IOException e )
|
||||
{
|
||||
throw new IllegalStateException( e );
|
||||
@@ -144,7 +134,7 @@ public abstract class DefinedPacket implements DataOutput
|
||||
{
|
||||
try
|
||||
{
|
||||
return input.readUnsignedByte();
|
||||
return in.readUnsignedByte();
|
||||
} catch ( IOException e )
|
||||
{
|
||||
throw new IllegalStateException( e );
|
||||
@@ -155,7 +145,7 @@ public abstract class DefinedPacket implements DataOutput
|
||||
{
|
||||
try
|
||||
{
|
||||
return input.readShort();
|
||||
return in.readShort();
|
||||
} catch ( IOException e )
|
||||
{
|
||||
throw new IllegalStateException( e );
|
||||
@@ -166,7 +156,7 @@ public abstract class DefinedPacket implements DataOutput
|
||||
{
|
||||
try
|
||||
{
|
||||
return input.readChar();
|
||||
return in.readChar();
|
||||
} catch ( IOException e )
|
||||
{
|
||||
throw new IllegalStateException( e );
|
||||
@@ -177,7 +167,7 @@ public abstract class DefinedPacket implements DataOutput
|
||||
{
|
||||
try
|
||||
{
|
||||
return input.readInt();
|
||||
return in.readInt();
|
||||
} catch ( IOException e )
|
||||
{
|
||||
throw new IllegalStateException( e );
|
||||
@@ -194,18 +184,27 @@ public abstract class DefinedPacket implements DataOutput
|
||||
public abstract String toString();
|
||||
|
||||
public abstract void handle(PacketHandler handler) throws Exception;
|
||||
@SuppressWarnings("unchecked")
|
||||
private static Class<? extends DefinedPacket>[] classes = new Class[ 256 ];
|
||||
@SuppressWarnings("unchecked")
|
||||
private static Constructor<? extends DefinedPacket>[] consructors = new Constructor[ 256 ];
|
||||
|
||||
public static DefinedPacket packet(byte[] buf)
|
||||
{
|
||||
int id = Util.getId( buf );
|
||||
int id = buf[0] & 0xFF;
|
||||
Class<? extends DefinedPacket> clazz = classes[id];
|
||||
DefinedPacket ret = null;
|
||||
if ( clazz != null )
|
||||
{
|
||||
try
|
||||
{
|
||||
Constructor<? extends DefinedPacket> constructor = clazz.getDeclaredConstructor( byte[].class );
|
||||
Constructor<? extends DefinedPacket> constructor = consructors[id];
|
||||
if ( constructor == null )
|
||||
{
|
||||
constructor = clazz.getDeclaredConstructor( byte[].class );
|
||||
consructors[id] = constructor;
|
||||
}
|
||||
|
||||
if ( constructor != null )
|
||||
{
|
||||
ret = constructor.newInstance( buf );
|
||||
@@ -215,7 +214,6 @@ public abstract class DefinedPacket implements DataOutput
|
||||
}
|
||||
}
|
||||
|
||||
Preconditions.checkState( ret != null, "Don't know how to deal with packet ID %s", Util.hex( id ) );
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -227,7 +225,12 @@ public abstract class DefinedPacket implements DataOutput
|
||||
classes[0x03] = Packet3Chat.class;
|
||||
classes[0x09] = Packet9Respawn.class;
|
||||
classes[0xC9] = PacketC9PlayerListItem.class;
|
||||
classes[0xCC] = PacketCCSettings.class;
|
||||
classes[0xCD] = PacketCDClientStatus.class;
|
||||
classes[0xCE] = PacketCEScoreboardObjective.class;
|
||||
classes[0xCF] = PacketCFScoreboardScore.class;
|
||||
classes[0xD0] = PacketD0DisplayScoreboard.class;
|
||||
classes[0xD1] = PacketD1Team.class;
|
||||
classes[0xFA] = PacketFAPluginMessage.class;
|
||||
classes[0xFC] = PacketFCEncryptionResponse.class;
|
||||
classes[0xFD] = PacketFDEncryptionRequest.class;
|
||||
|
@@ -10,9 +10,9 @@ public class Packet0KeepAlive extends DefinedPacket
|
||||
|
||||
public int id;
|
||||
|
||||
public Packet0KeepAlive(byte[] buffer)
|
||||
Packet0KeepAlive(byte[] buf)
|
||||
{
|
||||
super( 0x00, buffer );
|
||||
super( 0x00, buf );
|
||||
id = readInt();
|
||||
}
|
||||
|
||||
|
@@ -26,9 +26,16 @@ public class Packet1Login extends DefinedPacket
|
||||
writeByte( difficulty );
|
||||
writeByte( unused );
|
||||
writeByte( maxPlayers );
|
||||
this.entityId = entityId;
|
||||
this.levelType = levelType;
|
||||
this.gameMode = gameMode;
|
||||
this.dimension = dimension;
|
||||
this.difficulty = difficulty;
|
||||
this.unused = unused;
|
||||
this.maxPlayers = maxPlayers;
|
||||
}
|
||||
|
||||
public Packet1Login(byte[] buf)
|
||||
Packet1Login(byte[] buf)
|
||||
{
|
||||
super( 0x01, buf );
|
||||
this.entityId = readInt();
|
||||
|
@@ -20,9 +20,13 @@ public class Packet2Handshake extends DefinedPacket
|
||||
writeUTF( username );
|
||||
writeUTF( host );
|
||||
writeInt( port );
|
||||
this.procolVersion = protocolVersion;
|
||||
this.username = username;
|
||||
this.host = host;
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
public Packet2Handshake(byte[] buf)
|
||||
Packet2Handshake(byte[] buf)
|
||||
{
|
||||
super( 0x02, buf );
|
||||
this.procolVersion = readByte();
|
||||
|
@@ -14,9 +14,10 @@ public class Packet3Chat extends DefinedPacket
|
||||
{
|
||||
super( 0x03 );
|
||||
writeUTF( message );
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public Packet3Chat(byte[] buf)
|
||||
Packet3Chat(byte[] buf)
|
||||
{
|
||||
super( 0x03, buf );
|
||||
this.message = readUTF();
|
||||
|
@@ -8,6 +8,8 @@ import lombok.ToString;
|
||||
public class Packet9Respawn extends DefinedPacket
|
||||
{
|
||||
|
||||
public static final Packet9Respawn DIM1_SWITCH = new Packet9Respawn( (byte) 1, (byte) 0, (byte) 0, (short) 256, "DEFAULT" );
|
||||
public static final Packet9Respawn DIM2_SWITCH = new Packet9Respawn( (byte) -1, (byte) 0, (byte) 0, (short) 256, "DEFAULT" );
|
||||
public int dimension;
|
||||
public byte difficulty;
|
||||
public byte gameMode;
|
||||
@@ -22,9 +24,14 @@ public class Packet9Respawn extends DefinedPacket
|
||||
writeByte( gameMode );
|
||||
writeShort( worldHeight );
|
||||
writeUTF( levelType );
|
||||
this.dimension = dimension;
|
||||
this.difficulty = difficulty;
|
||||
this.gameMode = gameMode;
|
||||
this.worldHeight = worldHeight;
|
||||
this.levelType = levelType;
|
||||
}
|
||||
|
||||
public Packet9Respawn(byte[] buf)
|
||||
Packet9Respawn(byte[] buf)
|
||||
{
|
||||
super( 0x09, buf );
|
||||
this.dimension = readInt();
|
||||
|
@@ -12,9 +12,9 @@ public class PacketC9PlayerListItem extends DefinedPacket
|
||||
public boolean online;
|
||||
public int ping;
|
||||
|
||||
public PacketC9PlayerListItem(byte[] packet)
|
||||
PacketC9PlayerListItem(byte[] buf)
|
||||
{
|
||||
super( 0xC9, packet );
|
||||
super( 0xC9, buf );
|
||||
username = readUTF();
|
||||
online = readBoolean();
|
||||
ping = readShort();
|
||||
|
@@ -0,0 +1,32 @@
|
||||
package net.md_5.bungee.packet;
|
||||
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
|
||||
@ToString
|
||||
@EqualsAndHashCode(callSuper = false)
|
||||
public class PacketCCSettings extends DefinedPacket
|
||||
{
|
||||
|
||||
public String locale;
|
||||
public byte viewDistance;
|
||||
public byte chatFlags;
|
||||
public byte difficulty;
|
||||
public boolean showCape;
|
||||
|
||||
public PacketCCSettings(byte[] buf)
|
||||
{
|
||||
super( 0xCC, buf );
|
||||
locale = readUTF();
|
||||
viewDistance = readByte();
|
||||
chatFlags = readByte();
|
||||
difficulty = readByte();
|
||||
showCape = readBoolean();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(PacketHandler handler) throws Exception
|
||||
{
|
||||
handler.handle( this );
|
||||
}
|
||||
}
|
@@ -8,6 +8,10 @@ import lombok.ToString;
|
||||
public class PacketCDClientStatus extends DefinedPacket
|
||||
{
|
||||
|
||||
/**
|
||||
* Represents the packet the client sends to the server when it is ready to
|
||||
* login.
|
||||
*/
|
||||
public static PacketCDClientStatus CLIENT_LOGIN = new PacketCDClientStatus( (byte) 0 );
|
||||
|
||||
/**
|
||||
@@ -21,7 +25,7 @@ public class PacketCDClientStatus extends DefinedPacket
|
||||
writeByte( payload );
|
||||
}
|
||||
|
||||
public PacketCDClientStatus(byte[] buf)
|
||||
PacketCDClientStatus(byte[] buf)
|
||||
{
|
||||
super( 0xCD, buf );
|
||||
}
|
||||
|
@@ -0,0 +1,42 @@
|
||||
package net.md_5.bungee.packet;
|
||||
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
|
||||
@ToString
|
||||
@EqualsAndHashCode(callSuper = false)
|
||||
public class PacketCEScoreboardObjective extends DefinedPacket
|
||||
{
|
||||
|
||||
public String name;
|
||||
public String text;
|
||||
/**
|
||||
* 0 to create, 1 to remove.
|
||||
*/
|
||||
public byte action;
|
||||
|
||||
public PacketCEScoreboardObjective(String name, String text, byte status)
|
||||
{
|
||||
super( 0xCE );
|
||||
writeUTF( name );
|
||||
writeUTF( text );
|
||||
writeByte( status );
|
||||
this.name = name;
|
||||
this.text = text;
|
||||
this.action = status;
|
||||
}
|
||||
|
||||
PacketCEScoreboardObjective(byte[] buf)
|
||||
{
|
||||
super( 0xCE, buf );
|
||||
this.name = readUTF();
|
||||
this.text = readUTF();
|
||||
this.action = readByte();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(PacketHandler handler) throws Exception
|
||||
{
|
||||
handler.handle( this );
|
||||
}
|
||||
}
|
@@ -0,0 +1,52 @@
|
||||
package net.md_5.bungee.packet;
|
||||
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
|
||||
@ToString
|
||||
@EqualsAndHashCode(callSuper = false)
|
||||
public class PacketCFScoreboardScore extends DefinedPacket
|
||||
{
|
||||
|
||||
public String itemName;
|
||||
/**
|
||||
* 0 = create / update, 1 = remove.
|
||||
*/
|
||||
public byte action;
|
||||
public String scoreName;
|
||||
public int value;
|
||||
|
||||
public PacketCFScoreboardScore(byte[] buf)
|
||||
{
|
||||
super( 0xCF, buf );
|
||||
itemName = readUTF();
|
||||
action = readByte();
|
||||
if ( action == 0 )
|
||||
{
|
||||
scoreName = readUTF();
|
||||
value = readInt();
|
||||
}
|
||||
}
|
||||
|
||||
public PacketCFScoreboardScore(String itemName, byte action, String scoreName, int value)
|
||||
{
|
||||
super( 0xCF );
|
||||
writeUTF( itemName );
|
||||
writeByte( action );
|
||||
if ( action == 0 )
|
||||
{
|
||||
writeUTF( scoreName );
|
||||
writeInt( value );
|
||||
}
|
||||
this.itemName = itemName;
|
||||
this.action = action;
|
||||
this.scoreName = scoreName;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(PacketHandler handler) throws Exception
|
||||
{
|
||||
handler.handle( this );
|
||||
}
|
||||
}
|
@@ -0,0 +1,29 @@
|
||||
package net.md_5.bungee.packet;
|
||||
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
|
||||
@ToString
|
||||
@EqualsAndHashCode(callSuper = false)
|
||||
public class PacketD0DisplayScoreboard extends DefinedPacket
|
||||
{
|
||||
|
||||
/**
|
||||
* 0 = list, 1 = side, 2 = below.
|
||||
*/
|
||||
public byte position;
|
||||
public String name;
|
||||
|
||||
public PacketD0DisplayScoreboard(byte[] buf)
|
||||
{
|
||||
super( 0xD0, buf );
|
||||
position = readByte();
|
||||
name = readUTF();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(PacketHandler handler) throws Exception
|
||||
{
|
||||
handler.handle( this );
|
||||
}
|
||||
}
|
63
proxy/src/main/java/net/md_5/bungee/packet/PacketD1Team.java
Normal file
63
proxy/src/main/java/net/md_5/bungee/packet/PacketD1Team.java
Normal file
@@ -0,0 +1,63 @@
|
||||
package net.md_5.bungee.packet;
|
||||
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
|
||||
@ToString
|
||||
@EqualsAndHashCode(callSuper = false)
|
||||
public class PacketD1Team extends DefinedPacket
|
||||
{
|
||||
|
||||
public String name;
|
||||
/**
|
||||
* 0 - create, 1 remove, 2 info update, 3 player add, 4 player remove.
|
||||
*/
|
||||
public byte mode;
|
||||
public String displayName;
|
||||
public String prefix;
|
||||
public String suffix;
|
||||
public byte friendlyFire;
|
||||
public short playerCount;
|
||||
public String[] players;
|
||||
|
||||
public PacketD1Team(byte[] buf)
|
||||
{
|
||||
super( 0xD1, buf );
|
||||
name = readUTF();
|
||||
mode = readByte();
|
||||
if ( mode == 0 || mode == 2 )
|
||||
{
|
||||
displayName = readUTF();
|
||||
prefix = readUTF();
|
||||
suffix = readUTF();
|
||||
friendlyFire = readByte();
|
||||
}
|
||||
if ( mode == 0 || mode == 3 || mode == 4 )
|
||||
{
|
||||
players = new String[ readShort() ];
|
||||
for ( int i = 0; i < players.length; i++ )
|
||||
{
|
||||
players[i] = readUTF();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public PacketD1Team()
|
||||
{
|
||||
super( 0xD1 );
|
||||
}
|
||||
|
||||
public static PacketD1Team destroy(String name)
|
||||
{
|
||||
PacketD1Team packet = new PacketD1Team();
|
||||
packet.writeUTF( name );
|
||||
packet.writeByte( 1 );
|
||||
return packet;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(PacketHandler handler) throws Exception
|
||||
{
|
||||
handler.handle( this );
|
||||
}
|
||||
}
|
@@ -20,7 +20,7 @@ public class PacketFAPluginMessage extends DefinedPacket
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public PacketFAPluginMessage(byte[] buf)
|
||||
PacketFAPluginMessage(byte[] buf)
|
||||
{
|
||||
super( 0xFA, buf );
|
||||
this.tag = readUTF();
|
||||
|
@@ -23,9 +23,11 @@ public class PacketFCEncryptionResponse extends DefinedPacket
|
||||
super( 0xFC );
|
||||
writeArray( sharedSecret );
|
||||
writeArray( verifyToken );
|
||||
this.sharedSecret = sharedSecret;
|
||||
this.verifyToken = verifyToken;
|
||||
}
|
||||
|
||||
public PacketFCEncryptionResponse(byte[] buf)
|
||||
PacketFCEncryptionResponse(byte[] buf)
|
||||
{
|
||||
super( 0xFC, buf );
|
||||
this.sharedSecret = readArray();
|
||||
|
@@ -1,5 +1,6 @@
|
||||
package net.md_5.bungee.packet;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
|
||||
@@ -23,7 +24,7 @@ public class PacketFDEncryptionRequest extends DefinedPacket
|
||||
this.verifyToken = verifyToken;
|
||||
}
|
||||
|
||||
public PacketFDEncryptionRequest(byte[] buf)
|
||||
PacketFDEncryptionRequest(byte[] buf)
|
||||
{
|
||||
super( 0xFD, buf );
|
||||
serverId = readUTF();
|
||||
|
@@ -8,9 +8,12 @@ import lombok.ToString;
|
||||
public class PacketFEPing extends DefinedPacket
|
||||
{
|
||||
|
||||
public PacketFEPing(byte[] buffer)
|
||||
public byte version;
|
||||
|
||||
PacketFEPing(byte[] buffer)
|
||||
{
|
||||
super( 0xFE, buffer );
|
||||
version = readByte();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user