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>
|
<parent>
|
||||||
<groupId>net.md-5</groupId>
|
<groupId>net.md-5</groupId>
|
||||||
<artifactId>bungeecord-parent</artifactId>
|
<artifactId>bungeecord-parent</artifactId>
|
||||||
<version>1.4.7-SNAPSHOT</version>
|
<version>1.5-SNAPSHOT</version>
|
||||||
<relativePath>../pom.xml</relativePath>
|
<relativePath>../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<groupId>net.md-5</groupId>
|
<groupId>net.md-5</groupId>
|
||||||
<artifactId>bungeecord-api</artifactId>
|
<artifactId>bungeecord-api</artifactId>
|
||||||
<version>1.4.7-SNAPSHOT</version>
|
<version>1.5-SNAPSHOT</version>
|
||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
<name>BungeeCord-API</name>
|
<name>BungeeCord-API</name>
|
||||||
@@ -22,13 +22,19 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.google.guava</groupId>
|
<groupId>com.google.guava</groupId>
|
||||||
<artifactId>guava</artifactId>
|
<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>
|
<scope>compile</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.yaml</groupId>
|
<groupId>org.yaml</groupId>
|
||||||
<artifactId>snakeyaml</artifactId>
|
<artifactId>snakeyaml</artifactId>
|
||||||
<version>1.11</version>
|
<version>1.12</version>
|
||||||
<scope>compile</scope>
|
<scope>compile</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</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;
|
package net.md_5.bungee.api;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -105,13 +107,30 @@ public enum ChatColor
|
|||||||
* Pattern to remove all colour codes.
|
* Pattern to remove all colour codes.
|
||||||
*/
|
*/
|
||||||
private static final Pattern STRIP_COLOR_PATTERN = Pattern.compile( "(?i)" + String.valueOf( COLOR_CHAR ) + "[0-9A-FK-OR]" );
|
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}.
|
* This colour's colour char prefixed by the {@link #COLOR_CHAR}.
|
||||||
*/
|
*/
|
||||||
private final String toString;
|
private final String toString;
|
||||||
|
|
||||||
|
static
|
||||||
|
{
|
||||||
|
for ( ChatColor colour : values() )
|
||||||
|
{
|
||||||
|
BY_CHAR.put( colour.code, colour );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private ChatColor(char code)
|
private ChatColor(char code)
|
||||||
{
|
{
|
||||||
|
this.code = code;
|
||||||
this.toString = new String( new char[]
|
this.toString = new String( new char[]
|
||||||
{
|
{
|
||||||
COLOR_CHAR, code
|
COLOR_CHAR, code
|
||||||
@@ -153,4 +172,15 @@ public enum ChatColor
|
|||||||
}
|
}
|
||||||
return new String( b );
|
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);
|
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
|
* Get all groups this user is part of. This returns an unmodifiable
|
||||||
* collection.
|
* collection.
|
||||||
|
@@ -2,6 +2,8 @@ package net.md_5.bungee.api;
|
|||||||
|
|
||||||
import net.md_5.bungee.api.plugin.PluginManager;
|
import net.md_5.bungee.api.plugin.PluginManager;
|
||||||
import com.google.common.base.Preconditions;
|
import com.google.common.base.Preconditions;
|
||||||
|
import com.ning.http.client.AsyncHttpClient;
|
||||||
|
import java.io.File;
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Map;
|
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.ProxiedPlayer;
|
||||||
import net.md_5.bungee.api.connection.Server;
|
import net.md_5.bungee.api.connection.Server;
|
||||||
import net.md_5.bungee.api.plugin.Plugin;
|
import net.md_5.bungee.api.plugin.Plugin;
|
||||||
|
import net.md_5.bungee.api.scheduler.TaskScheduler;
|
||||||
|
|
||||||
public abstract class ProxyServer
|
public abstract class ProxyServer
|
||||||
{
|
{
|
||||||
@@ -208,7 +211,40 @@ public abstract class ProxyServer
|
|||||||
*
|
*
|
||||||
* @param name name of the server
|
* @param name name of the server
|
||||||
* @param address connectable Minecraft address + port 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
|
* @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.
|
* Name of the server which users will be taken to by default.
|
||||||
*/
|
*/
|
||||||
private final String defaultServer;
|
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
|
* Whether reconnect locations will be used, or else the user is simply
|
||||||
* transferred to the default server on connect.
|
* transferred to the default server on connect.
|
||||||
@@ -42,4 +47,9 @@ public class ListenerInfo
|
|||||||
* transferred depending on the host they connect to.
|
* transferred depending on the host they connect to.
|
||||||
*/
|
*/
|
||||||
private final Map<String, String> forcedHosts;
|
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;
|
package net.md_5.bungee.api.config;
|
||||||
|
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collection;
|
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.Callback;
|
||||||
|
import net.md_5.bungee.api.CommandSender;
|
||||||
import net.md_5.bungee.api.ServerPing;
|
import net.md_5.bungee.api.ServerPing;
|
||||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class used to represent a server to connect to.
|
* Class used to represent a server to connect to.
|
||||||
*/
|
*/
|
||||||
@Data
|
public interface ServerInfo
|
||||||
@AllArgsConstructor
|
|
||||||
public abstract class ServerInfo
|
|
||||||
{
|
{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Name this server displays as.
|
* Get the name of this server.
|
||||||
|
*
|
||||||
|
* @return the configured name for this server address
|
||||||
*/
|
*/
|
||||||
private final String name;
|
String getName();
|
||||||
/**
|
|
||||||
* 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.
|
* 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 add
|
* @return the IP and port pair for this server
|
||||||
*/
|
*/
|
||||||
@Synchronized("players")
|
InetSocketAddress getAddress();
|
||||||
public void addPlayer(ProxiedPlayer player)
|
|
||||||
{
|
|
||||||
players.add( player );
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove a player form the internal set of this server.
|
|
||||||
*
|
|
||||||
* @param player the player to remove
|
|
||||||
*/
|
|
||||||
@Synchronized("players")
|
|
||||||
public void removePlayer(ProxiedPlayer player)
|
|
||||||
{
|
|
||||||
players.remove( player );
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the set of all players on this server.
|
* Get the set of all players on this server.
|
||||||
*
|
*
|
||||||
* @return an unmodifiable collection of all players on this server
|
* @return an unmodifiable collection of all players on this server
|
||||||
*/
|
*/
|
||||||
@Synchronized("players")
|
Collection<ProxiedPlayer> getPlayers();
|
||||||
public Collection<ProxiedPlayer> getPlayers()
|
|
||||||
{
|
/**
|
||||||
return Collections.unmodifiableCollection( players );
|
* 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.
|
* 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 channel the channel to send this data via
|
||||||
* @param data the data to send
|
* @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.
|
* Asynchronously gets the current player count on this server.
|
||||||
*
|
*
|
||||||
* @param callback the callback to call when the count has been retrieved.
|
* @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
|
* @return the remote address
|
||||||
*/
|
*/
|
||||||
public InetSocketAddress getAddress();
|
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();
|
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.
|
* Get the listener that accepted this connection.
|
||||||
*
|
*
|
||||||
|
@@ -48,13 +48,6 @@ public interface ProxiedPlayer extends Connection, CommandSender
|
|||||||
*/
|
*/
|
||||||
public int getPing();
|
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.
|
* 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
|
* @return the pending connection that this player used
|
||||||
*/
|
*/
|
||||||
public PendingConnection getPendingConnection();
|
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;
|
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;
|
import net.md_5.bungee.api.config.ServerInfo;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -24,14 +22,4 @@ public interface Server extends Connection
|
|||||||
* @param data the data to send
|
* @param data the data to send
|
||||||
*/
|
*/
|
||||||
public abstract void sendData(String channel, byte[] data);
|
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 );
|
super( sender, receiver );
|
||||||
this.message = message;
|
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.Data;
|
||||||
import lombok.EqualsAndHashCode;
|
import lombok.EqualsAndHashCode;
|
||||||
import lombok.ToString;
|
import lombok.ToString;
|
||||||
|
import net.md_5.bungee.api.Callback;
|
||||||
import net.md_5.bungee.api.connection.PendingConnection;
|
import net.md_5.bungee.api.connection.PendingConnection;
|
||||||
import net.md_5.bungee.api.plugin.Cancellable;
|
import net.md_5.bungee.api.plugin.Cancellable;
|
||||||
import net.md_5.bungee.api.plugin.Event;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Event called to represent a player logging in.
|
* Event called to represent a player logging in.
|
||||||
@@ -13,7 +13,7 @@ import net.md_5.bungee.api.plugin.Event;
|
|||||||
@Data
|
@Data
|
||||||
@ToString(callSuper = false)
|
@ToString(callSuper = false)
|
||||||
@EqualsAndHashCode(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.
|
* Connection attempting to login.
|
||||||
*/
|
*/
|
||||||
private final PendingConnection connection;
|
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;
|
||||||
|
}
|
||||||
|
}
|
@@ -5,4 +5,11 @@ package net.md_5.bungee.api.plugin;
|
|||||||
*/
|
*/
|
||||||
public abstract class Event
|
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;
|
package net.md_5.bungee.api.plugin;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.InputStream;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
|
import net.md_5.bungee.api.ProxyServer;
|
||||||
import net.md_5.bungee.api.config.ConfigurationAdapter;
|
import net.md_5.bungee.api.config.ConfigurationAdapter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -12,6 +15,10 @@ public class Plugin
|
|||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
private PluginDescription description;
|
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
|
* 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.
|
* Called by the loader to initialize the fields in this plugin.
|
||||||
*
|
*
|
||||||
* @param description the description that describes 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.description = description;
|
||||||
|
this.file = file;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -45,6 +45,6 @@ public class PluginClassloader extends URLClassLoader
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
throw new ClassNotFoundException(name);
|
throw new ClassNotFoundException( name );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,12 +1,16 @@
|
|||||||
package net.md_5.bungee.api.plugin;
|
package net.md_5.bungee.api.plugin;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* POJO representing the plugin.yml file.
|
* POJO representing the plugin.yml file.
|
||||||
*/
|
*/
|
||||||
@Data
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
public class PluginDescription
|
public class PluginDescription
|
||||||
{
|
{
|
||||||
@@ -27,8 +31,8 @@ public class PluginDescription
|
|||||||
* Plugin author.
|
* Plugin author.
|
||||||
*/
|
*/
|
||||||
private String 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.Collection;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Stack;
|
||||||
import java.util.jar.JarEntry;
|
import java.util.jar.JarEntry;
|
||||||
import java.util.jar.JarFile;
|
import java.util.jar.JarFile;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
import net.md_5.bungee.api.ChatColor;
|
import net.md_5.bungee.api.ChatColor;
|
||||||
import net.md_5.bungee.api.CommandSender;
|
import net.md_5.bungee.api.CommandSender;
|
||||||
import net.md_5.bungee.api.ProxyServer;
|
import net.md_5.bungee.api.ProxyServer;
|
||||||
|
import net.md_5.bungee.api.event.LoginEvent;
|
||||||
import org.yaml.snakeyaml.Yaml;
|
import org.yaml.snakeyaml.Yaml;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class to manage bridging between plugin duties and implementation duties, for
|
* Class to manage bridging between plugin duties and implementation duties, for
|
||||||
* example event handling and plugin management.
|
* example event handling and plugin management.
|
||||||
*/
|
*/
|
||||||
|
@RequiredArgsConstructor
|
||||||
public class PluginManager
|
public class PluginManager
|
||||||
{
|
{
|
||||||
|
|
||||||
private static final Pattern argsSplit = Pattern.compile( " " );
|
private static final Pattern argsSplit = Pattern.compile( " " );
|
||||||
/*========================================================================*/
|
/*========================================================================*/
|
||||||
|
private final ProxyServer proxy;
|
||||||
|
/*========================================================================*/
|
||||||
private final Yaml yaml = new Yaml();
|
private final Yaml yaml = new Yaml();
|
||||||
private final EventBus eventBus = new EventBus();
|
private final EventBus eventBus = new EventBus();
|
||||||
private final Map<String, Plugin> plugins = new HashMap<>();
|
private final Map<String, Plugin> plugins = new HashMap<>();
|
||||||
@@ -37,9 +43,10 @@ public class PluginManager
|
|||||||
/**
|
/**
|
||||||
* Register a command so that it may be executed.
|
* Register a command so that it may be executed.
|
||||||
*
|
*
|
||||||
|
* @param plugin the plugin owning this command
|
||||||
* @param command the command to register
|
* @param command the command to register
|
||||||
*/
|
*/
|
||||||
public void registerCommand(Command command)
|
public void registerCommand(Plugin plugin, Command command)
|
||||||
{
|
{
|
||||||
commandMap.put( command.getName().toLowerCase(), command );
|
commandMap.put( command.getName().toLowerCase(), command );
|
||||||
for ( String alias : command.getAliases() )
|
for ( String alias : command.getAliases() )
|
||||||
@@ -120,21 +127,87 @@ public class PluginManager
|
|||||||
*/
|
*/
|
||||||
public void enablePlugins()
|
public void enablePlugins()
|
||||||
{
|
{
|
||||||
|
Map<Plugin, Boolean> pluginStatuses = new HashMap<>();
|
||||||
for ( Map.Entry<String, Plugin> entry : plugins.entrySet() )
|
for ( Map.Entry<String, Plugin> entry : plugins.entrySet() )
|
||||||
{
|
{
|
||||||
Plugin plugin = entry.getValue();
|
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
|
try
|
||||||
{
|
{
|
||||||
plugin.onEnable();
|
plugin.onEnable();
|
||||||
ProxyServer.getInstance().getLogger().log( Level.INFO, "Enabled plugin {0} version {1} by {2}", new Object[]
|
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() );
|
Class<?> main = loader.loadClass( desc.getMain() );
|
||||||
Plugin plugin = (Plugin) main.getDeclaredConstructor().newInstance();
|
Plugin plugin = (Plugin) main.getDeclaredConstructor().newInstance();
|
||||||
|
|
||||||
plugin.init( desc );
|
plugin.init( proxy, desc, file );
|
||||||
plugins.put( desc.getName(), plugin );
|
plugins.put( desc.getName(), plugin );
|
||||||
plugin.onLoad();
|
plugin.onLoad();
|
||||||
ProxyServer.getInstance().getLogger().log( Level.INFO, "Loaded plugin {0} version {1} by {2}", new Object[]
|
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)
|
public <T extends Event> T callEvent(T event)
|
||||||
{
|
{
|
||||||
|
Preconditions.checkNotNull( event, "event" );
|
||||||
|
|
||||||
|
long start = System.nanoTime();
|
||||||
eventBus.post( event );
|
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;
|
return event;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -220,9 +306,10 @@ public class PluginManager
|
|||||||
* Object which wish to receive events must be annotated with the
|
* Object which wish to receive events must be annotated with the
|
||||||
* {@link Subscribe} annotation.
|
* {@link Subscribe} annotation.
|
||||||
*
|
*
|
||||||
|
* @param plugin the owning plugin
|
||||||
* @param listener the listener to register events for
|
* @param listener the listener to register events for
|
||||||
*/
|
*/
|
||||||
public void registerListener(Listener listener)
|
public void registerListener(Plugin plugin, Listener listener)
|
||||||
{
|
{
|
||||||
eventBus.register( 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>
|
<groupId>net.md-5</groupId>
|
||||||
<artifactId>bungeecord-parent</artifactId>
|
<artifactId>bungeecord-parent</artifactId>
|
||||||
<version>1.4.7-SNAPSHOT</version>
|
<version>1.5-SNAPSHOT</version>
|
||||||
<packaging>pom</packaging>
|
<packaging>pom</packaging>
|
||||||
|
|
||||||
<name>BungeeCord</name>
|
<name>BungeeCord</name>
|
||||||
@@ -57,15 +57,16 @@
|
|||||||
</ciManagement>
|
</ciManagement>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
|
||||||
<build.number>unknown</build.number>
|
<build.number>unknown</build.number>
|
||||||
|
<netty.version>4.0.0.CR1</netty.version>
|
||||||
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.projectlombok</groupId>
|
<groupId>org.projectlombok</groupId>
|
||||||
<artifactId>lombok</artifactId>
|
<artifactId>lombok</artifactId>
|
||||||
<version>0.11.6</version>
|
<version>0.11.8</version>
|
||||||
<scope>provided</scope>
|
<scope>provided</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
@@ -92,7 +93,7 @@
|
|||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-compiler-plugin</artifactId>
|
<artifactId>maven-compiler-plugin</artifactId>
|
||||||
<version>3.0</version>
|
<version>2.5.1</version>
|
||||||
<configuration>
|
<configuration>
|
||||||
<source>1.7</source>
|
<source>1.7</source>
|
||||||
<target>1.7</target>
|
<target>1.7</target>
|
||||||
|
@@ -6,15 +6,24 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>net.md-5</groupId>
|
<groupId>net.md-5</groupId>
|
||||||
<artifactId>bungeecord-parent</artifactId>
|
<artifactId>bungeecord-parent</artifactId>
|
||||||
<version>1.4.7-SNAPSHOT</version>
|
<version>1.5-SNAPSHOT</version>
|
||||||
<relativePath>../pom.xml</relativePath>
|
<relativePath>../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<groupId>net.md-5</groupId>
|
<groupId>net.md-5</groupId>
|
||||||
<artifactId>bungeecord-protocol</artifactId>
|
<artifactId>bungeecord-protocol</artifactId>
|
||||||
<version>1.4.7-SNAPSHOT</version>
|
<version>1.5-SNAPSHOT</version>
|
||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
<name>BungeeCord-Protocol</name>
|
<name>BungeeCord-Protocol</name>
|
||||||
<description>Minimal implementation of the Minecraft protocol for use in BungeeCord</description>
|
<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>
|
</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
|
public class PacketDefinitions
|
||||||
{
|
{
|
||||||
|
|
||||||
private static final int MAX_PACKET = 256;
|
public static final OpCode[][] opCodes = new OpCode[ 512 ][];
|
||||||
public static final OpCode[][] opCodes = new OpCode[ MAX_PACKET * 2 ][];
|
|
||||||
public static final int VANILLA_PROTOCOL = 0;
|
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
|
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
|
static
|
||||||
@@ -214,6 +213,10 @@ public class PacketDefinitions
|
|||||||
{
|
{
|
||||||
STRING, INT, INT, INT, FLOAT, BYTE
|
STRING, INT, INT, INT, FLOAT, BYTE
|
||||||
};
|
};
|
||||||
|
opCodes[0x3F] = new OpCode[]
|
||||||
|
{
|
||||||
|
STRING, FLOAT, FLOAT, FLOAT, FLOAT, FLOAT, FLOAT, FLOAT, INT
|
||||||
|
};
|
||||||
opCodes[0x46] = new OpCode[]
|
opCodes[0x46] = new OpCode[]
|
||||||
{
|
{
|
||||||
BYTE, BYTE
|
BYTE, BYTE
|
||||||
@@ -224,7 +227,7 @@ public class PacketDefinitions
|
|||||||
};
|
};
|
||||||
opCodes[0x64] = new OpCode[]
|
opCodes[0x64] = new OpCode[]
|
||||||
{
|
{
|
||||||
BYTE, BYTE, STRING, BYTE
|
BYTE, BYTE, STRING, BYTE, BOOLEAN
|
||||||
};
|
};
|
||||||
opCodes[0x65] = new OpCode[]
|
opCodes[0x65] = new OpCode[]
|
||||||
{
|
{
|
||||||
@@ -298,6 +301,22 @@ public class PacketDefinitions
|
|||||||
{
|
{
|
||||||
BYTE
|
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[]
|
opCodes[0xFA] = new OpCode[]
|
||||||
{
|
{
|
||||||
STRING, SHORT_BYTE
|
STRING, SHORT_BYTE
|
||||||
@@ -312,7 +331,8 @@ public class PacketDefinitions
|
|||||||
};
|
};
|
||||||
opCodes[0xFE] = new OpCode[]
|
opCodes[0xFE] = new OpCode[]
|
||||||
{
|
{
|
||||||
}; // Should be byte, screw you too bitchy server admins!
|
BYTE
|
||||||
|
};
|
||||||
opCodes[0xFF] = new OpCode[]
|
opCodes[0xFF] = new OpCode[]
|
||||||
{
|
{
|
||||||
STRING
|
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;
|
import java.io.IOException;
|
||||||
|
|
||||||
public class BulkChunk extends Instruction
|
public class BulkChunk extends Instruction
|
||||||
{
|
{
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
void read(DataInput in, byte[] buffer) throws IOException
|
void read(ByteBuf in) throws IOException
|
||||||
{
|
{
|
||||||
short count = in.readShort();
|
short count = in.readShort();
|
||||||
int size = in.readInt();
|
int size = in.readInt();
|
||||||
in.readBoolean();
|
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;
|
import java.io.IOException;
|
||||||
|
|
||||||
class ByteHeader extends Instruction
|
class ByteHeader extends Instruction
|
||||||
@@ -14,12 +14,12 @@ class ByteHeader extends Instruction
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
void read(DataInput in, byte[] buffer) throws IOException
|
void read(ByteBuf in) throws IOException
|
||||||
{
|
{
|
||||||
byte size = in.readByte();
|
byte size = in.readByte();
|
||||||
for ( byte b = 0; b < size; b++ )
|
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;
|
import java.io.IOException;
|
||||||
|
|
||||||
abstract class Instruction
|
abstract class Instruction
|
||||||
@@ -19,18 +19,17 @@ abstract class Instruction
|
|||||||
static final Instruction LONG = new Jump( 8 );
|
static final Instruction LONG = new Jump( 8 );
|
||||||
static final Instruction METADATA = new MetaData();
|
static final Instruction METADATA = new MetaData();
|
||||||
static final Instruction OPTIONAL_MOTION = new OptionalMotion();
|
static final Instruction OPTIONAL_MOTION = new OptionalMotion();
|
||||||
|
static final Instruction SCORE = new Score();
|
||||||
static final Instruction SHORT = new Jump( 2 );
|
static final Instruction SHORT = new Jump( 2 );
|
||||||
static final Instruction SHORT_BYTE = new ShortHeader( BYTE );
|
static final Instruction SHORT_BYTE = new ShortHeader( BYTE );
|
||||||
static final Instruction SHORT_ITEM = new ShortHeader( ITEM );
|
static final Instruction SHORT_ITEM = new ShortHeader( ITEM );
|
||||||
static final Instruction STRING = new ShortHeader( new Jump( 2 ) );
|
static final Instruction STRING = new ShortHeader( new Jump( 2 ) );
|
||||||
|
static final Instruction TEAM = new Team();
|
||||||
static final Instruction USHORT_BYTE = new UnsignedShortByte();
|
static final Instruction USHORT_BYTE = new UnsignedShortByte();
|
||||||
// Illegal forward references below this line
|
// Illegal forward references below this line
|
||||||
static final Instruction BYTE_INT = new ByteHeader( INT );
|
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;
|
abstract void read(ByteBuf in) throws IOException;
|
||||||
|
|
||||||
final void skip(DataInput in, byte[] buffer, int len) throws IOException
|
|
||||||
{
|
|
||||||
in.readFully( buffer, 0, len );
|
|
||||||
}
|
|
||||||
}
|
}
|
@@ -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;
|
import java.io.IOException;
|
||||||
|
|
||||||
class IntHeader extends Instruction
|
class IntHeader extends Instruction
|
||||||
@@ -14,12 +14,12 @@ class IntHeader extends Instruction
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
void read(DataInput in, byte[] buffer) throws IOException
|
void read(ByteBuf in) throws IOException
|
||||||
{
|
{
|
||||||
int size = in.readInt();
|
int size = in.readInt();
|
||||||
for ( int i = 0; i < size; i++ )
|
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;
|
import java.io.IOException;
|
||||||
|
|
||||||
class Jump extends Instruction
|
class Jump extends Instruction
|
||||||
@@ -18,8 +18,8 @@ class Jump extends Instruction
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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;
|
import java.io.IOException;
|
||||||
|
|
||||||
class MetaData extends Instruction
|
class MetaData extends Instruction
|
||||||
{
|
{
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
void read(DataInput in, byte[] buffer) throws IOException
|
void read(ByteBuf in) throws IOException
|
||||||
{
|
{
|
||||||
int x = in.readUnsignedByte();
|
int x = in.readUnsignedByte();
|
||||||
while ( x != 127 )
|
while ( x != 127 )
|
||||||
@@ -16,25 +16,25 @@ class MetaData extends Instruction
|
|||||||
switch ( type )
|
switch ( type )
|
||||||
{
|
{
|
||||||
case 0:
|
case 0:
|
||||||
BYTE.read( in, buffer );
|
BYTE.read( in );
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
SHORT.read( in, buffer );
|
SHORT.read( in );
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
INT.read( in, buffer );
|
INT.read( in );
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
FLOAT.read( in, buffer );
|
FLOAT.read( in );
|
||||||
break;
|
break;
|
||||||
case 4:
|
case 4:
|
||||||
STRING.read( in, buffer );
|
STRING.read( in );
|
||||||
break;
|
break;
|
||||||
case 5:
|
case 5:
|
||||||
ITEM.read( in, buffer );
|
ITEM.read( in );
|
||||||
break;
|
break;
|
||||||
case 6:
|
case 6:
|
||||||
skip( in, buffer, 12 ); // int, int, int
|
in.skipBytes( 12 ); // int, int, int
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new IllegalArgumentException( "Unknown metadata type " + type );
|
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.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import net.md_5.mendax.PacketDefinitions;
|
import net.md_5.bungee.protocol.PacketDefinitions;
|
||||||
import net.md_5.mendax.PacketDefinitions.OpCode;
|
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
|
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;
|
Instruction[] packetDef = null;
|
||||||
if ( packetId + protocol < instructions.length )
|
if ( packetId + protocol < instructions.length )
|
||||||
@@ -74,20 +74,20 @@ public class DataInputPacketReader
|
|||||||
throw new IOException( "Unknown packet id " + packetId );
|
throw new IOException( "Unknown packet id " + packetId );
|
||||||
} else
|
} else
|
||||||
{
|
{
|
||||||
readPacket( packetId, in, buffer, PacketDefinitions.VANILLA_PROTOCOL );
|
readPacket( packetId, in, PacketDefinitions.VANILLA_PROTOCOL );
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for ( Instruction instruction : packetDef )
|
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();
|
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;
|
import java.io.IOException;
|
||||||
|
|
||||||
class ShortHeader extends Instruction
|
class ShortHeader extends Instruction
|
||||||
@@ -14,12 +14,12 @@ class ShortHeader extends Instruction
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
void read(DataInput in, byte[] buffer) throws IOException
|
void read(ByteBuf in) throws IOException
|
||||||
{
|
{
|
||||||
short size = in.readShort();
|
short size = in.readShort();
|
||||||
for ( short s = 0; s < size; s++ )
|
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>
|
<parent>
|
||||||
<groupId>net.md-5</groupId>
|
<groupId>net.md-5</groupId>
|
||||||
<artifactId>bungeecord-parent</artifactId>
|
<artifactId>bungeecord-parent</artifactId>
|
||||||
<version>1.4.7-SNAPSHOT</version>
|
<version>1.5-SNAPSHOT</version>
|
||||||
<relativePath>../pom.xml</relativePath>
|
<relativePath>../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<groupId>net.md-5</groupId>
|
<groupId>net.md-5</groupId>
|
||||||
<artifactId>bungeecord-proxy</artifactId>
|
<artifactId>bungeecord-proxy</artifactId>
|
||||||
<version>1.4.7-SNAPSHOT</version>
|
<version>1.5-SNAPSHOT</version>
|
||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
<name>BungeeCord-Proxy</name>
|
<name>BungeeCord-Proxy</name>
|
||||||
<description>Proxy component of the Elastic Portal Suite</description>
|
<description>Proxy component of the Elastic Portal Suite</description>
|
||||||
|
|
||||||
<dependencies>
|
<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>
|
<dependency>
|
||||||
<groupId>net.md-5</groupId>
|
<groupId>net.md-5</groupId>
|
||||||
<artifactId>bungeecord-protocol</artifactId>
|
<artifactId>bungeecord-protocol</artifactId>
|
||||||
<version>${project.version}</version>
|
<version>${project.version}</version>
|
||||||
|
<scope>compile</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>net.md-5</groupId>
|
<groupId>net.md-5</groupId>
|
||||||
<artifactId>bungeecord-api</artifactId>
|
<artifactId>bungeecord-api</artifactId>
|
||||||
<version>${project.version}</version>
|
<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>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>mysql</groupId>
|
<groupId>mysql</groupId>
|
||||||
<artifactId>mysql-connector-java</artifactId>
|
<artifactId>mysql-connector-java</artifactId>
|
||||||
<version>5.1.22</version>
|
<version>5.1.24</version>
|
||||||
|
<scope>compile</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
<finalName>BungeeCord</finalName>
|
<finalName>BungeeCord</finalName>
|
||||||
<plugins>
|
<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>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-jar-plugin</artifactId>
|
<artifactId>maven-jar-plugin</artifactId>
|
||||||
@@ -75,7 +104,6 @@
|
|||||||
</excludes>
|
</excludes>
|
||||||
</filter>
|
</filter>
|
||||||
</filters>
|
</filters>
|
||||||
<finalName>${project.build.finalName}-shaded</finalName>
|
|
||||||
</configuration>
|
</configuration>
|
||||||
</plugin>
|
</plugin>
|
||||||
</plugins>
|
</plugins>
|
||||||
|
@@ -1,29 +1,40 @@
|
|||||||
package net.md_5.bungee;
|
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 net.md_5.bungee.config.Configuration;
|
||||||
import java.io.BufferedReader;
|
import java.io.BufferedReader;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
import java.net.Socket;
|
import java.util.Calendar;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Timer;
|
import java.util.Timer;
|
||||||
import java.util.TimerTask;
|
import java.util.TimerTask;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.concurrent.ExecutorService;
|
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
||||||
import java.util.concurrent.Executors;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
import lombok.Synchronized;
|
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.ProxyServer;
|
||||||
import net.md_5.bungee.api.ReconnectHandler;
|
import net.md_5.bungee.api.ReconnectHandler;
|
||||||
import net.md_5.bungee.api.TabListHandler;
|
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.connection.Server;
|
||||||
import net.md_5.bungee.api.plugin.Plugin;
|
import net.md_5.bungee.api.plugin.Plugin;
|
||||||
import net.md_5.bungee.api.plugin.PluginManager;
|
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.command.*;
|
||||||
import net.md_5.bungee.config.YamlConfig;
|
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.DefinedPacket;
|
||||||
import net.md_5.bungee.packet.PacketFAPluginMessage;
|
import net.md_5.bungee.packet.PacketFAPluginMessage;
|
||||||
|
import net.md_5.bungee.scheduler.BungeeThreadPool;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Main BungeeCord proxy class.
|
* Main BungeeCord proxy class.
|
||||||
@@ -48,11 +62,11 @@ public class BungeeCord extends ProxyServer
|
|||||||
/**
|
/**
|
||||||
* Server protocol version.
|
* Server protocol version.
|
||||||
*/
|
*/
|
||||||
public static final byte PROTOCOL_VERSION = 51;
|
public static final byte PROTOCOL_VERSION = 60;
|
||||||
/**
|
/**
|
||||||
* Server game version.
|
* Server game version.
|
||||||
*/
|
*/
|
||||||
public static final String GAME_VERSION = "1.4.6";
|
public static final String GAME_VERSION = "1.5";
|
||||||
/**
|
/**
|
||||||
* Current operation state.
|
* Current operation state.
|
||||||
*/
|
*/
|
||||||
@@ -62,9 +76,10 @@ public class BungeeCord extends ProxyServer
|
|||||||
*/
|
*/
|
||||||
public final Configuration config = new Configuration();
|
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.
|
* locations.yml save thread.
|
||||||
*/
|
*/
|
||||||
@@ -72,7 +87,7 @@ public class BungeeCord extends ProxyServer
|
|||||||
/**
|
/**
|
||||||
* Server socket listener.
|
* Server socket listener.
|
||||||
*/
|
*/
|
||||||
private Collection<ListenThread> listeners = new HashSet<>();
|
private Collection<Channel> listeners = new HashSet<>();
|
||||||
/**
|
/**
|
||||||
* Fully qualified connections.
|
* Fully qualified connections.
|
||||||
*/
|
*/
|
||||||
@@ -87,7 +102,7 @@ public class BungeeCord extends ProxyServer
|
|||||||
* Plugin manager.
|
* Plugin manager.
|
||||||
*/
|
*/
|
||||||
@Getter
|
@Getter
|
||||||
public final PluginManager pluginManager = new PluginManager();
|
public final PluginManager pluginManager = new PluginManager( this );
|
||||||
@Getter
|
@Getter
|
||||||
@Setter
|
@Setter
|
||||||
private ReconnectHandler reconnectHandler;
|
private ReconnectHandler reconnectHandler;
|
||||||
@@ -95,17 +110,28 @@ public class BungeeCord extends ProxyServer
|
|||||||
@Setter
|
@Setter
|
||||||
private ConfigurationAdapter configurationAdapter = new YamlConfig();
|
private ConfigurationAdapter configurationAdapter = new YamlConfig();
|
||||||
private final Collection<String> pluginChannels = new HashSet<>();
|
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() );
|
// TODO: Proper fallback when we interface the manager
|
||||||
getPluginManager().registerCommand( new CommandEnd() );
|
getPluginManager().registerCommand( null, new CommandReload() );
|
||||||
getPluginManager().registerCommand( new CommandList() );
|
getPluginManager().registerCommand( null, new CommandEnd() );
|
||||||
getPluginManager().registerCommand( new CommandServer() );
|
getPluginManager().registerCommand( null, new CommandList() );
|
||||||
getPluginManager().registerCommand( new CommandIP() );
|
getPluginManager().registerCommand( null, new CommandServer() );
|
||||||
getPluginManager().registerCommand( new CommandAlert() );
|
getPluginManager().registerCommand( null, new CommandIP() );
|
||||||
getPluginManager().registerCommand( new CommandBungee() );
|
getPluginManager().registerCommand( null, new CommandAlert() );
|
||||||
getPluginManager().registerCommand( new CommandPerms() );
|
getPluginManager().registerCommand( null, new CommandBungee() );
|
||||||
|
getPluginManager().registerCommand( null, new CommandPerms() );
|
||||||
|
getPluginManager().registerCommand( null, new CommandSend() );
|
||||||
|
|
||||||
registerChannel( "BungeeCord" );
|
registerChannel( "BungeeCord" );
|
||||||
}
|
}
|
||||||
@@ -119,13 +145,24 @@ public class BungeeCord extends ProxyServer
|
|||||||
* Starts a new instance of BungeeCord.
|
* Starts a new instance of BungeeCord.
|
||||||
*
|
*
|
||||||
* @param args command line arguments, currently none are used
|
* @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();
|
BungeeCord bungee = new BungeeCord();
|
||||||
ProxyServer.setInstance( bungee );
|
ProxyServer.setInstance( bungee );
|
||||||
$().info( "Enabled BungeeCord version " + bungee.getVersion() );
|
bungee.getLogger().info( "Enabled BungeeCord version " + bungee.getVersion() );
|
||||||
bungee.start();
|
bungee.start();
|
||||||
|
|
||||||
BufferedReader br = new BufferedReader( new InputStreamReader( System.in ) );
|
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
|
* Start this proxy instance by loading the configuration, plugins and
|
||||||
* starting the connect thread.
|
* starting the connect thread.
|
||||||
*
|
*
|
||||||
* @throws IOException
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
public void start() throws IOException
|
@Override
|
||||||
|
public void start() throws Exception
|
||||||
{
|
{
|
||||||
File plugins = new File( "plugins" );
|
pluginsFolder.mkdir();
|
||||||
plugins.mkdir();
|
pluginManager.loadPlugins( pluginsFolder );
|
||||||
pluginManager.loadPlugins( plugins );
|
|
||||||
config.load();
|
config.load();
|
||||||
if ( reconnectHandler == null )
|
if ( reconnectHandler == null )
|
||||||
{
|
{
|
||||||
@@ -179,34 +216,43 @@ public class BungeeCord extends ProxyServer
|
|||||||
|
|
||||||
public void startListeners()
|
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 );
|
@Override
|
||||||
listener.start();
|
public void operationComplete(ChannelFuture future) throws Exception
|
||||||
listeners.add( listener );
|
{
|
||||||
$().info( "Listening on " + info.getHost() );
|
if ( future.isSuccess() )
|
||||||
} catch ( IOException ex )
|
{
|
||||||
{
|
listeners.add( future.channel() );
|
||||||
$().log( Level.SEVERE, "Could not start listener " + info, ex );
|
getLogger().info( "Listening on " + info.getHost() );
|
||||||
}
|
} else
|
||||||
|
{
|
||||||
|
getLogger().log( Level.WARNING, "Could not bind to host " + info.getHost(), future.cause() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void stopListeners()
|
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
|
try
|
||||||
{
|
{
|
||||||
listener.interrupt();
|
listener.close().syncUninterruptibly();
|
||||||
listener.socket.close();
|
} catch ( ChannelException ex )
|
||||||
listener.join();
|
|
||||||
} catch ( InterruptedException | IOException ex )
|
|
||||||
{
|
{
|
||||||
$().severe( "Could not close listen thread" );
|
getLogger().severe( "Could not close listen thread" );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
listeners.clear();
|
listeners.clear();
|
||||||
@@ -217,44 +263,37 @@ public class BungeeCord extends ProxyServer
|
|||||||
{
|
{
|
||||||
this.isRunning = false;
|
this.isRunning = false;
|
||||||
|
|
||||||
stopListeners();
|
httpClient.close();
|
||||||
$().info( "Closing pending connections" );
|
executors.shutdown();
|
||||||
threadPool.shutdown();
|
|
||||||
|
|
||||||
$().info( "Disconnecting " + connections.size() + " connections" );
|
stopListeners();
|
||||||
|
getLogger().info( "Closing pending connections" );
|
||||||
|
|
||||||
|
getLogger().info( "Disconnecting " + connections.size() + " connections" );
|
||||||
for ( UserConnection user : connections.values() )
|
for ( UserConnection user : connections.values() )
|
||||||
{
|
{
|
||||||
user.disconnect( "Proxy restarting, brb." );
|
user.disconnect( "Proxy restarting, brb." );
|
||||||
}
|
}
|
||||||
|
|
||||||
$().info( "Saving reconnect locations" );
|
getLogger().info( "Closing IO threads" );
|
||||||
|
eventLoops.shutdown();
|
||||||
|
|
||||||
|
getLogger().info( "Saving reconnect locations" );
|
||||||
reconnectHandler.save();
|
reconnectHandler.save();
|
||||||
saveThread.cancel();
|
saveThread.cancel();
|
||||||
|
|
||||||
$().info( "Disabling plugins" );
|
// TODO: Fix this shit
|
||||||
|
getLogger().info( "Disabling plugins" );
|
||||||
for ( Plugin plugin : pluginManager.getPlugins() )
|
for ( Plugin plugin : pluginManager.getPlugins() )
|
||||||
{
|
{
|
||||||
plugin.onDisable();
|
plugin.onDisable();
|
||||||
|
getScheduler().cancel( plugin );
|
||||||
}
|
}
|
||||||
|
|
||||||
$().info( "Thank you and goodbye" );
|
getLogger().info( "Thank you and goodbye" );
|
||||||
System.exit( 0 );
|
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.
|
* 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() )
|
for ( UserConnection con : connections.values() )
|
||||||
{
|
{
|
||||||
con.packetQueue.add( packet );
|
con.sendPacket( packet );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -283,7 +322,7 @@ public class BungeeCord extends ProxyServer
|
|||||||
@Override
|
@Override
|
||||||
public Logger getLogger()
|
public Logger getLogger()
|
||||||
{
|
{
|
||||||
return $();
|
return BungeeLogger.instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -364,8 +403,14 @@ public class BungeeCord extends ProxyServer
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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.IOException;
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.io.StringWriter;
|
import java.io.StringWriter;
|
||||||
import java.text.MessageFormat;
|
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.logging.FileHandler;
|
import java.util.logging.FileHandler;
|
||||||
import java.util.logging.Formatter;
|
import java.util.logging.Formatter;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.logging.LogRecord;
|
import java.util.logging.LogRecord;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Logger to handle formatting and storage of the proxy's 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 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
|
try
|
||||||
{
|
{
|
||||||
FileHandler handler = new FileHandler( "proxy.log", 1 << 14, 1, true );
|
FileHandler handler = new FileHandler( "proxy.log", 1 << 24, 8, true );
|
||||||
handler.setFormatter( formatter );
|
handler.setFormatter( formatter );
|
||||||
addHandler( handler );
|
addHandler( handler );
|
||||||
} catch ( IOException ex )
|
} 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
|
public static class ConsoleLogFormatter extends Formatter
|
||||||
{
|
{
|
||||||
|
|
@@ -1,31 +1,82 @@
|
|||||||
package net.md_5.bungee;
|
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.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.Queue;
|
||||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.Synchronized;
|
||||||
import net.md_5.bungee.api.Callback;
|
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.ProxyServer;
|
||||||
import net.md_5.bungee.api.ServerPing;
|
import net.md_5.bungee.api.ServerPing;
|
||||||
import net.md_5.bungee.api.config.ServerInfo;
|
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.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.DefinedPacket;
|
||||||
import net.md_5.bungee.packet.PacketFAPluginMessage;
|
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
|
@Getter
|
||||||
private final Queue<DefinedPacket> packetQueue = new ConcurrentLinkedQueue<>();
|
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
|
@Override
|
||||||
@@ -44,31 +95,26 @@ public class BungeeServerInfo extends ServerInfo
|
|||||||
@Override
|
@Override
|
||||||
public void ping(final Callback<ServerPing> callback)
|
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
|
@Override
|
||||||
public void run()
|
public void operationComplete(ChannelFuture future) throws Exception
|
||||||
{
|
{
|
||||||
try ( Socket socket = new Socket(); )
|
if ( future.isSuccess() )
|
||||||
{
|
{
|
||||||
socket.connect( getAddress() );
|
future.channel().pipeline().get( HandlerBoss.class ).setHandler( new PingHandler( BungeeServerInfo.this, callback ) );
|
||||||
|
} else
|
||||||
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 )
|
|
||||||
{
|
{
|
||||||
callback.done( null, t );
|
callback.done( null, future.cause() );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}.start();
|
} );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,17 +1,11 @@
|
|||||||
package net.md_5.bungee;
|
package net.md_5.bungee;
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
import java.security.GeneralSecurityException;
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStreamReader;
|
|
||||||
import java.math.BigInteger;
|
|
||||||
import java.net.URL;
|
|
||||||
import java.net.URLEncoder;
|
|
||||||
import java.security.InvalidAlgorithmParameterException;
|
import java.security.InvalidAlgorithmParameterException;
|
||||||
import java.security.InvalidKeyException;
|
import java.security.InvalidKeyException;
|
||||||
import java.security.Key;
|
import java.security.Key;
|
||||||
import java.security.KeyPair;
|
import java.security.KeyPair;
|
||||||
import java.security.KeyPairGenerator;
|
import java.security.KeyPairGenerator;
|
||||||
import java.security.MessageDigest;
|
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
@@ -32,7 +26,7 @@ public class EncryptionUtil
|
|||||||
{
|
{
|
||||||
|
|
||||||
private static final Random random = new Random();
|
private static final Random random = new Random();
|
||||||
private static KeyPair keys;
|
public static KeyPair keys;
|
||||||
|
|
||||||
public static PacketFDEncryptionRequest encryptRequest() throws NoSuchAlgorithmException
|
public static PacketFDEncryptionRequest encryptRequest() throws NoSuchAlgorithmException
|
||||||
{
|
{
|
||||||
@@ -66,31 +60,7 @@ public class EncryptionUtil
|
|||||||
return new SecretKeySpec( secret, "AES" );
|
return new SecretKeySpec( secret, "AES" );
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isAuthenticated(String username, String connectionHash, SecretKey shared) throws NoSuchAlgorithmException, IOException
|
public static Cipher getCipher(int opMode, Key shared) throws GeneralSecurityException
|
||||||
{
|
|
||||||
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
|
|
||||||
{
|
{
|
||||||
Cipher cip = Cipher.getInstance( "AES/CFB8/NoPadding" );
|
Cipher cip = Cipher.getInstance( "AES/CFB8/NoPadding" );
|
||||||
cip.init( opMode, shared, new IvParameterSpec( shared.getEncoded() ) );
|
cip.init( opMode, shared, new IvParameterSpec( shared.getEncoded() ) );
|
||||||
|
@@ -40,7 +40,7 @@ public class EntityMap
|
|||||||
};
|
};
|
||||||
entityIds[0x17] = new int[]
|
entityIds[0x17] = new int[]
|
||||||
{
|
{
|
||||||
1, 20
|
1 //, 20
|
||||||
};
|
};
|
||||||
entityIds[0x18] = new int[]
|
entityIds[0x18] = new int[]
|
||||||
{
|
{
|
||||||
@@ -115,7 +115,7 @@ public class EntityMap
|
|||||||
|
|
||||||
public static void rewrite(byte[] packet, int oldId, int newId)
|
public static void rewrite(byte[] packet, int oldId, int newId)
|
||||||
{
|
{
|
||||||
int packetId = Util.getId( packet );
|
int packetId = packet[0] & 0xFF;
|
||||||
if ( packetId == 0x1D )
|
if ( packetId == 0x1D )
|
||||||
{ // bulk entity
|
{ // bulk entity
|
||||||
for ( int pos = 2; pos < packet.length; pos += 4 )
|
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)
|
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.URL;
|
||||||
import java.net.URLConnection;
|
import java.net.URLConnection;
|
||||||
import java.net.URLEncoder;
|
import java.net.URLEncoder;
|
||||||
import static net.md_5.bungee.Logger.$;
|
|
||||||
import net.md_5.bungee.api.ProxyServer;
|
import net.md_5.bungee.api.ProxyServer;
|
||||||
|
|
||||||
public class Metrics extends Thread
|
public class Metrics extends Thread
|
||||||
@@ -55,7 +54,7 @@ public class Metrics extends Thread
|
|||||||
firstPost = false;
|
firstPost = false;
|
||||||
} catch ( IOException ex )
|
} catch ( IOException ex )
|
||||||
{
|
{
|
||||||
$().info( "[Metrics] " + ex.getMessage() );
|
ProxyServer.getInstance().getLogger().info( "[Metrics] " + ex.getMessage() );
|
||||||
}
|
}
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
@@ -1,47 +1,52 @@
|
|||||||
package net.md_5.bungee;
|
package net.md_5.bungee;
|
||||||
|
|
||||||
|
import io.netty.channel.Channel;
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
import java.net.Socket;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.Queue;
|
|
||||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import net.md_5.bungee.api.Callback;
|
import lombok.RequiredArgsConstructor;
|
||||||
import net.md_5.bungee.api.ServerPing;
|
import lombok.Setter;
|
||||||
import net.md_5.bungee.api.config.ServerInfo;
|
|
||||||
import net.md_5.bungee.api.connection.Server;
|
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.Packet1Login;
|
||||||
import net.md_5.bungee.packet.PacketFAPluginMessage;
|
import net.md_5.bungee.packet.PacketFAPluginMessage;
|
||||||
import net.md_5.bungee.packet.PacketStream;
|
import net.md_5.bungee.packet.PacketFFKick;
|
||||||
|
|
||||||
/**
|
@RequiredArgsConstructor
|
||||||
* Class representing a connection from the proxy to the server; ie upstream.
|
public class ServerConnection implements Server
|
||||||
*/
|
|
||||||
public class ServerConnection extends GenericConnection implements Server
|
|
||||||
{
|
{
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
private final ServerInfo info;
|
private final ChannelWrapper ch;
|
||||||
public final Packet1Login loginPacket;
|
@Getter
|
||||||
public Queue<DefinedPacket> packetQueue = new ConcurrentLinkedQueue<>();
|
private final BungeeServerInfo info;
|
||||||
|
@Getter
|
||||||
public ServerConnection(Socket socket, ServerInfo info, PacketStream stream, Packet1Login loginPacket)
|
private final Packet1Login loginPacket;
|
||||||
{
|
@Getter
|
||||||
super( socket, stream );
|
@Setter
|
||||||
this.info = info;
|
private boolean isObsolete;
|
||||||
this.loginPacket = loginPacket;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void sendData(String channel, byte[] data)
|
public void sendData(String channel, byte[] data)
|
||||||
{
|
{
|
||||||
packetQueue.add( new PacketFAPluginMessage( channel, data ) );
|
ch.write( new PacketFAPluginMessage( channel, data ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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
|
@Override
|
||||||
|
@@ -1,45 +1,157 @@
|
|||||||
package net.md_5.bungee;
|
package net.md_5.bungee;
|
||||||
|
|
||||||
import com.google.common.base.Preconditions;
|
import com.google.common.base.Preconditions;
|
||||||
import java.io.IOException;
|
import com.google.common.io.ByteArrayDataOutput;
|
||||||
import java.net.Socket;
|
import com.google.common.io.ByteStreams;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.Queue;
|
import java.util.Queue;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
import net.md_5.bungee.api.ChatColor;
|
import net.md_5.bungee.api.ChatColor;
|
||||||
import net.md_5.bungee.api.ProxyServer;
|
import net.md_5.bungee.api.ProxyServer;
|
||||||
import net.md_5.bungee.api.config.ServerInfo;
|
import net.md_5.bungee.api.config.ServerInfo;
|
||||||
import net.md_5.bungee.api.event.ServerConnectedEvent;
|
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.DefinedPacket;
|
||||||
import net.md_5.bungee.packet.Packet1Login;
|
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.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.PacketFDEncryptionRequest;
|
||||||
import net.md_5.bungee.packet.PacketFFKick;
|
import net.md_5.bungee.packet.PacketFFKick;
|
||||||
import net.md_5.bungee.packet.PacketHandler;
|
import net.md_5.bungee.packet.PacketHandler;
|
||||||
import net.md_5.bungee.packet.PacketStream;
|
|
||||||
|
|
||||||
|
@RequiredArgsConstructor
|
||||||
public class ServerConnector extends PacketHandler
|
public class ServerConnector extends PacketHandler
|
||||||
{
|
{
|
||||||
|
|
||||||
private final PacketStream stream;
|
private final ProxyServer bungee;
|
||||||
private Packet1Login loginPacket;
|
private ChannelWrapper ch;
|
||||||
|
private final UserConnection user;
|
||||||
|
private final BungeeServerInfo target;
|
||||||
private State thisState = State.ENCRYPT_REQUEST;
|
private State thisState = State.ENCRYPT_REQUEST;
|
||||||
|
|
||||||
public ServerConnector(PacketStream stream)
|
|
||||||
{
|
|
||||||
this.stream = stream;
|
|
||||||
}
|
|
||||||
|
|
||||||
private enum State
|
private enum State
|
||||||
{
|
{
|
||||||
|
|
||||||
ENCRYPT_REQUEST, LOGIN, FINISHED;
|
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
|
@Override
|
||||||
public void handle(Packet1Login login) throws Exception
|
public void handle(Packet1Login login) throws Exception
|
||||||
{
|
{
|
||||||
Preconditions.checkState( thisState == State.LOGIN, "Not exepcting LOGIN" );
|
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;
|
thisState = State.FINISHED;
|
||||||
|
|
||||||
|
throw new CancelSendSignal();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -52,66 +164,31 @@ public class ServerConnector extends PacketHandler
|
|||||||
@Override
|
@Override
|
||||||
public void handle(PacketFFKick kick) throws Exception
|
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 ) )
|
||||||
|
|
||||||
public static ServerConnection connect(UserConnection user, ServerInfo info, boolean retry)
|
|
||||||
{
|
|
||||||
Socket socket = null;
|
|
||||||
try
|
|
||||||
{
|
{
|
||||||
socket = new Socket();
|
def = null;
|
||||||
socket.connect( info.getAddress(), BungeeCord.getInstance().config.getTimeout() );
|
}
|
||||||
BungeeCord.getInstance().setSocketOptions( socket );
|
ServerKickEvent event = bungee.getPluginManager().callEvent( new ServerKickEvent( user, kick.message, def ) );
|
||||||
PacketStream stream = new PacketStream( socket.getInputStream(), socket.getOutputStream(), user.stream.getProtocol() );
|
if ( event.isCancelled() && event.getCancelServer() != null )
|
||||||
|
|
||||||
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 )
|
user.connect( event.getCancelServer() );
|
||||||
{
|
return;
|
||||||
try
|
}
|
||||||
{
|
|
||||||
socket.close();
|
String message = ChatColor.RED + "Kicked whilst connecting to " + target.getName() + ": " + kick.message;
|
||||||
} catch ( IOException ioe )
|
if ( user.getServer() == null )
|
||||||
{
|
{
|
||||||
}
|
user.disconnect( message );
|
||||||
}
|
} else
|
||||||
ServerInfo def = ProxyServer.getInstance().getServers().get( user.getPendingConnection().getListener().getDefaultServer() );
|
{
|
||||||
if ( retry && !info.equals( def ) )
|
user.sendMessage( message );
|
||||||
{
|
|
||||||
user.sendMessage( ChatColor.RED + "Could not connect to target server, you have been moved to the default server" );
|
|
||||||
return connect( user, def, false );
|
|
||||||
} else
|
|
||||||
{
|
|
||||||
if ( ex instanceof RuntimeException )
|
|
||||||
{
|
|
||||||
throw (RuntimeException) ex;
|
|
||||||
}
|
|
||||||
throw new RuntimeException( "Could not connect to target server " + Util.exception( ex ) );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString()
|
||||||
|
{
|
||||||
|
return "[" + user.getName() + "] <-> ServerConnector [" + target.getName() + "]";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,224 +1,270 @@
|
|||||||
package net.md_5.bungee;
|
package net.md_5.bungee;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
import com.google.common.base.Preconditions;
|
||||||
import java.io.ByteArrayOutputStream;
|
import io.netty.bootstrap.Bootstrap;
|
||||||
import java.io.DataInputStream;
|
import io.netty.channel.Channel;
|
||||||
import java.io.DataOutputStream;
|
import io.netty.channel.ChannelFuture;
|
||||||
import java.io.IOException;
|
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.InetSocketAddress;
|
||||||
import java.net.Socket;
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.Objects;
|
||||||
import java.util.Map;
|
import java.util.logging.Level;
|
||||||
import java.util.Queue;
|
|
||||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
|
||||||
import lombok.Getter;
|
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.ProxyServer;
|
||||||
import net.md_5.bungee.api.config.ServerInfo;
|
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.connection.ProxiedPlayer;
|
||||||
import net.md_5.bungee.api.event.ChatEvent;
|
import net.md_5.bungee.api.event.PermissionCheckEvent;
|
||||||
import net.md_5.bungee.api.event.PlayerDisconnectEvent;
|
|
||||||
import net.md_5.bungee.api.event.PluginMessageEvent;
|
|
||||||
import net.md_5.bungee.api.event.ServerConnectEvent;
|
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;
|
@NonNull
|
||||||
final List<PacketFAPluginMessage> loginMessages;
|
private final ProxyServer bungee;
|
||||||
public Queue<DefinedPacket> packetQueue = new ConcurrentLinkedQueue<>();
|
@NonNull
|
||||||
|
private final ChannelWrapper ch;
|
||||||
@Getter
|
@Getter
|
||||||
private final PendingConnection pendingConnection;
|
@NonNull
|
||||||
|
private final String name;
|
||||||
@Getter
|
@Getter
|
||||||
|
@NonNull
|
||||||
|
private final InitialHandler pendingConnection;
|
||||||
|
/*========================================================================*/
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
private ServerConnection server;
|
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
|
@Getter
|
||||||
private int ping = 1000;
|
private final Object switchMutex = new Object();
|
||||||
// Permissions
|
@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 Collection<String> groups = new HashSet<>();
|
||||||
private final Map<String, Boolean> permissions = new HashMap<>();
|
private final Collection<String> permissions = new HashSet<>();
|
||||||
private final Object permMutex = new Object();
|
/*========================================================================*/
|
||||||
// Hack for connect timings
|
@Getter
|
||||||
private ServerInfo nextServer;
|
private int clientEntityId;
|
||||||
private volatile boolean clientConnected = true;
|
@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.displayName = name;
|
||||||
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;
|
|
||||||
|
|
||||||
Collection<String> g = ProxyServer.getInstance().getConfigurationAdapter().getGroups( name );
|
Collection<String> g = bungee.getConfigurationAdapter().getGroups( name );
|
||||||
for ( String s : g )
|
for ( String s : g )
|
||||||
{
|
{
|
||||||
addGroups( s );
|
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
|
@Override
|
||||||
public void setDisplayName(String name)
|
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;
|
displayName = name;
|
||||||
ProxyServer.getInstance().getTabListHandler().onConnect( this );
|
bungee.getTabListHandler().onConnect( this );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void connect(ServerInfo target)
|
public void connect(ServerInfo target)
|
||||||
{
|
{
|
||||||
nextServer = target;
|
connect( target, false );
|
||||||
}
|
}
|
||||||
|
|
||||||
public void connect(ServerInfo target, boolean force)
|
public void connectNow(ServerInfo target)
|
||||||
{
|
{
|
||||||
nextServer = null;
|
sendPacket( Packet9Respawn.DIM1_SWITCH );
|
||||||
if ( server == null )
|
sendPacket( Packet9Respawn.DIM2_SWITCH );
|
||||||
|
connect( target );
|
||||||
|
}
|
||||||
|
|
||||||
|
public void connect(ServerInfo info, final boolean retry)
|
||||||
|
{
|
||||||
|
ServerConnectEvent event = new ServerConnectEvent( this, info );
|
||||||
|
ProxyServer.getInstance().getPluginManager().callEvent( event );
|
||||||
|
|
||||||
|
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 ) )
|
||||||
{
|
{
|
||||||
// First join
|
sendMessage( ChatColor.RED + "Cannot connect to server you are already on!" );
|
||||||
BungeeCord.getInstance().connections.put( name, this );
|
return;
|
||||||
ProxyServer.getInstance().getTabListHandler().onConnect( this );
|
}
|
||||||
|
if ( pendingConnects.contains( target ) )
|
||||||
|
{
|
||||||
|
sendMessage( ChatColor.RED + "Already connecting to this server!" );
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ServerConnectEvent event = new ServerConnectEvent( this, target );
|
pendingConnects.add( target );
|
||||||
BungeeCord.getInstance().getPluginManager().callEvent( event );
|
|
||||||
target = event.getTarget(); // Update in case the event changed target
|
|
||||||
|
|
||||||
ProxyServer.getInstance().getTabListHandler().onServerChange( this );
|
new Bootstrap()
|
||||||
try
|
.channel( NioSocketChannel.class )
|
||||||
|
.group( BungeeCord.getInstance().eventLoops )
|
||||||
|
.handler( new ChannelInitializer()
|
||||||
{
|
{
|
||||||
reconnecting = true;
|
@Override
|
||||||
|
protected void initChannel(Channel ch) throws Exception
|
||||||
if ( server != null )
|
|
||||||
{
|
{
|
||||||
stream.write( new Packet9Respawn( (byte) 1, (byte) 0, (byte) 0, (short) 256, "DEFAULT" ) );
|
PipelineUtils.BASE.initChannel( ch );
|
||||||
stream.write( new Packet9Respawn( (byte) -1, (byte) 0, (byte) 0, (short) 256, "DEFAULT" ) );
|
ch.pipeline().get( HandlerBoss.class ).setHandler( new ServerConnector( bungee, UserConnection.this, target ) );
|
||||||
}
|
}
|
||||||
|
} )
|
||||||
ServerConnection newServer = ServerConnector.connect( this, target, true );
|
.option( ChannelOption.CONNECT_TIMEOUT_MILLIS, 5000 ) // TODO: Configurable
|
||||||
if ( server == null )
|
.remoteAddress( target.getAddress() )
|
||||||
|
.connect().addListener( new ChannelFutureListener()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void operationComplete(ChannelFuture future) throws Exception
|
||||||
{
|
{
|
||||||
// Once again, first connection
|
if ( !future.isSuccess() )
|
||||||
clientEntityId = newServer.loginPacket.entityId;
|
{
|
||||||
serverEntityId = newServer.loginPacket.entityId;
|
future.channel().close();
|
||||||
// Set tab list size
|
pendingConnects.remove( target );
|
||||||
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() );
|
|
||||||
|
|
||||||
upBridge = new UpstreamBridge();
|
ServerInfo def = ProxyServer.getInstance().getServers().get( getPendingConnection().getListener().getFallbackServer() );
|
||||||
upBridge.start();
|
if ( retry & target != def && ( getServer() == null || def != getServer().getInfo() ) )
|
||||||
} else
|
{
|
||||||
{
|
sendMessage( ChatColor.RED + "Could not connect to target server, you have been moved to the lobby server" );
|
||||||
try
|
connect( def, false );
|
||||||
{
|
} else
|
||||||
downBridge.interrupt();
|
{
|
||||||
downBridge.join();
|
if ( server == null )
|
||||||
} 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
|
@Override
|
||||||
public synchronized void disconnect(String reason)
|
public synchronized void disconnect(String reason)
|
||||||
{
|
{
|
||||||
if ( clientConnected )
|
if ( ch.getHandle().isActive() )
|
||||||
{
|
{
|
||||||
PlayerDisconnectEvent event = new PlayerDisconnectEvent( this );
|
bungee.getLogger().log( Level.INFO, "[" + getName() + "] disconnected with: " + reason );
|
||||||
ProxyServer.getInstance().getPluginManager().callEvent( event );
|
sendPacket( new PacketFFKick( reason ) );
|
||||||
ProxyServer.getInstance().getTabListHandler().onDisconnect( this );
|
ch.getHandle().close();
|
||||||
ProxyServer.getInstance().getPlayers().remove( this );
|
|
||||||
|
|
||||||
super.disconnect( reason );
|
|
||||||
if ( server != null )
|
if ( server != null )
|
||||||
{
|
{
|
||||||
server.getInfo().removePlayer( this );
|
|
||||||
server.disconnect( "Quitting" );
|
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
|
@Override
|
||||||
public void sendMessage(String message)
|
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
|
@Override
|
||||||
public void sendData(String channel, byte[] data)
|
public void sendData(String channel, byte[] data)
|
||||||
{
|
{
|
||||||
server.packetQueue.add( new PacketFAPluginMessage( channel, data ) );
|
sendPacket( new PacketFAPluginMessage( channel, data ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public InetSocketAddress getAddress()
|
public InetSocketAddress getAddress()
|
||||||
{
|
{
|
||||||
return (InetSocketAddress) socket.getRemoteSocketAddress();
|
return (InetSocketAddress) ch.getHandle().remoteAddress();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Synchronized("permMutex")
|
|
||||||
public Collection<String> getGroups()
|
public Collection<String> getGroups()
|
||||||
{
|
{
|
||||||
return Collections.unmodifiableCollection( groups );
|
return Collections.unmodifiableCollection( groups );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Synchronized("permMutex")
|
|
||||||
public void addGroups(String... groups)
|
public void addGroups(String... groups)
|
||||||
{
|
{
|
||||||
for ( String group : groups )
|
for ( String group : groups )
|
||||||
{
|
{
|
||||||
this.groups.add( group );
|
this.groups.add( group );
|
||||||
for ( String permission : ProxyServer.getInstance().getConfigurationAdapter().getPermissions( group ) )
|
for ( String permission : bungee.getConfigurationAdapter().getPermissions( group ) )
|
||||||
{
|
{
|
||||||
setPermission( permission, true );
|
setPermission( permission, true );
|
||||||
}
|
}
|
||||||
@@ -226,13 +272,12 @@ public final class UserConnection extends GenericConnection implements ProxiedPl
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Synchronized("permMutex")
|
|
||||||
public void removeGroups(String... groups)
|
public void removeGroups(String... groups)
|
||||||
{
|
{
|
||||||
for ( String group : groups )
|
for ( String group : groups )
|
||||||
{
|
{
|
||||||
this.groups.remove( group );
|
this.groups.remove( group );
|
||||||
for ( String permission : ProxyServer.getInstance().getConfigurationAdapter().getPermissions( group ) )
|
for ( String permission : bungee.getConfigurationAdapter().getPermissions( group ) )
|
||||||
{
|
{
|
||||||
setPermission( permission, false );
|
setPermission( permission, false );
|
||||||
}
|
}
|
||||||
@@ -240,306 +285,32 @@ public final class UserConnection extends GenericConnection implements ProxiedPl
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Synchronized("permMutex")
|
|
||||||
public boolean hasPermission(String permission)
|
public boolean hasPermission(String permission)
|
||||||
{
|
{
|
||||||
Boolean val = permissions.get( permission );
|
return bungee.getPluginManager().callEvent( new PermissionCheckEvent( this, permission, permissions.contains( permission ) ) ).hasPermission();
|
||||||
return ( val == null ) ? false : val;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Synchronized("permMutex")
|
|
||||||
public void setPermission(String permission, boolean value)
|
public void setPermission(String permission, boolean value)
|
||||||
{
|
{
|
||||||
permissions.put( permission, value );
|
if ( value )
|
||||||
}
|
|
||||||
|
|
||||||
private class UpstreamBridge extends Thread
|
|
||||||
{
|
|
||||||
|
|
||||||
public UpstreamBridge()
|
|
||||||
{
|
{
|
||||||
super( "Upstream Bridge - " + name );
|
permissions.add( permission );
|
||||||
}
|
} else
|
||||||
|
|
||||||
@Override
|
|
||||||
public void run()
|
|
||||||
{
|
{
|
||||||
while ( !socket.isClosed() )
|
permissions.remove( permission );
|
||||||
{
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
PluginMessageEvent event = new PluginMessageEvent( UserConnection.this, server, message.tag, message.data );
|
|
||||||
ProxyServer.getInstance().getPluginManager().callEvent( event );
|
|
||||||
|
|
||||||
if ( event.isCancelled() )
|
|
||||||
{
|
|
||||||
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
|
@Override
|
||||||
|
public String toString()
|
||||||
{
|
{
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
public DownstreamBridge()
|
public void setClientEntityId(int clientEntityId)
|
||||||
{
|
{
|
||||||
super( "Downstream Bridge - " + name );
|
Preconditions.checkState( this.clientEntityId == 0, "Client entityId already set!" );
|
||||||
}
|
this.clientEntityId = clientEntityId;
|
||||||
|
|
||||||
@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 ) );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,6 +1,8 @@
|
|||||||
package net.md_5.bungee;
|
package net.md_5.bungee;
|
||||||
|
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Series of utility classes to perform various operations.
|
* Series of utility classes to perform various operations.
|
||||||
@@ -27,39 +29,6 @@ public class Util
|
|||||||
return new InetSocketAddress( split[0], port );
|
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.
|
* Formats an integer as a hex value.
|
||||||
*
|
*
|
||||||
@@ -80,6 +49,26 @@ public class Util
|
|||||||
*/
|
*/
|
||||||
public static String exception(Throwable t)
|
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.FileWriter;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import net.md_5.bungee.api.ProxyServer;
|
import net.md_5.bungee.api.ProxyServer;
|
||||||
import net.md_5.bungee.api.ReconnectHandler;
|
import net.md_5.bungee.api.ReconnectHandler;
|
||||||
@@ -41,10 +41,7 @@ public class YamlReconnectHandler implements ReconnectHandler
|
|||||||
|
|
||||||
if ( data == null )
|
if ( data == null )
|
||||||
{
|
{
|
||||||
data = new ConcurrentHashMap<>();
|
data = new HashMap<>();
|
||||||
} else
|
|
||||||
{
|
|
||||||
data = new ConcurrentHashMap<>( data );
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -53,15 +50,14 @@ public class YamlReconnectHandler implements ReconnectHandler
|
|||||||
{
|
{
|
||||||
ListenerInfo listener = player.getPendingConnection().getListener();
|
ListenerInfo listener = player.getPendingConnection().getListener();
|
||||||
String name;
|
String name;
|
||||||
if ( listener.isForceDefault() )
|
String forced = listener.getForcedHosts().get( player.getPendingConnection().getVirtualHost().getHostName().toLowerCase() );
|
||||||
|
if ( forced == null && listener.isForceDefault() )
|
||||||
{
|
{
|
||||||
name = listener.getDefaultServer();
|
forced = listener.getDefaultServer();
|
||||||
} else
|
|
||||||
{
|
|
||||||
String forced = listener.getForcedHosts().get( player.getPendingConnection().getVirtualHost().getHostName() );
|
|
||||||
String server = ( forced == null ) ? data.get( key( player ) ) : forced;
|
|
||||||
name = ( server != null ) ? server : listener.getDefaultServer();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String server = ( forced == null ) ? data.get( key( player ) ) : forced;
|
||||||
|
name = ( server != null ) ? server : listener.getDefaultServer();
|
||||||
ServerInfo info = ProxyServer.getInstance().getServerInfo( name );
|
ServerInfo info = ProxyServer.getInstance().getServerInfo( name );
|
||||||
if ( info == null )
|
if ( info == null )
|
||||||
{
|
{
|
||||||
@@ -84,7 +80,7 @@ public class YamlReconnectHandler implements ReconnectHandler
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void save()
|
public synchronized void save()
|
||||||
{
|
{
|
||||||
try ( FileWriter wr = new FileWriter( file ) )
|
try ( FileWriter wr = new FileWriter( file ) )
|
||||||
{
|
{
|
||||||
|
@@ -44,6 +44,7 @@ public class CommandAlert extends Command
|
|||||||
{
|
{
|
||||||
player.sendMessage( message );
|
player.sendMessage( message );
|
||||||
}
|
}
|
||||||
|
ProxyServer.getInstance().getConsole().sendMessage( message );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,9 +1,14 @@
|
|||||||
package net.md_5.bungee.command;
|
package net.md_5.bungee.command;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
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.ChatColor;
|
||||||
import net.md_5.bungee.api.CommandSender;
|
import net.md_5.bungee.api.CommandSender;
|
||||||
import net.md_5.bungee.api.ProxyServer;
|
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.connection.ProxiedPlayer;
|
||||||
import net.md_5.bungee.api.plugin.Command;
|
import net.md_5.bungee.api.plugin.Command;
|
||||||
|
|
||||||
@@ -21,23 +26,38 @@ public class CommandList extends Command
|
|||||||
@Override
|
@Override
|
||||||
public void execute(CommandSender sender, String[] args)
|
public void execute(CommandSender sender, String[] args)
|
||||||
{
|
{
|
||||||
StringBuilder users = new StringBuilder();
|
for ( ServerInfo server : ProxyServer.getInstance().getServers().values() )
|
||||||
Collection<ProxiedPlayer> connections = ProxyServer.getInstance().getPlayers();
|
|
||||||
|
|
||||||
if ( connections.isEmpty() )
|
|
||||||
{
|
{
|
||||||
sender.sendMessage( ChatColor.BLUE + "Currently no players online." );
|
if ( !server.canAccess( sender ) )
|
||||||
return;
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
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() );
|
||||||
}
|
}
|
||||||
|
|
||||||
for ( ProxiedPlayer player : connections )
|
sender.sendMessage( "Total players online: " + ProxyServer.getInstance().getPlayers().size() );
|
||||||
{
|
|
||||||
users.append( player.getDisplayName() );
|
|
||||||
users.append( ", " );
|
|
||||||
users.append( ChatColor.RESET );
|
|
||||||
}
|
|
||||||
|
|
||||||
users.setLength( users.length() - 2 );
|
|
||||||
sender.sendMessage( ChatColor.BLUE + "Currently online across all servers (" + connections.size() + "): " + ChatColor.RESET + users );
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
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;
|
package net.md_5.bungee.command;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import net.md_5.bungee.BungeeCord;
|
|
||||||
import net.md_5.bungee.api.ChatColor;
|
import net.md_5.bungee.api.ChatColor;
|
||||||
import net.md_5.bungee.api.CommandSender;
|
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.config.ServerInfo;
|
||||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||||
import net.md_5.bungee.api.plugin.Command;
|
import net.md_5.bungee.api.plugin.Command;
|
||||||
@@ -27,16 +27,22 @@ public class CommandServer extends Command
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ProxiedPlayer player = (ProxiedPlayer) sender;
|
ProxiedPlayer player = (ProxiedPlayer) sender;
|
||||||
Map<String, ServerInfo> servers = BungeeCord.getInstance().config.getServers();
|
Map<String, ServerInfo> servers = ProxyServer.getInstance().getServers();
|
||||||
if ( args.length == 0 )
|
if ( args.length == 0 )
|
||||||
{
|
{
|
||||||
StringBuilder serverList = new StringBuilder();
|
StringBuilder serverList = new StringBuilder();
|
||||||
for ( String server : servers.keySet() )
|
for ( ServerInfo server : servers.values() )
|
||||||
{
|
{
|
||||||
serverList.append( server );
|
if ( server.canAccess( player ) )
|
||||||
serverList.append( ", " );
|
{
|
||||||
|
serverList.append( server.getName() );
|
||||||
|
serverList.append( ", " );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ( serverList.length() != 0 )
|
||||||
|
{
|
||||||
|
serverList.setLength( serverList.length() - 2 );
|
||||||
}
|
}
|
||||||
serverList.setLength( serverList.length() - 2 );
|
|
||||||
player.sendMessage( ChatColor.GOLD + "You may connect to the following servers at this time: " + serverList.toString() );
|
player.sendMessage( ChatColor.GOLD + "You may connect to the following servers at this time: " + serverList.toString() );
|
||||||
} else
|
} else
|
||||||
{
|
{
|
||||||
@@ -44,9 +50,9 @@ public class CommandServer extends Command
|
|||||||
if ( server == null )
|
if ( server == null )
|
||||||
{
|
{
|
||||||
player.sendMessage( ChatColor.RED + "The specified server does not exist" );
|
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
|
} else
|
||||||
{
|
{
|
||||||
player.connect( server );
|
player.connect( server );
|
||||||
|
@@ -25,6 +25,15 @@ public class ConsoleCommandSender implements CommandSender
|
|||||||
System.out.println( ChatColor.stripColor( message ) );
|
System.out.println( ChatColor.stripColor( message ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void sendMessages(String... messages)
|
||||||
|
{
|
||||||
|
for ( String message : messages )
|
||||||
|
{
|
||||||
|
sendMessage( message );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getName()
|
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.ConfigurationAdapter;
|
||||||
import net.md_5.bungee.api.config.ListenerInfo;
|
import net.md_5.bungee.api.config.ListenerInfo;
|
||||||
import net.md_5.bungee.api.config.ServerInfo;
|
import net.md_5.bungee.api.config.ServerInfo;
|
||||||
import net.md_5.bungee.tablist.GlobalPingTabList;
|
import net.md_5.bungee.tablist.GlobalPing;
|
||||||
import net.md_5.bungee.tablist.GlobalTabList;
|
import net.md_5.bungee.tablist.Global;
|
||||||
import net.md_5.bungee.tablist.ServerUniqueTabList;
|
import net.md_5.bungee.tablist.ServerUnique;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Core configuration for the proxy.
|
* Core configuration for the proxy.
|
||||||
@@ -48,7 +48,7 @@ public class Configuration
|
|||||||
* Should we check minecraft.net auth.
|
* Should we check minecraft.net auth.
|
||||||
*/
|
*/
|
||||||
private boolean onlineMode = true;
|
private boolean onlineMode = true;
|
||||||
private int sleepTime = 1;
|
private int playerLimit = -1;
|
||||||
|
|
||||||
public void load()
|
public void load()
|
||||||
{
|
{
|
||||||
@@ -58,7 +58,7 @@ public class Configuration
|
|||||||
timeout = adapter.getInt( "timeout", timeout );
|
timeout = adapter.getInt( "timeout", timeout );
|
||||||
uuid = adapter.getString( "stats", uuid );
|
uuid = adapter.getString( "stats", uuid );
|
||||||
onlineMode = adapter.getBoolean( "online_mode", onlineMode );
|
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" ) );
|
DefaultTabList tab = DefaultTabList.valueOf( adapter.getString( "tab_list", "GLOBAL_PING" ) );
|
||||||
if ( tab == null )
|
if ( tab == null )
|
||||||
@@ -68,25 +68,46 @@ public class Configuration
|
|||||||
switch ( tab )
|
switch ( tab )
|
||||||
{
|
{
|
||||||
case GLOBAL:
|
case GLOBAL:
|
||||||
ProxyServer.getInstance().setTabListHandler( new GlobalTabList() );
|
ProxyServer.getInstance().setTabListHandler( new Global() );
|
||||||
break;
|
break;
|
||||||
case GLOBAL_PING:
|
case GLOBAL_PING:
|
||||||
ProxyServer.getInstance().setTabListHandler( new GlobalPingTabList() );
|
ProxyServer.getInstance().setTabListHandler( new GlobalPing() );
|
||||||
break;
|
break;
|
||||||
case SERVER:
|
case SERVER:
|
||||||
ProxyServer.getInstance().setTabListHandler( new ServerUniqueTabList() );
|
ProxyServer.getInstance().setTabListHandler( new ServerUnique() );
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
listeners = adapter.getListeners();
|
listeners = adapter.getListeners();
|
||||||
Preconditions.checkArgument( listeners != null && !listeners.isEmpty(), "No listeners defined." );
|
Preconditions.checkArgument( listeners != null && !listeners.isEmpty(), "No listeners defined." );
|
||||||
|
|
||||||
servers = adapter.getServers();
|
Map<String, ServerInfo> newServers = adapter.getServers();
|
||||||
Preconditions.checkArgument( servers != null && !servers.isEmpty(), "No servers defined" );
|
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 )
|
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.ConfigurationAdapter;
|
||||||
import net.md_5.bungee.api.config.ListenerInfo;
|
import net.md_5.bungee.api.config.ListenerInfo;
|
||||||
import net.md_5.bungee.api.config.ServerInfo;
|
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.DumperOptions;
|
||||||
import org.yaml.snakeyaml.Yaml;
|
import org.yaml.snakeyaml.Yaml;
|
||||||
|
|
||||||
@@ -151,8 +152,9 @@ public class YamlConfig implements ConfigurationAdapter
|
|||||||
Map<String, Object> val = entry.getValue();
|
Map<String, Object> val = entry.getValue();
|
||||||
String name = entry.getKey();
|
String name = entry.getKey();
|
||||||
String addr = get( "address", "localhost:25565", val );
|
String addr = get( "address", "localhost:25565", val );
|
||||||
|
boolean restricted = get( "restricted", false, val );
|
||||||
InetSocketAddress address = Util.getAddr( addr );
|
InetSocketAddress address = Util.getAddr( addr );
|
||||||
ServerInfo info = ProxyServer.getInstance().constructServerInfo( name, address );
|
ServerInfo info = ProxyServer.getInstance().constructServerInfo( name, address, restricted );
|
||||||
ret.put( name, info );
|
ret.put( name, info );
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -179,12 +181,16 @@ public class YamlConfig implements ConfigurationAdapter
|
|||||||
|
|
||||||
int maxPlayers = get( "max_players", 1, val );
|
int maxPlayers = get( "max_players", 1, val );
|
||||||
String defaultServer = get( "default_server", "lobby", val );
|
String defaultServer = get( "default_server", "lobby", val );
|
||||||
|
String fallbackServer = get( "fallback_server", defaultServer, val );
|
||||||
boolean forceDefault = get( "force_default_server", false, val );
|
boolean forceDefault = get( "force_default_server", false, val );
|
||||||
String host = get( "host", "0.0.0.0:25577", val );
|
String host = get( "host", "0.0.0.0:25577", val );
|
||||||
int tabListSize = get( "tab_size", 60, val );
|
int tabListSize = get( "tab_size", 60, val );
|
||||||
InetSocketAddress address = Util.getAddr( host );
|
InetSocketAddress address = Util.getAddr( host );
|
||||||
Map<String, String> forced = get( "forced_hosts", forcedDef, val );
|
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 );
|
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;
|
package net.md_5.bungee.packet;
|
||||||
|
|
||||||
import com.google.common.base.Preconditions;
|
|
||||||
import com.google.common.io.ByteArrayDataOutput;
|
import com.google.common.io.ByteArrayDataOutput;
|
||||||
import com.google.common.io.ByteStreams;
|
import com.google.common.io.ByteStreams;
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
@@ -20,42 +19,33 @@ import net.md_5.bungee.Util;
|
|||||||
public abstract class DefinedPacket implements DataOutput
|
public abstract class DefinedPacket implements DataOutput
|
||||||
{
|
{
|
||||||
|
|
||||||
private interface Overriden
|
private static interface Overriden
|
||||||
{
|
{
|
||||||
|
|
||||||
void readUTF();
|
void readUTF();
|
||||||
|
|
||||||
void writeUTF(String s);
|
void writeUTF(String s);
|
||||||
}
|
}
|
||||||
private ByteArrayInputStream bin;
|
private ByteArrayInputStream byteStream;
|
||||||
private DataInputStream input;
|
private DataInputStream in;
|
||||||
@Delegate(excludes = Overriden.class)
|
@Delegate(excludes = Overriden.class)
|
||||||
private ByteArrayDataOutput out;
|
private ByteArrayDataOutput out;
|
||||||
/**
|
private byte[] buf;
|
||||||
* Packet id.
|
|
||||||
*/
|
|
||||||
public final int id;
|
|
||||||
/**
|
|
||||||
* Already constructed packet.
|
|
||||||
*/
|
|
||||||
private byte[] packet;
|
|
||||||
|
|
||||||
public DefinedPacket(int id, byte[] buf)
|
public DefinedPacket(int id, byte[] buf)
|
||||||
{
|
{
|
||||||
bin = new ByteArrayInputStream( buf );
|
byteStream = new ByteArrayInputStream( buf );
|
||||||
input = new DataInputStream( bin );
|
in = new DataInputStream( byteStream );
|
||||||
if ( readUnsignedByte() != id )
|
if ( readUnsignedByte() != id )
|
||||||
{
|
{
|
||||||
throw new IllegalArgumentException( "Wasn't expecting packet id " + Util.hex( id ) );
|
throw new IllegalArgumentException( "Wasn't expecting packet id " + Util.hex( id ) );
|
||||||
}
|
}
|
||||||
this.id = id;
|
this.buf = buf;
|
||||||
packet = buf;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public DefinedPacket(int id)
|
public DefinedPacket(int id)
|
||||||
{
|
{
|
||||||
out = ByteStreams.newDataOutput();
|
out = ByteStreams.newDataOutput();
|
||||||
this.id = id;
|
|
||||||
writeByte( id );
|
writeByte( id );
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -67,7 +57,7 @@ public abstract class DefinedPacket implements DataOutput
|
|||||||
*/
|
*/
|
||||||
public byte[] getPacket()
|
public byte[] getPacket()
|
||||||
{
|
{
|
||||||
return packet == null ? packet = out.toByteArray() : packet;
|
return buf == null ? buf = out.toByteArray() : buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -104,14 +94,14 @@ public abstract class DefinedPacket implements DataOutput
|
|||||||
|
|
||||||
public final int available()
|
public final int available()
|
||||||
{
|
{
|
||||||
return bin.available();
|
return byteStream.available();
|
||||||
}
|
}
|
||||||
|
|
||||||
public final void readFully(byte b[])
|
public final void readFully(byte b[])
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
input.readFully( b );
|
in.readFully( b );
|
||||||
} catch ( IOException e )
|
} catch ( IOException e )
|
||||||
{
|
{
|
||||||
throw new IllegalStateException( e );
|
throw new IllegalStateException( e );
|
||||||
@@ -122,7 +112,7 @@ public abstract class DefinedPacket implements DataOutput
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
return input.readBoolean();
|
return in.readBoolean();
|
||||||
} catch ( IOException e )
|
} catch ( IOException e )
|
||||||
{
|
{
|
||||||
throw new IllegalStateException( e );
|
throw new IllegalStateException( e );
|
||||||
@@ -133,7 +123,7 @@ public abstract class DefinedPacket implements DataOutput
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
return input.readByte();
|
return in.readByte();
|
||||||
} catch ( IOException e )
|
} catch ( IOException e )
|
||||||
{
|
{
|
||||||
throw new IllegalStateException( e );
|
throw new IllegalStateException( e );
|
||||||
@@ -144,7 +134,7 @@ public abstract class DefinedPacket implements DataOutput
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
return input.readUnsignedByte();
|
return in.readUnsignedByte();
|
||||||
} catch ( IOException e )
|
} catch ( IOException e )
|
||||||
{
|
{
|
||||||
throw new IllegalStateException( e );
|
throw new IllegalStateException( e );
|
||||||
@@ -155,7 +145,7 @@ public abstract class DefinedPacket implements DataOutput
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
return input.readShort();
|
return in.readShort();
|
||||||
} catch ( IOException e )
|
} catch ( IOException e )
|
||||||
{
|
{
|
||||||
throw new IllegalStateException( e );
|
throw new IllegalStateException( e );
|
||||||
@@ -166,7 +156,7 @@ public abstract class DefinedPacket implements DataOutput
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
return input.readChar();
|
return in.readChar();
|
||||||
} catch ( IOException e )
|
} catch ( IOException e )
|
||||||
{
|
{
|
||||||
throw new IllegalStateException( e );
|
throw new IllegalStateException( e );
|
||||||
@@ -177,7 +167,7 @@ public abstract class DefinedPacket implements DataOutput
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
return input.readInt();
|
return in.readInt();
|
||||||
} catch ( IOException e )
|
} catch ( IOException e )
|
||||||
{
|
{
|
||||||
throw new IllegalStateException( e );
|
throw new IllegalStateException( e );
|
||||||
@@ -194,18 +184,27 @@ public abstract class DefinedPacket implements DataOutput
|
|||||||
public abstract String toString();
|
public abstract String toString();
|
||||||
|
|
||||||
public abstract void handle(PacketHandler handler) throws Exception;
|
public abstract void handle(PacketHandler handler) throws Exception;
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
private static Class<? extends DefinedPacket>[] classes = new Class[ 256 ];
|
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)
|
public static DefinedPacket packet(byte[] buf)
|
||||||
{
|
{
|
||||||
int id = Util.getId( buf );
|
int id = buf[0] & 0xFF;
|
||||||
Class<? extends DefinedPacket> clazz = classes[id];
|
Class<? extends DefinedPacket> clazz = classes[id];
|
||||||
DefinedPacket ret = null;
|
DefinedPacket ret = null;
|
||||||
if ( clazz != null )
|
if ( clazz != null )
|
||||||
{
|
{
|
||||||
try
|
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 )
|
if ( constructor != null )
|
||||||
{
|
{
|
||||||
ret = constructor.newInstance( buf );
|
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;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -227,7 +225,12 @@ public abstract class DefinedPacket implements DataOutput
|
|||||||
classes[0x03] = Packet3Chat.class;
|
classes[0x03] = Packet3Chat.class;
|
||||||
classes[0x09] = Packet9Respawn.class;
|
classes[0x09] = Packet9Respawn.class;
|
||||||
classes[0xC9] = PacketC9PlayerListItem.class;
|
classes[0xC9] = PacketC9PlayerListItem.class;
|
||||||
|
classes[0xCC] = PacketCCSettings.class;
|
||||||
classes[0xCD] = PacketCDClientStatus.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[0xFA] = PacketFAPluginMessage.class;
|
||||||
classes[0xFC] = PacketFCEncryptionResponse.class;
|
classes[0xFC] = PacketFCEncryptionResponse.class;
|
||||||
classes[0xFD] = PacketFDEncryptionRequest.class;
|
classes[0xFD] = PacketFDEncryptionRequest.class;
|
||||||
|
@@ -10,9 +10,9 @@ public class Packet0KeepAlive extends DefinedPacket
|
|||||||
|
|
||||||
public int id;
|
public int id;
|
||||||
|
|
||||||
public Packet0KeepAlive(byte[] buffer)
|
Packet0KeepAlive(byte[] buf)
|
||||||
{
|
{
|
||||||
super( 0x00, buffer );
|
super( 0x00, buf );
|
||||||
id = readInt();
|
id = readInt();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -26,9 +26,16 @@ public class Packet1Login extends DefinedPacket
|
|||||||
writeByte( difficulty );
|
writeByte( difficulty );
|
||||||
writeByte( unused );
|
writeByte( unused );
|
||||||
writeByte( maxPlayers );
|
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 );
|
super( 0x01, buf );
|
||||||
this.entityId = readInt();
|
this.entityId = readInt();
|
||||||
|
@@ -20,9 +20,13 @@ public class Packet2Handshake extends DefinedPacket
|
|||||||
writeUTF( username );
|
writeUTF( username );
|
||||||
writeUTF( host );
|
writeUTF( host );
|
||||||
writeInt( port );
|
writeInt( port );
|
||||||
|
this.procolVersion = protocolVersion;
|
||||||
|
this.username = username;
|
||||||
|
this.host = host;
|
||||||
|
this.port = port;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Packet2Handshake(byte[] buf)
|
Packet2Handshake(byte[] buf)
|
||||||
{
|
{
|
||||||
super( 0x02, buf );
|
super( 0x02, buf );
|
||||||
this.procolVersion = readByte();
|
this.procolVersion = readByte();
|
||||||
|
@@ -14,9 +14,10 @@ public class Packet3Chat extends DefinedPacket
|
|||||||
{
|
{
|
||||||
super( 0x03 );
|
super( 0x03 );
|
||||||
writeUTF( message );
|
writeUTF( message );
|
||||||
|
this.message = message;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Packet3Chat(byte[] buf)
|
Packet3Chat(byte[] buf)
|
||||||
{
|
{
|
||||||
super( 0x03, buf );
|
super( 0x03, buf );
|
||||||
this.message = readUTF();
|
this.message = readUTF();
|
||||||
|
@@ -8,6 +8,8 @@ import lombok.ToString;
|
|||||||
public class Packet9Respawn extends DefinedPacket
|
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 int dimension;
|
||||||
public byte difficulty;
|
public byte difficulty;
|
||||||
public byte gameMode;
|
public byte gameMode;
|
||||||
@@ -22,9 +24,14 @@ public class Packet9Respawn extends DefinedPacket
|
|||||||
writeByte( gameMode );
|
writeByte( gameMode );
|
||||||
writeShort( worldHeight );
|
writeShort( worldHeight );
|
||||||
writeUTF( levelType );
|
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 );
|
super( 0x09, buf );
|
||||||
this.dimension = readInt();
|
this.dimension = readInt();
|
||||||
|
@@ -12,9 +12,9 @@ public class PacketC9PlayerListItem extends DefinedPacket
|
|||||||
public boolean online;
|
public boolean online;
|
||||||
public int ping;
|
public int ping;
|
||||||
|
|
||||||
public PacketC9PlayerListItem(byte[] packet)
|
PacketC9PlayerListItem(byte[] buf)
|
||||||
{
|
{
|
||||||
super( 0xC9, packet );
|
super( 0xC9, buf );
|
||||||
username = readUTF();
|
username = readUTF();
|
||||||
online = readBoolean();
|
online = readBoolean();
|
||||||
ping = readShort();
|
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
|
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 );
|
public static PacketCDClientStatus CLIENT_LOGIN = new PacketCDClientStatus( (byte) 0 );
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -21,7 +25,7 @@ public class PacketCDClientStatus extends DefinedPacket
|
|||||||
writeByte( payload );
|
writeByte( payload );
|
||||||
}
|
}
|
||||||
|
|
||||||
public PacketCDClientStatus(byte[] buf)
|
PacketCDClientStatus(byte[] buf)
|
||||||
{
|
{
|
||||||
super( 0xCD, 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;
|
this.data = data;
|
||||||
}
|
}
|
||||||
|
|
||||||
public PacketFAPluginMessage(byte[] buf)
|
PacketFAPluginMessage(byte[] buf)
|
||||||
{
|
{
|
||||||
super( 0xFA, buf );
|
super( 0xFA, buf );
|
||||||
this.tag = readUTF();
|
this.tag = readUTF();
|
||||||
|
@@ -23,9 +23,11 @@ public class PacketFCEncryptionResponse extends DefinedPacket
|
|||||||
super( 0xFC );
|
super( 0xFC );
|
||||||
writeArray( sharedSecret );
|
writeArray( sharedSecret );
|
||||||
writeArray( verifyToken );
|
writeArray( verifyToken );
|
||||||
|
this.sharedSecret = sharedSecret;
|
||||||
|
this.verifyToken = verifyToken;
|
||||||
}
|
}
|
||||||
|
|
||||||
public PacketFCEncryptionResponse(byte[] buf)
|
PacketFCEncryptionResponse(byte[] buf)
|
||||||
{
|
{
|
||||||
super( 0xFC, buf );
|
super( 0xFC, buf );
|
||||||
this.sharedSecret = readArray();
|
this.sharedSecret = readArray();
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
package net.md_5.bungee.packet;
|
package net.md_5.bungee.packet;
|
||||||
|
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
import lombok.EqualsAndHashCode;
|
import lombok.EqualsAndHashCode;
|
||||||
import lombok.ToString;
|
import lombok.ToString;
|
||||||
|
|
||||||
@@ -23,7 +24,7 @@ public class PacketFDEncryptionRequest extends DefinedPacket
|
|||||||
this.verifyToken = verifyToken;
|
this.verifyToken = verifyToken;
|
||||||
}
|
}
|
||||||
|
|
||||||
public PacketFDEncryptionRequest(byte[] buf)
|
PacketFDEncryptionRequest(byte[] buf)
|
||||||
{
|
{
|
||||||
super( 0xFD, buf );
|
super( 0xFD, buf );
|
||||||
serverId = readUTF();
|
serverId = readUTF();
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user