From 318db73f3c7b48eaa5421cd4da460950fc5388a7 Mon Sep 17 00:00:00 2001 From: Myles Date: Wed, 2 Mar 2016 15:21:07 +0000 Subject: [PATCH] This is a huge commit, adds ProtocolLib support. (To the best extent I can): Channels now work as proxies for the minecraft encoder and decoder, this allows better compatibility when transforming packets. ConnectionInfo now holds an activate state to deactivate. Now only 2 handlers We now use info.getChannel().pipeline().writeAndFlush to ensure we catch it ourselves. Fix EntityMetadata from last commit so it sends empty metadata instead of cancelling whole packet. Warn if they reload and don't reinject. --- .../us/myles/ViaVersion/ConnectionInfo.java | 15 +++- .../us/myles/ViaVersion/ViaVersionPlugin.java | 15 ++-- .../ViaVersion/handlers/ViaDecodeHandler.java | 48 +++++++++++++ .../ViaVersion/handlers/ViaEncodeHandler.java | 72 +++++++++++++++++++ .../handlers/ViaInboundHandler.java | 51 ------------- .../handlers/ViaOutboundHandler.java | 53 -------------- .../handlers/ViaOutboundPacketHandler.java | 43 ----------- .../handlers/ViaVersionInitializer.java | 12 ++-- .../transformers/IncomingTransformer.java | 6 +- .../transformers/OutgoingTransformer.java | 5 +- .../us/myles/ViaVersion/util/PacketUtil.java | 35 +++++---- src/main/resources/plugin.yml | 4 +- 12 files changed, 176 insertions(+), 183 deletions(-) create mode 100644 src/main/java/us/myles/ViaVersion/handlers/ViaDecodeHandler.java create mode 100644 src/main/java/us/myles/ViaVersion/handlers/ViaEncodeHandler.java delete mode 100644 src/main/java/us/myles/ViaVersion/handlers/ViaInboundHandler.java delete mode 100644 src/main/java/us/myles/ViaVersion/handlers/ViaOutboundHandler.java delete mode 100644 src/main/java/us/myles/ViaVersion/handlers/ViaOutboundPacketHandler.java diff --git a/src/main/java/us/myles/ViaVersion/ConnectionInfo.java b/src/main/java/us/myles/ViaVersion/ConnectionInfo.java index 7e1dd89cb..4516fe804 100644 --- a/src/main/java/us/myles/ViaVersion/ConnectionInfo.java +++ b/src/main/java/us/myles/ViaVersion/ConnectionInfo.java @@ -9,11 +9,12 @@ import java.util.UUID; public class ConnectionInfo { private final SocketChannel channel; - private int protocol = 0; - private State state = State.HANDSHAKE; - private int compression = 0; private Object lastPacket; private java.util.UUID UUID; + private State state = State.HANDSHAKE; + private int protocol = 0; + private int compression = 0; + private boolean active = true; public ConnectionInfo(SocketChannel socketChannel) { this.channel = socketChannel; @@ -66,4 +67,12 @@ public class ConnectionInfo { public SocketChannel getChannel() { return channel; } + + public boolean isActive() { + return active; + } + + public void setActive(boolean active) { + this.active = active; + } } diff --git a/src/main/java/us/myles/ViaVersion/ViaVersionPlugin.java b/src/main/java/us/myles/ViaVersion/ViaVersionPlugin.java index 20af1c3d3..ae16e3305 100644 --- a/src/main/java/us/myles/ViaVersion/ViaVersionPlugin.java +++ b/src/main/java/us/myles/ViaVersion/ViaVersionPlugin.java @@ -34,14 +34,17 @@ public class ViaVersionPlugin extends JavaPlugin implements ViaVersionAPI { @Override public void onEnable() { ViaVersion.setInstance(this); - System.out.println("ViaVersion enabled, injecting. (Allows 1.8 to be accessed via 1.9)"); + if(System.getProperty("ViaVersion") != null){ + getLogger().severe("ViaVersion is already loaded, we don't support reloads. Please reboot if you wish to update."); + return; + } + + getLogger().info("ViaVersion enabled, injecting. (Allows 1.8 to be accessed via 1.9)"); try { injectPacketHandler(); + System.setProperty("ViaVersion", getDescription().getVersion()); } catch (Exception e) { - if(Bukkit.getPluginManager().getPlugin("ProtocolLib") != null){ - System.out.println("This plugin is not compatible with protocol lib."); - } - System.out.println("Unable to inject handlers, are you on 1.8? "); + getLogger().severe("Unable to inject handlers, are you on 1.8? "); e.printStackTrace(); } Bukkit.getPluginManager().registerEvents(new Listener() { @@ -59,7 +62,7 @@ public class ViaVersionPlugin extends JavaPlugin implements ViaVersionAPI { List futures = ReflectionUtil.get(connection, "g", List.class); if (futures.size() == 0) { - throw new Exception("Could not find server to inject (late bind?)"); + throw new Exception("Could not find server to inject (Please ensure late-bind in your spigot.yml is false)"); } for (ChannelFuture future : futures) { diff --git a/src/main/java/us/myles/ViaVersion/handlers/ViaDecodeHandler.java b/src/main/java/us/myles/ViaVersion/handlers/ViaDecodeHandler.java new file mode 100644 index 000000000..7b607d009 --- /dev/null +++ b/src/main/java/us/myles/ViaVersion/handlers/ViaDecodeHandler.java @@ -0,0 +1,48 @@ +package us.myles.ViaVersion.handlers; + +import io.netty.buffer.ByteBuf; +import io.netty.channel.Channel; +import io.netty.channel.ChannelHandler; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundHandlerAdapter; +import io.netty.handler.codec.ByteToMessageDecoder; +import us.myles.ViaVersion.CancelException; +import us.myles.ViaVersion.ConnectionInfo; +import us.myles.ViaVersion.util.PacketUtil; +import us.myles.ViaVersion.transformers.IncomingTransformer; + +import java.util.List; + +public class ViaDecodeHandler extends ByteToMessageDecoder { + private final IncomingTransformer incomingTransformer; + private final ByteToMessageDecoder minecraftDecoder; + private final ConnectionInfo info; + + public ViaDecodeHandler(ConnectionInfo info, ByteToMessageDecoder minecraftDecoder) { + this.info = info; + this.minecraftDecoder = minecraftDecoder; + this.incomingTransformer = new IncomingTransformer(info); + } + + @Override + protected void decode(ChannelHandlerContext ctx, ByteBuf bytebuf, List list) throws Exception { + // use transformers + if(bytebuf.readableBytes() > 0) { + if(info.isActive()) { + int id = PacketUtil.readVarInt(bytebuf); + // Transform + ByteBuf newPacket = ctx.alloc().buffer(); + try { + incomingTransformer.transform(id, bytebuf, newPacket); + bytebuf = newPacket; + } catch (CancelException e) { + return; + } + } + // call minecraft decoder + list.addAll(PacketUtil.callDecode(this.minecraftDecoder, ctx, bytebuf)); + } + } + + +} diff --git a/src/main/java/us/myles/ViaVersion/handlers/ViaEncodeHandler.java b/src/main/java/us/myles/ViaVersion/handlers/ViaEncodeHandler.java new file mode 100644 index 000000000..6dff7a4e4 --- /dev/null +++ b/src/main/java/us/myles/ViaVersion/handlers/ViaEncodeHandler.java @@ -0,0 +1,72 @@ +package us.myles.ViaVersion.handlers; + +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.MessageToByteEncoder; +import us.myles.ViaVersion.CancelException; +import us.myles.ViaVersion.ConnectionInfo; +import us.myles.ViaVersion.transformers.OutgoingTransformer; +import us.myles.ViaVersion.util.PacketUtil; +import us.myles.ViaVersion.util.ReflectionUtil; + +import java.lang.reflect.Constructor; + +public class ViaEncodeHandler extends MessageToByteEncoder { + private final ConnectionInfo info; + private final MessageToByteEncoder minecraftEncoder; + private final OutgoingTransformer outgoingTransformer; + + public ViaEncodeHandler(ConnectionInfo info, MessageToByteEncoder minecraftEncoder) { + this.info = info; + this.minecraftEncoder = minecraftEncoder; + this.outgoingTransformer = new OutgoingTransformer(info); + } + + + @Override + protected void encode(ChannelHandlerContext ctx, Object o, ByteBuf bytebuf) throws Exception { + // handle the packet type + if (o == null) return; + if (!(o instanceof ByteBuf)) { + info.setLastPacket(o); + /* This transformer is more for fixing issues which we find hard at packet level :) */ + if (o.getClass().getName().endsWith("PacketPlayOutMapChunkBulk")) { + int[] locX = ReflectionUtil.get(o, "a", int[].class); + int[] locZ = ReflectionUtil.get(o, "b", int[].class); + + Object world = ReflectionUtil.get(o, "world", ReflectionUtil.nms("World")); + Class mapChunk = ReflectionUtil.nms("PacketPlayOutMapChunk"); + Constructor constructor = mapChunk.getDeclaredConstructor(ReflectionUtil.nms("Chunk"), boolean.class, int.class); + for (int i = 0; i < locX.length; i++) { + int x = locX[i]; + int z = locZ[i]; + // world invoke function + Object chunk = ReflectionUtil.nms("World").getDeclaredMethod("getChunkAt", int.class, int.class).invoke(world, x, z); + Object packet = constructor.newInstance(chunk, true, 65535); + ctx.pipeline().writeAndFlush(packet); + } + bytebuf.clear(); + return; + } + // call minecraft encoder + PacketUtil.callEncode(this.minecraftEncoder, ctx, o, bytebuf); + } + if (bytebuf.readableBytes() == 0) { + return; + } + if(info.isActive()) { + int id = PacketUtil.readVarInt(bytebuf); + // Transform + ByteBuf oldPacket = bytebuf.copy(); + bytebuf.clear(); + try { + outgoingTransformer.transform(id, oldPacket, bytebuf); + } catch (CancelException e) { + return; + } finally { + oldPacket.release(); + } + } + } + +} diff --git a/src/main/java/us/myles/ViaVersion/handlers/ViaInboundHandler.java b/src/main/java/us/myles/ViaVersion/handlers/ViaInboundHandler.java deleted file mode 100644 index 8423e5dc4..000000000 --- a/src/main/java/us/myles/ViaVersion/handlers/ViaInboundHandler.java +++ /dev/null @@ -1,51 +0,0 @@ -package us.myles.ViaVersion.handlers; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.Channel; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInboundHandlerAdapter; -import us.myles.ViaVersion.CancelException; -import us.myles.ViaVersion.ConnectionInfo; -import us.myles.ViaVersion.util.PacketUtil; -import us.myles.ViaVersion.transformers.IncomingTransformer; - -@ChannelHandler.Sharable -public class ViaInboundHandler extends ChannelInboundHandlerAdapter { - private final IncomingTransformer incomingTransformer; - - public ViaInboundHandler(ConnectionInfo info) { - this.incomingTransformer = new IncomingTransformer(info); - } - - @Override - public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { - boolean compression = ctx.pipeline().get("compress") != null; - - if (msg instanceof ByteBuf) { - ByteBuf bytebuf = (ByteBuf) msg; - if (compression) { - // decompress :) - bytebuf = PacketUtil.decompress(ctx, bytebuf); - } - int id = PacketUtil.readVarInt(bytebuf); - // Transform - ByteBuf newPacket = ctx.alloc().buffer(); - try { - incomingTransformer.transform(id, bytebuf, newPacket); - } catch (CancelException e) { - return; - } finally { - bytebuf.release(); - } - if (compression) { - // recompress :) - newPacket = PacketUtil.compress(ctx, newPacket); - } - msg = newPacket; - } - super.channelRead(ctx, msg); - } - - -} diff --git a/src/main/java/us/myles/ViaVersion/handlers/ViaOutboundHandler.java b/src/main/java/us/myles/ViaVersion/handlers/ViaOutboundHandler.java deleted file mode 100644 index c42b00dfb..000000000 --- a/src/main/java/us/myles/ViaVersion/handlers/ViaOutboundHandler.java +++ /dev/null @@ -1,53 +0,0 @@ -package us.myles.ViaVersion.handlers; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.*; -import us.myles.ViaVersion.CancelException; -import us.myles.ViaVersion.ConnectionInfo; -import us.myles.ViaVersion.util.PacketUtil; -import us.myles.ViaVersion.transformers.OutgoingTransformer; - -@ChannelHandler.Sharable -public class ViaOutboundHandler extends ChannelOutboundHandlerAdapter { - private final OutgoingTransformer outgoingTransformer; - private final ConnectionInfo info; - - public ViaOutboundHandler(ConnectionInfo info) { - this.info = info; - this.outgoingTransformer = new OutgoingTransformer(info); - } - - @Override - public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise channelPromise) throws Exception { - try { - if (channelPromise.isDone()) return; // don't break any <3s - boolean compression = ctx.pipeline().get("compress") != null; - if (msg instanceof ByteBuf) { - ByteBuf bytebuf = (ByteBuf) msg; - if (compression) { - // decompress :) - bytebuf = PacketUtil.decompress(ctx, bytebuf); - } - int id = PacketUtil.readVarInt(bytebuf); - // Transform - ByteBuf newPacket = ctx.alloc().buffer(); - try { - outgoingTransformer.transform(id, bytebuf, newPacket); - } catch (CancelException e) { - return; - } finally { - bytebuf.release(); - } - if (compression) { - // recompress :) - newPacket = PacketUtil.compress(ctx, newPacket); - } - msg = newPacket; - } - super.write(ctx, msg, channelPromise); - } catch (Exception e) { - e.printStackTrace(); - } - } - -} diff --git a/src/main/java/us/myles/ViaVersion/handlers/ViaOutboundPacketHandler.java b/src/main/java/us/myles/ViaVersion/handlers/ViaOutboundPacketHandler.java deleted file mode 100644 index 45ed2b823..000000000 --- a/src/main/java/us/myles/ViaVersion/handlers/ViaOutboundPacketHandler.java +++ /dev/null @@ -1,43 +0,0 @@ -package us.myles.ViaVersion.handlers; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.*; -import us.myles.ViaVersion.ConnectionInfo; -import us.myles.ViaVersion.util.ReflectionUtil; - -import java.lang.reflect.Constructor; - -@ChannelHandler.Sharable -public class ViaOutboundPacketHandler extends ChannelOutboundHandlerAdapter { - private final ConnectionInfo info; - - public ViaOutboundPacketHandler(ConnectionInfo info) { - this.info = info; - } - - @Override - public void write(ChannelHandlerContext channelHandlerContext, Object o, ChannelPromise channelPromise) throws Exception { - if (!(o instanceof ByteBuf)) { - info.setLastPacket(o); - /* This transformer is more for fixing issues which we find hard at byte level :) */ - if (o.getClass().getName().endsWith("PacketPlayOutMapChunkBulk")) { - int[] locX = ReflectionUtil.get(o, "a", int[].class); - int[] locZ = ReflectionUtil.get(o, "b", int[].class); - - Object world = ReflectionUtil.get(o, "world", ReflectionUtil.nms("World")); - Class mapChunk = ReflectionUtil.nms("PacketPlayOutMapChunk"); - Constructor constructor = mapChunk.getDeclaredConstructor(ReflectionUtil.nms("Chunk"), boolean.class, int.class); - for (int i = 0; i < locX.length; i++) { - int x = locX[i]; - int z = locZ[i]; - // world invoke function - Object chunk = ReflectionUtil.nms("World").getDeclaredMethod("getChunkAt", int.class, int.class).invoke(world, x, z); - Object packet = constructor.newInstance(chunk, true, 65535); - channelHandlerContext.write(packet); - } - return; - } - } - super.write(channelHandlerContext, o, channelPromise); - } -} diff --git a/src/main/java/us/myles/ViaVersion/handlers/ViaVersionInitializer.java b/src/main/java/us/myles/ViaVersion/handlers/ViaVersionInitializer.java index cb915227a..585ce7c48 100644 --- a/src/main/java/us/myles/ViaVersion/handlers/ViaVersionInitializer.java +++ b/src/main/java/us/myles/ViaVersion/handlers/ViaVersionInitializer.java @@ -3,6 +3,8 @@ package us.myles.ViaVersion.handlers; import io.netty.channel.Channel; import io.netty.channel.ChannelInitializer; import io.netty.channel.socket.SocketChannel; +import io.netty.handler.codec.ByteToMessageDecoder; +import io.netty.handler.codec.MessageToByteEncoder; import us.myles.ViaVersion.ConnectionInfo; import java.lang.reflect.Method; @@ -27,12 +29,10 @@ public class ViaVersionInitializer extends ChannelInitializer { // Add originals this.method.invoke(this.oldInit, socketChannel); // Add our transformers - ViaInboundHandler inbound = new ViaInboundHandler(info); - ViaOutboundHandler outbound = new ViaOutboundHandler(info); - ViaOutboundPacketHandler outbound2 = new ViaOutboundPacketHandler(info); - socketChannel.pipeline().addBefore("decoder", "via_incoming", inbound); - socketChannel.pipeline().addBefore("packet_handler", "via_outgoing2", outbound2); - socketChannel.pipeline().addBefore("encoder", "via_outgoing", outbound); + ViaEncodeHandler encoder = new ViaEncodeHandler(info, (MessageToByteEncoder) socketChannel.pipeline().get("encoder")); + ViaDecodeHandler decoder = new ViaDecodeHandler(info, (ByteToMessageDecoder) socketChannel.pipeline().get("decoder")); + socketChannel.pipeline().replace("encoder", "encoder", encoder); + socketChannel.pipeline().replace("decoder", "decoder", decoder); } } diff --git a/src/main/java/us/myles/ViaVersion/transformers/IncomingTransformer.java b/src/main/java/us/myles/ViaVersion/transformers/IncomingTransformer.java index 0fde66d16..b7c99669d 100644 --- a/src/main/java/us/myles/ViaVersion/transformers/IncomingTransformer.java +++ b/src/main/java/us/myles/ViaVersion/transformers/IncomingTransformer.java @@ -45,9 +45,7 @@ public class IncomingTransformer { if (protVer <= 102) { // not 1.9, remove pipes - info.getChannel().pipeline().remove("via_incoming"); - info.getChannel().pipeline().remove("via_outgoing"); - info.getChannel().pipeline().remove("via_outgoing2"); + info.setActive(false); } String serverAddress = PacketUtil.readString(input); PacketUtil.writeString(serverAddress, output); @@ -105,7 +103,7 @@ public class IncomingTransformer { try { Class setSlot = ReflectionUtil.nms("PacketPlayOutSetSlot"); Object setSlotPacket = setSlot.getConstructors()[1].newInstance(windowID, slot, null); - info.getChannel().writeAndFlush(setSlotPacket); // slot is empty + info.getChannel().pipeline().writeAndFlush(setSlotPacket); // slot is empty slot = -999; // we're evil, they'll throw item on the ground } catch (ClassNotFoundException e) { e.printStackTrace(); diff --git a/src/main/java/us/myles/ViaVersion/transformers/OutgoingTransformer.java b/src/main/java/us/myles/ViaVersion/transformers/OutgoingTransformer.java index d05c602b0..3f28e184f 100644 --- a/src/main/java/us/myles/ViaVersion/transformers/OutgoingTransformer.java +++ b/src/main/java/us/myles/ViaVersion/transformers/OutgoingTransformer.java @@ -290,7 +290,6 @@ public class OutgoingTransformer { int id = PacketUtil.readVarInt(input); clientEntityTypes.put(id, EntityType.EXPERIENCE_ORB); PacketUtil.writeVarInt(id, output); - double x = input.readInt(); output.writeDouble(x / 32D); double y = input.readInt(); @@ -309,7 +308,6 @@ public class OutgoingTransformer { PacketUtil.writeVarInt(id, output); PacketUtil.writeUUID(getUUID(id), output); - String title = PacketUtil.readString(input); PacketUtil.writeString(title, output); @@ -533,7 +531,8 @@ public class OutgoingTransformer { EntityType type = clientEntityTypes.get(entityID); if (type == null) { System.out.println("Unable to get entity for ID: " + entityID); - throw new CancelException(); + output.writeByte(255); + return; } if (dw != null) { short id = -1; diff --git a/src/main/java/us/myles/ViaVersion/util/PacketUtil.java b/src/main/java/us/myles/ViaVersion/util/PacketUtil.java index 8da0ede76..b6d1fa0da 100644 --- a/src/main/java/us/myles/ViaVersion/util/PacketUtil.java +++ b/src/main/java/us/myles/ViaVersion/util/PacketUtil.java @@ -42,29 +42,38 @@ public class PacketUtil { } } - public static ByteBuf decompress(ChannelHandlerContext ctx, ByteBuf msg) { - ByteToMessageDecoder x = (ByteToMessageDecoder) ctx.pipeline().get("decompress"); + public static List callDecode(ByteToMessageDecoder decoder, ChannelHandlerContext ctx, Object input) { List output = new ArrayList(); try { - PacketUtil.DECODE_METHOD.invoke(x, ctx, msg, output); + PacketUtil.DECODE_METHOD.invoke(decoder, ctx, input, output); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } + return output; + } + + public static void callEncode(MessageToByteEncoder encoder, ChannelHandlerContext ctx, Object msg, ByteBuf output) { + try { + PacketUtil.ENCODE_METHOD.invoke(encoder, ctx, msg, output); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } catch (InvocationTargetException e) { + e.printStackTrace(); + } + } + + public static ByteBuf decompress(ChannelHandlerContext ctx, ByteBuf msg) { + ByteToMessageDecoder x = (ByteToMessageDecoder) ctx.pipeline().get("decompress"); + List output = callDecode(x, ctx, msg); return output.size() == 0 ? null : (ByteBuf) output.get(0); } public static ByteBuf compress(ChannelHandlerContext ctx, ByteBuf msg) { MessageToByteEncoder x = (MessageToByteEncoder) ctx.pipeline().get("compress"); ByteBuf output = ctx.alloc().buffer(); - try { - PacketUtil.ENCODE_METHOD.invoke(x, ctx, msg, output); - } catch (IllegalAccessException e) { - e.printStackTrace(); - } catch (InvocationTargetException e) { - e.printStackTrace(); - } + callEncode(x, ctx, msg, output); return output; } @@ -185,9 +194,9 @@ public class PacketUtil { } public static void writeVarIntArray(List integers, ByteBuf output) { - writeVarInt(integers.size(),output); - for (Integer i : integers){ - writeVarInt(i,output); + writeVarInt(integers.size(), output); + for (Integer i : integers) { + writeVarInt(i, output); } } diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 5f2537806..9cf47154c 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -1,4 +1,6 @@ name: ViaVersion main: us.myles.ViaVersion.ViaVersionPlugin author: _MylesC -version: 0.3.7 \ No newline at end of file +version: 0.3.7 +load: startup +loadbefore: [ProtocolLib, ProxyPipe] \ No newline at end of file