mirror of
https://github.com/SpigotMC/BungeeCord.git
synced 2024-11-24 03:05:49 +01:00
Merge NIO into master. I would not recommend this on a production server at all. Its 1.5 anyway.
This commit is contained in:
commit
a3e1493ce1
@ -16,4 +16,14 @@ public interface Connection
|
||||
* @return the remote address
|
||||
*/
|
||||
public InetSocketAddress getAddress();
|
||||
|
||||
/**
|
||||
* Disconnects this end of the connection for the specified reason. If this
|
||||
* is an {@link ProxiedPlayer} the respective server connection will be
|
||||
* closed too.
|
||||
*
|
||||
* @param reason the reason shown to the player / sent to the server on
|
||||
* disconnect
|
||||
*/
|
||||
public void disconnect(String reason);
|
||||
}
|
||||
|
@ -30,14 +30,6 @@ public interface PendingConnection extends Connection
|
||||
*/
|
||||
public InetSocketAddress getVirtualHost();
|
||||
|
||||
/**
|
||||
* Completely kick this user from the proxy and all of its child
|
||||
* connections.
|
||||
*
|
||||
* @param reason the disconnect reason displayed to the player
|
||||
*/
|
||||
public void disconnect(String reason);
|
||||
|
||||
/**
|
||||
* Get the listener that accepted this connection.
|
||||
*
|
||||
|
@ -48,13 +48,6 @@ public interface ProxiedPlayer extends Connection, CommandSender
|
||||
*/
|
||||
public int getPing();
|
||||
|
||||
/**
|
||||
* Disconnect (remove) this player from the proxy with the specified reason.
|
||||
*
|
||||
* @param reason the reason displayed to the player
|
||||
*/
|
||||
public void disconnect(String reason);
|
||||
|
||||
/**
|
||||
* Send a plugin message to this player.
|
||||
*
|
||||
|
@ -1,7 +1,5 @@
|
||||
package net.md_5.bungee.api.connection;
|
||||
|
||||
import net.md_5.bungee.api.Callback;
|
||||
import net.md_5.bungee.api.ServerPing;
|
||||
import net.md_5.bungee.api.config.ServerInfo;
|
||||
|
||||
/**
|
||||
@ -24,14 +22,4 @@ public interface Server extends Connection
|
||||
* @param data the data to send
|
||||
*/
|
||||
public abstract void sendData(String channel, byte[] data);
|
||||
|
||||
/**
|
||||
* Asynchronously gets the current player count on this server.
|
||||
*
|
||||
* @param callback the callback to call when the count has been retrieved.
|
||||
* @deprecated use the corresponding method in {@link ServerInfo} for
|
||||
* clarity
|
||||
*/
|
||||
@Deprecated
|
||||
public abstract void ping(Callback<ServerPing> callback);
|
||||
}
|
||||
|
2
pom.xml
2
pom.xml
@ -92,7 +92,7 @@
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.0</version>
|
||||
<version>2.5.1</version>
|
||||
<configuration>
|
||||
<source>1.7</source>
|
||||
<target>1.7</target>
|
||||
|
@ -17,4 +17,14 @@
|
||||
|
||||
<name>BungeeCord-Protocol</name>
|
||||
<description>Minimal implementation of the Minecraft protocol for use in BungeeCord</description>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<!-- TODO: Fix this -->
|
||||
<artifactId>netty-all</artifactId>
|
||||
<version>4.0.0.Beta3-SNAPSHOT</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
|
@ -1,14 +1,13 @@
|
||||
package net.md_5.mendax;
|
||||
package net.md_5.bungee.protocol;
|
||||
|
||||
import static net.md_5.mendax.PacketDefinitions.OpCode.*;
|
||||
import static net.md_5.bungee.protocol.PacketDefinitions.OpCode.*;
|
||||
|
||||
public class PacketDefinitions
|
||||
{
|
||||
|
||||
private static final int MAX_PACKET = 256;
|
||||
public static final OpCode[][] opCodes = new OpCode[ MAX_PACKET * 2 ][];
|
||||
public static final OpCode[][] opCodes = new OpCode[ 512 ][];
|
||||
public static final int VANILLA_PROTOCOL = 0;
|
||||
public static final int FORGE_PROTOCOL = MAX_PACKET;
|
||||
public static final int FORGE_PROTOCOL = 256;
|
||||
|
||||
public enum OpCode
|
||||
{
|
||||
@ -332,7 +331,8 @@ public class PacketDefinitions
|
||||
};
|
||||
opCodes[0xFE] = new OpCode[]
|
||||
{
|
||||
}; // Should be byte, screw you too bitchy server admins!
|
||||
BYTE
|
||||
};
|
||||
opCodes[0xFF] = new OpCode[]
|
||||
{
|
||||
STRING
|
@ -1,17 +1,17 @@
|
||||
package net.md_5.mendax.datainput;
|
||||
package net.md_5.bungee.protocol.netty;
|
||||
|
||||
import java.io.DataInput;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import java.io.IOException;
|
||||
|
||||
public class BulkChunk extends Instruction
|
||||
{
|
||||
|
||||
@Override
|
||||
void read(DataInput in, byte[] buffer) throws IOException
|
||||
void read(ByteBuf in) throws IOException
|
||||
{
|
||||
short count = in.readShort();
|
||||
int size = in.readInt();
|
||||
in.readBoolean();
|
||||
skip( in, buffer, size + count * 12 );
|
||||
in.skipBytes( size + count * 12 );
|
||||
}
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
package net.md_5.mendax.datainput;
|
||||
package net.md_5.bungee.protocol.netty;
|
||||
|
||||
import java.io.DataInput;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import java.io.IOException;
|
||||
|
||||
class ByteHeader extends Instruction
|
||||
@ -14,12 +14,12 @@ class ByteHeader extends Instruction
|
||||
}
|
||||
|
||||
@Override
|
||||
void read(DataInput in, byte[] buffer) throws IOException
|
||||
void read(ByteBuf in) throws IOException
|
||||
{
|
||||
byte size = in.readByte();
|
||||
for ( byte b = 0; b < size; b++ )
|
||||
{
|
||||
child.read( in, buffer );
|
||||
child.read( in );
|
||||
}
|
||||
}
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
package net.md_5.mendax.datainput;
|
||||
package net.md_5.bungee.protocol.netty;
|
||||
|
||||
import java.io.DataInput;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import java.io.IOException;
|
||||
|
||||
abstract class Instruction
|
||||
@ -30,10 +30,5 @@ abstract class Instruction
|
||||
// Custom instructions
|
||||
static final Instruction STRING_ARRAY = new ShortHeader( STRING );
|
||||
|
||||
abstract void read(DataInput in, byte[] buffer) throws IOException;
|
||||
|
||||
final void skip(DataInput in, byte[] buffer, int len) throws IOException
|
||||
{
|
||||
in.readFully( buffer, 0, len );
|
||||
}
|
||||
abstract void read(ByteBuf in) throws IOException;
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
package net.md_5.mendax.datainput;
|
||||
package net.md_5.bungee.protocol.netty;
|
||||
|
||||
import java.io.DataInput;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import java.io.IOException;
|
||||
|
||||
class IntHeader extends Instruction
|
||||
@ -14,12 +14,12 @@ class IntHeader extends Instruction
|
||||
}
|
||||
|
||||
@Override
|
||||
void read(DataInput in, byte[] buffer) throws IOException
|
||||
void read(ByteBuf in) throws IOException
|
||||
{
|
||||
int size = in.readInt();
|
||||
for ( int i = 0; i < size; i++ )
|
||||
{
|
||||
child.read( in, buffer );
|
||||
child.read( in );
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
package net.md_5.bungee.protocol.netty;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import java.io.IOException;
|
||||
|
||||
class Item extends Instruction
|
||||
{
|
||||
|
||||
@Override
|
||||
void read(ByteBuf in) throws IOException
|
||||
{
|
||||
short type = in.readShort();
|
||||
if ( type >= 0 )
|
||||
{
|
||||
in.skipBytes( 3 );
|
||||
SHORT_BYTE.read( in );
|
||||
}
|
||||
}
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
package net.md_5.mendax.datainput;
|
||||
package net.md_5.bungee.protocol.netty;
|
||||
|
||||
import java.io.DataInput;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import java.io.IOException;
|
||||
|
||||
class Jump extends Instruction
|
||||
@ -18,8 +18,8 @@ class Jump extends Instruction
|
||||
}
|
||||
|
||||
@Override
|
||||
void read(DataInput in, byte[] buffer) throws IOException
|
||||
void read(ByteBuf in) throws IOException
|
||||
{
|
||||
skip( in, buffer, len );
|
||||
in.skipBytes( len );
|
||||
}
|
||||
}
|
@ -1,13 +1,13 @@
|
||||
package net.md_5.mendax.datainput;
|
||||
package net.md_5.bungee.protocol.netty;
|
||||
|
||||
import java.io.DataInput;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import java.io.IOException;
|
||||
|
||||
class MetaData extends Instruction
|
||||
{
|
||||
|
||||
@Override
|
||||
void read(DataInput in, byte[] buffer) throws IOException
|
||||
void read(ByteBuf in) throws IOException
|
||||
{
|
||||
int x = in.readUnsignedByte();
|
||||
while ( x != 127 )
|
||||
@ -16,25 +16,25 @@ class MetaData extends Instruction
|
||||
switch ( type )
|
||||
{
|
||||
case 0:
|
||||
BYTE.read( in, buffer );
|
||||
BYTE.read( in );
|
||||
break;
|
||||
case 1:
|
||||
SHORT.read( in, buffer );
|
||||
SHORT.read( in );
|
||||
break;
|
||||
case 2:
|
||||
INT.read( in, buffer );
|
||||
INT.read( in );
|
||||
break;
|
||||
case 3:
|
||||
FLOAT.read( in, buffer );
|
||||
FLOAT.read( in );
|
||||
break;
|
||||
case 4:
|
||||
STRING.read( in, buffer );
|
||||
STRING.read( in );
|
||||
break;
|
||||
case 5:
|
||||
ITEM.read( in, buffer );
|
||||
ITEM.read( in );
|
||||
break;
|
||||
case 6:
|
||||
skip( in, buffer, 12 ); // int, int, int
|
||||
in.skipBytes( 12 ); // int, int, int
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException( "Unknown metadata type " + type );
|
@ -0,0 +1,18 @@
|
||||
package net.md_5.bungee.protocol.netty;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import java.io.IOException;
|
||||
|
||||
class OptionalMotion extends Instruction
|
||||
{
|
||||
|
||||
@Override
|
||||
void read(ByteBuf in) throws IOException
|
||||
{
|
||||
int data = in.readInt();
|
||||
if ( data > 0 )
|
||||
{
|
||||
in.skipBytes( 6 );
|
||||
}
|
||||
}
|
||||
}
|
@ -1,16 +1,16 @@
|
||||
package net.md_5.mendax.datainput;
|
||||
package net.md_5.bungee.protocol.netty;
|
||||
|
||||
import java.io.DataInput;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import net.md_5.mendax.PacketDefinitions;
|
||||
import net.md_5.mendax.PacketDefinitions.OpCode;
|
||||
import net.md_5.bungee.protocol.PacketDefinitions;
|
||||
import net.md_5.bungee.protocol.PacketDefinitions.OpCode;
|
||||
|
||||
public class DataInputPacketReader
|
||||
public class PacketReader
|
||||
{
|
||||
|
||||
private static final Instruction[][] instructions = new Instruction[ 256 ][];
|
||||
private static final Instruction[][] instructions = new Instruction[ PacketDefinitions.opCodes.length ][];
|
||||
|
||||
static
|
||||
{
|
||||
@ -59,7 +59,7 @@ public class DataInputPacketReader
|
||||
}
|
||||
}
|
||||
|
||||
private static void readPacket(int packetId, DataInput in, byte[] buffer, int protocol) throws IOException
|
||||
private static void readPacket(int packetId, ByteBuf in, int protocol) throws IOException
|
||||
{
|
||||
Instruction[] packetDef = null;
|
||||
if ( packetId + protocol < instructions.length )
|
||||
@ -74,20 +74,20 @@ public class DataInputPacketReader
|
||||
throw new IOException( "Unknown packet id " + packetId );
|
||||
} else
|
||||
{
|
||||
readPacket( packetId, in, buffer, PacketDefinitions.VANILLA_PROTOCOL );
|
||||
readPacket( packetId, in, PacketDefinitions.VANILLA_PROTOCOL );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
for ( Instruction instruction : packetDef )
|
||||
{
|
||||
instruction.read( in, buffer );
|
||||
instruction.read( in );
|
||||
}
|
||||
}
|
||||
|
||||
public static void readPacket(DataInput in, byte[] buffer, int protocol) throws IOException
|
||||
public static void readPacket(ByteBuf in, int protocol) throws IOException
|
||||
{
|
||||
int packetId = in.readUnsignedByte();
|
||||
readPacket( packetId, in, buffer, protocol );
|
||||
readPacket( packetId, in, protocol );
|
||||
}
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
package net.md_5.mendax.datainput;
|
||||
package net.md_5.bungee.protocol.netty;
|
||||
|
||||
import java.io.DataInput;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import java.io.IOException;
|
||||
|
||||
class ShortHeader extends Instruction
|
||||
@ -14,12 +14,12 @@ class ShortHeader extends Instruction
|
||||
}
|
||||
|
||||
@Override
|
||||
void read(DataInput in, byte[] buffer) throws IOException
|
||||
void read(ByteBuf in) throws IOException
|
||||
{
|
||||
short size = in.readShort();
|
||||
for ( short s = 0; s < size; s++ )
|
||||
{
|
||||
child.read( in, buffer );
|
||||
child.read( in );
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
package net.md_5.bungee.protocol.netty;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import java.io.IOException;
|
||||
|
||||
class Team extends Instruction
|
||||
{
|
||||
|
||||
@Override
|
||||
void read(ByteBuf in) throws IOException
|
||||
{
|
||||
STRING.read( in );
|
||||
byte mode = in.readByte();
|
||||
if ( mode == 0 || mode == 2 )
|
||||
{
|
||||
STRING.read( in );
|
||||
STRING.read( in );
|
||||
STRING.read( in );
|
||||
BYTE.read( in );
|
||||
}
|
||||
if ( mode == 0 || mode == 3 || mode == 4 )
|
||||
{
|
||||
STRING_ARRAY.read( in );
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
package net.md_5.bungee.protocol.netty;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import java.io.IOException;
|
||||
|
||||
class UnsignedShortByte extends Instruction
|
||||
{
|
||||
|
||||
@Override
|
||||
void read(ByteBuf in) throws IOException
|
||||
{
|
||||
int size = in.readUnsignedShort();
|
||||
in.skipBytes( size );
|
||||
}
|
||||
}
|
@ -1,19 +0,0 @@
|
||||
package net.md_5.mendax.datainput;
|
||||
|
||||
import java.io.DataInput;
|
||||
import java.io.IOException;
|
||||
|
||||
class Item extends Instruction
|
||||
{
|
||||
|
||||
@Override
|
||||
void read(DataInput in, byte[] buffer) throws IOException
|
||||
{
|
||||
short type = in.readShort();
|
||||
if ( type >= 0 )
|
||||
{
|
||||
skip( in, buffer, 3 );
|
||||
SHORT_BYTE.read( in, buffer );
|
||||
}
|
||||
}
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
package net.md_5.mendax.datainput;
|
||||
|
||||
import java.io.DataInput;
|
||||
import java.io.IOException;
|
||||
|
||||
public class OptionalMotion extends Instruction
|
||||
{
|
||||
|
||||
@Override
|
||||
void read(DataInput in, byte[] buffer) throws IOException
|
||||
{
|
||||
int data = in.readInt();
|
||||
if ( data > 0 )
|
||||
{
|
||||
skip( in, buffer, 6 );
|
||||
}
|
||||
}
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
package net.md_5.mendax.datainput;
|
||||
|
||||
import java.io.DataInput;
|
||||
import java.io.IOException;
|
||||
|
||||
class Team extends Instruction
|
||||
{
|
||||
|
||||
@Override
|
||||
void read(DataInput in, byte[] buffer) throws IOException
|
||||
{
|
||||
STRING.read( in, buffer );
|
||||
byte mode = in.readByte();
|
||||
if ( mode == 0 || mode == 2 )
|
||||
{
|
||||
STRING.read( in, buffer );
|
||||
STRING.read( in, buffer );
|
||||
STRING.read( in, buffer );
|
||||
BYTE.read( in, buffer );
|
||||
}
|
||||
if ( mode == 0 || mode == 3 || mode == 4 )
|
||||
{
|
||||
STRING_ARRAY.read( in, buffer );
|
||||
}
|
||||
}
|
||||
}
|
@ -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 );
|
||||
}
|
||||
}
|
@ -19,6 +19,11 @@
|
||||
<description>Proxy component of the Elastic Portal Suite</description>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-all</artifactId>
|
||||
<version>4.0.0.Beta3-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.md-5</groupId>
|
||||
<artifactId>bungeecord-protocol</artifactId>
|
||||
@ -29,6 +34,11 @@
|
||||
<artifactId>bungeecord-api</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.sf.trove4j</groupId>
|
||||
<artifactId>trove4j</artifactId>
|
||||
<version>3.0.3</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>mysql</groupId>
|
||||
<artifactId>mysql-connector-java</artifactId>
|
||||
|
@ -1,23 +1,26 @@
|
||||
package net.md_5.bungee;
|
||||
|
||||
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||
import io.netty.bootstrap.ServerBootstrap;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelException;
|
||||
import io.netty.channel.MultithreadEventLoopGroup;
|
||||
import io.netty.channel.nio.NioEventLoopGroup;
|
||||
import io.netty.channel.socket.nio.NioServerSocketChannel;
|
||||
import net.md_5.bungee.config.Configuration;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.Socket;
|
||||
import java.util.Calendar;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
@ -37,6 +40,7 @@ import net.md_5.bungee.api.plugin.Plugin;
|
||||
import net.md_5.bungee.api.plugin.PluginManager;
|
||||
import net.md_5.bungee.command.*;
|
||||
import net.md_5.bungee.config.YamlConfig;
|
||||
import net.md_5.bungee.netty.PipelineUtils;
|
||||
import net.md_5.bungee.packet.DefinedPacket;
|
||||
import net.md_5.bungee.packet.PacketFAPluginMessage;
|
||||
|
||||
@ -65,7 +69,7 @@ public class BungeeCord extends ProxyServer
|
||||
/**
|
||||
* Thread pool.
|
||||
*/
|
||||
public final ExecutorService threadPool = Executors.newCachedThreadPool();
|
||||
public final MultithreadEventLoopGroup eventLoops = new NioEventLoopGroup( 8, new ThreadFactoryBuilder().setNameFormat( "Netty IO Thread - %1$d" ).build() );
|
||||
/**
|
||||
* locations.yml save thread.
|
||||
*/
|
||||
@ -73,7 +77,7 @@ public class BungeeCord extends ProxyServer
|
||||
/**
|
||||
* Server socket listener.
|
||||
*/
|
||||
private Collection<ListenThread> listeners = new HashSet<>();
|
||||
private Collection<Channel> listeners = new HashSet<>();
|
||||
/**
|
||||
* Fully qualified connections.
|
||||
*/
|
||||
@ -161,6 +165,7 @@ public class BungeeCord extends ProxyServer
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
@Override
|
||||
public void start() throws IOException
|
||||
{
|
||||
File plugins = new File( "plugins" );
|
||||
@ -193,30 +198,28 @@ public class BungeeCord extends ProxyServer
|
||||
{
|
||||
for ( ListenerInfo info : config.getListeners() )
|
||||
{
|
||||
try
|
||||
{
|
||||
ListenThread listener = new ListenThread( info );
|
||||
listener.start();
|
||||
listeners.add( listener );
|
||||
$().info( "Listening on " + info.getHost() );
|
||||
} catch ( IOException ex )
|
||||
{
|
||||
$().log( Level.SEVERE, "Could not start listener " + info, ex );
|
||||
}
|
||||
Channel server = new ServerBootstrap()
|
||||
.channel( NioServerSocketChannel.class )
|
||||
.childAttr( PipelineUtils.LISTENER, info )
|
||||
.childHandler( PipelineUtils.SERVER_CHILD )
|
||||
.group( eventLoops )
|
||||
.localAddress( info.getHost() )
|
||||
.bind().channel();
|
||||
listeners.add( server );
|
||||
|
||||
$().info( "Listening on " + info.getHost() );
|
||||
}
|
||||
}
|
||||
|
||||
public void stopListeners()
|
||||
{
|
||||
for ( ListenThread listener : listeners )
|
||||
for ( Channel listener : listeners )
|
||||
{
|
||||
$().log( Level.INFO, "Closing listen thread {0}", listener.socket );
|
||||
$().log( Level.INFO, "Closing listener {0}", listener );
|
||||
try
|
||||
{
|
||||
listener.interrupt();
|
||||
listener.socket.close();
|
||||
listener.join();
|
||||
} catch ( InterruptedException | IOException ex )
|
||||
listener.close().syncUninterruptibly();
|
||||
} catch ( ChannelException ex )
|
||||
{
|
||||
$().severe( "Could not close listen thread" );
|
||||
}
|
||||
@ -231,7 +234,6 @@ public class BungeeCord extends ProxyServer
|
||||
|
||||
stopListeners();
|
||||
$().info( "Closing pending connections" );
|
||||
threadPool.shutdown();
|
||||
|
||||
$().info( "Disconnecting " + connections.size() + " connections" );
|
||||
for ( UserConnection user : connections.values() )
|
||||
@ -239,6 +241,9 @@ public class BungeeCord extends ProxyServer
|
||||
user.disconnect( "Proxy restarting, brb." );
|
||||
}
|
||||
|
||||
$().info( "Closing IO threads" );
|
||||
eventLoops.shutdown();
|
||||
|
||||
$().info( "Saving reconnect locations" );
|
||||
reconnectHandler.save();
|
||||
saveThread.cancel();
|
||||
@ -253,20 +258,6 @@ public class BungeeCord extends ProxyServer
|
||||
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.
|
||||
*
|
||||
@ -276,7 +267,7 @@ public class BungeeCord extends ProxyServer
|
||||
{
|
||||
for ( UserConnection con : connections.values() )
|
||||
{
|
||||
con.packetQueue.add( packet );
|
||||
con.sendPacket( packet );
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,8 +1,11 @@
|
||||
package net.md_5.bungee;
|
||||
|
||||
import java.io.DataOutputStream;
|
||||
import io.netty.bootstrap.Bootstrap;
|
||||
import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.ChannelFutureListener;
|
||||
import io.netty.channel.ChannelOption;
|
||||
import io.netty.channel.socket.nio.NioSocketChannel;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.Socket;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import lombok.Getter;
|
||||
@ -11,11 +14,11 @@ import net.md_5.bungee.api.ProxyServer;
|
||||
import net.md_5.bungee.api.ServerPing;
|
||||
import net.md_5.bungee.api.config.ServerInfo;
|
||||
import net.md_5.bungee.api.connection.Server;
|
||||
import net.md_5.bungee.connection.PingHandler;
|
||||
import net.md_5.bungee.netty.HandlerBoss;
|
||||
import net.md_5.bungee.netty.PipelineUtils;
|
||||
import net.md_5.bungee.packet.DefinedPacket;
|
||||
import net.md_5.bungee.packet.PacketFAPluginMessage;
|
||||
import net.md_5.bungee.packet.PacketFFKick;
|
||||
import net.md_5.bungee.packet.PacketStream;
|
||||
import net.md_5.mendax.PacketDefinitions;
|
||||
|
||||
public class BungeeServerInfo extends ServerInfo
|
||||
{
|
||||
@ -44,31 +47,24 @@ public class BungeeServerInfo extends ServerInfo
|
||||
@Override
|
||||
public void ping(final Callback<ServerPing> callback)
|
||||
{
|
||||
new Thread()
|
||||
new Bootstrap()
|
||||
.channel( NioSocketChannel.class )
|
||||
.group( BungeeCord.getInstance().eventLoops )
|
||||
.handler( PipelineUtils.BASE )
|
||||
.option( ChannelOption.CONNECT_TIMEOUT_MILLIS, 5000 ) // TODO: Configurable
|
||||
.remoteAddress( getAddress() )
|
||||
.connect()
|
||||
.addListener( new ChannelFutureListener()
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
public void operationComplete(ChannelFuture future) throws Exception
|
||||
{
|
||||
try ( Socket socket = new Socket(); )
|
||||
if ( !future.isSuccess() )
|
||||
{
|
||||
socket.connect( getAddress() );
|
||||
|
||||
DataOutputStream out = new DataOutputStream( socket.getOutputStream() );
|
||||
out.write( 0xFE );
|
||||
out.write( 0x01 );
|
||||
|
||||
PacketStream in = new PacketStream( socket.getInputStream(), PacketDefinitions.VANILLA_PROTOCOL );
|
||||
PacketFFKick response = new PacketFFKick( in.readPacket() );
|
||||
|
||||
String[] split = response.message.split( "\00" );
|
||||
|
||||
ServerPing ping = new ServerPing( Byte.parseByte( split[1] ), split[2], split[3], Integer.parseInt( split[4] ), Integer.parseInt( split[5] ) );
|
||||
callback.done( ping, null );
|
||||
} catch ( Throwable t )
|
||||
{
|
||||
callback.done( null, t );
|
||||
callback.done( null, future.cause() );
|
||||
}
|
||||
}
|
||||
}.start();
|
||||
} )
|
||||
.channel().pipeline().get( HandlerBoss.class ).setHandler( new PingHandler( this, callback ) );
|
||||
}
|
||||
}
|
||||
|
@ -1,17 +1,10 @@
|
||||
package net.md_5.bungee;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.math.BigInteger;
|
||||
import java.net.URL;
|
||||
import java.net.URLEncoder;
|
||||
import java.security.InvalidAlgorithmParameterException;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.Key;
|
||||
import java.security.KeyPair;
|
||||
import java.security.KeyPairGenerator;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Random;
|
||||
@ -32,7 +25,7 @@ public class EncryptionUtil
|
||||
{
|
||||
|
||||
private static final Random random = new Random();
|
||||
private static KeyPair keys;
|
||||
public static KeyPair keys;
|
||||
|
||||
public static PacketFDEncryptionRequest encryptRequest() throws NoSuchAlgorithmException
|
||||
{
|
||||
@ -66,30 +59,6 @@ public class EncryptionUtil
|
||||
return new SecretKeySpec( secret, "AES" );
|
||||
}
|
||||
|
||||
public static boolean isAuthenticated(String username, String connectionHash, SecretKey shared) throws NoSuchAlgorithmException, IOException
|
||||
{
|
||||
String encName = URLEncoder.encode( username, "UTF-8" );
|
||||
|
||||
MessageDigest sha = MessageDigest.getInstance( "SHA-1" );
|
||||
for ( byte[] bit : new byte[][]
|
||||
{
|
||||
connectionHash.getBytes( "ISO_8859_1" ), shared.getEncoded(), keys.getPublic().getEncoded()
|
||||
} )
|
||||
{
|
||||
sha.update( bit );
|
||||
}
|
||||
|
||||
String encodedHash = URLEncoder.encode( new BigInteger( sha.digest() ).toString( 16 ), "UTF-8" );
|
||||
String authURL = "http://session.minecraft.net/game/checkserver.jsp?user=" + encName + "&serverId=" + encodedHash;
|
||||
String reply;
|
||||
try ( BufferedReader in = new BufferedReader( new InputStreamReader( new URL( authURL ).openStream() ) ) )
|
||||
{
|
||||
reply = in.readLine();
|
||||
}
|
||||
|
||||
return "YES".equals( reply );
|
||||
}
|
||||
|
||||
public static Cipher getCipher(int opMode, Key shared) throws InvalidAlgorithmParameterException, InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException
|
||||
{
|
||||
Cipher cip = Cipher.getInstance( "AES/CFB8/NoPadding" );
|
||||
|
@ -1,5 +1,7 @@
|
||||
package net.md_5.bungee;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
|
||||
/**
|
||||
* Class to rewrite integers within packets.
|
||||
*/
|
||||
@ -113,20 +115,20 @@ public class EntityMap
|
||||
};
|
||||
}
|
||||
|
||||
public static void rewrite(byte[] packet, int oldId, int newId)
|
||||
public static void rewrite(ByteBuf packet, int oldId, int newId)
|
||||
{
|
||||
int packetId = Util.getId( packet );
|
||||
int packetId = packet.getUnsignedByte( 0 );
|
||||
if ( packetId == 0x1D )
|
||||
{ // bulk entity
|
||||
for ( int pos = 2; pos < packet.length; pos += 4 )
|
||||
for ( int pos = 2; pos < packet.writerIndex(); pos += 4 )
|
||||
{
|
||||
int readId = readInt( packet, pos );
|
||||
int readId = packet.getInt( pos );
|
||||
if ( readId == oldId )
|
||||
{
|
||||
setInt( packet, pos, newId );
|
||||
packet.setInt( pos, newId );
|
||||
} else if ( readId == newId )
|
||||
{
|
||||
setInt( packet, pos, oldId );
|
||||
packet.setInt( pos, oldId );
|
||||
}
|
||||
}
|
||||
} else
|
||||
@ -136,29 +138,16 @@ public class EntityMap
|
||||
{
|
||||
for ( int pos : idArray )
|
||||
{
|
||||
int readId = readInt( packet, pos );
|
||||
int readId = packet.getInt( pos );
|
||||
if ( readId == oldId )
|
||||
{
|
||||
setInt( packet, pos, newId );
|
||||
packet.setInt( pos, newId );
|
||||
} else if ( readId == newId )
|
||||
{
|
||||
setInt( packet, pos, oldId );
|
||||
packet.setInt( pos, oldId );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void setInt(byte[] buf, int pos, int i)
|
||||
{
|
||||
buf[pos] = (byte) ( i >> 24 );
|
||||
buf[pos + 1] = (byte) ( i >> 16 );
|
||||
buf[pos + 2] = (byte) ( i >> 8 );
|
||||
buf[pos + 3] = (byte) i;
|
||||
}
|
||||
|
||||
private static int readInt(byte[] buf, int pos)
|
||||
{
|
||||
return ( ( ( buf[pos] & 0xFF ) << 24 ) | ( ( buf[pos + 1] & 0xFF ) << 16 ) | ( ( buf[pos + 2] & 0xFF ) << 8 ) | buf[pos + 3] & 0xFF );
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -3,7 +3,6 @@ package net.md_5.bungee;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.text.MessageFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.logging.FileHandler;
|
||||
import java.util.logging.Formatter;
|
||||
@ -21,7 +20,7 @@ public class Logger extends java.util.logging.Logger
|
||||
|
||||
public Logger()
|
||||
{
|
||||
super( "RubberBand", null );
|
||||
super( "BungeeCord", null );
|
||||
try
|
||||
{
|
||||
FileHandler handler = new FileHandler( "proxy.log", 1 << 14, 1, true );
|
||||
|
@ -1,47 +1,57 @@
|
||||
package net.md_5.bungee;
|
||||
|
||||
import io.netty.channel.Channel;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.Socket;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import lombok.Getter;
|
||||
import net.md_5.bungee.api.Callback;
|
||||
import net.md_5.bungee.api.ServerPing;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import net.md_5.bungee.api.config.ServerInfo;
|
||||
import net.md_5.bungee.api.connection.Server;
|
||||
import net.md_5.bungee.packet.DefinedPacket;
|
||||
import net.md_5.bungee.packet.Packet1Login;
|
||||
import net.md_5.bungee.packet.PacketFAPluginMessage;
|
||||
import net.md_5.bungee.packet.PacketStream;
|
||||
import net.md_5.bungee.packet.PacketFFKick;
|
||||
|
||||
/**
|
||||
* Class representing a connection from the proxy to the server; ie upstream.
|
||||
*/
|
||||
public class ServerConnection extends GenericConnection implements Server
|
||||
@RequiredArgsConstructor
|
||||
public class ServerConnection implements Server
|
||||
{
|
||||
|
||||
@Getter
|
||||
private final Channel ch;
|
||||
@Getter
|
||||
private final ServerInfo info;
|
||||
public final Packet1Login loginPacket;
|
||||
public Queue<DefinedPacket> packetQueue = new ConcurrentLinkedQueue<>();
|
||||
|
||||
public ServerConnection(Socket socket, ServerInfo info, PacketStream stream, Packet1Login loginPacket)
|
||||
{
|
||||
super( socket, stream );
|
||||
this.info = info;
|
||||
this.loginPacket = loginPacket;
|
||||
}
|
||||
@Getter
|
||||
private final Packet1Login loginPacket;
|
||||
@Getter
|
||||
@Setter
|
||||
private boolean isObsolete;
|
||||
|
||||
@Override
|
||||
public void sendData(String channel, byte[] data)
|
||||
{
|
||||
packetQueue.add( new PacketFAPluginMessage( channel, data ) );
|
||||
ch.write( new PacketFAPluginMessage( channel, data ) );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void ping(final Callback<ServerPing> callback)
|
||||
public synchronized void disconnect(String reason)
|
||||
{
|
||||
getInfo().ping( callback );
|
||||
disconnect( ch, reason );
|
||||
}
|
||||
|
||||
static void disconnect(final Channel ch, String reason)
|
||||
{
|
||||
if ( ch.isActive() )
|
||||
{
|
||||
ch.write( new PacketFFKick( reason ) );
|
||||
ch.eventLoop().schedule( new Runnable()
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
ch.close();
|
||||
}
|
||||
}, 100, TimeUnit.MILLISECONDS );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1,45 +1,119 @@
|
||||
package net.md_5.bungee;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import java.io.IOException;
|
||||
import java.net.Socket;
|
||||
import io.netty.channel.Channel;
|
||||
import java.util.Queue;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import net.md_5.bungee.api.ChatColor;
|
||||
import net.md_5.bungee.api.ProxyServer;
|
||||
import net.md_5.bungee.api.config.ServerInfo;
|
||||
import net.md_5.bungee.api.event.ServerConnectedEvent;
|
||||
import net.md_5.bungee.connection.CancelSendSignal;
|
||||
import net.md_5.bungee.connection.DownstreamBridge;
|
||||
import net.md_5.bungee.netty.HandlerBoss;
|
||||
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.Packet9Respawn;
|
||||
import net.md_5.bungee.packet.PacketCDClientStatus;
|
||||
import net.md_5.bungee.packet.PacketFDEncryptionRequest;
|
||||
import net.md_5.bungee.packet.PacketFFKick;
|
||||
import net.md_5.bungee.packet.PacketHandler;
|
||||
import net.md_5.bungee.packet.PacketStream;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class ServerConnector extends PacketHandler
|
||||
{
|
||||
|
||||
private final PacketStream stream;
|
||||
private Packet1Login loginPacket;
|
||||
private final ProxyServer bungee;
|
||||
private Channel ch;
|
||||
private final UserConnection user;
|
||||
private final ServerInfo target;
|
||||
private State thisState = State.ENCRYPT_REQUEST;
|
||||
|
||||
public ServerConnector(PacketStream stream)
|
||||
{
|
||||
this.stream = stream;
|
||||
}
|
||||
|
||||
private enum State
|
||||
{
|
||||
|
||||
ENCRYPT_REQUEST, LOGIN, FINISHED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void connected(Channel channel) throws Exception
|
||||
{
|
||||
this.ch = channel;
|
||||
// TODO: Fix this crap
|
||||
channel.write( new Packet2Handshake( user.handshake.procolVersion, user.handshake.username, user.handshake.host, user.handshake.port ) );
|
||||
channel.write( PacketCDClientStatus.CLIENT_LOGIN );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(Packet1Login login) throws Exception
|
||||
{
|
||||
Preconditions.checkState( thisState == State.LOGIN, "Not exepcting LOGIN" );
|
||||
loginPacket = login;
|
||||
|
||||
ServerConnection server = new ServerConnection( ch, target, login );
|
||||
ServerConnectedEvent event = new ServerConnectedEvent( user, server );
|
||||
bungee.getPluginManager().callEvent( event );
|
||||
|
||||
ch.write( BungeeCord.getInstance().registerChannels() );
|
||||
|
||||
Queue<DefinedPacket> packetQueue = ( (BungeeServerInfo) target ).getPacketQueue();
|
||||
while ( !packetQueue.isEmpty() )
|
||||
{
|
||||
ch.write( packetQueue.poll() );
|
||||
}
|
||||
|
||||
synchronized ( user.getSwitchMutex() )
|
||||
{
|
||||
if ( user.getServer() == null )
|
||||
{
|
||||
BungeeCord.getInstance().connections.put( user.getName(), user );
|
||||
bungee.getTabListHandler().onConnect( user );
|
||||
// Once again, first connection
|
||||
user.clientEntityId = login.entityId;
|
||||
user.serverEntityId = 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.ch.write( modLogin );
|
||||
ch.write( BungeeCord.getInstance().registerChannels() );
|
||||
} else
|
||||
{
|
||||
bungee.getTabListHandler().onServerChange( user );
|
||||
user.sendPacket( Packet9Respawn.DIM1_SWITCH );
|
||||
user.sendPacket( Packet9Respawn.DIM2_SWITCH );
|
||||
|
||||
user.serverEntityId = login.entityId;
|
||||
user.ch.write( 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.ch.isActive() )
|
||||
{
|
||||
server.disconnect( "Quitting" );
|
||||
throw new IllegalStateException( "No client connected for pending server!" );
|
||||
}
|
||||
|
||||
// Add to new server
|
||||
// TODO: Move this to the connected() method of DownstreamBridge
|
||||
target.addPlayer( user );
|
||||
|
||||
user.setServer( server );
|
||||
ch.pipeline().get( HandlerBoss.class ).setHandler( new DownstreamBridge( bungee, user, server ) );
|
||||
}
|
||||
|
||||
thisState = State.FINISHED;
|
||||
|
||||
throw new CancelSendSignal();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -52,66 +126,19 @@ public class ServerConnector extends PacketHandler
|
||||
@Override
|
||||
public void handle(PacketFFKick kick) throws Exception
|
||||
{
|
||||
throw new KickException( kick.message );
|
||||
}
|
||||
|
||||
public static ServerConnection connect(UserConnection user, ServerInfo info, boolean retry)
|
||||
{
|
||||
Socket socket = null;
|
||||
try
|
||||
String message = ChatColor.RED + "Kicked whilst connecting to " + target.getName() + ": " + kick.message;
|
||||
if ( user.getServer() == null )
|
||||
{
|
||||
socket = new Socket();
|
||||
socket.connect( info.getAddress(), BungeeCord.getInstance().config.getTimeout() );
|
||||
BungeeCord.getInstance().setSocketOptions( socket );
|
||||
PacketStream stream = new PacketStream( socket.getInputStream(), socket.getOutputStream(), user.stream.getProtocol() );
|
||||
|
||||
ServerConnector connector = new ServerConnector( stream );
|
||||
stream.write( user.handshake );
|
||||
stream.write( PacketCDClientStatus.CLIENT_LOGIN );
|
||||
|
||||
while ( connector.thisState != State.FINISHED )
|
||||
{
|
||||
byte[] buf = stream.readPacket();
|
||||
DefinedPacket packet = DefinedPacket.packet( buf );
|
||||
packet.handle( connector );
|
||||
}
|
||||
|
||||
ServerConnection server = new ServerConnection( socket, info, stream, connector.loginPacket );
|
||||
ServerConnectedEvent event = new ServerConnectedEvent( user, server );
|
||||
ProxyServer.getInstance().getPluginManager().callEvent( event );
|
||||
|
||||
stream.write( BungeeCord.getInstance().registerChannels() );
|
||||
|
||||
Queue<DefinedPacket> packetQueue = ( (BungeeServerInfo) info ).getPacketQueue();
|
||||
while ( !packetQueue.isEmpty() )
|
||||
{
|
||||
stream.write( packetQueue.poll() );
|
||||
}
|
||||
return server;
|
||||
} catch ( Exception ex )
|
||||
user.disconnect( message );
|
||||
} else
|
||||
{
|
||||
if ( socket != null )
|
||||
{
|
||||
try
|
||||
{
|
||||
socket.close();
|
||||
} catch ( IOException ioe )
|
||||
{
|
||||
}
|
||||
}
|
||||
ServerInfo def = ProxyServer.getInstance().getServers().get( user.getPendingConnection().getListener().getDefaultServer() );
|
||||
if ( retry && !info.equals( def ) )
|
||||
{
|
||||
user.sendMessage( ChatColor.RED + "Could not connect to target server, you have been moved to the default server" );
|
||||
return connect( user, def, false );
|
||||
} else
|
||||
{
|
||||
if ( ex instanceof RuntimeException )
|
||||
{
|
||||
throw (RuntimeException) ex;
|
||||
}
|
||||
throw new RuntimeException( "Could not connect to target server " + Util.exception( ex ) );
|
||||
}
|
||||
user.sendMessage( message );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return "[" + user.getName() + "] <-> ServerConnector [" + target.getName() + "]";
|
||||
}
|
||||
}
|
||||
|
@ -1,214 +1,188 @@
|
||||
package net.md_5.bungee;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import com.google.common.base.Preconditions;
|
||||
import gnu.trove.set.hash.THashSet;
|
||||
import io.netty.bootstrap.Bootstrap;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.ChannelFutureListener;
|
||||
import io.netty.channel.ChannelInitializer;
|
||||
import io.netty.channel.ChannelOption;
|
||||
import io.netty.channel.socket.nio.NioSocketChannel;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.Socket;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.logging.Level;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.Synchronized;
|
||||
import net.md_5.bungee.api.ChatColor;
|
||||
import net.md_5.bungee.api.ProxyServer;
|
||||
import net.md_5.bungee.api.config.ServerInfo;
|
||||
import net.md_5.bungee.api.connection.PendingConnection;
|
||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||
import net.md_5.bungee.api.event.ChatEvent;
|
||||
import net.md_5.bungee.api.event.PlayerDisconnectEvent;
|
||||
import net.md_5.bungee.api.event.PluginMessageEvent;
|
||||
import net.md_5.bungee.api.event.ServerConnectEvent;
|
||||
import net.md_5.bungee.netty.HandlerBoss;
|
||||
import net.md_5.bungee.netty.PipelineUtils;
|
||||
import net.md_5.bungee.packet.*;
|
||||
|
||||
public final class UserConnection extends GenericConnection implements ProxiedPlayer
|
||||
public final class UserConnection implements ProxiedPlayer
|
||||
{
|
||||
|
||||
public final Packet2Handshake handshake;
|
||||
private final ProxyServer bungee;
|
||||
public final Channel ch;
|
||||
final Packet1Login forgeLogin;
|
||||
final List<PacketFAPluginMessage> loginMessages;
|
||||
public Queue<DefinedPacket> packetQueue = new ConcurrentLinkedQueue<>();
|
||||
@Getter
|
||||
private final PendingConnection pendingConnection;
|
||||
@Getter
|
||||
@Setter(AccessLevel.PACKAGE)
|
||||
private ServerConnection server;
|
||||
private UpstreamBridge upBridge;
|
||||
private DownstreamBridge downBridge;
|
||||
// reconnect stuff
|
||||
private int clientEntityId;
|
||||
private int serverEntityId;
|
||||
private volatile boolean reconnecting;
|
||||
public int clientEntityId;
|
||||
public int serverEntityId;
|
||||
// ping stuff
|
||||
private int trackingPingId;
|
||||
private long pingTime;
|
||||
public int trackingPingId;
|
||||
public long pingTime;
|
||||
@Getter
|
||||
private String name;
|
||||
@Getter
|
||||
private String displayName;
|
||||
@Getter
|
||||
@Setter
|
||||
private int ping = 1000;
|
||||
// Permissions
|
||||
private final Collection<String> groups = new HashSet<>();
|
||||
private final Map<String, Boolean> permissions = new HashMap<>();
|
||||
private final Collection<String> playerGroups = new THashSet<>();
|
||||
private final Collection<String> permissions = new THashSet<>();
|
||||
private final Object permMutex = new Object();
|
||||
// Hack for connect timings
|
||||
private ServerInfo nextServer;
|
||||
private volatile boolean clientConnected = true;
|
||||
@Getter
|
||||
private final Object switchMutex = new Object();
|
||||
|
||||
public UserConnection(Socket socket, PendingConnection pendingConnection, PacketStream stream, Packet2Handshake handshake, Packet1Login forgeLogin, List<PacketFAPluginMessage> loginMessages)
|
||||
public UserConnection(BungeeCord bungee, Channel channel, PendingConnection pendingConnection, Packet2Handshake handshake, Packet1Login forgeLogin, List<PacketFAPluginMessage> loginMessages)
|
||||
{
|
||||
super( socket, stream );
|
||||
this.bungee = bungee;
|
||||
this.ch = channel;
|
||||
this.handshake = handshake;
|
||||
this.pendingConnection = pendingConnection;
|
||||
this.forgeLogin = forgeLogin;
|
||||
this.loginMessages = loginMessages;
|
||||
name = handshake.username.substring( 0, Math.min( handshake.username.length(), 16 ) );
|
||||
displayName = name;
|
||||
this.name = handshake.username;
|
||||
this.displayName = name;
|
||||
|
||||
Collection<String> g = ProxyServer.getInstance().getConfigurationAdapter().getGroups( name );
|
||||
Collection<String> g = bungee.getConfigurationAdapter().getGroups( name );
|
||||
for ( String s : g )
|
||||
{
|
||||
addGroups( s );
|
||||
}
|
||||
}
|
||||
|
||||
public void sendPacket(DefinedPacket p)
|
||||
{
|
||||
ch.write( p );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDisplayName(String name)
|
||||
{
|
||||
ProxyServer.getInstance().getTabListHandler().onDisconnect( this );
|
||||
displayName = name;
|
||||
ProxyServer.getInstance().getTabListHandler().onConnect( this );
|
||||
Preconditions.checkArgument( name.length() <= 16, "Display name cannot be longer than 16 characters" );
|
||||
bungee.getTabListHandler().onDisconnect( this );
|
||||
bungee.getTabListHandler().onConnect( this );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void connect(ServerInfo target)
|
||||
{
|
||||
nextServer = target;
|
||||
connect( target, false );
|
||||
}
|
||||
|
||||
public void connect(ServerInfo target, boolean force)
|
||||
public void connect(ServerInfo info, final boolean retry)
|
||||
{
|
||||
nextServer = null;
|
||||
if ( server == null )
|
||||
ServerConnectEvent event = new ServerConnectEvent( this, info );
|
||||
ProxyServer.getInstance().getPluginManager().callEvent( event );
|
||||
final ServerInfo target = event.getTarget(); // Update in case the event changed target
|
||||
new Bootstrap()
|
||||
.channel( NioSocketChannel.class )
|
||||
.group( BungeeCord.getInstance().eventLoops )
|
||||
.handler( new ChannelInitializer()
|
||||
{
|
||||
// First join
|
||||
BungeeCord.getInstance().connections.put( name, this );
|
||||
ProxyServer.getInstance().getTabListHandler().onConnect( this );
|
||||
}
|
||||
|
||||
ServerConnectEvent event = new ServerConnectEvent( this, target );
|
||||
BungeeCord.getInstance().getPluginManager().callEvent( event );
|
||||
target = event.getTarget(); // Update in case the event changed target
|
||||
|
||||
ProxyServer.getInstance().getTabListHandler().onServerChange( this );
|
||||
try
|
||||
{
|
||||
reconnecting = true;
|
||||
|
||||
if ( server != null )
|
||||
@Override
|
||||
protected void initChannel(Channel ch) throws Exception
|
||||
{
|
||||
stream.write( new Packet9Respawn( (byte) 1, (byte) 0, (byte) 0, (short) 256, "DEFAULT" ) );
|
||||
stream.write( new Packet9Respawn( (byte) -1, (byte) 0, (byte) 0, (short) 256, "DEFAULT" ) );
|
||||
PipelineUtils.BASE.initChannel( ch );
|
||||
ch.pipeline().get( HandlerBoss.class ).setHandler( new ServerConnector( bungee, UserConnection.this, target ) );
|
||||
}
|
||||
|
||||
ServerConnection newServer = ServerConnector.connect( this, target, true );
|
||||
if ( server == null )
|
||||
} )
|
||||
.option( ChannelOption.CONNECT_TIMEOUT_MILLIS, 5000 ) // TODO: Configurable
|
||||
.remoteAddress( target.getAddress() )
|
||||
.connect().addListener( new ChannelFutureListener()
|
||||
{
|
||||
@Override
|
||||
public void operationComplete(ChannelFuture future) throws Exception
|
||||
{
|
||||
// Once again, first connection
|
||||
clientEntityId = newServer.loginPacket.entityId;
|
||||
serverEntityId = newServer.loginPacket.entityId;
|
||||
// Set tab list size
|
||||
Packet1Login s = newServer.loginPacket;
|
||||
Packet1Login login = new Packet1Login( s.entityId, s.levelType, s.gameMode, (byte) s.dimension, s.difficulty, s.unused, (byte) pendingConnection.getListener().getTabListSize() );
|
||||
stream.write( login );
|
||||
stream.write( BungeeCord.getInstance().registerChannels() );
|
||||
|
||||
upBridge = new UpstreamBridge();
|
||||
upBridge.start();
|
||||
} else
|
||||
{
|
||||
try
|
||||
{
|
||||
downBridge.interrupt();
|
||||
downBridge.join();
|
||||
} catch ( InterruptedException ie )
|
||||
if ( !future.isSuccess() )
|
||||
{
|
||||
future.channel().close();
|
||||
ServerInfo def = ProxyServer.getInstance().getServers().get( getPendingConnection().getListener().getDefaultServer() );
|
||||
if ( retry && !target.equals( def ) )
|
||||
{
|
||||
sendMessage( ChatColor.RED + "Could not connect to target server, you have been moved to the default server" );
|
||||
connect( def, false );
|
||||
} else
|
||||
{
|
||||
if ( server == null )
|
||||
{
|
||||
disconnect( "Server down, could not connect to default! " + future.cause().getClass().getName() );
|
||||
} else
|
||||
{
|
||||
sendMessage( ChatColor.RED + "Could not connect to target server: " + future.cause().getClass().getName() );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
server.disconnect( "Quitting" );
|
||||
server.getInfo().removePlayer( this );
|
||||
|
||||
Packet1Login login = newServer.loginPacket;
|
||||
serverEntityId = login.entityId;
|
||||
stream.write( new Packet9Respawn( login.dimension, login.difficulty, login.gameMode, (short) 256, login.levelType ) );
|
||||
}
|
||||
|
||||
// Reconnect process has finished, lets get the player moving again
|
||||
reconnecting = false;
|
||||
|
||||
// Add to new
|
||||
target.addPlayer( this );
|
||||
|
||||
// Start the bridges and move on
|
||||
server = newServer;
|
||||
downBridge = new DownstreamBridge();
|
||||
downBridge.start();
|
||||
} catch ( KickException ex )
|
||||
{
|
||||
disconnect( ex.getMessage() );
|
||||
} catch ( Exception ex )
|
||||
{
|
||||
disconnect( "Could not connect to server - " + Util.exception( ex ) );
|
||||
}
|
||||
} );
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void disconnect(String reason)
|
||||
{
|
||||
if ( clientConnected )
|
||||
if ( ch.isActive() )
|
||||
{
|
||||
PlayerDisconnectEvent event = new PlayerDisconnectEvent( this );
|
||||
ProxyServer.getInstance().getPluginManager().callEvent( event );
|
||||
ProxyServer.getInstance().getTabListHandler().onDisconnect( this );
|
||||
ProxyServer.getInstance().getPlayers().remove( this );
|
||||
|
||||
super.disconnect( reason );
|
||||
bungee.getLogger().log( Level.INFO, "[" + getName() + "] disconnected with: " + reason );
|
||||
ch.write( new PacketFFKick( reason ) );
|
||||
ch.close();
|
||||
if ( server != null )
|
||||
{
|
||||
server.getInfo().removePlayer( this );
|
||||
server.disconnect( "Quitting" );
|
||||
ProxyServer.getInstance().getReconnectHandler().setServer( this );
|
||||
}
|
||||
|
||||
clientConnected = false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendMessage(String message)
|
||||
{
|
||||
packetQueue.add( new Packet3Chat( message ) );
|
||||
ch.write( new Packet3Chat( message ) );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendData(String channel, byte[] data)
|
||||
{
|
||||
server.packetQueue.add( new PacketFAPluginMessage( channel, data ) );
|
||||
ch.write( new PacketFAPluginMessage( channel, data ) );
|
||||
}
|
||||
|
||||
@Override
|
||||
public InetSocketAddress getAddress()
|
||||
{
|
||||
return (InetSocketAddress) socket.getRemoteSocketAddress();
|
||||
return (InetSocketAddress) ch.remoteAddress();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Synchronized("permMutex")
|
||||
public Collection<String> getGroups()
|
||||
{
|
||||
return Collections.unmodifiableCollection( groups );
|
||||
return Collections.unmodifiableCollection( playerGroups );
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -217,8 +191,8 @@ public final class UserConnection extends GenericConnection implements ProxiedPl
|
||||
{
|
||||
for ( String group : groups )
|
||||
{
|
||||
this.groups.add( group );
|
||||
for ( String permission : ProxyServer.getInstance().getConfigurationAdapter().getPermissions( group ) )
|
||||
playerGroups.add( group );
|
||||
for ( String permission : bungee.getConfigurationAdapter().getPermissions( group ) )
|
||||
{
|
||||
setPermission( permission, true );
|
||||
}
|
||||
@ -231,8 +205,8 @@ public final class UserConnection extends GenericConnection implements ProxiedPl
|
||||
{
|
||||
for ( String group : groups )
|
||||
{
|
||||
this.groups.remove( group );
|
||||
for ( String permission : ProxyServer.getInstance().getConfigurationAdapter().getPermissions( group ) )
|
||||
playerGroups.remove( group );
|
||||
for ( String permission : bungee.getConfigurationAdapter().getPermissions( group ) )
|
||||
{
|
||||
setPermission( permission, false );
|
||||
}
|
||||
@ -243,303 +217,19 @@ public final class UserConnection extends GenericConnection implements ProxiedPl
|
||||
@Synchronized("permMutex")
|
||||
public boolean hasPermission(String permission)
|
||||
{
|
||||
Boolean val = permissions.get( permission );
|
||||
return ( val == null ) ? false : val;
|
||||
return permissions.contains( permission );
|
||||
}
|
||||
|
||||
@Override
|
||||
@Synchronized("permMutex")
|
||||
public void setPermission(String permission, boolean value)
|
||||
{
|
||||
permissions.put( permission, value );
|
||||
}
|
||||
|
||||
private class UpstreamBridge extends Thread
|
||||
{
|
||||
|
||||
public UpstreamBridge()
|
||||
if ( value )
|
||||
{
|
||||
super( "Upstream Bridge - " + name );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
permissions.add( permission );
|
||||
} else
|
||||
{
|
||||
while ( !socket.isClosed() )
|
||||
{
|
||||
try
|
||||
{
|
||||
byte[] packet = stream.readPacket();
|
||||
boolean sendPacket = true;
|
||||
int id = Util.getId( packet );
|
||||
|
||||
switch ( id )
|
||||
{
|
||||
case 0x00:
|
||||
if ( trackingPingId == new Packet0KeepAlive( packet ).id )
|
||||
{
|
||||
int newPing = (int) ( System.currentTimeMillis() - pingTime );
|
||||
ProxyServer.getInstance().getTabListHandler().onPingChange( UserConnection.this, newPing );
|
||||
ping = newPing;
|
||||
}
|
||||
break;
|
||||
case 0x03:
|
||||
Packet3Chat chat = new Packet3Chat( packet );
|
||||
if ( chat.message.startsWith( "/" ) )
|
||||
{
|
||||
sendPacket = !ProxyServer.getInstance().getPluginManager().dispatchCommand( UserConnection.this, chat.message.substring( 1 ) );
|
||||
} else
|
||||
{
|
||||
ChatEvent chatEvent = new ChatEvent( UserConnection.this, server, chat.message );
|
||||
ProxyServer.getInstance().getPluginManager().callEvent( chatEvent );
|
||||
sendPacket = !chatEvent.isCancelled();
|
||||
}
|
||||
break;
|
||||
case 0xFA:
|
||||
// Call the onPluginMessage event
|
||||
PacketFAPluginMessage message = new PacketFAPluginMessage( packet );
|
||||
|
||||
// Might matter in the future
|
||||
if ( message.tag.equals( "BungeeCord" ) )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
public DownstreamBridge()
|
||||
{
|
||||
super( "Downstream Bridge - " + name );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
try
|
||||
{
|
||||
outer:
|
||||
while ( !reconnecting )
|
||||
{
|
||||
byte[] packet = server.stream.readPacket();
|
||||
int id = Util.getId( packet );
|
||||
|
||||
switch ( id )
|
||||
{
|
||||
case 0x00:
|
||||
trackingPingId = new Packet0KeepAlive( packet ).id;
|
||||
pingTime = System.currentTimeMillis();
|
||||
break;
|
||||
case 0x03:
|
||||
Packet3Chat chat = new Packet3Chat( packet );
|
||||
ChatEvent chatEvent = new ChatEvent( server, UserConnection.this, chat.message );
|
||||
ProxyServer.getInstance().getPluginManager().callEvent( chatEvent );
|
||||
|
||||
if ( chatEvent.isCancelled() )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
case 0xC9:
|
||||
PacketC9PlayerListItem playerList = new PacketC9PlayerListItem( packet );
|
||||
if ( !ProxyServer.getInstance().getTabListHandler().onListUpdate( UserConnection.this, playerList.username, playerList.online, playerList.ping ) )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
case 0xFA:
|
||||
// Call the onPluginMessage event
|
||||
PacketFAPluginMessage message = new PacketFAPluginMessage( packet );
|
||||
DataInputStream in = new DataInputStream( new ByteArrayInputStream( message.data ) );
|
||||
PluginMessageEvent event = new PluginMessageEvent( server, UserConnection.this, message.tag, message.data );
|
||||
ProxyServer.getInstance().getPluginManager().callEvent( event );
|
||||
|
||||
if ( event.isCancelled() )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( message.tag.equals( "BungeeCord" ) )
|
||||
{
|
||||
String subChannel = in.readUTF();
|
||||
if ( subChannel.equals( "Forward" ) )
|
||||
{
|
||||
String target = in.readUTF();
|
||||
String channel = in.readUTF();
|
||||
short len = in.readShort();
|
||||
byte[] data = new byte[ len ];
|
||||
in.readFully( data );
|
||||
|
||||
|
||||
ByteArrayOutputStream b = new ByteArrayOutputStream();
|
||||
DataOutputStream out = new DataOutputStream( b );
|
||||
out.writeUTF( channel );
|
||||
out.writeShort( data.length );
|
||||
out.write( data );
|
||||
|
||||
if ( target.equals( "ALL" ) )
|
||||
{
|
||||
for ( ServerInfo server : BungeeCord.getInstance().getServers().values() )
|
||||
{
|
||||
server.sendData( "BungeeCord", b.toByteArray() );
|
||||
}
|
||||
} else
|
||||
{
|
||||
ServerInfo server = BungeeCord.getInstance().getServerInfo( target );
|
||||
if ( server != null )
|
||||
{
|
||||
server.sendData( "BungeeCord", b.toByteArray() );
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( subChannel.equals( "Connect" ) )
|
||||
{
|
||||
ServerInfo server = ProxyServer.getInstance().getServerInfo( in.readUTF() );
|
||||
if ( server != null )
|
||||
{
|
||||
connect( server, true );
|
||||
break outer;
|
||||
}
|
||||
}
|
||||
if ( subChannel.equals( "IP" ) )
|
||||
{
|
||||
ByteArrayOutputStream b = new ByteArrayOutputStream();
|
||||
DataOutputStream out = new DataOutputStream( b );
|
||||
out.writeUTF( "IP" );
|
||||
out.writeUTF( getAddress().getHostString() );
|
||||
out.writeInt( getAddress().getPort() );
|
||||
getServer().sendData( "BungeeCord", b.toByteArray() );
|
||||
}
|
||||
if ( subChannel.equals( "PlayerCount" ) )
|
||||
{
|
||||
ServerInfo server = ProxyServer.getInstance().getServerInfo( in.readUTF() );
|
||||
if ( server != null )
|
||||
{
|
||||
ByteArrayOutputStream b = new ByteArrayOutputStream();
|
||||
DataOutputStream out = new DataOutputStream( b );
|
||||
out.writeUTF( "PlayerCount" );
|
||||
out.writeUTF( server.getName() );
|
||||
out.writeInt( server.getPlayers().size() );
|
||||
getServer().sendData( "BungeeCord", b.toByteArray() );
|
||||
}
|
||||
}
|
||||
if ( subChannel.equals( "PlayerList" ) )
|
||||
{
|
||||
ServerInfo server = ProxyServer.getInstance().getServerInfo( in.readUTF() );
|
||||
if ( server != null )
|
||||
{
|
||||
ByteArrayOutputStream b = new ByteArrayOutputStream();
|
||||
DataOutputStream out = new DataOutputStream( b );
|
||||
out.writeUTF( "PlayerList" );
|
||||
out.writeUTF( server.getName() );
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for ( ProxiedPlayer p : server.getPlayers() )
|
||||
{
|
||||
sb.append( p.getName() );
|
||||
sb.append( "," );
|
||||
}
|
||||
out.writeUTF( sb.substring( 0, sb.length() - 1 ) );
|
||||
|
||||
getServer().sendData( "BungeeCord", b.toByteArray() );
|
||||
}
|
||||
}
|
||||
if ( subChannel.equals( "GetServers" ) )
|
||||
{
|
||||
ByteArrayOutputStream b = new ByteArrayOutputStream();
|
||||
DataOutputStream out = new DataOutputStream( b );
|
||||
out.writeUTF( "GetServers" );
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for ( String server : ProxyServer.getInstance().getServers().keySet() )
|
||||
{
|
||||
sb.append( server );
|
||||
sb.append( "," );
|
||||
}
|
||||
out.writeUTF( sb.substring( 0, sb.length() - 1 ) );
|
||||
|
||||
getServer().sendData( "BungeeCord", b.toByteArray() );
|
||||
}
|
||||
if ( subChannel.equals( "Message" ) )
|
||||
{
|
||||
ProxiedPlayer target = ProxyServer.getInstance().getPlayer( in.readUTF() );
|
||||
if ( target != null )
|
||||
{
|
||||
target.sendMessage( in.readUTF() );
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
case 0xFF:
|
||||
disconnect( new PacketFFKick( packet ).message );
|
||||
break outer;
|
||||
}
|
||||
|
||||
while ( !packetQueue.isEmpty() )
|
||||
{
|
||||
DefinedPacket p = packetQueue.poll();
|
||||
if ( p != null )
|
||||
{
|
||||
stream.write( p );
|
||||
}
|
||||
}
|
||||
|
||||
EntityMap.rewrite( packet, serverEntityId, clientEntityId );
|
||||
stream.write( packet );
|
||||
|
||||
if ( nextServer != null )
|
||||
{
|
||||
connect( nextServer, true );
|
||||
break outer;
|
||||
}
|
||||
}
|
||||
} catch ( Exception ex )
|
||||
{
|
||||
disconnect( Util.exception( ex ) );
|
||||
}
|
||||
permissions.remove( permission );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -27,18 +27,6 @@ public class Util
|
||||
return new InetSocketAddress( split[0], port );
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of the first unsigned byte of the specified array. Useful
|
||||
* for getting the id of a packet array .
|
||||
*
|
||||
* @param b the array to read from
|
||||
* @return the unsigned value of the first byte
|
||||
*/
|
||||
public static int getId(byte[] b)
|
||||
{
|
||||
return b[0] & 0xFF;
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizes a config path by prefix upper case letters with '_' and
|
||||
* turning them to lowercase.
|
||||
|
@ -0,0 +1,17 @@
|
||||
package net.md_5.bungee.connection;
|
||||
|
||||
public class CancelSendSignal extends Error
|
||||
{
|
||||
|
||||
@Override
|
||||
public synchronized Throwable initCause(Throwable cause)
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized Throwable fillInStackTrace()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
}
|
@ -0,0 +1,225 @@
|
||||
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.buffer.ByteBuf;
|
||||
import io.netty.channel.Channel;
|
||||
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.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.packet.Packet0KeepAlive;
|
||||
import net.md_5.bungee.packet.Packet3Chat;
|
||||
import net.md_5.bungee.packet.PacketC9PlayerListItem;
|
||||
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
|
||||
{
|
||||
con.disconnect( Util.exception( t ) );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disconnected(Channel 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(ByteBuf buf) throws Exception
|
||||
{
|
||||
EntityMap.rewrite( buf, con.serverEntityId, con.clientEntityId );
|
||||
con.ch.write( buf );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(Packet0KeepAlive alive) throws Exception
|
||||
{
|
||||
con.trackingPingId = alive.id;
|
||||
}
|
||||
|
||||
@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(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( "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" ) )
|
||||
{
|
||||
ServerInfo server = bungee.getServerInfo( in.readUTF() );
|
||||
if ( server != null )
|
||||
{
|
||||
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 ) );
|
||||
}
|
||||
}
|
||||
if ( subChannel.equals( "GetServers" ) )
|
||||
{
|
||||
out.writeUTF( "GetServers" );
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for ( String server : bungee.getServers().keySet() )
|
||||
{
|
||||
sb.append( server );
|
||||
sb.append( "," );
|
||||
}
|
||||
out.writeUTF( sb.substring( 0, sb.length() - 1 ) );
|
||||
}
|
||||
if ( subChannel.equals( "Message" ) )
|
||||
{
|
||||
ProxiedPlayer target = bungee.getPlayer( in.readUTF() );
|
||||
if ( target != null )
|
||||
{
|
||||
target.sendMessage( in.readUTF() );
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
{
|
||||
con.disconnect( "[Kicked] " + kick.message );
|
||||
throw new CancelSendSignal();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return "[" + con.getName() + "] <-> DownstreamBridge <-> [" + server.getInfo().getName() + "]";
|
||||
}
|
||||
}
|
@ -0,0 +1,249 @@
|
||||
package net.md_5.bungee.connection;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import io.netty.channel.Channel;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.math.BigInteger;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.URL;
|
||||
import java.net.URLEncoder;
|
||||
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.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.netty.CipherCodec;
|
||||
import net.md_5.bungee.netty.HandlerBoss;
|
||||
import net.md_5.bungee.netty.PacketDecoder;
|
||||
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.protocol.PacketDefinitions;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class InitialHandler extends PacketHandler implements PendingConnection
|
||||
{
|
||||
|
||||
private final ProxyServer bungee;
|
||||
private Channel ch;
|
||||
@Getter
|
||||
private final ListenerInfo listener;
|
||||
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
|
||||
} );
|
||||
|
||||
private enum State
|
||||
{
|
||||
|
||||
HANDSHAKE, ENCRYPT, LOGIN, FINISHED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void connected(Channel channel) throws Exception
|
||||
{
|
||||
this.ch = channel;
|
||||
}
|
||||
|
||||
@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;
|
||||
|
||||
ch.pipeline().get( PacketDecoder.class ).setProtocol( PacketDefinitions.FORGE_PROTOCOL );
|
||||
}
|
||||
|
||||
@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" );
|
||||
Preconditions.checkArgument( handshake.username.length() <= 16, "Cannot have username longer than 16 characters" );
|
||||
this.handshake = handshake;
|
||||
ch.write( forgeMods );
|
||||
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" );
|
||||
|
||||
// TODO: This is shit
|
||||
new Thread( "Login Verifier - " + getName() )
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
try
|
||||
{
|
||||
SecretKey shared = EncryptionUtil.getSecret( encryptResponse, request );
|
||||
if ( BungeeCord.getInstance().config.isOnlineMode() )
|
||||
{
|
||||
String reply = null;
|
||||
try
|
||||
{
|
||||
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" ), shared.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;
|
||||
|
||||
try ( BufferedReader in = new BufferedReader( new InputStreamReader( new URL( authURL ).openStream() ) ) )
|
||||
{
|
||||
reply = in.readLine();
|
||||
}
|
||||
} catch ( IOException ex )
|
||||
{
|
||||
}
|
||||
|
||||
if ( !"YES".equals( reply ) )
|
||||
{
|
||||
disconnect( "Not authenticated with Minecraft.net" );
|
||||
}
|
||||
|
||||
// Check for multiple connections
|
||||
ProxiedPlayer old = bungee.getPlayer( handshake.username );
|
||||
if ( old != null )
|
||||
{
|
||||
old.disconnect( "You are already connected to the server" );
|
||||
}
|
||||
|
||||
// fire login event
|
||||
LoginEvent event = new LoginEvent( InitialHandler.this );
|
||||
if ( bungee.getPluginManager().callEvent( event ).isCancelled() )
|
||||
{
|
||||
disconnect( event.getCancelReason() );
|
||||
}
|
||||
}
|
||||
|
||||
Cipher encrypt = EncryptionUtil.getCipher( Cipher.ENCRYPT_MODE, shared );
|
||||
Cipher decrypt = EncryptionUtil.getCipher( Cipher.DECRYPT_MODE, shared );
|
||||
ch.write( new PacketFCEncryptionResponse() );
|
||||
ch.pipeline().addBefore( "decoder", "cipher", new CipherCodec( encrypt, decrypt ) );
|
||||
|
||||
thisState = InitialHandler.State.LOGIN;
|
||||
} catch ( Throwable t )
|
||||
{
|
||||
disconnect( "[Report to md_5 / Server Owner] " + Util.exception( t ) );
|
||||
}
|
||||
}
|
||||
}.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(PacketCDClientStatus clientStatus) throws Exception
|
||||
{
|
||||
Preconditions.checkState( thisState == State.LOGIN, "Not expecting LOGIN" );
|
||||
|
||||
UserConnection userCon = new UserConnection( (BungeeCord) bungee, ch, this, handshake, forgeLogin, loginMessages );
|
||||
ch.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.isActive() )
|
||||
{
|
||||
ch.write( new PacketFFKick( reason ) );
|
||||
ch.close();
|
||||
}
|
||||
}
|
||||
|
||||
@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.remoteAddress();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return "[" + ( ( getName() != null ) ? getName() : getAddress() ) + "] <-> InitialHandler";
|
||||
}
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
package net.md_5.bungee.connection;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.channel.Channel;
|
||||
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.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 ByteBuf pingBuf = Unpooled.wrappedBuffer( new byte[]
|
||||
{
|
||||
(byte) 0xFE, (byte) 0x01
|
||||
} );
|
||||
|
||||
@Override
|
||||
public void connected(Channel 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,106 @@
|
||||
package net.md_5.bungee.connection;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.Channel;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
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.event.ChatEvent;
|
||||
import net.md_5.bungee.api.event.PlayerDisconnectEvent;
|
||||
import net.md_5.bungee.api.event.PluginMessageEvent;
|
||||
import net.md_5.bungee.packet.Packet0KeepAlive;
|
||||
import net.md_5.bungee.packet.Packet3Chat;
|
||||
import net.md_5.bungee.packet.PacketFAPluginMessage;
|
||||
import net.md_5.bungee.packet.PacketHandler;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class UpstreamBridge extends PacketHandler
|
||||
{
|
||||
|
||||
private final ProxyServer bungee;
|
||||
private final UserConnection con;
|
||||
|
||||
@Override
|
||||
public void exception(Throwable t) throws Exception
|
||||
{
|
||||
con.disconnect( Util.exception( t ) );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disconnected(Channel channel) throws Exception
|
||||
{
|
||||
// We lost connection to the client
|
||||
PlayerDisconnectEvent event = new PlayerDisconnectEvent( con );
|
||||
bungee.getPluginManager().callEvent( event );
|
||||
bungee.getTabListHandler().onDisconnect( con );
|
||||
bungee.getPlayers().remove( con );
|
||||
|
||||
if ( con.getServer() != null )
|
||||
{
|
||||
con.getServer().disconnect( "Quitting" );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(ByteBuf buf) throws Exception
|
||||
{
|
||||
EntityMap.rewrite( buf, con.clientEntityId, con.serverEntityId );
|
||||
if ( con.getServer() != null )
|
||||
{
|
||||
con.getServer().getCh().write( buf );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(Packet0KeepAlive alive) throws Exception
|
||||
{
|
||||
if ( alive.id == con.trackingPingId )
|
||||
{
|
||||
int newPing = (int) ( System.currentTimeMillis() - con.pingTime );
|
||||
bungee.getTabListHandler().onPingChange( con, newPing );
|
||||
con.setPing( newPing );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(Packet3Chat chat) throws Exception
|
||||
{
|
||||
if ( chat.message.charAt( 0 ) == '/' )
|
||||
{
|
||||
if ( bungee.getPluginManager().dispatchCommand( con, chat.message.substring( 1 ) ) )
|
||||
{
|
||||
throw new CancelSendSignal();
|
||||
}
|
||||
} else
|
||||
{
|
||||
ChatEvent chatEvent = new ChatEvent( con, con.getServer(), chat.message );
|
||||
if ( bungee.getPluginManager().callEvent( chatEvent ).isCancelled() )
|
||||
{
|
||||
throw new CancelSendSignal();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@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";
|
||||
}
|
||||
}
|
50
proxy/src/main/java/net/md_5/bungee/netty/CipherCodec.java
Normal file
50
proxy/src/main/java/net/md_5/bungee/netty/CipherCodec.java
Normal file
@ -0,0 +1,50 @@
|
||||
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;
|
||||
|
||||
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( encrypt, in, out );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void decode(ChannelHandlerContext ctx, ByteBuf in, ByteBuf out) throws Exception
|
||||
{
|
||||
cipher( decrypt, in, out );
|
||||
}
|
||||
|
||||
private void cipher(Cipher cipher, ByteBuf in, ByteBuf out) throws ShortBufferException
|
||||
{
|
||||
int available = in.readableBytes();
|
||||
int outputSize = cipher.getOutputSize( available );
|
||||
if ( out.capacity() < outputSize )
|
||||
{
|
||||
out.capacity( outputSize );
|
||||
}
|
||||
int processed = cipher.update( in.nioBuffer(), out.nioBuffer( out.readerIndex(), outputSize ) );
|
||||
in.readerIndex( in.readerIndex() + processed );
|
||||
out.writerIndex( out.writerIndex() + processed );
|
||||
}
|
||||
}
|
89
proxy/src/main/java/net/md_5/bungee/netty/HandlerBoss.java
Normal file
89
proxy/src/main/java/net/md_5/bungee/netty/HandlerBoss.java
Normal file
@ -0,0 +1,89 @@
|
||||
package net.md_5.bungee.netty;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelInboundMessageHandlerAdapter;
|
||||
import io.netty.handler.timeout.ReadTimeoutException;
|
||||
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<ByteBuf>
|
||||
{
|
||||
|
||||
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 )
|
||||
{
|
||||
handler.connected( ctx.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( ctx.channel() );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void messageReceived(ChannelHandlerContext ctx, ByteBuf 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
|
||||
{
|
||||
ProxyServer.getInstance().getLogger().log( Level.SEVERE, handler + " - encountered exception", cause );
|
||||
}
|
||||
ctx.close();
|
||||
}
|
||||
}
|
||||
}
|
34
proxy/src/main/java/net/md_5/bungee/netty/PacketDecoder.java
Normal file
34
proxy/src/main/java/net/md_5/bungee/netty/PacketDecoder.java
Normal file
@ -0,0 +1,34 @@
|
||||
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<ByteBuf>
|
||||
{
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
private int protocol;
|
||||
|
||||
@Override
|
||||
protected ByteBuf decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception
|
||||
{
|
||||
int startIndex = in.readerIndex();
|
||||
PacketReader.readPacket( in, protocol );
|
||||
return in.copy( startIndex, in.readerIndex() - startIndex );
|
||||
}
|
||||
}
|
65
proxy/src/main/java/net/md_5/bungee/netty/PipelineUtils.java
Normal file
65
proxy/src/main/java/net/md_5/bungee/netty/PipelineUtils.java
Normal file
@ -0,0 +1,65 @@
|
||||
package net.md_5.bungee.netty;
|
||||
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelConfig;
|
||||
import io.netty.channel.ChannelException;
|
||||
import io.netty.channel.ChannelInitializer;
|
||||
import io.netty.channel.ChannelOption;
|
||||
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.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.api.config.ServerInfo;
|
||||
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<ServerInfo> 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() ) );
|
||||
ch.config().setDefaultHandlerByteBufType( ChannelConfig.ChannelHandlerByteBufType.HEAP );
|
||||
}
|
||||
};
|
||||
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();
|
||||
|
||||
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( "handler", new HandlerBoss() );
|
||||
}
|
||||
};
|
||||
}
|
@ -1,12 +1,8 @@
|
||||
package net.md_5.bungee.packet;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.io.ByteArrayDataOutput;
|
||||
import com.google.common.io.ByteStreams;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutput;
|
||||
import java.io.IOException;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.ReferenceCounted;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import lombok.Delegate;
|
||||
@ -17,67 +13,40 @@ import net.md_5.bungee.Util;
|
||||
* subclasses can read and write to the backing byte array which can be
|
||||
* retrieved via the {@link #getPacket()} method.
|
||||
*/
|
||||
public abstract class DefinedPacket implements DataOutput
|
||||
public abstract class DefinedPacket implements ByteBuf
|
||||
{
|
||||
|
||||
private interface Overriden
|
||||
@Delegate(types =
|
||||
{
|
||||
ByteBuf.class, ReferenceCounted.class
|
||||
})
|
||||
private ByteBuf buf;
|
||||
|
||||
void readUTF();
|
||||
|
||||
void writeUTF(String s);
|
||||
}
|
||||
private ByteArrayInputStream bin;
|
||||
private DataInputStream input;
|
||||
@Delegate(excludes = Overriden.class)
|
||||
private ByteArrayDataOutput out;
|
||||
/**
|
||||
* Packet id.
|
||||
*/
|
||||
public final int id;
|
||||
/**
|
||||
* Already constructed packet.
|
||||
*/
|
||||
private byte[] packet;
|
||||
|
||||
public DefinedPacket(int id, byte[] buf)
|
||||
public DefinedPacket(int id, ByteBuf buf)
|
||||
{
|
||||
bin = new ByteArrayInputStream( buf );
|
||||
input = new DataInputStream( bin );
|
||||
this.buf = buf;
|
||||
if ( readUnsignedByte() != id )
|
||||
{
|
||||
throw new IllegalArgumentException( "Wasn't expecting packet id " + Util.hex( id ) );
|
||||
}
|
||||
this.id = id;
|
||||
packet = buf;
|
||||
}
|
||||
|
||||
public DefinedPacket(int id)
|
||||
{
|
||||
out = ByteStreams.newDataOutput();
|
||||
this.id = id;
|
||||
buf = Unpooled.buffer();
|
||||
writeByte( id );
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the bytes that make up this packet.
|
||||
*
|
||||
* @return the bytes which make up this packet, either the original byte
|
||||
* array or the newly written one.
|
||||
*/
|
||||
public byte[] getPacket()
|
||||
{
|
||||
return packet == null ? packet = out.toByteArray() : packet;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeUTF(String s)
|
||||
public void writeString(String s)
|
||||
{
|
||||
writeShort( s.length() );
|
||||
writeChars( s );
|
||||
for ( char c : s.toCharArray() )
|
||||
{
|
||||
writeChar( c );
|
||||
}
|
||||
}
|
||||
|
||||
public String readUTF()
|
||||
public String readString()
|
||||
{
|
||||
short len = readShort();
|
||||
char[] chars = new char[ len ];
|
||||
@ -91,99 +60,17 @@ public abstract class DefinedPacket implements DataOutput
|
||||
public void writeArray(byte[] b)
|
||||
{
|
||||
writeShort( b.length );
|
||||
write( b );
|
||||
writeBytes( b );
|
||||
}
|
||||
|
||||
public byte[] readArray()
|
||||
{
|
||||
short len = readShort();
|
||||
byte[] ret = new byte[ len ];
|
||||
readFully( ret );
|
||||
readBytes( ret );
|
||||
return ret;
|
||||
}
|
||||
|
||||
public final int available()
|
||||
{
|
||||
return bin.available();
|
||||
}
|
||||
|
||||
public final void readFully(byte b[])
|
||||
{
|
||||
try
|
||||
{
|
||||
input.readFully( b );
|
||||
} catch ( IOException e )
|
||||
{
|
||||
throw new IllegalStateException( e );
|
||||
}
|
||||
}
|
||||
|
||||
public final boolean readBoolean()
|
||||
{
|
||||
try
|
||||
{
|
||||
return input.readBoolean();
|
||||
} catch ( IOException e )
|
||||
{
|
||||
throw new IllegalStateException( e );
|
||||
}
|
||||
}
|
||||
|
||||
public final byte readByte()
|
||||
{
|
||||
try
|
||||
{
|
||||
return input.readByte();
|
||||
} catch ( IOException e )
|
||||
{
|
||||
throw new IllegalStateException( e );
|
||||
}
|
||||
}
|
||||
|
||||
public final int readUnsignedByte()
|
||||
{
|
||||
try
|
||||
{
|
||||
return input.readUnsignedByte();
|
||||
} catch ( IOException e )
|
||||
{
|
||||
throw new IllegalStateException( e );
|
||||
}
|
||||
}
|
||||
|
||||
public final short readShort()
|
||||
{
|
||||
try
|
||||
{
|
||||
return input.readShort();
|
||||
} catch ( IOException e )
|
||||
{
|
||||
throw new IllegalStateException( e );
|
||||
}
|
||||
}
|
||||
|
||||
public final char readChar()
|
||||
{
|
||||
try
|
||||
{
|
||||
return input.readChar();
|
||||
} catch ( IOException e )
|
||||
{
|
||||
throw new IllegalStateException( e );
|
||||
}
|
||||
}
|
||||
|
||||
public final int readInt()
|
||||
{
|
||||
try
|
||||
{
|
||||
return input.readInt();
|
||||
} catch ( IOException e )
|
||||
{
|
||||
throw new IllegalStateException( e );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public abstract boolean equals(Object obj);
|
||||
|
||||
@ -194,28 +81,38 @@ public abstract class DefinedPacket implements DataOutput
|
||||
public abstract String toString();
|
||||
|
||||
public abstract void handle(PacketHandler handler) throws Exception;
|
||||
@SuppressWarnings("unchecked")
|
||||
private static Class<? extends DefinedPacket>[] classes = new Class[ 256 ];
|
||||
@SuppressWarnings("unchecked")
|
||||
private static Constructor<? extends DefinedPacket>[] consructors = new Constructor[ 256 ];
|
||||
|
||||
public static DefinedPacket packet(byte[] buf)
|
||||
public static DefinedPacket packet(ByteBuf buf)
|
||||
{
|
||||
int id = Util.getId( buf );
|
||||
short id = buf.getUnsignedByte( 0 );
|
||||
Class<? extends DefinedPacket> clazz = classes[id];
|
||||
DefinedPacket ret = null;
|
||||
if ( clazz != null )
|
||||
{
|
||||
try
|
||||
{
|
||||
Constructor<? extends DefinedPacket> constructor = clazz.getDeclaredConstructor( byte[].class );
|
||||
Constructor<? extends DefinedPacket> constructor = consructors[id];
|
||||
if ( constructor == null )
|
||||
{
|
||||
constructor = clazz.getDeclaredConstructor( ByteBuf.class );
|
||||
consructors[id] = constructor;
|
||||
}
|
||||
|
||||
if ( constructor != null )
|
||||
{
|
||||
buf.markReaderIndex();
|
||||
ret = constructor.newInstance( buf );
|
||||
buf.resetReaderIndex();
|
||||
}
|
||||
} catch ( IllegalAccessException | InstantiationException | InvocationTargetException | NoSuchMethodException ex )
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
Preconditions.checkState( ret != null, "Don't know how to deal with packet ID %s", Util.hex( id ) );
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
package net.md_5.bungee.packet;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
|
||||
@ -10,9 +11,9 @@ public class Packet0KeepAlive extends DefinedPacket
|
||||
|
||||
public int id;
|
||||
|
||||
public Packet0KeepAlive(byte[] buffer)
|
||||
Packet0KeepAlive(ByteBuf buf)
|
||||
{
|
||||
super( 0x00, buffer );
|
||||
super( 0x00, buf );
|
||||
id = readInt();
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
package net.md_5.bungee.packet;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
|
||||
@ -20,24 +21,31 @@ public class Packet1Login extends DefinedPacket
|
||||
{
|
||||
super( 0x01 );
|
||||
writeInt( entityId );
|
||||
writeUTF( levelType );
|
||||
writeString( levelType );
|
||||
writeByte( gameMode );
|
||||
writeByte( dimension );
|
||||
writeByte( difficulty );
|
||||
writeByte( unused );
|
||||
writeByte( maxPlayers );
|
||||
this.entityId = entityId;
|
||||
this.levelType = levelType;
|
||||
this.gameMode = gameMode;
|
||||
this.dimension = dimension;
|
||||
this.difficulty = difficulty;
|
||||
this.unused = unused;
|
||||
this.maxPlayers = maxPlayers;
|
||||
}
|
||||
|
||||
public Packet1Login(byte[] buf)
|
||||
Packet1Login(ByteBuf buf)
|
||||
{
|
||||
super( 0x01, buf );
|
||||
this.entityId = readInt();
|
||||
this.levelType = readUTF();
|
||||
this.levelType = readString();
|
||||
this.gameMode = readByte();
|
||||
if ( available() == 4 )
|
||||
if ( readableBytes() == 4 )
|
||||
{
|
||||
this.dimension = readByte();
|
||||
} else if ( available() == 7 )
|
||||
} else if ( readableBytes() == 7 )
|
||||
{
|
||||
this.dimension = readInt();
|
||||
} else
|
||||
|
@ -1,5 +1,6 @@
|
||||
package net.md_5.bungee.packet;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
|
||||
@ -17,17 +18,21 @@ public class Packet2Handshake extends DefinedPacket
|
||||
{
|
||||
super( 0x02 );
|
||||
writeByte( protocolVersion );
|
||||
writeUTF( username );
|
||||
writeUTF( host );
|
||||
writeString( username );
|
||||
writeString( host );
|
||||
writeInt( port );
|
||||
this.procolVersion = protocolVersion;
|
||||
this.username = username;
|
||||
this.host = host;
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
public Packet2Handshake(byte[] buf)
|
||||
Packet2Handshake(ByteBuf buf)
|
||||
{
|
||||
super( 0x02, buf );
|
||||
this.procolVersion = readByte();
|
||||
this.username = readUTF();
|
||||
this.host = readUTF();
|
||||
this.username = readString();
|
||||
this.host = readString();
|
||||
this.port = readInt();
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
package net.md_5.bungee.packet;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
|
||||
@ -13,13 +14,14 @@ public class Packet3Chat extends DefinedPacket
|
||||
public Packet3Chat(String message)
|
||||
{
|
||||
super( 0x03 );
|
||||
writeUTF( message );
|
||||
writeString( message );
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public Packet3Chat(byte[] buf)
|
||||
Packet3Chat(ByteBuf buf)
|
||||
{
|
||||
super( 0x03, buf );
|
||||
this.message = readUTF();
|
||||
this.message = readString();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1,5 +1,6 @@
|
||||
package net.md_5.bungee.packet;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
|
||||
@ -8,6 +9,8 @@ import lombok.ToString;
|
||||
public class Packet9Respawn extends DefinedPacket
|
||||
{
|
||||
|
||||
public static final Packet9Respawn DIM1_SWITCH = new Packet9Respawn( (byte) 1, (byte) 0, (byte) 0, (short) 256, "DEFAULT" );
|
||||
public static final Packet9Respawn DIM2_SWITCH = new Packet9Respawn( (byte) -1, (byte) 0, (byte) 0, (short) 256, "DEFAULT" );
|
||||
public int dimension;
|
||||
public byte difficulty;
|
||||
public byte gameMode;
|
||||
@ -21,17 +24,22 @@ public class Packet9Respawn extends DefinedPacket
|
||||
writeByte( difficulty );
|
||||
writeByte( gameMode );
|
||||
writeShort( worldHeight );
|
||||
writeUTF( levelType );
|
||||
writeString( levelType );
|
||||
this.dimension = dimension;
|
||||
this.difficulty = difficulty;
|
||||
this.gameMode = gameMode;
|
||||
this.worldHeight = worldHeight;
|
||||
this.levelType = levelType;
|
||||
}
|
||||
|
||||
public Packet9Respawn(byte[] buf)
|
||||
Packet9Respawn(ByteBuf buf)
|
||||
{
|
||||
super( 0x09, buf );
|
||||
this.dimension = readInt();
|
||||
this.difficulty = readByte();
|
||||
this.gameMode = readByte();
|
||||
this.worldHeight = readShort();
|
||||
this.levelType = readUTF();
|
||||
this.levelType = readString();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1,5 +1,6 @@
|
||||
package net.md_5.bungee.packet;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
|
||||
@ -12,10 +13,10 @@ public class PacketC9PlayerListItem extends DefinedPacket
|
||||
public boolean online;
|
||||
public int ping;
|
||||
|
||||
public PacketC9PlayerListItem(byte[] packet)
|
||||
PacketC9PlayerListItem(ByteBuf buf)
|
||||
{
|
||||
super( 0xC9, packet );
|
||||
username = readUTF();
|
||||
super( 0xC9, buf );
|
||||
username = readString();
|
||||
online = readBoolean();
|
||||
ping = readShort();
|
||||
}
|
||||
@ -23,7 +24,7 @@ public class PacketC9PlayerListItem extends DefinedPacket
|
||||
public PacketC9PlayerListItem(String username, boolean online, int ping)
|
||||
{
|
||||
super( 0xC9 );
|
||||
writeUTF( username );
|
||||
writeString( username );
|
||||
writeBoolean( online );
|
||||
writeShort( ping );
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
package net.md_5.bungee.packet;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
|
||||
@ -8,6 +9,10 @@ import lombok.ToString;
|
||||
public class PacketCDClientStatus extends DefinedPacket
|
||||
{
|
||||
|
||||
/**
|
||||
* Represents the packet the client sends to the server when it is ready to
|
||||
* login.
|
||||
*/
|
||||
public static PacketCDClientStatus CLIENT_LOGIN = new PacketCDClientStatus( (byte) 0 );
|
||||
|
||||
/**
|
||||
@ -21,7 +26,7 @@ public class PacketCDClientStatus extends DefinedPacket
|
||||
writeByte( payload );
|
||||
}
|
||||
|
||||
public PacketCDClientStatus(byte[] buf)
|
||||
PacketCDClientStatus(ByteBuf buf)
|
||||
{
|
||||
super( 0xCD, buf );
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
package net.md_5.bungee.packet;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
|
||||
@ -14,16 +15,16 @@ public class PacketFAPluginMessage extends DefinedPacket
|
||||
public PacketFAPluginMessage(String tag, byte[] data)
|
||||
{
|
||||
super( 0xFA );
|
||||
writeUTF( tag );
|
||||
writeString( tag );
|
||||
writeArray( data );
|
||||
this.tag = tag;
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public PacketFAPluginMessage(byte[] buf)
|
||||
PacketFAPluginMessage(ByteBuf buf)
|
||||
{
|
||||
super( 0xFA, buf );
|
||||
this.tag = readUTF();
|
||||
this.tag = readString();
|
||||
this.data = readArray();
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
package net.md_5.bungee.packet;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
|
||||
@ -23,9 +24,11 @@ public class PacketFCEncryptionResponse extends DefinedPacket
|
||||
super( 0xFC );
|
||||
writeArray( sharedSecret );
|
||||
writeArray( verifyToken );
|
||||
this.sharedSecret = sharedSecret;
|
||||
this.verifyToken = verifyToken;
|
||||
}
|
||||
|
||||
public PacketFCEncryptionResponse(byte[] buf)
|
||||
PacketFCEncryptionResponse(ByteBuf buf)
|
||||
{
|
||||
super( 0xFC, buf );
|
||||
this.sharedSecret = readArray();
|
||||
|
@ -1,5 +1,6 @@
|
||||
package net.md_5.bungee.packet;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
|
||||
@ -15,7 +16,7 @@ public class PacketFDEncryptionRequest extends DefinedPacket
|
||||
public PacketFDEncryptionRequest(String serverId, byte[] publicKey, byte[] verifyToken)
|
||||
{
|
||||
super( 0xFD );
|
||||
writeUTF( serverId );
|
||||
writeString( serverId );
|
||||
writeArray( publicKey );
|
||||
writeArray( verifyToken );
|
||||
this.serverId = serverId;
|
||||
@ -23,10 +24,10 @@ public class PacketFDEncryptionRequest extends DefinedPacket
|
||||
this.verifyToken = verifyToken;
|
||||
}
|
||||
|
||||
public PacketFDEncryptionRequest(byte[] buf)
|
||||
PacketFDEncryptionRequest(ByteBuf buf)
|
||||
{
|
||||
super( 0xFD, buf );
|
||||
serverId = readUTF();
|
||||
serverId = readString();
|
||||
publicKey = readArray();
|
||||
verifyToken = readArray();
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
package net.md_5.bungee.packet;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
|
||||
@ -8,9 +9,12 @@ import lombok.ToString;
|
||||
public class PacketFEPing extends DefinedPacket
|
||||
{
|
||||
|
||||
public PacketFEPing(byte[] buffer)
|
||||
public byte version;
|
||||
|
||||
PacketFEPing(ByteBuf buffer)
|
||||
{
|
||||
super( 0xFE, buffer );
|
||||
version = readByte();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1,5 +1,6 @@
|
||||
package net.md_5.bungee.packet;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
|
||||
@ -13,13 +14,13 @@ public class PacketFFKick extends DefinedPacket
|
||||
public PacketFFKick(String message)
|
||||
{
|
||||
super( 0xFF );
|
||||
writeUTF( message );
|
||||
writeString( message );
|
||||
}
|
||||
|
||||
public PacketFFKick(byte[] buf)
|
||||
PacketFFKick(ByteBuf buf)
|
||||
{
|
||||
super( 0xFF, buf );
|
||||
this.message = readUTF();
|
||||
this.message = readString();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1,70 +1,75 @@
|
||||
package net.md_5.bungee.packet;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.Channel;
|
||||
|
||||
public abstract class PacketHandler
|
||||
{
|
||||
|
||||
private void nop(DefinedPacket packet)
|
||||
@Override
|
||||
public abstract String toString();
|
||||
|
||||
public void connected(Channel channel) throws Exception
|
||||
{
|
||||
}
|
||||
|
||||
public void disconnected(Channel channel) throws Exception
|
||||
{
|
||||
}
|
||||
|
||||
public void exception(Throwable t) throws Exception
|
||||
{
|
||||
}
|
||||
|
||||
public void handle(ByteBuf buf) throws Exception
|
||||
{
|
||||
throw new UnsupportedOperationException( "No handler defined for packet " + packet.getClass() );
|
||||
}
|
||||
|
||||
public void handle(Packet0KeepAlive alive) throws Exception
|
||||
{
|
||||
nop( alive );
|
||||
}
|
||||
|
||||
public void handle(Packet1Login login) throws Exception
|
||||
{
|
||||
nop( login );
|
||||
}
|
||||
|
||||
public void handle(Packet2Handshake handshake) throws Exception
|
||||
{
|
||||
nop( handshake );
|
||||
}
|
||||
|
||||
public void handle(Packet3Chat chat) throws Exception
|
||||
{
|
||||
nop( chat );
|
||||
}
|
||||
|
||||
public void handle(Packet9Respawn respawn) throws Exception
|
||||
{
|
||||
nop( respawn );
|
||||
}
|
||||
|
||||
public void handle(PacketC9PlayerListItem playerList) throws Exception
|
||||
{
|
||||
nop( playerList );
|
||||
}
|
||||
|
||||
public void handle(PacketCDClientStatus clientStatus) throws Exception
|
||||
{
|
||||
nop( clientStatus );
|
||||
}
|
||||
|
||||
public void handle(PacketFAPluginMessage pluginMessage) throws Exception
|
||||
{
|
||||
nop( pluginMessage );
|
||||
}
|
||||
|
||||
public void handle(PacketFCEncryptionResponse encryptResponse) throws Exception
|
||||
{
|
||||
nop( encryptResponse );
|
||||
}
|
||||
|
||||
public void handle(PacketFDEncryptionRequest encryptRequest) throws Exception
|
||||
{
|
||||
nop( encryptRequest );
|
||||
}
|
||||
|
||||
public void handle(PacketFEPing ping) throws Exception
|
||||
{
|
||||
nop( ping );
|
||||
}
|
||||
|
||||
public void handle(PacketFFKick kick) throws Exception
|
||||
{
|
||||
nop( kick );
|
||||
}
|
||||
}
|
||||
|
@ -1,101 +0,0 @@
|
||||
package net.md_5.bungee.packet;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.FilterInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import net.md_5.mendax.datainput.DataInputPacketReader;
|
||||
|
||||
/**
|
||||
* A specialized input stream to parse packets using the Mojang packet
|
||||
* definitions and then return them as a byte array.
|
||||
*/
|
||||
public class PacketStream implements AutoCloseable
|
||||
{
|
||||
|
||||
private final DataInputStream dataInput;
|
||||
@Getter
|
||||
private OutputStream out;
|
||||
@Getter
|
||||
@Setter
|
||||
private int protocol;
|
||||
private final TrackingInputStream tracker;
|
||||
private final byte[] buffer = new byte[ 1 << 18 ];
|
||||
|
||||
public PacketStream(InputStream in, int protocol)
|
||||
{
|
||||
this( in, null, protocol );
|
||||
}
|
||||
|
||||
public PacketStream(InputStream in, OutputStream out, int protocol)
|
||||
{
|
||||
tracker = new TrackingInputStream( in );
|
||||
dataInput = new DataInputStream( tracker );
|
||||
this.out = out;
|
||||
this.protocol = protocol;
|
||||
}
|
||||
|
||||
public void write(byte[] b) throws IOException
|
||||
{
|
||||
out.write( b );
|
||||
}
|
||||
|
||||
public void write(DefinedPacket packet) throws IOException
|
||||
{
|
||||
out.write( packet.getPacket() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Read an entire packet from the stream and return it as a byte array.
|
||||
*
|
||||
* @return the read packet
|
||||
* @throws IOException when the underlying input stream throws an exception
|
||||
*/
|
||||
public byte[] readPacket() throws IOException
|
||||
{
|
||||
tracker.out.reset();
|
||||
DataInputPacketReader.readPacket( dataInput, buffer, protocol );
|
||||
return tracker.out.toByteArray();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws Exception
|
||||
{
|
||||
dataInput.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Input stream which will wrap another stream and copy all bytes read to a
|
||||
* {@link ByteArrayOutputStream}.
|
||||
*/
|
||||
private class TrackingInputStream extends FilterInputStream
|
||||
{
|
||||
|
||||
private final ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
|
||||
public TrackingInputStream(InputStream in)
|
||||
{
|
||||
super( in );
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read() throws IOException
|
||||
{
|
||||
int ret = in.read();
|
||||
out.write( ret );
|
||||
return ret;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(byte[] b, int off, int len) throws IOException
|
||||
{
|
||||
int ret = in.read( b, off, len );
|
||||
out.write( b, off, ret );
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
@ -22,7 +22,7 @@ public class GlobalTabList implements TabListHandler
|
||||
UserConnection con = (UserConnection) player;
|
||||
for ( ProxiedPlayer p : ProxyServer.getInstance().getPlayers() )
|
||||
{
|
||||
con.packetQueue.add( new PacketC9PlayerListItem( p.getDisplayName(), true, p.getPing() ) );
|
||||
con.sendPacket(new PacketC9PlayerListItem( p.getDisplayName(), true, p.getPing() ) );
|
||||
}
|
||||
BungeeCord.getInstance().broadcast( new PacketC9PlayerListItem( player.getDisplayName(), true, player.getPing() ) );
|
||||
}
|
||||
|
@ -40,7 +40,7 @@ public class ServerUniqueTabList implements TabListHandler
|
||||
{
|
||||
for ( String username : usernames )
|
||||
{
|
||||
( (UserConnection) player ).packetQueue.add( new PacketC9PlayerListItem( username, false, 9999 ) );
|
||||
( (UserConnection) player ).sendPacket(new PacketC9PlayerListItem( username, false, 9999 ) );
|
||||
}
|
||||
usernames.clear();
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user