From bc76609524f570537f4ad3b05e6f03b9063134e5 Mon Sep 17 00:00:00 2001 From: Troy Frew Date: Tue, 15 Nov 2016 10:31:04 -0500 Subject: [PATCH] 1.7.x Protocol Patch diff --git a/protocol/src/main/java/io/github/waterfallmc/travertine/protocol/MultiVersionPacketV17.java b/protocol/src/main/java/io/github/waterfallmc/travertine/protocol/MultiVersionPacketV17.java new file mode 100644 index 00000000..90064112 --- /dev/null +++ b/protocol/src/main/java/io/github/waterfallmc/travertine/protocol/MultiVersionPacketV17.java @@ -0,0 +1,90 @@ +package io.github.waterfallmc.travertine.protocol; + +import com.google.common.base.Preconditions; +import io.netty.buffer.ByteBuf; + +import net.md_5.bungee.protocol.DefinedPacket; +import net.md_5.bungee.protocol.ProtocolConstants; + +public abstract class MultiVersionPacketV17 extends DefinedPacket +{ + + protected void v17Read(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) + { + v17Read( buf ); + } + + @Override + public void read0(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) + { + switch ( protocolVersion ) + { + case ProtocolConstants.MINECRAFT_1_7_2: + case ProtocolConstants.MINECRAFT_1_7_6: + v17Read(buf, direction, protocolVersion); + break; + default: + read(buf, direction, protocolVersion); + break; + } + } + + protected void v17Write(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) + { + v17Write( buf ); + } + + @Override + public void write0(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) + { + switch ( protocolVersion ) + { + case ProtocolConstants.MINECRAFT_1_7_2: + case ProtocolConstants.MINECRAFT_1_7_6: + v17Write(buf, direction, protocolVersion); + break; + default: + write(buf, direction, protocolVersion); + break; + } + } + protected void v17Read(ByteBuf buf) + { + throw new UnsupportedOperationException( "Packet must implement read method" ); + } + + protected void v17Write(ByteBuf buf) + { + throw new UnsupportedOperationException( "Packet must implement write method" ); + } + + public static void v17writeArray(byte[] b, ByteBuf buf, boolean allowExtended) + { + // (Integer.MAX_VALUE & 0x1FFF9A ) = 2097050 - Forge's current upper limit + if ( allowExtended ) + { + Preconditions.checkArgument( b.length <= ( Integer.MAX_VALUE & 0x1FFF9A ), "Cannot send array longer than 2097050 (got %s bytes)", b.length ); + } else + { + Preconditions.checkArgument( b.length <= Short.MAX_VALUE, "Cannot send array longer than Short.MAX_VALUE (got %s bytes)", b.length ); + } + // Write a 2 or 3 byte number that represents the length of the packet. (3 byte "shorts" for Forge only) + // No vanilla packet should give a 3 byte packet, this method will still retain vanilla behaviour. + writeVarShort( buf, b.length ); + buf.writeBytes( b ); + } + + public static byte[] v17readArray(ByteBuf buf) + { + // Read in a 2 or 3 byte number that represents the length of the packet. (3 byte "shorts" for Forge only) + // No vanilla packet should give a 3 byte packet, this method will still retain vanilla behaviour. + int len = readVarShort( buf ); + + // (Integer.MAX_VALUE & 0x1FFF9A ) = 2097050 - Forge's current upper limit + Preconditions.checkArgument( len <= ( Integer.MAX_VALUE & 0x1FFF9A ), "Cannot receive array longer than 2097050 (got %s bytes)", len ); + + byte[] ret = new byte[ len ]; + buf.readBytes( ret ); + return ret; + } +} diff --git a/protocol/src/main/java/net/md_5/bungee/protocol/DefinedPacket.java b/protocol/src/main/java/net/md_5/bungee/protocol/DefinedPacket.java index 10e16d79..04a34033 100644 --- a/protocol/src/main/java/net/md_5/bungee/protocol/DefinedPacket.java +++ b/protocol/src/main/java/net/md_5/bungee/protocol/DefinedPacket.java @@ -193,6 +193,11 @@ public abstract class DefinedPacket read( buf ); } + public void read0(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) + { + read( buf, direction, protocolVersion ); + } + public void write(ByteBuf buf) { throw new UnsupportedOperationException( "Packet must implement write method" ); @@ -203,6 +208,11 @@ public abstract class DefinedPacket write( buf ); } + public void write0(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) + { + write( buf, direction, protocolVersion ); + } + public abstract void handle(AbstractPacketHandler handler) throws Exception; @Override diff --git a/protocol/src/main/java/net/md_5/bungee/protocol/MinecraftDecoder.java b/protocol/src/main/java/net/md_5/bungee/protocol/MinecraftDecoder.java index 0b780e2e..a38d8019 100644 --- a/protocol/src/main/java/net/md_5/bungee/protocol/MinecraftDecoder.java +++ b/protocol/src/main/java/net/md_5/bungee/protocol/MinecraftDecoder.java @@ -5,10 +5,11 @@ import io.netty.buffer.ByteBufUtil; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.DecoderException; import io.netty.handler.codec.MessageToMessageDecoder; -import java.util.List; import lombok.AllArgsConstructor; import lombok.Setter; +import java.util.List; + @AllArgsConstructor public class MinecraftDecoder extends MessageToMessageDecoder { @@ -43,7 +44,7 @@ public class MinecraftDecoder extends MessageToMessageDecoder if ( packet != null ) { packetTypeInfo = packet.getClass(); - packet.read( in, prot.getDirection(), protocolVersion ); + packet.read0( in, prot.getDirection(), protocolVersion ); if ( in.isReadable() ) { diff --git a/protocol/src/main/java/net/md_5/bungee/protocol/MinecraftEncoder.java b/protocol/src/main/java/net/md_5/bungee/protocol/MinecraftEncoder.java index d4b03843..9aac7ca9 100644 --- a/protocol/src/main/java/net/md_5/bungee/protocol/MinecraftEncoder.java +++ b/protocol/src/main/java/net/md_5/bungee/protocol/MinecraftEncoder.java @@ -21,6 +21,6 @@ public class MinecraftEncoder extends MessageToByteEncoder { Protocol.DirectionData prot = ( server ) ? protocol.TO_CLIENT : protocol.TO_SERVER; DefinedPacket.writeVarInt( prot.getId( msg.getClass(), protocolVersion ), out ); - msg.write( out, prot.getDirection(), protocolVersion ); + msg.write0( out, prot.getDirection(), protocolVersion ); } } diff --git a/protocol/src/main/java/net/md_5/bungee/protocol/Protocol.java b/protocol/src/main/java/net/md_5/bungee/protocol/Protocol.java index fb2fea87..795896fa 100644 --- a/protocol/src/main/java/net/md_5/bungee/protocol/Protocol.java +++ b/protocol/src/main/java/net/md_5/bungee/protocol/Protocol.java @@ -341,6 +341,8 @@ public enum Protocol private final TIntObjectMap> linkedProtocols = new TIntObjectHashMap<>(); { linkedProtocols.put( ProtocolConstants.MINECRAFT_1_8, Arrays.asList( + ProtocolConstants.MINECRAFT_1_7_2, + ProtocolConstants.MINECRAFT_1_7_6, ProtocolConstants.MINECRAFT_1_9, ProtocolConstants.MINECRAFT_1_12 ) ); @@ -393,7 +395,11 @@ public enum Protocol } if ( !hasPacket(id, supportsForge) ) { - throw new BadPacketException( "Packet with id " + id + " outside of range " ); + if ( ProtocolConstants.isBeforeOrEq( version, ProtocolConstants.MINECRAFT_1_7_6 ) ) { + return null; + } else { + throw new BadPacketException( "Packet with id " + id + " outside of range " ); + } } Constructor constructor = protocolData.packetConstructors[id]; diff --git a/protocol/src/main/java/net/md_5/bungee/protocol/Protocol.java.orig b/protocol/src/main/java/net/md_5/bungee/protocol/Protocol.java.orig new file mode 100644 index 00000000..2aa68858 --- /dev/null +++ b/protocol/src/main/java/net/md_5/bungee/protocol/Protocol.java.orig @@ -0,0 +1,375 @@ +package net.md_5.bungee.protocol; + +import com.google.common.base.Preconditions; +import com.google.common.collect.Iterables; +import gnu.trove.map.TIntObjectMap; +import gnu.trove.map.TObjectIntMap; +import gnu.trove.map.hash.TIntObjectHashMap; +import gnu.trove.map.hash.TObjectIntHashMap; +import java.lang.reflect.Constructor; +import java.util.Arrays; +import java.util.List; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import net.md_5.bungee.protocol.packet.BossBar; +import net.md_5.bungee.protocol.packet.Chat; +import net.md_5.bungee.protocol.packet.ClientSettings; +import net.md_5.bungee.protocol.packet.EncryptionRequest; +import net.md_5.bungee.protocol.packet.EncryptionResponse; +import net.md_5.bungee.protocol.packet.EntityEffect; +import net.md_5.bungee.protocol.packet.EntityRemoveEffect; +import net.md_5.bungee.protocol.packet.Handshake; +import net.md_5.bungee.protocol.packet.KeepAlive; +import net.md_5.bungee.protocol.packet.Kick; +import net.md_5.bungee.protocol.packet.Login; +import net.md_5.bungee.protocol.packet.LoginRequest; +import net.md_5.bungee.protocol.packet.LoginSuccess; +import net.md_5.bungee.protocol.packet.PingPacket; +import net.md_5.bungee.protocol.packet.PlayerListHeaderFooter; +import net.md_5.bungee.protocol.packet.PlayerListItem; +import net.md_5.bungee.protocol.packet.PluginMessage; +import net.md_5.bungee.protocol.packet.Respawn; +import net.md_5.bungee.protocol.packet.ScoreboardDisplay; +import net.md_5.bungee.protocol.packet.ScoreboardObjective; +import net.md_5.bungee.protocol.packet.ScoreboardScore; +import net.md_5.bungee.protocol.packet.SetCompression; +import net.md_5.bungee.protocol.packet.StatusRequest; +import net.md_5.bungee.protocol.packet.StatusResponse; +import net.md_5.bungee.protocol.packet.TabCompleteRequest; +import net.md_5.bungee.protocol.packet.TabCompleteResponse; +import net.md_5.bungee.protocol.packet.Team; +import net.md_5.bungee.protocol.packet.Title; + +public enum Protocol +{ + + // Undef + HANDSHAKE + { + + { + TO_SERVER.registerPacket( + Handshake.class, + map( ProtocolConstants.MINECRAFT_1_8, 0x00 ) + ); + } + }, + // 0 + GAME + { + + { + TO_CLIENT.registerPacket( + KeepAlive.class, + map( ProtocolConstants.MINECRAFT_1_8, 0x00 ), + map( ProtocolConstants.MINECRAFT_1_9, 0x1F ) + ); + TO_CLIENT.registerPacket( + Login.class, + map( ProtocolConstants.MINECRAFT_1_8, 0x01 ), + map( ProtocolConstants.MINECRAFT_1_9, 0x23 ) + ); + TO_CLIENT.registerPacket( + Chat.class, + map( ProtocolConstants.MINECRAFT_1_8, 0x02 ), + map( ProtocolConstants.MINECRAFT_1_9, 0x0F ) + ); + TO_CLIENT.registerPacket( + Respawn.class, + map( ProtocolConstants.MINECRAFT_1_8, 0x07 ), + map( ProtocolConstants.MINECRAFT_1_9, 0x33 ) + ); + TO_CLIENT.registerPacket( + BossBar.class, + map( ProtocolConstants.MINECRAFT_1_9, 0x0C ) + ); + // Waterfall start + TO_CLIENT.registerPacket( + EntityEffect.class, + map(ProtocolConstants.MINECRAFT_1_8, 0x1D), + map(ProtocolConstants.MINECRAFT_1_9, 0x4C), + map(ProtocolConstants.MINECRAFT_1_9_4, 0x4B), + map(ProtocolConstants.MINECRAFT_1_10, 0x4B) + + ); + TO_CLIENT.registerPacket( + EntityRemoveEffect.class, + map(ProtocolConstants.MINECRAFT_1_8, 0x1E), + map(ProtocolConstants.MINECRAFT_1_9, 0x31) + ); + // Waterfall end + TO_CLIENT.registerPacket( + PlayerListItem.class, // PlayerInfo + map( ProtocolConstants.MINECRAFT_1_8, 0x38 ), + map( ProtocolConstants.MINECRAFT_1_9, 0x2D ) + ); + TO_CLIENT.registerPacket( + TabCompleteResponse.class, + map( ProtocolConstants.MINECRAFT_1_8, 0x3A ), + map( ProtocolConstants.MINECRAFT_1_9, 0x0E ) + ); + TO_CLIENT.registerPacket( + ScoreboardObjective.class, + map( ProtocolConstants.MINECRAFT_1_8, 0x3B ), + map( ProtocolConstants.MINECRAFT_1_9, 0x3F ) + ); + TO_CLIENT.registerPacket( + ScoreboardScore.class, + map( ProtocolConstants.MINECRAFT_1_8, 0x3C ), + map( ProtocolConstants.MINECRAFT_1_9, 0x42 ) + ); + TO_CLIENT.registerPacket( + ScoreboardDisplay.class, + map( ProtocolConstants.MINECRAFT_1_8, 0x3D ), + map( ProtocolConstants.MINECRAFT_1_9, 0x38 ) + ); + TO_CLIENT.registerPacket( + Team.class, + map( ProtocolConstants.MINECRAFT_1_8, 0x3E ), + map( ProtocolConstants.MINECRAFT_1_9, 0x41 ) + ); + TO_CLIENT.registerPacket( + PluginMessage.class, + map( ProtocolConstants.MINECRAFT_1_8, 0x3F ), + map( ProtocolConstants.MINECRAFT_1_9, 0x18 ) + ); + TO_CLIENT.registerPacket( + Kick.class, + map( ProtocolConstants.MINECRAFT_1_8, 0x40 ), + map( ProtocolConstants.MINECRAFT_1_9, 0x1A ) + ); + TO_CLIENT.registerPacket( + Title.class, + map( ProtocolConstants.MINECRAFT_1_8, 0x45 ) + ); + TO_CLIENT.registerPacket( + PlayerListHeaderFooter.class, + map( ProtocolConstants.MINECRAFT_1_8, 0x47 ), + map( ProtocolConstants.MINECRAFT_1_9, 0x48 ), + map( ProtocolConstants.MINECRAFT_1_9_4, 0x47 ), + map( ProtocolConstants.MINECRAFT_1_10, 0x47 ), + map( ProtocolConstants.MINECRAFT_1_11, 0x47 ), + map( ProtocolConstants.MINECRAFT_1_11_1, 0x47 ) + ); + + TO_SERVER.registerPacket( + KeepAlive.class, + map( ProtocolConstants.MINECRAFT_1_8, 0x00 ), + map( ProtocolConstants.MINECRAFT_1_9, 0x0B ) + ); + TO_SERVER.registerPacket( + Chat.class, + map( ProtocolConstants.MINECRAFT_1_8, 0x01 ), + map( ProtocolConstants.MINECRAFT_1_9, 0x02 ) + ); + TO_SERVER.registerPacket( + TabCompleteRequest.class, + map( ProtocolConstants.MINECRAFT_1_8, 0x14 ), + map( ProtocolConstants.MINECRAFT_1_9, 0x01 ) + ); + TO_SERVER.registerPacket( + ClientSettings.class, + map( ProtocolConstants.MINECRAFT_1_8, 0x15 ), + map( ProtocolConstants.MINECRAFT_1_9, 0x04 ) + ); + TO_SERVER.registerPacket( + PluginMessage.class, + map( ProtocolConstants.MINECRAFT_1_8, 0x17 ), + map( ProtocolConstants.MINECRAFT_1_9, 0x09 ) + ); + } + }, + // 1 + STATUS + { + + { + TO_CLIENT.registerPacket( + StatusResponse.class, + map( ProtocolConstants.MINECRAFT_1_8, 0x00 ) + ); + TO_CLIENT.registerPacket( + PingPacket.class, + map( ProtocolConstants.MINECRAFT_1_8, 0x01 ) + ); + + TO_SERVER.registerPacket( + StatusRequest.class, + map( ProtocolConstants.MINECRAFT_1_8, 0x00 ) + ); + TO_SERVER.registerPacket( + PingPacket.class, + map( ProtocolConstants.MINECRAFT_1_8, 0x01 ) + ); + } + }, + //2 + LOGIN + { + + { + TO_CLIENT.registerPacket( + Kick.class, + map( ProtocolConstants.MINECRAFT_1_8, 0x00 ) + ); + TO_CLIENT.registerPacket( + EncryptionRequest.class, + map( ProtocolConstants.MINECRAFT_1_8, 0x01 ) + ); + TO_CLIENT.registerPacket( + LoginSuccess.class, + map( ProtocolConstants.MINECRAFT_1_8, 0x02 ) + ); + TO_CLIENT.registerPacket( + SetCompression.class, + map( ProtocolConstants.MINECRAFT_1_8, 0x03 ) + ); + + TO_SERVER.registerPacket( + LoginRequest.class, + map( ProtocolConstants.MINECRAFT_1_8, 0x00 ) + ); + TO_SERVER.registerPacket( + EncryptionResponse.class, + map( ProtocolConstants.MINECRAFT_1_8, 0x01 ) + ); + } + }; + /*========================================================================*/ + public static final int MAX_PACKET_ID = 0xFF; + /*========================================================================*/ + public final DirectionData TO_SERVER = new DirectionData(this, ProtocolConstants.Direction.TO_SERVER ); + public final DirectionData TO_CLIENT = new DirectionData(this, ProtocolConstants.Direction.TO_CLIENT ); + + @RequiredArgsConstructor + private static class ProtocolData { + + private final int protocolVersion; + private final TObjectIntMap> packetMap = new TObjectIntHashMap<>( MAX_PACKET_ID ); + private final TIntObjectMap> packetConstructors = new TIntObjectHashMap<>( MAX_PACKET_ID ); + } + + @RequiredArgsConstructor + private static class ProtocolMapping { + private final int protocolVersion; + private final int packetID; + } + // Helper method + private static ProtocolMapping map(int protocol, int id) { + return new ProtocolMapping(protocol, id); + } + + @RequiredArgsConstructor + public static class DirectionData + { + + private final Protocol protocolPhase; + private final TIntObjectMap protocols = new TIntObjectHashMap<>(); + { + for ( int protocol : ProtocolConstants.SUPPORTED_VERSION_IDS ) + { + protocols.put( protocol, new ProtocolData( protocol ) ); + } + } + private final TIntObjectMap> linkedProtocols = new TIntObjectHashMap<>(); + { + linkedProtocols.put( ProtocolConstants.MINECRAFT_1_8, Arrays.asList( + ProtocolConstants.MINECRAFT_1_9 + ) ); + linkedProtocols.put( ProtocolConstants.MINECRAFT_1_9, Arrays.asList( + ProtocolConstants.MINECRAFT_1_9_1, + ProtocolConstants.MINECRAFT_1_9_2, + ProtocolConstants.MINECRAFT_1_9_4, + ProtocolConstants.MINECRAFT_1_10, + ProtocolConstants.MINECRAFT_1_11, + ProtocolConstants.MINECRAFT_1_11_1 + ) ); + } + + @Getter + private final ProtocolConstants.Direction direction; + + private ProtocolData getProtocolData(int version) + { + ProtocolData protocol = protocols.get( version ); + if ( protocol == null && ( protocolPhase != Protocol.GAME ) ) + { + protocol = Iterables.getFirst( protocols.valueCollection(), null ); + } + return protocol; + } + + public boolean hasPacket(int i, boolean supportsForge) { + return supportsForge || i >= 0 && i <= MAX_PACKET_ID; + } + + public final DefinedPacket createPacket(int id, int version, boolean supportsForge) + { + ProtocolData protocolData = getProtocolData( version ); + if (protocolData == null) + { + throw new BadPacketException( "Unsupported protocol version" ); + } + if ( !hasPacket(id, supportsForge) ) + { + throw new BadPacketException( "Packet with id " + id + " outside of range " ); + } + + Constructor constructor = protocolData.packetConstructors.get( id ); + try + { + return ( constructor == null ) ? null : constructor.newInstance(); + } catch ( ReflectiveOperationException ex ) + { + throw new BadPacketException( "Could not construct packet with id " + id, ex ); + } + } + + protected final void registerPacket(Class packetClass, ProtocolMapping ...mappings) + { + try + { + Constructor constructor = packetClass.getDeclaredConstructor(); + for ( ProtocolMapping mapping : mappings ) + { + ProtocolData data = protocols.get( mapping.protocolVersion ); + data.packetMap.put( packetClass, mapping.packetID ); + data.packetConstructors.put( mapping.packetID, constructor ); + + List links = linkedProtocols.get( mapping.protocolVersion ); + if ( links != null ) + { + links: for ( int link : links ) + { + // Check for manual mappings + for ( ProtocolMapping m : mappings ) + { + if ( m == mapping ) continue; + if ( m.protocolVersion == link ) continue links; + List innerLinks = linkedProtocols.get( m.protocolVersion ); + if ( innerLinks != null && innerLinks.contains( link ) ) continue links; + } + registerPacket( packetClass, map( link, mapping.packetID ) ); + } + } + } + } catch ( NoSuchMethodException ex ) + { + throw new BadPacketException( "No NoArgsConstructor for packet class " + packetClass ); + } + } + + final int getId(Class packet, int version) + { + + ProtocolData protocolData = getProtocolData( version ); + if (protocolData == null) + { + throw new BadPacketException( "Unsupported protocol version" ); + } + Preconditions.checkArgument( protocolData.packetMap.containsKey( packet ), "Cannot get ID for packet %s in state %s", packet, protocolPhase); + + return protocolData.packetMap.get( packet ); + } + } +} diff --git a/protocol/src/main/java/net/md_5/bungee/protocol/ProtocolConstants.java b/protocol/src/main/java/net/md_5/bungee/protocol/ProtocolConstants.java index a9398eef..aa801b87 100644 --- a/protocol/src/main/java/net/md_5/bungee/protocol/ProtocolConstants.java +++ b/protocol/src/main/java/net/md_5/bungee/protocol/ProtocolConstants.java @@ -6,6 +6,8 @@ import java.util.List; public class ProtocolConstants { + public static final int MINECRAFT_1_7_2 = 4; + public static final int MINECRAFT_1_7_6 = 5; public static final int MINECRAFT_1_8 = 47; public static final int MINECRAFT_1_9 = 107; public static final int MINECRAFT_1_9_1 = 108; @@ -18,6 +20,7 @@ public class ProtocolConstants public static final int MINECRAFT_1_12_1 = 338; public static final int MINECRAFT_1_12_2 = 340; public static final List SUPPORTED_VERSIONS = Arrays.asList( + "1.7.x", "1.8.x", "1.9.x", "1.10.x", @@ -25,6 +28,8 @@ public class ProtocolConstants "1.12.x" ); public static final List SUPPORTED_VERSION_IDS = Arrays.asList( + ProtocolConstants.MINECRAFT_1_7_2, + ProtocolConstants.MINECRAFT_1_7_6, ProtocolConstants.MINECRAFT_1_8, ProtocolConstants.MINECRAFT_1_9, ProtocolConstants.MINECRAFT_1_9_1, diff --git a/protocol/src/main/java/net/md_5/bungee/protocol/ProtocolConstants.java.orig b/protocol/src/main/java/net/md_5/bungee/protocol/ProtocolConstants.java.orig new file mode 100644 index 00000000..af80c95e --- /dev/null +++ b/protocol/src/main/java/net/md_5/bungee/protocol/ProtocolConstants.java.orig @@ -0,0 +1,49 @@ +package net.md_5.bungee.protocol; + +import java.util.Arrays; +import java.util.List; + +public class ProtocolConstants +{ + + public static final int MINECRAFT_1_8 = 47; + public static final int MINECRAFT_1_9 = 107; + public static final int MINECRAFT_1_9_1 = 108; + public static final int MINECRAFT_1_9_2 = 109; + public static final int MINECRAFT_1_9_4 = 110; + public static final int MINECRAFT_1_10 = 210; + public static final int MINECRAFT_1_11 = 315; + public static final int MINECRAFT_1_11_1 = 316; + public static final List SUPPORTED_VERSIONS = Arrays.asList( + "1.8.x", + "1.9.x", + "1.10.x", + "1.11.x" + ); + public static final List SUPPORTED_VERSION_IDS = Arrays.asList( + ProtocolConstants.MINECRAFT_1_8, + ProtocolConstants.MINECRAFT_1_9, + ProtocolConstants.MINECRAFT_1_9_1, + ProtocolConstants.MINECRAFT_1_9_2, + ProtocolConstants.MINECRAFT_1_9_4, + ProtocolConstants.MINECRAFT_1_10, + ProtocolConstants.MINECRAFT_1_11, + ProtocolConstants.MINECRAFT_1_11_1 + ); + + public static final boolean isBeforeOrEq(int before, int other) + { + return before <= other; + } + + public static final boolean isAfterOrEq(int after, int other) + { + return after >= other; + } + + public enum Direction + { + + TO_CLIENT, TO_SERVER; + } +} diff --git a/protocol/src/main/java/net/md_5/bungee/protocol/Varint21FrameDecoder.java b/protocol/src/main/java/net/md_5/bungee/protocol/Varint21FrameDecoder.java index 29e54dba..1f9e64dc 100644 --- a/protocol/src/main/java/net/md_5/bungee/protocol/Varint21FrameDecoder.java +++ b/protocol/src/main/java/net/md_5/bungee/protocol/Varint21FrameDecoder.java @@ -7,10 +7,12 @@ import io.netty.handler.codec.ByteToMessageDecoder; import io.netty.handler.codec.CorruptedFrameException; import java.util.List; +import java.util.concurrent.atomic.AtomicLong; public class Varint21FrameDecoder extends ByteToMessageDecoder { + private AtomicLong lastEmptyPacket = new AtomicLong(0); // Travertine private static boolean DIRECT_WARNING; @Override @@ -33,7 +35,15 @@ public class Varint21FrameDecoder extends ByteToMessageDecoder int length = DefinedPacket.readVarInt( Unpooled.wrappedBuffer( buf ) ); if ( length == 0 ) { - throw new CorruptedFrameException( "Empty Packet!" ); + // Travertine start - vanilla 1.7 client sometimes sends empty packets. + long currentTime = System.currentTimeMillis(); + long lastEmptyPacket = this.lastEmptyPacket.getAndSet(currentTime); + + if (currentTime - lastEmptyPacket < 50L) + { + throw new CorruptedFrameException( "Too many empty packets" ); + } + // Travertine end } if ( in.readableBytes() < length ) diff --git a/protocol/src/main/java/net/md_5/bungee/protocol/packet/Chat.java b/protocol/src/main/java/net/md_5/bungee/protocol/packet/Chat.java index 95ad39b7..1f78125a 100644 --- a/protocol/src/main/java/net/md_5/bungee/protocol/packet/Chat.java +++ b/protocol/src/main/java/net/md_5/bungee/protocol/packet/Chat.java @@ -1,6 +1,6 @@ package net.md_5.bungee.protocol.packet; -import net.md_5.bungee.protocol.DefinedPacket; +import io.github.waterfallmc.travertine.protocol.MultiVersionPacketV17; import io.netty.buffer.ByteBuf; import lombok.AllArgsConstructor; import lombok.Data; @@ -13,7 +13,7 @@ import net.md_5.bungee.protocol.ProtocolConstants; @NoArgsConstructor @AllArgsConstructor @EqualsAndHashCode(callSuper = false) -public class Chat extends DefinedPacket +public class Chat extends MultiVersionPacketV17 { private String message; @@ -24,6 +24,14 @@ public class Chat extends DefinedPacket this( message, (byte) 0 ); } + // Travertine start + @Override + public void v17Read(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) + { + message = readString( buf ); + } + // Travertine end + @Override public void read(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) { @@ -34,6 +42,14 @@ public class Chat extends DefinedPacket } } + // Travertine start + @Override + public void v17Write(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) + { + writeString( message, buf ); + } + // Travertine end + @Override public void write(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) { diff --git a/protocol/src/main/java/net/md_5/bungee/protocol/packet/ClientSettings.java b/protocol/src/main/java/net/md_5/bungee/protocol/packet/ClientSettings.java index 5c79727c..431b2eb8 100644 --- a/protocol/src/main/java/net/md_5/bungee/protocol/packet/ClientSettings.java +++ b/protocol/src/main/java/net/md_5/bungee/protocol/packet/ClientSettings.java @@ -1,5 +1,6 @@ package net.md_5.bungee.protocol.packet; +import io.github.waterfallmc.travertine.protocol.MultiVersionPacketV17; import net.md_5.bungee.protocol.DefinedPacket; import io.netty.buffer.ByteBuf; import lombok.AllArgsConstructor; @@ -13,7 +14,7 @@ import net.md_5.bungee.protocol.ProtocolConstants; @NoArgsConstructor @AllArgsConstructor @EqualsAndHashCode(callSuper = false) -public class ClientSettings extends DefinedPacket +public class ClientSettings extends MultiVersionPacketV17 { private String locale; @@ -24,6 +25,19 @@ public class ClientSettings extends DefinedPacket private byte skinParts; private int mainHand; + // Travertine start + @Override + public void v17Read(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) + { + locale = readString( buf ); + viewDistance = buf.readByte(); + chatFlags = buf.readUnsignedByte(); + chatColours = buf.readBoolean(); + skinParts = buf.readByte(); + difficulty = buf.readByte(); + } + // Travertine end + @Override public void read(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) { @@ -58,6 +72,19 @@ public class ClientSettings extends DefinedPacket } } + // Travertine start + @Override + public void v17Write(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) + { + writeString( locale, buf ); + buf.writeByte( viewDistance ); + buf.writeByte( chatFlags ); + buf.writeBoolean( chatColours ); + buf.writeByte( skinParts ); + buf.writeByte( difficulty ); + } + // Travertine end + @Override public void handle(AbstractPacketHandler handler) throws Exception { diff --git a/protocol/src/main/java/net/md_5/bungee/protocol/packet/EncryptionRequest.java b/protocol/src/main/java/net/md_5/bungee/protocol/packet/EncryptionRequest.java index a29524ca..8d9f4ccb 100644 --- a/protocol/src/main/java/net/md_5/bungee/protocol/packet/EncryptionRequest.java +++ b/protocol/src/main/java/net/md_5/bungee/protocol/packet/EncryptionRequest.java @@ -1,25 +1,35 @@ package net.md_5.bungee.protocol.packet; +import io.github.waterfallmc.travertine.protocol.MultiVersionPacketV17; import io.netty.buffer.ByteBuf; import lombok.AllArgsConstructor; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import net.md_5.bungee.protocol.AbstractPacketHandler; -import net.md_5.bungee.protocol.DefinedPacket; import net.md_5.bungee.protocol.ProtocolConstants; @Data @NoArgsConstructor @AllArgsConstructor @EqualsAndHashCode(callSuper = false) -public class EncryptionRequest extends DefinedPacket +public class EncryptionRequest extends MultiVersionPacketV17 { private String serverId; private byte[] publicKey; private byte[] verifyToken; + // Travertine start + @Override + public void v17Read(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) + { + serverId = readString( buf ); + publicKey = v17readArray( buf ); + verifyToken = v17readArray( buf ); + } + // Travertine end + @Override public void read(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) { @@ -28,6 +38,16 @@ public class EncryptionRequest extends DefinedPacket verifyToken = readArray( buf ); } + // Travertine start + @Override + public void v17Write(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) + { + writeString( serverId, buf ); + v17writeArray( publicKey, buf, false ); + v17writeArray( verifyToken, buf, false ); + } + // Travertine end + @Override public void write(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) { diff --git a/protocol/src/main/java/net/md_5/bungee/protocol/packet/EncryptionResponse.java b/protocol/src/main/java/net/md_5/bungee/protocol/packet/EncryptionResponse.java index 06676e42..19a85492 100644 --- a/protocol/src/main/java/net/md_5/bungee/protocol/packet/EncryptionResponse.java +++ b/protocol/src/main/java/net/md_5/bungee/protocol/packet/EncryptionResponse.java @@ -1,6 +1,6 @@ package net.md_5.bungee.protocol.packet; -import net.md_5.bungee.protocol.DefinedPacket; +import io.github.waterfallmc.travertine.protocol.MultiVersionPacketV17; import io.netty.buffer.ByteBuf; import lombok.AllArgsConstructor; import lombok.Data; @@ -13,12 +13,21 @@ import net.md_5.bungee.protocol.ProtocolConstants; @NoArgsConstructor @AllArgsConstructor @EqualsAndHashCode(callSuper = false) -public class EncryptionResponse extends DefinedPacket +public class EncryptionResponse extends MultiVersionPacketV17 { private byte[] sharedSecret; private byte[] verifyToken; + // Travertine start + @Override + public void v17Read(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) + { + sharedSecret = v17readArray( buf ); + verifyToken = v17readArray( buf ); + } + // Travertine end + @Override public void read(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) { @@ -26,6 +35,15 @@ public class EncryptionResponse extends DefinedPacket verifyToken = readArray( buf, 128 ); } + // Travertine start + @Override + public void v17Write(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) + { + v17writeArray( sharedSecret, buf, false ); + v17writeArray( verifyToken, buf, false ); + } + // Travertine end + @Override public void write(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) { diff --git a/protocol/src/main/java/net/md_5/bungee/protocol/packet/EntityEffect.java b/protocol/src/main/java/net/md_5/bungee/protocol/packet/EntityEffect.java index d11a9ea9..07fc21b6 100644 --- a/protocol/src/main/java/net/md_5/bungee/protocol/packet/EntityEffect.java +++ b/protocol/src/main/java/net/md_5/bungee/protocol/packet/EntityEffect.java @@ -1,18 +1,19 @@ package net.md_5.bungee.protocol.packet; +import io.github.waterfallmc.travertine.protocol.MultiVersionPacketV17; import io.netty.buffer.ByteBuf; import lombok.AllArgsConstructor; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import net.md_5.bungee.protocol.AbstractPacketHandler; -import net.md_5.bungee.protocol.DefinedPacket; +import net.md_5.bungee.protocol.ProtocolConstants; @Data @NoArgsConstructor @AllArgsConstructor @EqualsAndHashCode(callSuper = false) -public class EntityEffect extends DefinedPacket { +public class EntityEffect extends MultiVersionPacketV17 { private int entityId; private int effectId; @@ -20,6 +21,14 @@ public class EntityEffect extends DefinedPacket { private int duration; private boolean hideParticles; + @Override + protected void v17Read(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) { + this.entityId = buf.readInt(); + this.effectId = buf.readUnsignedByte(); + this.amplifier = buf.readUnsignedByte(); + this.duration = buf.readShort(); + } + @Override public void read(ByteBuf buf) { this.entityId = readVarInt(buf); @@ -29,6 +38,14 @@ public class EntityEffect extends DefinedPacket { this.hideParticles = buf.readBoolean(); } + @Override + protected void v17Write(ByteBuf buf) { + buf.writeInt(effectId); + buf.writeByte(effectId); + buf.writeByte(amplifier); + buf.writeShort(duration); + } + @Override public void write(ByteBuf buf) { writeVarInt(this.entityId, buf); diff --git a/protocol/src/main/java/net/md_5/bungee/protocol/packet/EntityRemoveEffect.java b/protocol/src/main/java/net/md_5/bungee/protocol/packet/EntityRemoveEffect.java index 7ed2dc3a..9f8d56fc 100644 --- a/protocol/src/main/java/net/md_5/bungee/protocol/packet/EntityRemoveEffect.java +++ b/protocol/src/main/java/net/md_5/bungee/protocol/packet/EntityRemoveEffect.java @@ -1,18 +1,18 @@ package net.md_5.bungee.protocol.packet; +import io.github.waterfallmc.travertine.protocol.MultiVersionPacketV17; import io.netty.buffer.ByteBuf; import lombok.AllArgsConstructor; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import net.md_5.bungee.protocol.AbstractPacketHandler; -import net.md_5.bungee.protocol.DefinedPacket; @Data @NoArgsConstructor @AllArgsConstructor @EqualsAndHashCode(callSuper = false) -public class EntityRemoveEffect extends DefinedPacket { +public class EntityRemoveEffect extends MultiVersionPacketV17 { private int entityId; private int effectId; @@ -23,9 +23,15 @@ public class EntityRemoveEffect extends DefinedPacket { this.effectId = buf.readUnsignedByte(); } + @Override + protected void v17Read(ByteBuf buf) { + this.entityId = buf.readInt(); + this.effectId = buf.readUnsignedByte(); + } + @Override public void write(ByteBuf buf) { - writeVarInt(this.entityId, buf); + writeVarInt(entityId, buf); buf.writeByte(effectId); } @@ -33,4 +39,10 @@ public class EntityRemoveEffect extends DefinedPacket { public void handle(AbstractPacketHandler handler) throws Exception { handler.handle(this); } + + @Override + protected void v17Write(ByteBuf buf) { + buf.writeInt(entityId); + buf.writeByte(effectId); + } } diff --git a/protocol/src/main/java/net/md_5/bungee/protocol/packet/KeepAlive.java b/protocol/src/main/java/net/md_5/bungee/protocol/packet/KeepAlive.java index 9df9ef56..56e6e522 100644 --- a/protocol/src/main/java/net/md_5/bungee/protocol/packet/KeepAlive.java +++ b/protocol/src/main/java/net/md_5/bungee/protocol/packet/KeepAlive.java @@ -1,6 +1,6 @@ package net.md_5.bungee.protocol.packet; -import net.md_5.bungee.protocol.DefinedPacket; +import io.github.waterfallmc.travertine.protocol.MultiVersionPacketV17; import io.netty.buffer.ByteBuf; import lombok.AllArgsConstructor; import lombok.Data; @@ -13,17 +13,33 @@ import net.md_5.bungee.protocol.ProtocolConstants; @NoArgsConstructor @AllArgsConstructor @EqualsAndHashCode(callSuper = false) -public class KeepAlive extends DefinedPacket +public class KeepAlive extends MultiVersionPacketV17 { private long randomId; + // Travertine start + @Override + public void v17Read(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) + { + randomId = buf.readInt(); + } + // Travertine end + @Override public void read(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) { randomId = ( protocolVersion >= ProtocolConstants.MINECRAFT_1_12_2 ) ? buf.readLong() : readVarInt( buf ); } + // Travertine start + @Override + public void v17Write(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) + { + buf.writeInt((int) randomId); + } + // Travertine end + @Override public void write(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) { diff --git a/protocol/src/main/java/net/md_5/bungee/protocol/packet/PlayerListItem.java b/protocol/src/main/java/net/md_5/bungee/protocol/packet/PlayerListItem.java index 9983ef2e..ae5dad52 100644 --- a/protocol/src/main/java/net/md_5/bungee/protocol/packet/PlayerListItem.java +++ b/protocol/src/main/java/net/md_5/bungee/protocol/packet/PlayerListItem.java @@ -1,5 +1,6 @@ package net.md_5.bungee.protocol.packet; +import io.github.waterfallmc.travertine.protocol.MultiVersionPacketV17; import net.md_5.bungee.protocol.DefinedPacket; import io.netty.buffer.ByteBuf; import lombok.Data; @@ -13,12 +14,24 @@ import java.util.UUID; @Data @NoArgsConstructor @EqualsAndHashCode(callSuper = false) -public class PlayerListItem extends DefinedPacket +public class PlayerListItem extends MultiVersionPacketV17 { private Action action; private Item[] items; + // Travertine start + @Override + public void v17Read(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) + { + items = new Item[ 1 ]; + Item item = items[ 0 ] = new Item(); + item.displayName = item.username = readString( buf ); + action = !buf.readBoolean() ? Action.REMOVE_PLAYER : Action.ADD_PLAYER; + item.ping = buf.readShort(); + } + // Travertine end + @Override public void read(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) { @@ -73,6 +86,17 @@ public class PlayerListItem extends DefinedPacket } } + // Travertine start + @Override + public void v17Write(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) + { + Item item = items[0]; // Only one at a time + writeString( item.displayName, buf ); // TODO: Server unique only! + buf.writeBoolean( action != Action.REMOVE_PLAYER ); + buf.writeShort( item.ping ); + } + // Travertine end + @Override public void write(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) { diff --git a/protocol/src/main/java/net/md_5/bungee/protocol/packet/PluginMessage.java b/protocol/src/main/java/net/md_5/bungee/protocol/packet/PluginMessage.java index e67773d9..97cef3c4 100644 --- a/protocol/src/main/java/net/md_5/bungee/protocol/packet/PluginMessage.java +++ b/protocol/src/main/java/net/md_5/bungee/protocol/packet/PluginMessage.java @@ -2,8 +2,8 @@ package net.md_5.bungee.protocol.packet; import com.google.common.base.Preconditions; import com.google.common.base.Predicate; +import io.github.waterfallmc.travertine.protocol.MultiVersionPacketV17; import io.netty.buffer.ByteBufUtil; -import net.md_5.bungee.protocol.DefinedPacket; import io.netty.buffer.ByteBuf; import java.io.ByteArrayInputStream; import java.io.DataInput; @@ -19,7 +19,7 @@ import net.md_5.bungee.protocol.ProtocolConstants; @NoArgsConstructor @AllArgsConstructor @EqualsAndHashCode(callSuper = false) -public class PluginMessage extends DefinedPacket +public class PluginMessage extends MultiVersionPacketV17 { public static final Predicate SHOULD_RELAY = new Predicate() @@ -52,6 +52,15 @@ public class PluginMessage extends DefinedPacket */ private boolean allowExtendedPacket = false; + // Travertine start + @Override + public void v17Read(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) + { + tag = readString( buf ); + data = v17readArray( buf ); + } + // Travertine end + @Override public void read(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) { @@ -62,6 +71,15 @@ public class PluginMessage extends DefinedPacket buf.readBytes( data ); } + // Travertine start + @Override + public void v17Write(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) + { + writeString( tag, buf ); + v17writeArray( data, buf, allowExtendedPacket ); + } + // Travertine end + @Override public void write(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) { diff --git a/protocol/src/main/java/net/md_5/bungee/protocol/packet/ScoreboardObjective.java b/protocol/src/main/java/net/md_5/bungee/protocol/packet/ScoreboardObjective.java index ef9b8cf6..43af3189 100644 --- a/protocol/src/main/java/net/md_5/bungee/protocol/packet/ScoreboardObjective.java +++ b/protocol/src/main/java/net/md_5/bungee/protocol/packet/ScoreboardObjective.java @@ -1,6 +1,6 @@ package net.md_5.bungee.protocol.packet; -import net.md_5.bungee.protocol.DefinedPacket; +import io.github.waterfallmc.travertine.protocol.MultiVersionPacketV17; import io.netty.buffer.ByteBuf; import lombok.AllArgsConstructor; import lombok.Data; @@ -13,7 +13,7 @@ import net.md_5.bungee.protocol.ProtocolConstants; @NoArgsConstructor @AllArgsConstructor @EqualsAndHashCode(callSuper = false) -public class ScoreboardObjective extends DefinedPacket +public class ScoreboardObjective extends MultiVersionPacketV17 { private String name; @@ -24,6 +24,16 @@ public class ScoreboardObjective extends DefinedPacket */ private byte action; + // Travertine start + @Override + public void v17Read(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) + { + name = readString( buf ); + value = readString( buf ); + action = buf.readByte(); + } + // Travertine end + @Override public void read(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) { @@ -36,6 +46,16 @@ public class ScoreboardObjective extends DefinedPacket } } + // Travertine start + @Override + public void v17Write(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) + { + writeString( name, buf ); + writeString( value, buf ); + buf.writeByte( action ); + } + // Travertine end + @Override public void write(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) { diff --git a/protocol/src/main/java/net/md_5/bungee/protocol/packet/ScoreboardScore.java b/protocol/src/main/java/net/md_5/bungee/protocol/packet/ScoreboardScore.java index 6f0de535..b967f89f 100644 --- a/protocol/src/main/java/net/md_5/bungee/protocol/packet/ScoreboardScore.java +++ b/protocol/src/main/java/net/md_5/bungee/protocol/packet/ScoreboardScore.java @@ -1,6 +1,6 @@ package net.md_5.bungee.protocol.packet; -import net.md_5.bungee.protocol.DefinedPacket; +import io.github.waterfallmc.travertine.protocol.MultiVersionPacketV17; import io.netty.buffer.ByteBuf; import lombok.AllArgsConstructor; import lombok.Data; @@ -13,7 +13,7 @@ import net.md_5.bungee.protocol.ProtocolConstants; @NoArgsConstructor @AllArgsConstructor @EqualsAndHashCode(callSuper = false) -public class ScoreboardScore extends DefinedPacket +public class ScoreboardScore extends MultiVersionPacketV17 { private String itemName; @@ -24,6 +24,20 @@ public class ScoreboardScore extends DefinedPacket private String scoreName; private int value; + // Travertine start + @Override + public void v17Read(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) + { + itemName = readString( buf ); + action = buf.readByte(); + if ( action != 1 ) + { + scoreName = readString( buf ); + value = buf.readInt(); + } + } + // Travertine end + @Override public void read(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) { @@ -36,6 +50,20 @@ public class ScoreboardScore extends DefinedPacket } } + // Travertine start + @Override + public void v17Write(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) + { + writeString( itemName, buf ); + buf.writeByte( action ); + if ( action != 1 ) + { + writeString( scoreName, buf ); + buf.writeInt( value ); + } + } + // Travertine end + @Override public void write(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) { diff --git a/protocol/src/main/java/net/md_5/bungee/protocol/packet/TabCompleteRequest.java b/protocol/src/main/java/net/md_5/bungee/protocol/packet/TabCompleteRequest.java index e3bcbc33..7634ebfd 100644 --- a/protocol/src/main/java/net/md_5/bungee/protocol/packet/TabCompleteRequest.java +++ b/protocol/src/main/java/net/md_5/bungee/protocol/packet/TabCompleteRequest.java @@ -1,6 +1,6 @@ package net.md_5.bungee.protocol.packet; -import net.md_5.bungee.protocol.DefinedPacket; +import io.github.waterfallmc.travertine.protocol.MultiVersionPacketV17; import io.netty.buffer.ByteBuf; import lombok.AllArgsConstructor; import lombok.Data; @@ -13,7 +13,7 @@ import net.md_5.bungee.protocol.ProtocolConstants; @NoArgsConstructor @AllArgsConstructor @EqualsAndHashCode(callSuper = false) -public class TabCompleteRequest extends DefinedPacket +public class TabCompleteRequest extends MultiVersionPacketV17 { private String cursor; @@ -21,6 +21,14 @@ public class TabCompleteRequest extends DefinedPacket private boolean hasPositon; private long position; + // Travertine start + @Override + public void v17Read(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) + { + cursor = readString( buf ); + } + // Travertine end + @Override public void read(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) { @@ -36,6 +44,14 @@ public class TabCompleteRequest extends DefinedPacket } } + // Travertine start + @Override + public void v17Write(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) + { + writeString( cursor, buf ); + } + // Travertine end + @Override public void write(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) { diff --git a/protocol/src/main/java/net/md_5/bungee/protocol/packet/Team.java b/protocol/src/main/java/net/md_5/bungee/protocol/packet/Team.java index b4705799..da1066cc 100644 --- a/protocol/src/main/java/net/md_5/bungee/protocol/packet/Team.java +++ b/protocol/src/main/java/net/md_5/bungee/protocol/packet/Team.java @@ -1,6 +1,6 @@ package net.md_5.bungee.protocol.packet; -import net.md_5.bungee.protocol.DefinedPacket; +import io.github.waterfallmc.travertine.protocol.MultiVersionPacketV17; import io.netty.buffer.ByteBuf; import lombok.AllArgsConstructor; import lombok.Data; @@ -13,7 +13,7 @@ import net.md_5.bungee.protocol.ProtocolConstants; @NoArgsConstructor @AllArgsConstructor @EqualsAndHashCode(callSuper = false) -public class Team extends DefinedPacket +public class Team extends MultiVersionPacketV17 { private String name; @@ -39,6 +39,31 @@ public class Team extends DefinedPacket this.mode = 1; } + // Travertine start + @Override + public void v17Read(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) + { + name = readString( buf ); + mode = buf.readByte(); + if ( mode == 0 || mode == 2 ) + { + displayName = readString( buf ); + prefix = readString( buf ); + suffix = readString( buf ); + friendlyFire = buf.readByte(); + } + if ( mode == 0 || mode == 3 || mode == 4 ) + { + int len = buf.readShort(); + players = new String[ len ]; + for ( int i = 0; i < len; i++ ) + { + players[i] = readString( buf ); + } + } + } + // Travertine end + @Override public void read(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) { @@ -68,6 +93,30 @@ public class Team extends DefinedPacket } } + // Travertine start + @Override + public void v17Write(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) + { + writeString( name, buf ); + buf.writeByte( mode ); + if ( mode == 0 || mode == 2 ) + { + writeString( displayName, buf ); + writeString( prefix, buf ); + writeString( suffix, buf ); + buf.writeByte( friendlyFire ); + } + if ( mode == 0 || mode == 3 || mode == 4 ) + { + buf.writeShort( players.length ); + for ( String player : players ) + { + writeString( player, buf ); + } + } + } + // Travertine end + @Override public void write(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) { diff --git a/proxy/src/main/java/net/md_5/bungee/BungeeCord.java b/proxy/src/main/java/net/md_5/bungee/BungeeCord.java index f9fddf07..e5d491f6 100644 --- a/proxy/src/main/java/net/md_5/bungee/BungeeCord.java +++ b/proxy/src/main/java/net/md_5/bungee/BungeeCord.java @@ -154,6 +154,14 @@ public class BungeeCord extends ProxyServer .registerTypeAdapter( TranslatableComponent.class, new TranslatableComponentSerializer() ) .registerTypeAdapter( ServerPing.PlayerInfo.class, new PlayerInfoSerializer() ) .registerTypeAdapter( Favicon.class, Favicon.getFaviconTypeAdapter() ).create(); + // Travertine start + public final Gson gsonLegacy = new GsonBuilder() + .registerTypeAdapter( BaseComponent.class, new ComponentSerializer() ) + .registerTypeAdapter( TextComponent.class, new TextComponentSerializer() ) + .registerTypeAdapter( TranslatableComponent.class, new TranslatableComponentSerializer() ) + .registerTypeAdapter( ServerPing.PlayerInfo.class, new PlayerInfoSerializer( ProtocolConstants.MINECRAFT_1_7_2 ) ) + .registerTypeAdapter( Favicon.class, Favicon.getFaviconTypeAdapter() ).create(); + // Travertine end @Getter private ConnectionThrottle connectionThrottle; private final ModuleManager moduleManager = new ModuleManager(); diff --git a/proxy/src/main/java/net/md_5/bungee/BungeeTitle.java b/proxy/src/main/java/net/md_5/bungee/BungeeTitle.java index 494213db..1d89acf5 100644 --- a/proxy/src/main/java/net/md_5/bungee/BungeeTitle.java +++ b/proxy/src/main/java/net/md_5/bungee/BungeeTitle.java @@ -5,6 +5,7 @@ import net.md_5.bungee.api.chat.BaseComponent; import net.md_5.bungee.api.connection.ProxiedPlayer; import net.md_5.bungee.chat.ComponentSerializer; import net.md_5.bungee.protocol.DefinedPacket; +import net.md_5.bungee.protocol.ProtocolConstants; import net.md_5.bungee.protocol.packet.Title.Action; public class BungeeTitle implements Title @@ -151,6 +152,7 @@ public class BungeeTitle implements Title @Override public Title send(ProxiedPlayer player) { + if ( ProtocolConstants.isBeforeOrEq( player.getPendingConnection().getVersion(), ProtocolConstants.MINECRAFT_1_7_6 ) ) return this; sendPacket( player, clear ); sendPacket( player, reset ); sendPacket( player, times ); diff --git a/proxy/src/main/java/net/md_5/bungee/PlayerInfoSerializer.java b/proxy/src/main/java/net/md_5/bungee/PlayerInfoSerializer.java index 491cf1a1..299a216c 100644 --- a/proxy/src/main/java/net/md_5/bungee/PlayerInfoSerializer.java +++ b/proxy/src/main/java/net/md_5/bungee/PlayerInfoSerializer.java @@ -10,17 +10,32 @@ import com.google.gson.JsonSerializer; import java.lang.reflect.Type; import java.util.UUID; import net.md_5.bungee.api.ServerPing; +import net.md_5.bungee.protocol.ProtocolConstants; public class PlayerInfoSerializer implements JsonSerializer, JsonDeserializer { + // Travertine start + private final int protocol; + + public PlayerInfoSerializer() + { + this.protocol = ProtocolConstants.MINECRAFT_1_7_6; + } + + public PlayerInfoSerializer(int protocol) + { + this.protocol = protocol; + } + // Travertine end + @Override public ServerPing.PlayerInfo deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { JsonObject js = json.getAsJsonObject(); ServerPing.PlayerInfo info = new ServerPing.PlayerInfo( js.get( "name" ).getAsString(), (UUID) null ); String id = js.get( "id" ).getAsString(); - if ( !id.contains( "-" ) ) + if ( ProtocolConstants.isBeforeOrEq( protocol, ProtocolConstants.MINECRAFT_1_7_2 ) || !id.contains( "-" ) ) // Travertine { info.setId( id ); } else @@ -35,7 +50,15 @@ public class PlayerInfoSerializer implements JsonSerializer= ProtocolConstants.MINECRAFT_1_8 ) // Travertine { sendMessage( position, ComponentSerializer.toString( new TextComponent( BaseComponent.toLegacyText( message ) ) ) ); } else @@ -435,7 +436,7 @@ public final class UserConnection implements ProxiedPlayer public void sendMessage(ChatMessageType position, BaseComponent message) { // Action bar doesn't display the new JSON formattings, legacy works - send it using this for now - if ( position == ChatMessageType.ACTION_BAR ) + if ( position == ChatMessageType.ACTION_BAR && getPendingConnection().getVersion() >= ProtocolConstants.MINECRAFT_1_8 ) // Travertine { sendMessage( position, ComponentSerializer.toString( new TextComponent( BaseComponent.toLegacyText( message ) ) ) ); } else @@ -616,6 +617,7 @@ public final class UserConnection implements ProxiedPlayer @Override public void setTabHeader(BaseComponent header, BaseComponent footer) { + if ( ProtocolConstants.isBeforeOrEq( pendingConnection.getVersion(), ProtocolConstants.MINECRAFT_1_7_6 ) ) return; // Travertine unsafe().sendPacket( new PlayerListHeaderFooter( ( header != null ) ? ComponentSerializer.toString( header ) : EMPTY_TEXT, ( footer != null ) ? ComponentSerializer.toString( footer ) : EMPTY_TEXT @@ -625,6 +627,7 @@ public final class UserConnection implements ProxiedPlayer @Override public void setTabHeader(BaseComponent[] header, BaseComponent[] footer) { + if ( ProtocolConstants.isBeforeOrEq( pendingConnection.getVersion(), ProtocolConstants.MINECRAFT_1_7_6 ) ) return; // Travertine unsafe().sendPacket( new PlayerListHeaderFooter( ( header != null ) ? ComponentSerializer.toString( header ) : EMPTY_TEXT, ( footer != null ) ? ComponentSerializer.toString( footer ) : EMPTY_TEXT @@ -651,6 +654,7 @@ public final class UserConnection implements ProxiedPlayer public void setCompressionThreshold(int compressionThreshold) { + if ( ProtocolConstants.isBeforeOrEq( pendingConnection.getVersion(), ProtocolConstants.MINECRAFT_1_7_6 ) ) return; // Travertine if ( !ch.isClosing() && this.compressionThreshold == -1 && compressionThreshold >= 0 ) { this.compressionThreshold = compressionThreshold; diff --git a/proxy/src/main/java/net/md_5/bungee/connection/DownstreamBridge.java b/proxy/src/main/java/net/md_5/bungee/connection/DownstreamBridge.java index 201b6c3e..d2feeb1b 100644 --- a/proxy/src/main/java/net/md_5/bungee/connection/DownstreamBridge.java +++ b/proxy/src/main/java/net/md_5/bungee/connection/DownstreamBridge.java @@ -30,6 +30,7 @@ import net.md_5.bungee.netty.ChannelWrapper; import net.md_5.bungee.netty.PacketHandler; import net.md_5.bungee.protocol.DefinedPacket; import net.md_5.bungee.protocol.PacketWrapper; +import net.md_5.bungee.protocol.ProtocolConstants; import net.md_5.bungee.protocol.packet.BossBar; import net.md_5.bungee.protocol.packet.EntityEffect; import net.md_5.bungee.protocol.packet.EntityRemoveEffect; @@ -249,16 +250,28 @@ public class DownstreamBridge extends PacketHandler if ( pluginMessage.getTag().equals( "MC|Brand" ) ) { - ByteBuf brand = Unpooled.wrappedBuffer( pluginMessage.getData() ); - String serverBrand = DefinedPacket.readString( brand ); - brand.release(); - - Preconditions.checkState( !serverBrand.contains( bungee.getName() ), "Cannot connect proxy to itself!" ); - - brand = ByteBufAllocator.DEFAULT.heapBuffer(); - DefinedPacket.writeString( bungee.getName() + " (" + bungee.getVersion() + ")" + " <- " + serverBrand, brand ); - pluginMessage.setData( brand ); - brand.release(); + // Travertine start + if ( ProtocolConstants.isAfterOrEq( con.getPendingConnection().getVersion(), ProtocolConstants.MINECRAFT_1_8 ) ) + { + try + { + ByteBuf brand = Unpooled.wrappedBuffer(pluginMessage.getData()); + String serverBrand = DefinedPacket.readString(brand); + brand.release(); + brand = ByteBufAllocator.DEFAULT.heapBuffer(); + DefinedPacket.writeString(bungee.getName() + " (" + bungee.getVersion() + ")" + " <- " + serverBrand, brand); + pluginMessage.setData(brand); + brand.release(); + } catch (Exception ProtocolHacksSuck) + { + return; + } + } else + { + String serverBrand = new String( pluginMessage.getData(), "UTF-8" ); + pluginMessage.setData( ( bungee.getName() + " (" + bungee.getVersion() + ")" + " <- " + serverBrand ).getBytes( "UTF-8" ) ); + } + // Travertine end // changes in the packet are ignored so we need to send it manually con.unsafe().sendPacket( pluginMessage ); throw CancelSendSignal.INSTANCE; diff --git a/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java b/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java index 9e501c00..cc27726a 100644 --- a/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java +++ b/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java @@ -13,6 +13,13 @@ import java.util.UUID; import java.util.concurrent.TimeUnit; import java.util.logging.Level; import javax.crypto.SecretKey; + +import com.google.common.base.Charsets; +import com.google.common.base.Preconditions; +import com.google.gson.Gson; + +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; import lombok.Getter; import lombok.RequiredArgsConstructor; import net.md_5.bungee.BungeeCord; @@ -222,8 +229,24 @@ public class InitialHandler extends PacketHandler implements PendingConnection @Override public void done(ProxyPingEvent pingResult, Throwable error) { - Gson gson = BungeeCord.getInstance().gson; - unsafe.sendPacket( new StatusResponse( gson.toJson( pingResult.getResponse() ) ) ); + Gson gson = handshake.getProtocolVersion() == ProtocolConstants.MINECRAFT_1_7_2 ? BungeeCord.getInstance().gsonLegacy : BungeeCord.getInstance().gson; // Travertine + // Travertine start + if ( ProtocolConstants.isBeforeOrEq( handshake.getProtocolVersion() , ProtocolConstants.MINECRAFT_1_8 ) ) + { + // Minecraft < 1.9 doesn't send string server descriptions as chat components. Older 1.7 + // clients even crash when encountering a chat component instead of a string. To be on the + // safe side, always send legacy descriptions for < 1.9 clients. + JsonElement element = gson.toJsonTree(pingResult.getResponse()); + Preconditions.checkArgument(element.isJsonObject(), "Response is not a JSON object"); + JsonObject object = element.getAsJsonObject(); + object.addProperty("description", pingResult.getResponse().getDescription()); + + unsafe.sendPacket(new StatusResponse(gson.toJson(element))); + } else + { + unsafe.sendPacket( new StatusResponse( gson.toJson( pingResult.getResponse() ) ) ); + } + // Travertine end } }; @@ -507,7 +530,15 @@ public class InitialHandler extends PacketHandler implements PendingConnection userCon.setCompressionThreshold( BungeeCord.getInstance().config.getCompressionThreshold() ); userCon.init(); - unsafe.sendPacket( new LoginSuccess( getUniqueId().toString(), getName() ) ); // With dashes in between + // Travertine start + if ( ProtocolConstants.isAfterOrEq( getVersion() , ProtocolConstants.MINECRAFT_1_7_6 ) ) + { + unsafe.sendPacket( new LoginSuccess( getUniqueId().toString(), getName() ) ); // With dashes in between + } else + { + unsafe.sendPacket( new LoginSuccess( getUUID(), getName() ) ); // Without dashes, for older clients. + } + // Travertine end ch.setProtocol( Protocol.GAME ); ch.getHandle().pipeline().get( HandlerBoss.class ).setHandler( new UpstreamBridge( bungee, userCon ) ); diff --git a/proxy/src/main/java/net/md_5/bungee/connection/PingHandler.java b/proxy/src/main/java/net/md_5/bungee/connection/PingHandler.java index 3cd5cf69..ff30b61a 100644 --- a/proxy/src/main/java/net/md_5/bungee/connection/PingHandler.java +++ b/proxy/src/main/java/net/md_5/bungee/connection/PingHandler.java @@ -14,6 +14,7 @@ import net.md_5.bungee.netty.PipelineUtils; import net.md_5.bungee.protocol.MinecraftDecoder; import net.md_5.bungee.protocol.MinecraftEncoder; import net.md_5.bungee.protocol.Protocol; +import net.md_5.bungee.protocol.ProtocolConstants; import net.md_5.bungee.protocol.packet.Handshake; import net.md_5.bungee.protocol.packet.StatusRequest; import net.md_5.bungee.protocol.packet.StatusResponse; @@ -52,7 +53,7 @@ public class PingHandler extends PacketHandler @SuppressFBWarnings("UWF_FIELD_NOT_INITIALIZED_IN_CONSTRUCTOR") public void handle(StatusResponse statusResponse) throws Exception { - Gson gson = BungeeCord.getInstance().gson; + Gson gson = protocol == ProtocolConstants.MINECRAFT_1_7_2 ? BungeeCord.getInstance().gsonLegacy : BungeeCord.getInstance().gson; // Travertine callback.done( gson.fromJson( statusResponse.getResponse(), ServerPing.class ), null ); channel.close(); } diff --git a/proxy/src/main/java/net/md_5/bungee/connection/UpstreamBridge.java b/proxy/src/main/java/net/md_5/bungee/connection/UpstreamBridge.java index 3000a567..13f7c182 100644 --- a/proxy/src/main/java/net/md_5/bungee/connection/UpstreamBridge.java +++ b/proxy/src/main/java/net/md_5/bungee/connection/UpstreamBridge.java @@ -78,7 +78,12 @@ public class UpstreamBridge extends PacketHandler } ); for ( ProxiedPlayer player : con.getServer().getInfo().getPlayers() ) { - player.unsafe().sendPacket( packet ); + // Travertine start + if ( ProtocolConstants.isAfterOrEq( player.getPendingConnection().getVersion(), ProtocolConstants.MINECRAFT_1_8 ) ) + { + player.unsafe().sendPacket( packet ); + } + // Travertine end } con.getServer().disconnect( "Quitting" ); } diff --git a/proxy/src/main/java/net/md_5/bungee/entitymap/EntityMap.java b/proxy/src/main/java/net/md_5/bungee/entitymap/EntityMap.java index 27ee21f2..64649c7c 100644 --- a/proxy/src/main/java/net/md_5/bungee/entitymap/EntityMap.java +++ b/proxy/src/main/java/net/md_5/bungee/entitymap/EntityMap.java @@ -29,6 +29,10 @@ public abstract class EntityMap { switch ( version ) { + case ProtocolConstants.MINECRAFT_1_7_2: + return EntityMap_1_7_2.INSTANCE; + case ProtocolConstants.MINECRAFT_1_7_6: + return EntityMap_1_7_6.INSTANCE; case ProtocolConstants.MINECRAFT_1_8: return EntityMap_1_8.INSTANCE; case ProtocolConstants.MINECRAFT_1_9: diff --git a/proxy/src/main/java/net/md_5/bungee/entitymap/EntityMap.java.orig b/proxy/src/main/java/net/md_5/bungee/entitymap/EntityMap.java.orig new file mode 100644 index 00000000..d98a8056 --- /dev/null +++ b/proxy/src/main/java/net/md_5/bungee/entitymap/EntityMap.java.orig @@ -0,0 +1,215 @@ +package net.md_5.bungee.entitymap; + +import com.flowpowered.nbt.stream.NBTInputStream; +import com.google.common.base.Throwables; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufInputStream; +import java.io.IOException; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import net.md_5.bungee.protocol.DefinedPacket; +import net.md_5.bungee.protocol.ProtocolConstants; + +/** + * Class to rewrite integers within packets. + */ +@NoArgsConstructor(access = AccessLevel.PACKAGE) +public abstract class EntityMap +{ + + private final boolean[] clientboundInts = new boolean[ 256 ]; + private final boolean[] clientboundVarInts = new boolean[ 256 ]; + + private final boolean[] serverboundInts = new boolean[ 256 ]; + private final boolean[] serverboundVarInts = new boolean[ 256 ]; + + // Returns the correct entity map for the protocol version + public static EntityMap getEntityMap(int version) + { + switch ( version ) + { + case ProtocolConstants.MINECRAFT_1_8: + return EntityMap_1_8.INSTANCE; + case ProtocolConstants.MINECRAFT_1_9: + case ProtocolConstants.MINECRAFT_1_9_1: + case ProtocolConstants.MINECRAFT_1_9_2: + return EntityMap_1_9.INSTANCE; + case ProtocolConstants.MINECRAFT_1_9_4: + return EntityMap_1_9_4.INSTANCE; + case ProtocolConstants.MINECRAFT_1_10: + return EntityMap_1_10.INSTANCE; + case ProtocolConstants.MINECRAFT_1_11: + case ProtocolConstants.MINECRAFT_1_11_1: + return EntityMap_1_11.INSTANCE; + } + throw new RuntimeException( "Version " + version + " has no entity map" ); + } + + protected void addRewrite(int id, ProtocolConstants.Direction direction, boolean varint) + { + if ( direction == ProtocolConstants.Direction.TO_CLIENT ) + { + if ( varint ) + { + clientboundVarInts[id] = true; + } else + { + clientboundInts[id] = true; + } + } else if ( varint ) + { + serverboundVarInts[id] = true; + } else + { + serverboundInts[id] = true; + } + } + + public void rewriteServerbound(ByteBuf packet, int oldId, int newId) + { + rewrite( packet, oldId, newId, serverboundInts, serverboundVarInts ); + } + + public void rewriteClientbound(ByteBuf packet, int oldId, int newId) + { + rewrite( packet, oldId, newId, clientboundInts, clientboundVarInts ); + } + + protected static void rewriteInt(ByteBuf packet, int oldId, int newId, int offset) + { + int readId = packet.getInt( offset ); + if ( readId == oldId ) + { + packet.setInt( offset, newId ); + } else if ( readId == newId ) + { + packet.setInt( offset, oldId ); + } + } + + @SuppressFBWarnings("DLS_DEAD_LOCAL_STORE") + protected static void rewriteVarInt(ByteBuf packet, int oldId, int newId, int offset) + { + // Need to rewrite the packet because VarInts are variable length + int readId = DefinedPacket.readVarInt( packet ); + int readIdLength = packet.readerIndex() - offset; + if ( readId == oldId || readId == newId ) + { + ByteBuf data = packet.copy(); + packet.readerIndex( offset ); + packet.writerIndex( offset ); + DefinedPacket.writeVarInt( readId == oldId ? newId : oldId, packet ); + packet.writeBytes( data ); + data.release(); + } + } + + protected static void rewriteMetaVarInt(ByteBuf packet, int oldId, int newId, int metaIndex) + { + int readerIndex = packet.readerIndex(); + + short index; + while ( ( index = packet.readUnsignedByte() ) != 0xFF ) + { + int type = DefinedPacket.readVarInt( packet ); + + switch ( type ) + { + case 0: + packet.skipBytes( 1 ); // byte + break; + case 1: + if ( index == metaIndex ) + { + int position = packet.readerIndex(); + rewriteVarInt( packet, oldId, newId, position ); + packet.readerIndex( position ); + } + DefinedPacket.readVarInt( packet ); + break; + case 2: + packet.skipBytes( 4 ); // float + break; + case 3: + case 4: + DefinedPacket.readString( packet ); + break; + case 5: + if ( packet.readShort() != -1 ) + { + packet.skipBytes( 3 ); // byte, short + + int position = packet.readerIndex(); + if ( packet.readByte() != 0 ) + { + packet.readerIndex( position ); + + try + { + new NBTInputStream( new ByteBufInputStream( packet ), false ).readTag(); + } catch ( IOException ex ) + { + throw Throwables.propagate( ex ); + } + } + } + break; + case 6: + packet.skipBytes( 1 ); // boolean + break; + case 7: + packet.skipBytes( 12 ); // float, float, float + break; + case 8: + packet.readLong(); + break; + case 9: + if ( packet.readBoolean() ) + { + packet.skipBytes( 8 ); // long + } + break; + case 10: + DefinedPacket.readVarInt( packet ); + break; + case 11: + if ( packet.readBoolean() ) + { + packet.skipBytes( 16 ); // long, long + } + break; + case 12: + DefinedPacket.readVarInt( packet ); + break; + default: + throw new IllegalArgumentException( "Unknown meta type " + type ); + } + } + + packet.readerIndex( readerIndex ); + } + + // Handles simple packets + private static void rewrite(ByteBuf packet, int oldId, int newId, boolean[] ints, boolean[] varints) + { + int readerIndex = packet.readerIndex(); + int packetId = DefinedPacket.readVarInt( packet ); + int packetIdLength = packet.readerIndex() - readerIndex; + + if (packetId < 0 || packetId > ints.length || packetId > varints.length) { // Invalid packet id + // Ignore these invalid packets for compatibility reasons + packet.readerIndex( readerIndex ); + return; + } + + if ( ints[packetId] ) + { + rewriteInt( packet, oldId, newId, readerIndex + packetIdLength ); + } else if ( varints[packetId] ) + { + rewriteVarInt( packet, oldId, newId, readerIndex + packetIdLength ); + } + packet.readerIndex( readerIndex ); + } +} diff --git a/proxy/src/main/java/net/md_5/bungee/entitymap/EntityMap_1_7_2.java b/proxy/src/main/java/net/md_5/bungee/entitymap/EntityMap_1_7_2.java new file mode 100644 index 00000000..65c1a9ec --- /dev/null +++ b/proxy/src/main/java/net/md_5/bungee/entitymap/EntityMap_1_7_2.java @@ -0,0 +1,102 @@ +// Travertine start +package net.md_5.bungee.entitymap; + +import io.netty.buffer.ByteBuf; +import net.md_5.bungee.protocol.DefinedPacket; +import net.md_5.bungee.protocol.ProtocolConstants; + +class EntityMap_1_7_2 extends EntityMap +{ + + static final EntityMap INSTANCE = new EntityMap_1_7_2(); + + EntityMap_1_7_2() + { + addRewrite( 0x04, ProtocolConstants.Direction.TO_CLIENT, false ); // Entity Equipment + addRewrite( 0x0A, ProtocolConstants.Direction.TO_CLIENT, false ); // Use bed + addRewrite( 0x0B, ProtocolConstants.Direction.TO_CLIENT, true ); // Animation + addRewrite( 0x0C, ProtocolConstants.Direction.TO_CLIENT, true ); // Spawn Player + addRewrite( 0x0D, ProtocolConstants.Direction.TO_CLIENT, false ); // Collect Item + addRewrite( 0x0E, ProtocolConstants.Direction.TO_CLIENT, true ); // Spawn Object + addRewrite( 0x0F, ProtocolConstants.Direction.TO_CLIENT, true ); // Spawn Mob + addRewrite( 0x10, ProtocolConstants.Direction.TO_CLIENT, true ); // Spawn Painting + addRewrite( 0x11, ProtocolConstants.Direction.TO_CLIENT, true ); // Spawn Experience Orb + addRewrite( 0x12, ProtocolConstants.Direction.TO_CLIENT, false ); // Entity Velocity + addRewrite( 0x14, ProtocolConstants.Direction.TO_CLIENT, false ); // Entity + addRewrite( 0x15, ProtocolConstants.Direction.TO_CLIENT, false ); // Entity Relative Move + addRewrite( 0x16, ProtocolConstants.Direction.TO_CLIENT, false ); // Entity Look + addRewrite( 0x17, ProtocolConstants.Direction.TO_CLIENT, false ); // Entity Look and Relative Move + addRewrite( 0x18, ProtocolConstants.Direction.TO_CLIENT, false ); // Entity Teleport + addRewrite( 0x19, ProtocolConstants.Direction.TO_CLIENT, false ); // Entity Head Look + addRewrite( 0x1A, ProtocolConstants.Direction.TO_CLIENT, false ); // Entity Status + addRewrite( 0x1B, ProtocolConstants.Direction.TO_CLIENT, false ); // Attach Entity + addRewrite( 0x1C, ProtocolConstants.Direction.TO_CLIENT, false ); // Entity Metadata + addRewrite( 0x1D, ProtocolConstants.Direction.TO_CLIENT, false ); // Entity Effect + addRewrite( 0x1E, ProtocolConstants.Direction.TO_CLIENT, false ); // Remove Entity Effect + addRewrite( 0x20, ProtocolConstants.Direction.TO_CLIENT, false ); // Entity Properties + addRewrite( 0x25, ProtocolConstants.Direction.TO_CLIENT, true ); // Block Break Animation + addRewrite( 0x2C, ProtocolConstants.Direction.TO_CLIENT, true ); // Spawn Global Entity + + addRewrite( 0x02, ProtocolConstants.Direction.TO_SERVER, false ); // Use Entity + addRewrite( 0x0A, ProtocolConstants.Direction.TO_SERVER, false ); // Animation + addRewrite( 0x0B, ProtocolConstants.Direction.TO_SERVER, false ); // Entity Action + } + + @Override + public void rewriteClientbound(ByteBuf packet, int oldId, int newId) + { + super.rewriteClientbound( packet, oldId, newId ); + + //Special cases + int readerIndex = packet.readerIndex(); + int packetId = DefinedPacket.readVarInt( packet ); + int packetIdLength = packet.readerIndex() - readerIndex; + if ( packetId == 0x0D /* Collect Item */ || packetId == 0x1B /* Attach Entity */ ) + { + rewriteInt( packet, oldId, newId, readerIndex + packetIdLength + 4 ); + } else if ( packetId == 0x13 /* Destroy Entities */ ) + { + int count = packet.getByte( packetIdLength ); + for ( int i = 0; i < count; i++ ) + { + rewriteInt( packet, oldId, newId, packetIdLength + 1 + i * 4 ); + } + } else if ( packetId == 0x0E /* Spawn Object */ ) + { + DefinedPacket.readVarInt( packet ); + int type = packet.readUnsignedByte(); + + if ( type == 60 || type == 90 ) + { + packet.skipBytes( 14 ); + int position = packet.readerIndex(); + int readId = packet.readInt(); + int changedId = -1; + if ( readId == oldId ) + { + packet.setInt( position, newId ); + changedId = newId; + } else if ( readId == newId ) + { + packet.setInt( position, oldId ); + changedId = oldId; + } + if ( changedId != -1 ) + { + if ( changedId == 0 && readId != 0 ) + { // Trim off the extra data + packet.readerIndex( readerIndex ); + packet.writerIndex( packet.readableBytes() - 6 ); + } else if ( changedId != 0 && readId == 0 ) + { // Add on the extra data + packet.readerIndex( readerIndex ); + packet.capacity( packet.readableBytes() + 6 ); + packet.writerIndex( packet.readableBytes() + 6 ); + } + } + } + } + packet.readerIndex( readerIndex ); + } +} +// Travertine end diff --git a/proxy/src/main/java/net/md_5/bungee/entitymap/EntityMap_1_7_6.java b/proxy/src/main/java/net/md_5/bungee/entitymap/EntityMap_1_7_6.java new file mode 100644 index 00000000..6755fe84 --- /dev/null +++ b/proxy/src/main/java/net/md_5/bungee/entitymap/EntityMap_1_7_6.java @@ -0,0 +1,62 @@ +// Travertine start +package net.md_5.bungee.entitymap; + +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; +import io.netty.buffer.ByteBuf; +import net.md_5.bungee.BungeeCord; +import net.md_5.bungee.UserConnection; +import net.md_5.bungee.connection.LoginResult; +import net.md_5.bungee.protocol.DefinedPacket; + +class EntityMap_1_7_6 extends EntityMap_1_7_2 +{ + + static final EntityMap_1_7_6 INSTANCE = new EntityMap_1_7_6(); + + @Override + @SuppressFBWarnings("DLS_DEAD_LOCAL_STORE") + public void rewriteClientbound(ByteBuf packet, int oldId, int newId) + { + super.rewriteClientbound( packet, oldId, newId ); + + int readerIndex = packet.readerIndex(); + int packetId = DefinedPacket.readVarInt( packet ); + int packetIdLength = packet.readerIndex() - readerIndex; + if ( packetId == 0x0C /* Spawn Player */ ) + { + DefinedPacket.readVarInt( packet ); + int idLength = packet.readerIndex() - readerIndex - packetIdLength; + String uuid = DefinedPacket.readString( packet ); + String username = DefinedPacket.readString( packet ); + int props = DefinedPacket.readVarInt( packet ); + if ( props == 0 ) + { + UserConnection player = (UserConnection) BungeeCord.getInstance().getPlayer( username ); + if ( player != null ) + { + LoginResult profile = player.getPendingConnection().getLoginProfile(); + if ( profile != null && profile.getProperties() != null + && profile.getProperties().length >= 1 ) + { + ByteBuf rest = packet.copy(); + packet.readerIndex( readerIndex ); + packet.writerIndex( readerIndex + packetIdLength + idLength ); + DefinedPacket.writeString( player.getUniqueId().toString(), packet ); + DefinedPacket.writeString( username, packet ); + DefinedPacket.writeVarInt( profile.getProperties().length, packet ); + for ( LoginResult.Property property : profile.getProperties() ) + { + DefinedPacket.writeString( property.getName(), packet ); + DefinedPacket.writeString( property.getValue(), packet ); + DefinedPacket.writeString( property.getSignature(), packet ); + } + packet.writeBytes( rest ); + rest.release(); + } + } + } + } + packet.readerIndex( readerIndex ); + } +} +// Travertine end diff --git a/proxy/src/main/java/net/md_5/bungee/forge/ForgeClientHandler.java b/proxy/src/main/java/net/md_5/bungee/forge/ForgeClientHandler.java index 17e250d4..bcc6a4f6 100644 --- a/proxy/src/main/java/net/md_5/bungee/forge/ForgeClientHandler.java +++ b/proxy/src/main/java/net/md_5/bungee/forge/ForgeClientHandler.java @@ -7,6 +7,7 @@ import lombok.Getter; import lombok.NonNull; import lombok.RequiredArgsConstructor; import lombok.Setter; +import net.md_5.bungee.BungeeCord; import net.md_5.bungee.UserConnection; import net.md_5.bungee.protocol.ProtocolConstants; import net.md_5.bungee.protocol.packet.EntityRemoveEffect; @@ -22,6 +23,12 @@ public class ForgeClientHandler @NonNull private final UserConnection con; + // Travertine start + @Getter + @Setter(AccessLevel.PACKAGE) + private boolean forgeOutdated = false; + // Travertine end + /** * The users' mod list. */ @@ -171,4 +178,22 @@ public class ForgeClientHandler { return fmlTokenInHandshake || clientModList != null; } + + // Travertine start + /** + * Checks to see if a user is using an outdated FML build, and takes + * appropriate action on the User side. This should only be called during a + * server connection, by the ServerConnector + * + * @return true if the user's FML build is outdated, otherwise + * false + */ + public boolean checkUserOutdated() { + if (forgeOutdated) { + con.disconnect( BungeeCord.getInstance().getTranslation("connect_kick_outdated_forge") ); + } + return forgeOutdated; + } + // Travertine end + } diff --git a/proxy/src/main/java/net/md_5/bungee/forge/ForgeClientHandshakeState.java b/proxy/src/main/java/net/md_5/bungee/forge/ForgeClientHandshakeState.java index 5e02f8c8..e3c1b9b9 100644 --- a/proxy/src/main/java/net/md_5/bungee/forge/ForgeClientHandshakeState.java +++ b/proxy/src/main/java/net/md_5/bungee/forge/ForgeClientHandshakeState.java @@ -3,6 +3,7 @@ package net.md_5.bungee.forge; import java.util.Map; import net.md_5.bungee.ServerConnector; import net.md_5.bungee.UserConnection; +import net.md_5.bungee.protocol.ProtocolConstants; import net.md_5.bungee.protocol.packet.PluginMessage; /** @@ -84,6 +85,22 @@ enum ForgeClientHandshakeState implements IForgeClientPacketHandler clientModList = ForgeUtils.readModList( message ); con.getForgeClientHandler().setClientModList( clientModList ); + // Travertine start + // If the user is below 1.8, we need to check the version of FML - it's not always an OK version. + if ( ProtocolConstants.isBeforeOrEq( con.getPendingConnection().getVersion(), ProtocolConstants.MINECRAFT_1_7_6 ) ) + { + // Get the version from the mod list. + int buildNumber = ForgeUtils.getFmlBuildNumber( clientModList ); + + // If we get 0, we're probably using a testing build, so let it though. Otherwise, check the build number. + if ( buildNumber < ForgeConstants.FML_MIN_BUILD_VERSION && buildNumber != 0 ) + { + // Mark the user as an old Forge user. This will then cause any Forge ServerConnectors to cancel any + // connections to it. + con.getForgeClientHandler().setForgeOutdated( true ); + } + } + // Travertine end } return WAITINGSERVERDATA; diff --git a/proxy/src/main/java/net/md_5/bungee/tab/ServerUnique.java b/proxy/src/main/java/net/md_5/bungee/tab/ServerUnique.java index daf12f74..e33861ab 100644 --- a/proxy/src/main/java/net/md_5/bungee/tab/ServerUnique.java +++ b/proxy/src/main/java/net/md_5/bungee/tab/ServerUnique.java @@ -4,12 +4,14 @@ import java.util.Collection; import java.util.HashSet; import java.util.UUID; import net.md_5.bungee.api.connection.ProxiedPlayer; +import net.md_5.bungee.protocol.ProtocolConstants; import net.md_5.bungee.protocol.packet.PlayerListItem; public class ServerUnique extends TabList { private final Collection uuids = new HashSet<>(); + private final Collection usernames = new HashSet<>(); // Travertine - Support for <=1.7.9 public ServerUnique(ProxiedPlayer player) { @@ -23,10 +25,26 @@ public class ServerUnique extends TabList { if ( playerListItem.getAction() == PlayerListItem.Action.ADD_PLAYER ) { - uuids.add( item.getUuid() ); + // Travertine start + if ( item.getUuid() != null ) + { + uuids.add( item.getUuid() ); + } else + { + usernames.add( item.getUsername() ); + } + // Travertine end } else if ( playerListItem.getAction() == PlayerListItem.Action.REMOVE_PLAYER ) { - uuids.remove( item.getUuid() ); + // Travertine start + if ( item.getUuid() != null ) + { + uuids.remove( item.getUuid() ); + } else + { + usernames.remove( item.getUsername() ); + } + // Travertine end } } player.unsafe().sendPacket( playerListItem ); @@ -43,16 +61,44 @@ public class ServerUnique extends TabList { PlayerListItem packet = new PlayerListItem(); packet.setAction( PlayerListItem.Action.REMOVE_PLAYER ); - PlayerListItem.Item[] items = new PlayerListItem.Item[ uuids.size() ]; + PlayerListItem.Item[] items = new PlayerListItem.Item[ uuids.size() + usernames.size() ]; // Travertine int i = 0; for ( UUID uuid : uuids ) { PlayerListItem.Item item = items[i++] = new PlayerListItem.Item(); item.setUuid( uuid ); } + // Travertine start + for ( String username : usernames ) + { + PlayerListItem.Item item = items[i++] = new PlayerListItem.Item(); + item.setUsername( username ); + item.setDisplayName( username ); + } + // Travertine end packet.setItems( items ); - player.unsafe().sendPacket( packet ); + // Travertine start + if ( ProtocolConstants.isAfterOrEq( player.getPendingConnection().getVersion(), ProtocolConstants.MINECRAFT_1_8 ) ) + { + player.unsafe().sendPacket( packet ); + } else + { + // Split up the packet + for ( PlayerListItem.Item item : packet.getItems() ) + { + PlayerListItem p2 = new PlayerListItem(); + p2.setAction( packet.getAction() ); + + p2.setItems( new PlayerListItem.Item[] + { + item + } ); + player.unsafe().sendPacket( p2 ); + } + } + // Travertine end uuids.clear(); + usernames.clear(); // Travertine } @Override -- 2.15.1