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