Redo parts of login and connection sequences

This commit is contained in:
md_5 2013-03-07 21:33:49 +11:00
parent b6e76f4054
commit 644deee3c6
3 changed files with 86 additions and 138 deletions

View File

@ -1,17 +1,15 @@
package net.md_5.bungee;
import com.google.common.base.Preconditions;
import io.netty.channel.Channel;
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 lombok.RequiredArgsConstructor;
import net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.api.ProxyServer;
import net.md_5.bungee.api.ServerPing;
@ -21,6 +19,8 @@ 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.PacketDecoder;
import net.md_5.bungee.packet.DefinedPacket;
import net.md_5.bungee.packet.Packet1Login;
import net.md_5.bungee.packet.Packet2Handshake;
@ -31,16 +31,16 @@ 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.bungee.protocol.PacketDefinitions;
@RequiredArgsConstructor
public class InitialHandler extends PacketHandler implements Runnable, PendingConnection
{
private final Socket socket;
private final ProxyServer bungee;
private final Channel ch;
@Getter
private final ListenerInfo listener;
private PacketStream stream;
private Packet1Login forgeLogin;
private Packet2Handshake handshake;
private PacketFDEncryptionRequest request;
@ -51,13 +51,6 @@ public class InitialHandler extends PacketHandler implements Runnable, PendingCo
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
{
@ -70,7 +63,8 @@ public class InitialHandler extends PacketHandler implements Runnable, PendingCo
Preconditions.checkState( thisState == State.LOGIN, "Not expecting FORGE LOGIN" );
Preconditions.checkState( forgeLogin == null, "Already received FORGE LOGIN" );
forgeLogin = login;
stream.setProtocol( PacketDefinitions.FORGE_PROTOCOL );
ch.pipeline().get( PacketDecoder.class ).setProtocol( PacketDefinitions.FORGE_PROTOCOL );
}
@Override
@ -82,28 +76,17 @@ public class InitialHandler extends PacketHandler implements Runnable, PendingCo
@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() );
listener.getMotd(), bungee.getPlayers().size(), listener.getMaxPlayers() );
pingevent = ProxyServer.getInstance().getPluginManager().callEvent( new ProxyPingEvent( this, pingevent ) ).getResponse();
pingevent = bungee.getPluginManager().callEvent( new ProxyPingEvent( this, pingevent ) ).getResponse();
String response = ( newPing ) ? ChatColor.COLOR_CHAR + "1"
String response = 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();
+ "\00" + pingevent.getMaxPlayers();
disconnect( response );
}
@ -111,9 +94,10 @@ public class InitialHandler extends PacketHandler implements Runnable, PendingCo
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;
stream.write( forgeMods );
stream.write( request = EncryptionUtil.encryptRequest() );
ch.write( forgeMods );
ch.write( request = EncryptionUtil.encryptRequest() );
thisState = State.ENCRYPT;
}
@ -129,7 +113,7 @@ public class InitialHandler extends PacketHandler implements Runnable, PendingCo
}
// Check for multiple connections
ProxiedPlayer old = ProxyServer.getInstance().getPlayer( handshake.username );
ProxiedPlayer old = bungee.getInstance().getPlayer( handshake.username );
if ( old != null )
{
old.disconnect( "You are already connected to the server" );
@ -137,15 +121,16 @@ public class InitialHandler extends PacketHandler implements Runnable, PendingCo
// fire login event
LoginEvent event = new LoginEvent( this );
ProxyServer.getInstance().getPluginManager().callEvent( event );
if ( event.isCancelled() )
if ( bungee.getPluginManager().callEvent( event ).isCancelled() )
{
throw new KickException( event.getCancelReason() );
disconnect( 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() );
ch.write( new PacketFCEncryptionResponse() );
Cipher decrypt = EncryptionUtil.getCipher( Cipher.DECRYPT_MODE, shared );
Cipher encrypt = EncryptionUtil.getCipher( Cipher.ENCRYPT_MODE, shared );
ch.pipeline().addBefore( "decoder", "cipher", new CipherCodec( encrypt, decrypt ) );
thisState = State.LOGIN;
}
@ -186,23 +171,12 @@ public class InitialHandler extends PacketHandler implements Runnable, PendingCo
}
@Override
public void disconnect(String reason)
public synchronized void disconnect(String reason)
{
thisState = State.FINISHED;
try
if ( ch.isActive() )
{
stream.write( new PacketFFKick( reason ) );
} catch ( IOException ioe )
{
} finally
{
try
{
socket.shutdownOutput();
socket.close();
} catch ( IOException ioe2 )
{
}
ch.write( new PacketFFKick( reason ) );
ch.close();
}
}
@ -227,6 +201,6 @@ public class InitialHandler extends PacketHandler implements Runnable, PendingCo
@Override
public InetSocketAddress getAddress()
{
return (InetSocketAddress) socket.getRemoteSocketAddress();
return (InetSocketAddress) ch.remoteAddress();
}
}

View File

@ -2,6 +2,7 @@ package net.md_5.bungee;
import com.google.common.base.Preconditions;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.socket.nio.NioSocketChannel;
@ -9,10 +10,12 @@ import java.util.Queue;
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.ServerConnectEvent;
import net.md_5.bungee.api.event.ServerConnectedEvent;
import net.md_5.bungee.netty.ChannelBootstrapper;
import net.md_5.bungee.packet.DefinedPacket;
import net.md_5.bungee.packet.Packet1Login;
import net.md_5.bungee.packet.Packet9Respawn;
import net.md_5.bungee.packet.PacketCDClientStatus;
import net.md_5.bungee.packet.PacketFDEncryptionRequest;
import net.md_5.bungee.packet.PacketFFKick;
@ -22,7 +25,9 @@ import net.md_5.bungee.packet.PacketStream;
public class ServerConnector extends PacketHandler
{
private final PacketStream stream;
private final ProxyServer bungee;
private final Channel ch;
private final UserConnection user;
private Packet1Login loginPacket;
private State thisState = State.ENCRYPT_REQUEST;
@ -45,15 +50,50 @@ public class ServerConnector extends PacketHandler
ServerConnection server = new ServerConnection( socket, info, stream, connector.loginPacket );
ServerConnectedEvent event = new ServerConnectedEvent( user, server );
ProxyServer.getInstance().getPluginManager().callEvent( event );
bungee.getPluginManager().callEvent( event );
stream.write( BungeeCord.getInstance().registerChannels() );
ch.write( BungeeCord.getInstance().registerChannels() );
Queue<DefinedPacket> packetQueue = ( (BungeeServerInfo) info ).getPacketQueue();
while ( !packetQueue.isEmpty() )
{
stream.write( packetQueue.poll() );
ch.write( packetQueue.poll() );
}
if ( user.getServer() == null )
{
BungeeCord.getInstance().connections.put( user.getName(), this );
bungee.getTabListHandler().onConnect( user );
// 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() );
} else
{
bungee.getTabListHandler().onServerChange( user );
user.ch.write( new Packet9Respawn( (byte) 1, (byte) 0, (byte) 0, (short) 256, "DEFAULT" ) );
user.ch.write( new Packet9Respawn( (byte) -1, (byte) 0, (byte) 0, (short) 256, "DEFAULT" ) );
Packet1Login login = newServer.loginPacket;
serverEntityId = login.entityId;
stream.write( new Packet9Respawn( login.dimension, login.difficulty, login.gameMode, (short) 256, login.levelType ) );
// newServer.add(user)
user.getServer().disconnect( "Quitting" );
user.getServer().getInfo().removePlayer( user );
}
//
thisState = State.FINISHED;
}
@ -70,13 +110,17 @@ public class ServerConnector extends PacketHandler
throw new KickException( kick.message );
}
public static void connect(final UserConnection user, final ServerInfo info, final boolean retry)
public static void connect(final UserConnection user, ServerInfo info, final boolean retry)
{
ServerConnectEvent event = new ServerConnectEvent( user, 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( ChannelBootstrapper.CLIENT )
.remoteAddress( info.getAddress() )
.remoteAddress( target.getAddress() )
.connect().addListener( new ChannelFutureListener()
{
@Override
@ -90,7 +134,7 @@ public class ServerConnector extends PacketHandler
{
future.channel().close();
ServerInfo def = ProxyServer.getInstance().getServers().get( user.getPendingConnection().getListener().getDefaultServer() );
if ( retry && !info.equals( def ) )
if ( retry && !target.equals( def ) )
{
user.sendMessage( ChatColor.RED + "Could not connect to target server, you have been moved to the default server" );
connect( user, def, false );

View File

@ -1,5 +1,6 @@
package net.md_5.bungee;
import com.google.common.base.Preconditions;
import gnu.trove.set.hash.THashSet;
import io.netty.channel.Channel;
import java.net.InetSocketAddress;
@ -18,9 +19,6 @@ 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.PlayerDisconnectEvent;
import net.md_5.bungee.api.event.ServerConnectEvent;
import net.md_5.bungee.connection.DownstreamBridge;
import net.md_5.bungee.connection.UpstreamBridge;
import net.md_5.bungee.packet.*;
public final class UserConnection implements ProxiedPlayer
@ -57,10 +55,9 @@ public final class UserConnection implements ProxiedPlayer
this.pendingConnection = pendingConnection;
this.forgeLogin = forgeLogin;
this.loginMessages = loginMessages;
name = handshake.username.substring( 0, Math.min( handshake.username.length(), 16 ) );
displayName = name;
Collection<String> g = ProxyServer.getInstance().getConfigurationAdapter().getGroups( name );
Collection<String> g = bungee.getConfigurationAdapter().getGroups( name );
for ( String s : g )
{
addGroups( s );
@ -70,81 +67,14 @@ public final class UserConnection implements ProxiedPlayer
@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;
}
public void connect(ServerInfo target, boolean force)
{
nextServer = null;
if ( server == null )
{
// 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 );
reconnecting = true;
if ( server != null )
{
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" ) );
}
ServerConnection newServer = ServerConnector.connect( this, target, true );
if ( server == null )
{
// Once again, first connection
clientEntityId = newServer.loginPacket.entityId;
serverEntityId = newServer.loginPacket.entityId;
// Set tab list size
Packet1Login s = newServer.loginPacket;
Packet1Login login = new Packet1Login( s.entityId, s.levelType, s.gameMode, (byte) s.dimension, s.difficulty, s.unused, (byte) pendingConnection.getListener().getTabListSize() );
stream.write( login );
stream.write( BungeeCord.getInstance().registerChannels() );
upBridge = new UpstreamBridge();
upBridge.start();
} else
{
try
{
downBridge.interrupt();
downBridge.join();
} catch ( InterruptedException ie )
{
}
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;
}
@Override