Implement dual protocol version support.

This commit is contained in:
md_5 2014-01-27 11:26:27 +11:00
parent 5c12f900b3
commit b2f517fa63
15 changed files with 114 additions and 44 deletions

View File

@ -174,6 +174,7 @@ public abstract class ProxyServer
* *
* @return the supported Minecraft version * @return the supported Minecraft version
*/ */
@Deprecated
public abstract String getGameVersion(); public abstract String getGameVersion();
/** /**
@ -181,6 +182,7 @@ public abstract class ProxyServer
* *
* @return the Minecraft protocol version * @return the Minecraft protocol version
*/ */
@Deprecated
public abstract int getProtocolVersion(); public abstract int getProtocolVersion();
/** /**

View File

@ -114,9 +114,25 @@ public abstract class DefinedPacket
} }
} }
public abstract void read(ByteBuf buf); public void read(ByteBuf buf)
{
throw new UnsupportedOperationException( "Packet must implement read method" );
}
public abstract void write(ByteBuf buf); public void read(ByteBuf buf, Protocol.ProtocolDirection direction, int protocolVersion)
{
read( buf );
}
public void write(ByteBuf buf)
{
throw new UnsupportedOperationException( "Packet must implement write method" );
}
public void write(ByteBuf buf, Protocol.ProtocolDirection direction, int protocolVersion)
{
write( buf );
}
public abstract void handle(AbstractPacketHandler handler) throws Exception; public abstract void handle(AbstractPacketHandler handler) throws Exception;

View File

@ -1,6 +1,5 @@
package net.md_5.bungee.protocol; package net.md_5.bungee.protocol;
import com.google.common.base.Charsets;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder; import io.netty.handler.codec.ByteToMessageDecoder;
@ -15,6 +14,8 @@ public class MinecraftDecoder extends ByteToMessageDecoder
@Setter @Setter
private Protocol protocol; private Protocol protocol;
private boolean server; private boolean server;
@Setter
private int protocolVersion;
@Override @Override
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception
@ -28,7 +29,7 @@ public class MinecraftDecoder extends ByteToMessageDecoder
if ( prot.hasPacket( packetId ) ) if ( prot.hasPacket( packetId ) )
{ {
packet = prot.createPacket( packetId ); packet = prot.createPacket( packetId );
packet.read( in ); packet.read( in, prot, protocolVersion );
if ( in.readableBytes() != 0 ) if ( in.readableBytes() != 0 )
{ {
throw new BadPacketException( "Did not read all bytes from packet " + packet.getClass() + " " + packetId + " Protocol " + protocol + " Direction " + prot ); throw new BadPacketException( "Did not read all bytes from packet " + packet.getClass() + " " + packetId + " Protocol " + protocol + " Direction " + prot );

View File

@ -13,12 +13,14 @@ public class MinecraftEncoder extends MessageToByteEncoder<DefinedPacket>
@Setter @Setter
private Protocol protocol; private Protocol protocol;
private boolean server; private boolean server;
@Setter
private int protocolVersion;
@Override @Override
protected void encode(ChannelHandlerContext ctx, DefinedPacket msg, ByteBuf out) throws Exception protected void encode(ChannelHandlerContext ctx, DefinedPacket msg, ByteBuf out) throws Exception
{ {
Protocol.ProtocolDirection prot = ( server ) ? protocol.TO_CLIENT : protocol.TO_SERVER; Protocol.ProtocolDirection prot = ( server ) ? protocol.TO_CLIENT : protocol.TO_SERVER;
DefinedPacket.writeVarInt( prot.getId( msg.getClass() ), out ); DefinedPacket.writeVarInt( prot.getId( msg.getClass() ), out );
msg.write( out ); msg.write( out, prot, protocolVersion );
} }
} }

View File

@ -4,6 +4,8 @@ import com.google.common.base.Preconditions;
import gnu.trove.map.TObjectIntMap; import gnu.trove.map.TObjectIntMap;
import gnu.trove.map.hash.TObjectIntHashMap; import gnu.trove.map.hash.TObjectIntHashMap;
import java.lang.reflect.Constructor; import java.lang.reflect.Constructor;
import java.util.Arrays;
import java.util.List;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import net.md_5.bungee.protocol.packet.Chat; import net.md_5.bungee.protocol.packet.Chat;
import net.md_5.bungee.protocol.packet.ClientSettings; import net.md_5.bungee.protocol.packet.ClientSettings;
@ -95,8 +97,7 @@ public enum Protocol
}; };
/*========================================================================*/ /*========================================================================*/
public static final int MAX_PACKET_ID = 0xFF; public static final int MAX_PACKET_ID = 0xFF;
public static final int PROTOCOL_VERSION = 0x04; public static List<Integer> supportedVersions = Arrays.asList( 4, 8 );
public static final String MINECRAFT_VERSION = "1.7.2";
/*========================================================================*/ /*========================================================================*/
public final ProtocolDirection TO_SERVER = new ProtocolDirection( "TO_SERVER" ); public final ProtocolDirection TO_SERVER = new ProtocolDirection( "TO_SERVER" );
public final ProtocolDirection TO_CLIENT = new ProtocolDirection( "TO_CLIENT" ); public final ProtocolDirection TO_CLIENT = new ProtocolDirection( "TO_CLIENT" );

View File

@ -7,6 +7,7 @@ import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import net.md_5.bungee.protocol.AbstractPacketHandler; import net.md_5.bungee.protocol.AbstractPacketHandler;
import net.md_5.bungee.protocol.Protocol;
@Data @Data
@NoArgsConstructor @NoArgsConstructor
@ -16,17 +17,31 @@ public class Chat extends DefinedPacket
{ {
private String message; private String message;
private byte position;
@Override public Chat(String message)
public void read(ByteBuf buf)
{ {
message = readString( buf ); this( message, (byte) 0 );
} }
@Override @Override
public void write(ByteBuf buf) public void read(ByteBuf buf, Protocol.ProtocolDirection direction, int protocolVersion)
{
message = readString( buf );
if ( direction.toString().equals( "TO_CLIENT" ) && protocolVersion >= 5 )
{
position = buf.readByte();
}
}
@Override
public void write(ByteBuf buf, Protocol.ProtocolDirection direction, int protocolVersion)
{ {
writeString( message, buf ); writeString( message, buf );
if ( direction.toString().equals( "TO_CLIENT" ) && protocolVersion >= 5 )
{
buf.writeByte( position );
}
} }
@Override @Override

View File

@ -7,6 +7,7 @@ import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import net.md_5.bungee.protocol.AbstractPacketHandler; import net.md_5.bungee.protocol.AbstractPacketHandler;
import net.md_5.bungee.protocol.Protocol;
@Data @Data
@NoArgsConstructor @NoArgsConstructor
@ -20,28 +21,34 @@ public class ClientSettings extends DefinedPacket
private byte chatFlags; private byte chatFlags;
private boolean unknown; private boolean unknown;
private byte difficulty; private byte difficulty;
private boolean showCape; private byte showCape;
@Override @Override
public void read(ByteBuf buf) public void read(ByteBuf buf, Protocol.ProtocolDirection direction, int protocolVersion)
{ {
locale = readString( buf ); locale = readString( buf );
viewDistance = buf.readByte(); viewDistance = buf.readByte();
chatFlags = buf.readByte(); chatFlags = buf.readByte();
unknown = buf.readBoolean(); unknown = buf.readBoolean();
if ( protocolVersion < 5 )
{
difficulty = buf.readByte(); difficulty = buf.readByte();
showCape = buf.readBoolean(); }
showCape = buf.readByte();
} }
@Override @Override
public void write(ByteBuf buf) public void write(ByteBuf buf, Protocol.ProtocolDirection direction, int protocolVersion)
{ {
writeString( locale, buf ); writeString( locale, buf );
buf.writeByte( viewDistance ); buf.writeByte( viewDistance );
buf.writeByte( chatFlags ); buf.writeByte( chatFlags );
buf.writeBoolean( unknown ); buf.writeBoolean( unknown );
if ( protocolVersion < 5 )
{
buf.writeByte( difficulty ); buf.writeByte( difficulty );
buf.writeBoolean( showCape ); }
buf.writeByte( showCape );
} }
@Override @Override

View File

@ -7,6 +7,7 @@ import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import net.md_5.bungee.protocol.AbstractPacketHandler; import net.md_5.bungee.protocol.AbstractPacketHandler;
import net.md_5.bungee.protocol.Protocol;
@Data @Data
@NoArgsConstructor @NoArgsConstructor
@ -24,28 +25,40 @@ public class ScoreboardScore extends DefinedPacket
private int value; private int value;
@Override @Override
public void read(ByteBuf buf) public void read(ByteBuf buf, Protocol.ProtocolDirection direction, int protocolVersion)
{ {
itemName = readString( buf ); itemName = readString( buf );
action = buf.readByte(); action = buf.readByte();
if ( action != 1 ) if ( action != 1 )
{ {
scoreName = readString( buf ); scoreName = readString( buf );
if ( protocolVersion >= 7 )
{
value = readVarInt( buf );
} else
{
value = buf.readInt(); value = buf.readInt();
} }
} }
}
@Override @Override
public void write(ByteBuf buf) public void write(ByteBuf buf, Protocol.ProtocolDirection direction, int protocolVersion)
{ {
writeString( itemName, buf ); writeString( itemName, buf );
buf.writeByte( action ); buf.writeByte( action );
if ( action != 1 ) if ( action != 1 )
{ {
writeString( scoreName, buf ); writeString( scoreName, buf );
if ( protocolVersion >= 7 )
{
writeVarInt( value, buf );
} else
{
buf.writeInt( value ); buf.writeInt( value );
} }
} }
}
@Override @Override
public void handle(AbstractPacketHandler handler) throws Exception public void handle(AbstractPacketHandler handler) throws Exception

View File

@ -7,6 +7,7 @@ import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import net.md_5.bungee.protocol.AbstractPacketHandler; import net.md_5.bungee.protocol.AbstractPacketHandler;
import net.md_5.bungee.protocol.Protocol;
@Data @Data
@NoArgsConstructor @NoArgsConstructor
@ -40,7 +41,7 @@ public class Team extends DefinedPacket
} }
@Override @Override
public void read(ByteBuf buf) public void read(ByteBuf buf, Protocol.ProtocolDirection direction, int protocolVersion)
{ {
name = readString( buf ); name = readString( buf );
mode = buf.readByte(); mode = buf.readByte();
@ -53,8 +54,9 @@ public class Team extends DefinedPacket
} }
if ( mode == 0 || mode == 3 || mode == 4 ) if ( mode == 0 || mode == 3 || mode == 4 )
{ {
players = new String[ buf.readShort() ]; int len = ( protocolVersion >= 7 ) ? readVarInt( buf ) : buf.readInt();
for ( int i = 0; i < getPlayers().length; i++ ) players = new String[ len ];
for ( int i = 0; i < len; i++ )
{ {
players[i] = readString( buf ); players[i] = readString( buf );
} }
@ -62,7 +64,7 @@ public class Team extends DefinedPacket
} }
@Override @Override
public void write(ByteBuf buf) public void write(ByteBuf buf, Protocol.ProtocolDirection direction, int protocolVersion)
{ {
writeString( name, buf ); writeString( name, buf );
buf.writeByte( mode ); buf.writeByte( mode );
@ -75,10 +77,16 @@ public class Team extends DefinedPacket
} }
if ( mode == 0 || mode == 3 || mode == 4 ) if ( mode == 0 || mode == 3 || mode == 4 )
{ {
buf.writeShort( players.length ); if ( protocolVersion >= 7 )
for ( int i = 0; i < players.length; i++ )
{ {
writeString( players[i], buf ); writeVarInt( players.length, buf );
} else
{
buf.writeShort( players.length );
}
for ( String player : players )
{
writeString( player, buf );
} }
} }
} }

View File

@ -469,13 +469,13 @@ public class BungeeCord extends ProxyServer
@Override @Override
public int getProtocolVersion() public int getProtocolVersion()
{ {
return Protocol.PROTOCOL_VERSION; return Protocol.supportedVersions.get( Protocol.supportedVersions.size() - 1 );
} }
@Override @Override
public String getGameVersion() public String getGameVersion()
{ {
return Protocol.MINECRAFT_VERSION; return "1.7.4";
} }
@Override @Override

View File

@ -216,8 +216,8 @@ public final class UserConnection implements ProxiedPlayer
protected void initChannel(Channel ch) throws Exception protected void initChannel(Channel ch) throws Exception
{ {
PipelineUtils.BASE.initChannel( ch ); PipelineUtils.BASE.initChannel( ch );
ch.pipeline().addAfter( PipelineUtils.FRAME_DECODER, PipelineUtils.PACKET_DECODER, new MinecraftDecoder( Protocol.HANDSHAKE, false ) ); ch.pipeline().addAfter( PipelineUtils.FRAME_DECODER, PipelineUtils.PACKET_DECODER, new MinecraftDecoder( Protocol.HANDSHAKE, false, getPendingConnection().getVersion() ) );
ch.pipeline().addAfter( PipelineUtils.FRAME_PREPENDER, PipelineUtils.PACKET_ENCODER, new MinecraftEncoder( Protocol.HANDSHAKE, false ) ); ch.pipeline().addAfter( PipelineUtils.FRAME_PREPENDER, PipelineUtils.PACKET_ENCODER, new MinecraftEncoder( Protocol.HANDSHAKE, false, getPendingConnection().getVersion() ) );
ch.pipeline().get( HandlerBoss.class ).setHandler( new ServerConnector( bungee, UserConnection.this, target ) ); ch.pipeline().get( HandlerBoss.class ).setHandler( new ServerConnector( bungee, UserConnection.this, target ) );
} }
}; };

View File

@ -175,8 +175,9 @@ public class InitialHandler extends PacketHandler implements PendingConnection
forced.ping( pingBack ); forced.ping( pingBack );
} else } else
{ {
int protocol = ( Protocol.supportedVersions.contains( handshake.getProtocolVersion() ) ) ? handshake.getProtocolVersion() : -1;
pingBack.done( new ServerPing( pingBack.done( new ServerPing(
new ServerPing.Protocol( bungee.getGameVersion(), bungee.getProtocolVersion() ), new ServerPing.Protocol( bungee.getGameVersion(), protocol ),
new ServerPing.Players( listener.getMaxPlayers(), bungee.getOnlineCount(), null ), new ServerPing.Players( listener.getMaxPlayers(), bungee.getOnlineCount(), null ),
motd, BungeeCord.getInstance().config.favicon ), motd, BungeeCord.getInstance().config.favicon ),
null ); null );
@ -198,6 +199,7 @@ public class InitialHandler extends PacketHandler implements PendingConnection
{ {
Preconditions.checkState( thisState == State.HANDSHAKE, "Not expecting HANDSHAKE" ); Preconditions.checkState( thisState == State.HANDSHAKE, "Not expecting HANDSHAKE" );
this.handshake = handshake; this.handshake = handshake;
ch.setVersion( handshake.getProtocolVersion() );
// SRV records can end with a . depending on DNS / client. // SRV records can end with a . depending on DNS / client.
if ( handshake.getHost().endsWith( "." ) ) if ( handshake.getHost().endsWith( "." ) )
@ -233,14 +235,10 @@ public class InitialHandler extends PacketHandler implements PendingConnection
Preconditions.checkState( thisState == State.USERNAME, "Not expecting USERNAME" ); Preconditions.checkState( thisState == State.USERNAME, "Not expecting USERNAME" );
this.loginRequest = loginRequest; this.loginRequest = loginRequest;
if ( handshake.getProtocolVersion() > bungee.getProtocolVersion() ) if ( !Protocol.supportedVersions.contains( handshake.getProtocolVersion() ) )
{ {
disconnect( bungee.getTranslation( "outdated_server" ) ); disconnect( bungee.getTranslation( "outdated_server" ) );
return; return;
} else if ( handshake.getProtocolVersion() < bungee.getProtocolVersion() )
{
disconnect( bungee.getTranslation( "outdated_client" ) );
return;
} }
if ( getName().length() > 16 ) if ( getName().length() > 16 )

View File

@ -3,6 +3,7 @@ package net.md_5.bungee.connection;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import net.md_5.bungee.BungeeCord; import net.md_5.bungee.BungeeCord;
import net.md_5.bungee.api.Callback; import net.md_5.bungee.api.Callback;
import net.md_5.bungee.api.ProxyServer;
import net.md_5.bungee.api.ServerPing; import net.md_5.bungee.api.ServerPing;
import net.md_5.bungee.api.config.ServerInfo; import net.md_5.bungee.api.config.ServerInfo;
import net.md_5.bungee.netty.ChannelWrapper; import net.md_5.bungee.netty.ChannelWrapper;
@ -27,12 +28,12 @@ public class PingHandler extends PacketHandler
public void connected(ChannelWrapper channel) throws Exception public void connected(ChannelWrapper channel) throws Exception
{ {
this.channel = channel; this.channel = channel;
MinecraftEncoder encoder = new MinecraftEncoder( Protocol.HANDSHAKE, false ); MinecraftEncoder encoder = new MinecraftEncoder( Protocol.HANDSHAKE, false, ProxyServer.getInstance().getProtocolVersion() );
channel.getHandle().pipeline().addAfter( PipelineUtils.FRAME_DECODER, PipelineUtils.PACKET_DECODER, new MinecraftDecoder( Protocol.STATUS, false ) ); channel.getHandle().pipeline().addAfter( PipelineUtils.FRAME_DECODER, PipelineUtils.PACKET_DECODER, new MinecraftDecoder( Protocol.STATUS, false, ProxyServer.getInstance().getProtocolVersion() ) );
channel.getHandle().pipeline().addAfter( PipelineUtils.FRAME_PREPENDER, PipelineUtils.PACKET_ENCODER, encoder ); channel.getHandle().pipeline().addAfter( PipelineUtils.FRAME_PREPENDER, PipelineUtils.PACKET_ENCODER, encoder );
channel.write( new Handshake( Protocol.PROTOCOL_VERSION, target.getAddress().getHostString(), target.getAddress().getPort(), 1 ) ); channel.write( new Handshake( ProxyServer.getInstance().getProtocolVersion(), target.getAddress().getHostString(), target.getAddress().getPort(), 1 ) );
encoder.setProtocol( Protocol.STATUS ); encoder.setProtocol( Protocol.STATUS );
channel.write( new StatusRequest() ); channel.write( new StatusRequest() );

View File

@ -28,6 +28,12 @@ public class ChannelWrapper
ch.pipeline().get( MinecraftEncoder.class ).setProtocol( protocol ); ch.pipeline().get( MinecraftEncoder.class ).setProtocol( protocol );
} }
public void setVersion(int protocol)
{
ch.pipeline().get( MinecraftDecoder.class ).setProtocolVersion( protocol );
ch.pipeline().get( MinecraftEncoder.class ).setProtocolVersion( protocol );
}
public synchronized void write(Object packet) public synchronized void write(Object packet)
{ {
if ( !closed ) if ( !closed )

View File

@ -43,8 +43,8 @@ public class PipelineUtils
BASE.initChannel( ch ); BASE.initChannel( ch );
ch.pipeline().addBefore( FRAME_DECODER, LEGACY_DECODER, new LegacyDecoder() ); ch.pipeline().addBefore( FRAME_DECODER, LEGACY_DECODER, new LegacyDecoder() );
ch.pipeline().addAfter( FRAME_DECODER, PACKET_DECODER, new MinecraftDecoder( Protocol.HANDSHAKE, true ) ); ch.pipeline().addAfter( FRAME_DECODER, PACKET_DECODER, new MinecraftDecoder( Protocol.HANDSHAKE, true, ProxyServer.getInstance().getProtocolVersion() ) );
ch.pipeline().addAfter( FRAME_PREPENDER, PACKET_ENCODER, new MinecraftEncoder( Protocol.HANDSHAKE, true ) ); ch.pipeline().addAfter( FRAME_PREPENDER, PACKET_ENCODER, new MinecraftEncoder( Protocol.HANDSHAKE, true, ProxyServer.getInstance().getProtocolVersion() ) );
ch.pipeline().addBefore( FRAME_PREPENDER, LEGACY_KICKER, new KickStringWriter() ); ch.pipeline().addBefore( FRAME_PREPENDER, LEGACY_KICKER, new KickStringWriter() );
ch.pipeline().get( HandlerBoss.class ).setHandler( new InitialHandler( ProxyServer.getInstance(), ch.attr( LISTENER ).get() ) ); ch.pipeline().get( HandlerBoss.class ).setHandler( new InitialHandler( ProxyServer.getInstance(), ch.attr( LISTENER ).get() ) );
} }