diff --git a/README.md b/README.md index 1d23ee72c..4bd878f48 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,9 @@ -# ViaVersion 0.3.3 +# ViaVersion 0.4.5 **Allows the connection of 1.8 clients to 1.9** This plugin modifies netty to allow connection of 1.9 clients to 1.8, -**Don't use late bind nor ProtocolLib** +**Don't use late bind* **As of this point it doesn't have everything, I need to fix:** @@ -13,9 +13,6 @@ Remap spawn eggs If you have a bug with entities, please report the full stack trace -Some items with JSON data cause crashing due to the change in how strict minecraft is. - - This took hours of work, so if you enjoy this consider looking into contacting me and supporting my projects. @@ -31,10 +28,11 @@ Contributors: -------- **Myself** (harhar) - -**Matsv/StatBoom** - +**Matsv/StamBoom** **HugoDaBosss** +**SanderGielisse** +**Paulomart** +**gigosaurus** License: -------- diff --git a/src/main/java/org/spacehq/mc/protocol/data/game/chunk/BlockStorage.java b/src/main/java/org/spacehq/mc/protocol/data/game/chunk/BlockStorage.java index 273b195d6..055991c5f 100644 --- a/src/main/java/org/spacehq/mc/protocol/data/game/chunk/BlockStorage.java +++ b/src/main/java/org/spacehq/mc/protocol/data/game/chunk/BlockStorage.java @@ -1,7 +1,7 @@ package org.spacehq.mc.protocol.data.game.chunk; import io.netty.buffer.ByteBuf; -import us.myles.ViaVersion.PacketUtil; +import us.myles.ViaVersion.util.PacketUtil; import java.io.IOException; import java.util.ArrayList; diff --git a/src/main/java/org/spacehq/mc/protocol/util/NetUtil.java b/src/main/java/org/spacehq/mc/protocol/util/NetUtil.java index f8f332ccf..67dad9ce0 100644 --- a/src/main/java/org/spacehq/mc/protocol/util/NetUtil.java +++ b/src/main/java/org/spacehq/mc/protocol/util/NetUtil.java @@ -39,7 +39,7 @@ public class NetUtil { public static Column readOldChunkData(int x, int z, boolean isFullChunk, int bitmask, byte[] input, boolean checkForSky, boolean hasSkyLight) { int pos = 0; - int expected = 0; + int expected = isFullChunk ? 256 : 0; boolean sky = false; ShortBuffer buf = ByteBuffer.wrap(input).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer(); // 0 = Calculate expected length and determine if the packet has skylight. @@ -85,7 +85,7 @@ public class NetUtil { } if(pass == 3) { - if(chunks[ind].getSkyLight() != null) { + if(sky) { NibbleArray3d skylight = chunks[ind].getSkyLight(); System.arraycopy(input, pos, skylight.getData(), 0, skylight.getData().length); pos += skylight.getData().length; diff --git a/src/main/java/us/myles/ViaVersion/ConnectionInfo.java b/src/main/java/us/myles/ViaVersion/ConnectionInfo.java index bdb0651cd..4516fe804 100644 --- a/src/main/java/us/myles/ViaVersion/ConnectionInfo.java +++ b/src/main/java/us/myles/ViaVersion/ConnectionInfo.java @@ -1,5 +1,6 @@ package us.myles.ViaVersion; +import io.netty.channel.socket.SocketChannel; import org.bukkit.Bukkit; import org.bukkit.entity.Player; import us.myles.ViaVersion.packets.State; @@ -7,11 +8,17 @@ import us.myles.ViaVersion.packets.State; import java.util.UUID; public class ConnectionInfo { - private int protocol = 0; - private State state = State.HANDSHAKE; - private int compression = 0; + private final SocketChannel channel; 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; + } public int getProtocol() { return protocol; @@ -56,4 +63,16 @@ public class ConnectionInfo { public Player getPlayer() { return UUID == null ? null : Bukkit.getPlayer(UUID); } + + 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/Core.java b/src/main/java/us/myles/ViaVersion/ViaVersionPlugin.java similarity index 59% rename from src/main/java/us/myles/ViaVersion/Core.java rename to src/main/java/us/myles/ViaVersion/ViaVersionPlugin.java index cff6755f8..0bdaf7820 100644 --- a/src/main/java/us/myles/ViaVersion/Core.java +++ b/src/main/java/us/myles/ViaVersion/ViaVersionPlugin.java @@ -6,30 +6,52 @@ import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelPipeline; import io.netty.channel.socket.SocketChannel; import org.bukkit.Bukkit; -import org.bukkit.entity.Entity; import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerQuitEvent; import org.bukkit.inventory.ItemStack; import org.bukkit.plugin.java.JavaPlugin; -import us.myles.ViaVersion.handlers.ViaVersionInitializer; +import us.myles.ViaVersion.api.ViaVersion; +import us.myles.ViaVersion.api.ViaVersionAPI; +import us.myles.ViaVersion.handlers.ViaVersionInitializer; +import us.myles.ViaVersion.util.ReflectionUtil; + +import java.util.Collections; import java.util.List; +import java.util.Set; import java.util.UUID; import java.util.concurrent.Callable; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; -public class Core extends JavaPlugin { +public class ViaVersionPlugin extends JavaPlugin implements ViaVersionAPI { + + private final Set portedPlayers = Collections.newSetFromMap(new ConcurrentHashMap()); + @Override public void onEnable() { - System.out.println("ViaVersion enabled, injecting. (Allows 1.8 to be accessed via 1.9)"); + ViaVersion.setInstance(this); + 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() { + @EventHandler + public void onPlayerQuit(PlayerQuitEvent e) { + setPorted(e.getPlayer().getUniqueId(), false); + } + }, this); } public void injectPacketHandler() throws Exception { @@ -39,7 +61,7 @@ public class Core extends JavaPlugin { 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) { @@ -51,32 +73,22 @@ public class Core extends JavaPlugin { } } + @Override + public boolean isPorted(Player player) { + return portedPlayers.contains(player.getUniqueId()); + } - public static Entity getEntity(final UUID player, final int id) { - try { - return Bukkit.getScheduler().callSyncMethod(getPlugin(Core.class), new Callable() { - @Override - public Entity call() throws Exception { - Player p = Bukkit.getPlayer(player); - if (p == null) return null; - for (Entity e : p.getWorld().getEntities()) { - if (e.getEntityId() == id) { - return e; - } - } - return null; - } - }).get(10, TimeUnit.SECONDS); - } catch (Exception e) { - System.out.println("Error fetching entity "); - e.printStackTrace(); - return null; + public void setPorted(UUID id, boolean value) { + if (value) { + portedPlayers.add(id); + } else { + portedPlayers.remove(id); } } public static ItemStack getHandItem(final ConnectionInfo info) { try { - return Bukkit.getScheduler().callSyncMethod(getPlugin(Core.class), new Callable() { + return Bukkit.getScheduler().callSyncMethod(getPlugin(ViaVersionPlugin.class), new Callable() { @Override public ItemStack call() throws Exception { if (info.getPlayer() != null) { diff --git a/src/main/java/us/myles/ViaVersion/api/ViaVersion.java b/src/main/java/us/myles/ViaVersion/api/ViaVersion.java new file mode 100644 index 000000000..857e2ab86 --- /dev/null +++ b/src/main/java/us/myles/ViaVersion/api/ViaVersion.java @@ -0,0 +1,17 @@ +package us.myles.ViaVersion.api; + +public class ViaVersion { + + private static ViaVersionAPI INSTANCE; + + public static void setInstance(ViaVersionAPI api) { + if (INSTANCE != null) { + throw new IllegalStateException("Instance already set."); + } + INSTANCE = api; + } + + public static ViaVersionAPI getInstance() { + return INSTANCE; + } +} diff --git a/src/main/java/us/myles/ViaVersion/api/ViaVersionAPI.java b/src/main/java/us/myles/ViaVersion/api/ViaVersionAPI.java new file mode 100644 index 000000000..cdccc80ce --- /dev/null +++ b/src/main/java/us/myles/ViaVersion/api/ViaVersionAPI.java @@ -0,0 +1,8 @@ +package us.myles.ViaVersion.api; + +import org.bukkit.entity.Player; + +public interface ViaVersionAPI { + + boolean isPorted(Player player); +} 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..ad39cd125 --- /dev/null +++ b/src/main/java/us/myles/ViaVersion/handlers/ViaDecodeHandler.java @@ -0,0 +1,62 @@ +package us.myles.ViaVersion.handlers; + +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.ByteToMessageDecoder; +import us.myles.ViaVersion.CancelException; +import us.myles.ViaVersion.ConnectionInfo; +import us.myles.ViaVersion.transformers.IncomingTransformer; +import us.myles.ViaVersion.util.PacketUtil; + +import java.nio.channels.ClosedChannelException; +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) { + bytebuf.readBytes(bytebuf.readableBytes()); + throw e; + } + } + // call minecraft decoder + list.addAll(PacketUtil.callDecode(this.minecraftDecoder, ctx, bytebuf)); + } + } + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { + if (!(cause.getCause().getCause() instanceof CancelException) + && !(cause.getCause().getCause() instanceof ClosedChannelException)) { + if (!(cause.getCause() instanceof CancelException) + && !(cause.getCause() instanceof ClosedChannelException)) { + if (!(cause instanceof CancelException) + && !(cause instanceof ClosedChannelException)) { + if (cause instanceof Exception){ + cause.printStackTrace(); + } + } + } + } + } + +} 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..4cc88c572 --- /dev/null +++ b/src/main/java/us/myles/ViaVersion/handlers/ViaEncodeHandler.java @@ -0,0 +1,87 @@ +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; +import java.nio.channels.ClosedChannelException; + +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 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") && info.isActive()) { + 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.readBytes(bytebuf.readableBytes()); + throw new CancelException(); + } + // call minecraft encoder + PacketUtil.callEncode(this.minecraftEncoder, ctx, o, bytebuf); + } + if (bytebuf.readableBytes() == 0) { + throw new CancelException(); + } + if (info.isActive()) { + int id = PacketUtil.readVarInt(bytebuf); + // Transform + ByteBuf oldPacket = bytebuf.copy(); + bytebuf.clear(); + try { + outgoingTransformer.transform(id, oldPacket, bytebuf); + } catch (CancelException e) { + bytebuf.readBytes(bytebuf.readableBytes()); + throw e; + } finally { + oldPacket.release(); + } + } + } + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { + if (!(cause.getCause().getCause() instanceof CancelException) + && !(cause.getCause().getCause() instanceof ClosedChannelException)) { + if (!(cause.getCause() instanceof CancelException) + && !(cause.getCause() instanceof ClosedChannelException)) { + if (!(cause instanceof CancelException) + && !(cause instanceof ClosedChannelException)) { + if (cause instanceof Exception) + cause.printStackTrace(); + } + } + } + } +} 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 5751b99b0..000000000 --- a/src/main/java/us/myles/ViaVersion/handlers/ViaInboundHandler.java +++ /dev/null @@ -1,53 +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.PacketUtil; -import us.myles.ViaVersion.transformers.IncomingTransformer; - -@ChannelHandler.Sharable -public class ViaInboundHandler extends ChannelInboundHandlerAdapter { - private final IncomingTransformer incomingTransformer; - private final ViaVersionInitializer init; - - public ViaInboundHandler(Channel c, ConnectionInfo info, ViaVersionInitializer init) { - this.init = init; - this.incomingTransformer = new IncomingTransformer(c, info, init); - } - - @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 428fce04d..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.PacketUtil; -import us.myles.ViaVersion.transformers.OutgoingTransformer; - -@ChannelHandler.Sharable -public class ViaOutboundHandler extends ChannelOutboundHandlerAdapter { - private final OutgoingTransformer outgoingTransformer; - private final ViaVersionInitializer init; - - public ViaOutboundHandler(Channel c, ConnectionInfo info, ViaVersionInitializer init) { - this.init = init; - this.outgoingTransformer = new OutgoingTransformer(c, info, init); - } - - @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 29f6b889d..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.ReflectionUtil; - -import java.lang.reflect.Constructor; - -@ChannelHandler.Sharable -public class ViaOutboundPacketHandler extends ChannelOutboundHandlerAdapter { - private final ConnectionInfo info; - - public ViaOutboundPacketHandler(Channel c, 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 5bf25b7b5..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; @@ -10,11 +12,6 @@ import java.lang.reflect.Method; public class ViaVersionInitializer extends ChannelInitializer { private final ChannelInitializer oldInit; private Method method; - private ConnectionInfo info; - private ViaInboundHandler inbound; - private ViaOutboundHandler outbound; - private SocketChannel socketChannel; - private ViaOutboundPacketHandler outbound2; public ViaVersionInitializer(ChannelInitializer oldInit) { this.oldInit = oldInit; @@ -28,24 +25,14 @@ public class ViaVersionInitializer extends ChannelInitializer { @Override protected void initChannel(SocketChannel socketChannel) throws Exception { - info = new ConnectionInfo(); + ConnectionInfo info = new ConnectionInfo(socketChannel); // Add originals this.method.invoke(this.oldInit, socketChannel); // Add our transformers - this.socketChannel = socketChannel; - this.inbound = new ViaInboundHandler(socketChannel, info, this); - this.outbound = new ViaOutboundHandler(socketChannel, info, this); - this.outbound2 = new ViaOutboundPacketHandler(socketChannel, info); - socketChannel.pipeline().addBefore("decoder", "via_incoming", this.inbound); - socketChannel.pipeline().addBefore("packet_handler", "via_outgoing2", this.outbound2); - socketChannel.pipeline().addBefore("encoder", "via_outgoing", this.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); } - - public void remove(){ - socketChannel.pipeline().remove("via_incoming"); - socketChannel.pipeline().remove("via_outgoing"); - socketChannel.pipeline().remove("via_outgoing2"); - } - } diff --git a/src/main/java/us/myles/ViaVersion/metadata/MetaIndex.java b/src/main/java/us/myles/ViaVersion/metadata/MetaIndex.java index 50df771af..0224f5401 100644 --- a/src/main/java/us/myles/ViaVersion/metadata/MetaIndex.java +++ b/src/main/java/us/myles/ViaVersion/metadata/MetaIndex.java @@ -72,7 +72,7 @@ public enum MetaIndex { // creeper CREEPER_FUSE(Creeper.class, 16, Type.Byte, 11, NewType.VarInt), // -1 idle, 1 is fuse CREEPER_ISPOWERED(Creeper.class, 17, Type.Byte, 12, NewType.Boolean), - CREEPER_ISIGNITED(Creeper.class, 18, Type.Byte, 13, NewType.Boolean), // TODO: Write on wiki.vg for current prot + CREEPER_ISIGNITED(Creeper.class, 18, Type.Byte, 13, NewType.Boolean), // ghast GHAST_ISATTACKING(Ghast.class, 16, Type.Byte, 11, NewType.Boolean), // slime @@ -88,8 +88,10 @@ public enum MetaIndex { WITHER_TARGET2(Wither.class, 18, Type.Int, 12, NewType.VarInt), WITHER_TARGET3(Wither.class, 19, Type.Int, 13, NewType.VarInt), WITHER_INVULN_TIME(Wither.class, 20, Type.Int, 14, NewType.VarInt), + // wither skull + WITHERSKULL_INVULN(WitherSkull.class, 10, Type.Byte, 5, NewType.Boolean), // guardian - GUARDIAN_INFO(Guardian.class, 16, Type.Byte, 11, NewType.Byte), + GUARDIAN_INFO(Guardian.class, 16, Type.Int, 11, NewType.Byte), GUARDIAN_TARGET(Guardian.class, 17, Type.Int, 12, NewType.VarInt), // boat BOAT_SINCEHIT(Boat.class, 17, Type.Int, 5, NewType.VarInt), @@ -159,16 +161,6 @@ public enum MetaIndex { return this.clazz; } - public static MetaIndex getIndex(Entity entity, int index) { - EntityType type; - if (entity instanceof Player) { - type = EntityType.PLAYER; - } else { - type = entity.getType(); - } - return getIndex(type, index); - } - public static MetaIndex getIndex(EntityType type, int index) { Class entityClass = type.getEntityClass(); if(entityClass == null){ diff --git a/src/main/java/us/myles/ViaVersion/metadata/MetadataRewriter.java b/src/main/java/us/myles/ViaVersion/metadata/MetadataRewriter.java new file mode 100644 index 000000000..7a4d6770d --- /dev/null +++ b/src/main/java/us/myles/ViaVersion/metadata/MetadataRewriter.java @@ -0,0 +1,183 @@ +package us.myles.ViaVersion.metadata; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.UUID; + +import org.bukkit.entity.EntityType; +import org.bukkit.util.EulerAngle; +import org.bukkit.util.Vector; + +import io.netty.buffer.ByteBuf; + +import us.myles.ViaVersion.util.PacketUtil; + +public class MetadataRewriter { + + public static void writeMetadata1_9(EntityType type, List list, ByteBuf output) { + short id = -1; + int data = -1; + Iterator iterator = list.iterator(); + while (iterator.hasNext()) { + Entry entry = iterator.next(); // + MetaIndex metaIndex = entry.index; + try { + if (metaIndex.getNewType() != NewType.Discontinued) { + if (metaIndex.getNewType() != NewType.BlockID || id != -1 && data == -1 || id == -1 && data != -1) { // block ID is only written if we have both parts + output.writeByte(metaIndex.getNewIndex()); + output.writeByte(metaIndex.getNewType().getTypeID()); + } + Object value = entry.value; + switch (metaIndex.getNewType()) { + case Byte: + // convert from int, byte + if (metaIndex.getOldType() == Type.Byte) { + output.writeByte(((Byte) value).byteValue()); + } + if (metaIndex.getOldType() == Type.Int) { + output.writeByte(((Integer) value).byteValue()); + } + break; + case OptUUID: + String owner = (String) value; + UUID toWrite = null; + if (owner.length() != 0) { + try { + toWrite = UUID.fromString(owner); + } catch (Exception ignored) { + } + } + output.writeBoolean(toWrite != null); + if (toWrite != null) + PacketUtil.writeUUID((UUID) toWrite, output); + break; + case BlockID: + // if we have both sources :)) + if (metaIndex.getOldType() == Type.Byte) { + data = ((Byte) value).byteValue(); + } + if (metaIndex.getOldType() == Type.Short) { + id = ((Short) value).shortValue(); + } + if (id != -1 && data != -1) { + int combined = id << 4 | data; + data = -1; + id = -1; + PacketUtil.writeVarInt(combined, output); + } + break; + case VarInt: + // convert from int, short, byte + if (metaIndex.getOldType() == Type.Byte) { + PacketUtil.writeVarInt(((Byte) value).intValue(), output); + } + if (metaIndex.getOldType() == Type.Short) { + PacketUtil.writeVarInt(((Short) value).intValue(), output); + } + if (metaIndex.getOldType() == Type.Int) { + PacketUtil.writeVarInt(((Integer) value).intValue(), output); + } + break; + case Float: + output.writeFloat(((Float) value).floatValue()); + break; + case String: + PacketUtil.writeString((String) value, output); + break; + case Boolean: + output.writeBoolean(((Byte) value).byteValue() != 0); + break; + case Slot: + PacketUtil.writeItem(value, output); + break; + case Position: + Vector vector = (Vector) value; + output.writeInt((int) vector.getX()); + output.writeInt((int) vector.getY()); + output.writeInt((int) vector.getZ()); + break; + case Vector3F: + EulerAngle angle = (EulerAngle) value; + output.writeFloat((float) angle.getX()); + output.writeFloat((float) angle.getY()); + output.writeFloat((float) angle.getZ()); + break; + default: + System.out.println("[Out] Unhandled MetaDataType: " + metaIndex.getNewType()); + break; + } + } + } catch (Exception e) { + if (type != null) { + System.out.println("An error occurred with entity meta data for " + type); + if (metaIndex != null) { + System.out.println("Old ID: " + metaIndex.getIndex() + " New ID: " + metaIndex.getNewIndex()); + System.out.println("Old Type: " + metaIndex.getOldType() + " New Type: " + metaIndex.getNewType()); + } + } + e.printStackTrace(); + } + } + output.writeByte(255); + } + + public static List readMetadata1_8(EntityType entityType, ByteBuf buf) { + List entries = new ArrayList<>(); + byte item; + while ((item = buf.readByte()) != 127) { + Type type = Type.byId((item & 0xE0) >> 5); + int id = item & 0x1F; + MetaIndex index = MetaIndex.getIndex(entityType, id); + switch (type) { + case Byte: + entries.add(new Entry(index, buf.readByte())); + break; + case Short: + entries.add(new Entry(index, buf.readShort())); + break; + case Int: + entries.add(new Entry(index, buf.readInt())); + break; + case Float: + entries.add(new Entry(index, buf.readFloat())); + break; + case String: + entries.add(new Entry(index, PacketUtil.readString(buf))); + break; + case Slot: + entries.add(new Entry(index, PacketUtil.readItem(buf))); + break; + case Position: { + int x = buf.readInt(); + int y = buf.readInt(); + int z = buf.readInt(); + entries.add(new Entry(index, new Vector(x, y, z))); + break; + } + case Rotation: { + float x = buf.readFloat(); + float y = buf.readFloat(); + float z = buf.readFloat(); + entries.add(new Entry(index, new EulerAngle(x, y, z))); + break; + } + default: + System.out.println("[Out] Unhandled MetaDataType: " + type); + break; + } + } + return entries; + } + + public static class Entry { + + private MetaIndex index; + private Object value; + + private Entry(MetaIndex index, Object value) { + this.index = index; + this.value = value; + } + } +} diff --git a/src/main/java/us/myles/ViaVersion/metadata/Type.java b/src/main/java/us/myles/ViaVersion/metadata/Type.java index 48ceb3ac5..94c2300a0 100644 --- a/src/main/java/us/myles/ViaVersion/metadata/Type.java +++ b/src/main/java/us/myles/ViaVersion/metadata/Type.java @@ -18,4 +18,8 @@ public enum Type { public int getTypeID() { return typeID; } + + public static Type byId(int id) { + return values()[id]; + } } diff --git a/src/main/java/us/myles/ViaVersion/packets/PacketType.java b/src/main/java/us/myles/ViaVersion/packets/PacketType.java index 052485fea..e0dbd4066 100644 --- a/src/main/java/us/myles/ViaVersion/packets/PacketType.java +++ b/src/main/java/us/myles/ViaVersion/packets/PacketType.java @@ -20,7 +20,7 @@ public enum PacketType { /* Play serverbound */ PLAY_TP_CONFIRM(State.PLAY, Direction.INCOMING, -1, 0x00), PLAY_TAB_COMPLETE_REQUEST(State.PLAY, Direction.INCOMING, 0x14, 0x01), - PLAY_CLICHAT_MESSAGE_CLIENT(State.PLAY, Direction.INCOMING, 0x01, 0x02), + PLAY_CHAT_MESSAGE_CLIENT(State.PLAY, Direction.INCOMING, 0x01, 0x02), PLAY_CLIENT_STATUS(State.PLAY, Direction.INCOMING, 0x16, 0x03), PLAY_CLIENT_SETTINGS(State.PLAY, Direction.INCOMING, 0x15, 0x04), PLAY_CONFIRM_TRANS(State.PLAY, Direction.INCOMING, 0x0F, 0x05), @@ -96,7 +96,7 @@ public enum PacketType { PLAY_COMBAT_EVENT(State.PLAY, Direction.OUTGOING, 0x42, 0x2C), PLAY_PLAYER_LIST_ITEM(State.PLAY, Direction.OUTGOING, 0x38, 0x2D), PLAY_PLAYER_POSITION_LOOK(State.PLAY, Direction.OUTGOING, 0x08, 0x2E), - PLAY_USE_BED(State.PLAY, Direction.OUTGOING, 0x2F, 0x2F), + PLAY_USE_BED(State.PLAY, Direction.OUTGOING, 0x0A, 0x2F), PLAY_DESTROY_ENTITIES(State.PLAY, Direction.OUTGOING, 0x13, 0x30), PLAY_REMOVE_ENTITY_EFFECT(State.PLAY, Direction.OUTGOING, 0x1E, 0x31), PLAY_RESOURCE_PACK_SEND(State.PLAY, Direction.OUTGOING, 0x48, 0x32), diff --git a/src/main/java/us/myles/ViaVersion/transformers/IncomingTransformer.java b/src/main/java/us/myles/ViaVersion/transformers/IncomingTransformer.java index 9fa56fc9b..b7c99669d 100644 --- a/src/main/java/us/myles/ViaVersion/transformers/IncomingTransformer.java +++ b/src/main/java/us/myles/ViaVersion/transformers/IncomingTransformer.java @@ -1,28 +1,23 @@ package us.myles.ViaVersion.transformers; import io.netty.buffer.ByteBuf; -import io.netty.channel.Channel; - -import org.bukkit.Material; import org.bukkit.inventory.ItemStack; - -import us.myles.ViaVersion.*; -import us.myles.ViaVersion.handlers.ViaVersionInitializer; +import us.myles.ViaVersion.CancelException; +import us.myles.ViaVersion.ConnectionInfo; +import us.myles.ViaVersion.ViaVersionPlugin; import us.myles.ViaVersion.packets.PacketType; import us.myles.ViaVersion.packets.State; +import us.myles.ViaVersion.util.PacketUtil; +import us.myles.ViaVersion.util.ReflectionUtil; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; public class IncomingTransformer { - private final Channel channel; private final ConnectionInfo info; - private final ViaVersionInitializer init; - public IncomingTransformer(Channel channel, ConnectionInfo info, ViaVersionInitializer init) { - this.channel = channel; + public IncomingTransformer(ConnectionInfo info) { this.info = info; - this.init = init; } public void transform(int packetID, ByteBuf input, ByteBuf output) throws CancelException { @@ -49,8 +44,8 @@ public class IncomingTransformer { PacketUtil.writeVarInt(protVer <= 102 ? protVer : 47, output); // pretend to be older if (protVer <= 102) { - // Not 1.9 remove pipes - this.init.remove(); + // not 1.9, remove pipes + info.setActive(false); } String serverAddress = PacketUtil.readString(input); PacketUtil.writeString(serverAddress, output); @@ -108,7 +103,7 @@ public class IncomingTransformer { try { Class setSlot = ReflectionUtil.nms("PacketPlayOutSetSlot"); Object setSlotPacket = setSlot.getConstructors()[1].newInstance(windowID, slot, null); - channel.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(); @@ -178,7 +173,7 @@ public class IncomingTransformer { output.writeByte(face); int hand = PacketUtil.readVarInt(input); - ItemStack inHand = Core.getHandItem(info); + ItemStack inHand = ViaVersionPlugin.getHandItem(info); Object item = null; try { Method m = ReflectionUtil.obc("inventory.CraftItemStack").getDeclaredMethod("asNMSCopy", ItemStack.class); @@ -205,12 +200,12 @@ public class IncomingTransformer { } if (packet == PacketType.PLAY_USE_ITEM) { output.clear(); - PacketUtil.writeVarInt(PacketType.PLAY_PLAYER_BLOCK_PLACEMENT.getPacketID(), output); - // Simulate using item :) - output.writeLong(-1L); - output.writeByte(255); - // write item in hand - ItemStack inHand = Core.getHandItem(info); + PacketUtil.writeVarInt(PacketType.PLAY_PLAYER_BLOCK_PLACEMENT.getPacketID(), output); + // Simulate using item :) + output.writeLong(-1L); + output.writeByte(255); + // write item in hand + ItemStack inHand = ViaVersionPlugin.getHandItem(info); Object item = null; try { Method m = ReflectionUtil.obc("inventory.CraftItemStack").getDeclaredMethod("asNMSCopy", ItemStack.class); @@ -225,10 +220,10 @@ public class IncomingTransformer { e.printStackTrace(); } PacketUtil.writeItem(item, output); - - output.writeByte(-1); - output.writeByte(-1); - output.writeByte(-1); + + output.writeByte(-1); + output.writeByte(-1); + output.writeByte(-1); return; } output.writeBytes(input); diff --git a/src/main/java/us/myles/ViaVersion/transformers/OutgoingTransformer.java b/src/main/java/us/myles/ViaVersion/transformers/OutgoingTransformer.java index 7a40a28ff..ceda3a0f8 100644 --- a/src/main/java/us/myles/ViaVersion/transformers/OutgoingTransformer.java +++ b/src/main/java/us/myles/ViaVersion/transformers/OutgoingTransformer.java @@ -2,43 +2,37 @@ package us.myles.ViaVersion.transformers; import com.google.gson.Gson; import com.google.gson.JsonObject; - import io.netty.buffer.ByteBuf; -import io.netty.channel.Channel; - -import org.bukkit.Bukkit; -import org.bukkit.entity.Entity; +import org.bukkit.entity.EntityType; import org.spacehq.mc.protocol.data.game.chunk.Column; import org.spacehq.mc.protocol.util.NetUtil; - -import us.myles.ViaVersion.*; -import us.myles.ViaVersion.handlers.ViaVersionInitializer; -import us.myles.ViaVersion.metadata.MetaIndex; -import us.myles.ViaVersion.metadata.NewType; -import us.myles.ViaVersion.sounds.SoundEffect; -import us.myles.ViaVersion.metadata.Type; +import us.myles.ViaVersion.CancelException; +import us.myles.ViaVersion.ConnectionInfo; +import us.myles.ViaVersion.ViaVersionPlugin; +import us.myles.ViaVersion.api.ViaVersion; +import us.myles.ViaVersion.metadata.MetadataRewriter; import us.myles.ViaVersion.packets.PacketType; import us.myles.ViaVersion.packets.State; +import us.myles.ViaVersion.sounds.SoundEffect; +import us.myles.ViaVersion.util.EntityUtil; +import us.myles.ViaVersion.util.PacketUtil; +import us.myles.ViaVersion.util.ReflectionUtil; import java.io.IOException; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; import java.util.*; -import static us.myles.ViaVersion.PacketUtil.*; +import static us.myles.ViaVersion.util.PacketUtil.*; public class OutgoingTransformer { private static Gson gson = new Gson(); - private final Channel channel; private final ConnectionInfo info; - private final ViaVersionInitializer init; + private final ViaVersionPlugin plugin = (ViaVersionPlugin) ViaVersion.getInstance(); private boolean cancel = false; private Map uuidMap = new HashMap(); + private Map clientEntityTypes = new HashMap(); - public OutgoingTransformer(Channel channel, ConnectionInfo info, ViaVersionInitializer init) { - this.channel = channel; + public OutgoingTransformer(ConnectionInfo info) { this.info = info; - this.init = init; } public void transform(int packetID, ByteBuf input, ByteBuf output) throws CancelException { @@ -60,14 +54,13 @@ public class OutgoingTransformer { // By default no transform PacketUtil.writeVarInt(packetID, output); if (packet == PacketType.PLAY_NAMED_SOUND_EFFECT) { - String name = PacketUtil.readString(input); + String name = PacketUtil.readString(input); SoundEffect effect = SoundEffect.getByName(name); int catid = 0; String newname = name; - if(effect != null) - { - catid = effect.getCategory().getId(); - newname = effect.getNewName(); + if (effect != null) { + catid = effect.getCategory().getId(); + newname = effect.getNewName(); } PacketUtil.writeString(newname, output); PacketUtil.writeVarInt(catid, output); @@ -77,22 +70,37 @@ public class OutgoingTransformer { int passenger = input.readInt(); int vehicle = input.readInt(); boolean lead = input.readBoolean(); - if (!lead){ + if (!lead) { output.clear(); - writeVarInt(PacketType.PLAY_SET_PASSENGERS.getNewPacketID(),output); - writeVarInt(vehicle,output); - writeVarIntArray(Collections.singletonList(passenger),output); + writeVarInt(PacketType.PLAY_SET_PASSENGERS.getNewPacketID(), output); + writeVarInt(vehicle, output); + writeVarIntArray(Collections.singletonList(passenger), output); return; } output.writeInt(passenger); output.writeInt(vehicle); return; } - if (packet == PacketType.PLAY_DISCONNECT){ + if (packet == PacketType.PLAY_DISCONNECT) { String reason = readString(input); - if (reason.startsWith("\"")) - reason = "{\"text\":" + reason + "}"; - writeString(reason,output); + writeString(fixJson(reason), output); + return; + } + if (packet == PacketType.PLAY_TITLE) { + int action = PacketUtil.readVarInt(input); + PacketUtil.writeVarInt(action, output); + if (action == 0 || action == 1) { + String text = PacketUtil.readString(input); + PacketUtil.writeString(fixJson(text), output); + } + output.writeBytes(input); + return; + } + if (packet == PacketType.PLAY_PLAYER_LIST_HEADER_FOOTER) { + String header = readString(input); + String footer = readString(input); + writeString(fixJson(header), output); + writeString(fixJson(footer), output); return; } if (packet == PacketType.PLAY_ENTITY_TELEPORT) { @@ -173,7 +181,9 @@ public class OutgoingTransformer { if (packet == PacketType.LOGIN_SUCCESS) { String uu = PacketUtil.readString(input); PacketUtil.writeString(uu, output); - info.setUUID(UUID.fromString(uu)); + UUID uniqueId = UUID.fromString(uu); + info.setUUID(uniqueId); + plugin.setPorted(uniqueId, true); output.writeBytes(input); return; } @@ -198,30 +208,45 @@ public class OutgoingTransformer { int id = PacketUtil.readVarInt(input); PacketUtil.writeVarInt(id, output); - try { - List dw = ReflectionUtil.get(info.getLastPacket(), "b", List.class); - // get entity via entityID, not preferred but we need it. - Entity entity = Core.getEntity(info.getUUID(), id); - if (entity != null) { - transformMetadata(entity, dw, output); - } else { - // Died before we could get to it. rip - throw new CancelException(); - } - } catch (NoSuchFieldException e) { - e.printStackTrace(); - } catch (IllegalAccessException e) { - e.printStackTrace(); + transformMetadata(id, input, output); + return; + } + + if (packet == PacketType.PLAY_SPAWN_GLOBAL_ENTITY) { + int id = PacketUtil.readVarInt(input); + PacketUtil.writeVarInt(id, output); + + // only used for lightning + byte type = input.readByte(); + clientEntityTypes.put(id, EntityType.LIGHTNING); + output.writeByte(type); + + double x = input.readInt(); + output.writeDouble(x / 32D); + double y = input.readInt(); + output.writeDouble(y / 32D); + double z = input.readInt(); + output.writeDouble(z / 32D); + return; + } + if (packet == PacketType.PLAY_DESTROY_ENTITIES) { + int count = PacketUtil.readVarInt(input); + PacketUtil.writeVarInt(count, output); + + int[] toDestroy = PacketUtil.readVarInts(count, input); + for (int entityID : toDestroy) { + clientEntityTypes.remove(entityID); + PacketUtil.writeVarInt(entityID, output); } return; } if (packet == PacketType.PLAY_SPAWN_OBJECT) { int id = PacketUtil.readVarInt(input); PacketUtil.writeVarInt(id, output); - PacketUtil.writeUUID(getUUID(id), output); byte type = input.readByte(); + clientEntityTypes.put(id, EntityUtil.getTypeFromID(type, true)); output.writeByte(type); double x = input.readInt(); @@ -237,13 +262,12 @@ public class OutgoingTransformer { int data = input.readInt(); output.writeInt(data); - + short vX = 0, vY = 0, vZ = 0; - if(data > 0) - { - vX = input.readShort(); - vY = input.readShort(); - vZ = input.readShort(); + if (data > 0) { + vX = input.readShort(); + vY = input.readShort(); + vZ = input.readShort(); } output.writeShort(vX); output.writeShort(vY); @@ -251,10 +275,10 @@ public class OutgoingTransformer { return; } - if (packet == PacketType.PLAY_SPAWN_XP_ORB) { // TODO: Verify + if (packet == PacketType.PLAY_SPAWN_XP_ORB) { 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(); @@ -267,13 +291,43 @@ public class OutgoingTransformer { return; } + if (packet == PacketType.PLAY_SPAWN_PAINTING) { + int id = PacketUtil.readVarInt(input); + clientEntityTypes.put(id, EntityType.PAINTING); + PacketUtil.writeVarInt(id, output); + + PacketUtil.writeUUID(getUUID(id), output); + String title = PacketUtil.readString(input); + PacketUtil.writeString(title, output); + + long[] position = PacketUtil.readBlockPosition(input); + PacketUtil.writeBlockPosition(output, position[0], position[1], position[2]); + + byte direction = input.readByte(); + output.writeByte(direction); + + return; + } + if (packet == PacketType.PLAY_OPEN_WINDOW) { + int windowId = input.readUnsignedByte(); + String type = readString(input); + String windowTitle = readString(input); + + output.writeByte(windowId); + writeString(type, output); + writeString(fixJson(windowTitle), output); + output.writeBytes(input); + return; + } if (packet == PacketType.PLAY_SPAWN_MOB) { int id = PacketUtil.readVarInt(input); PacketUtil.writeVarInt(id, output); PacketUtil.writeUUID(getUUID(id), output); short type = input.readUnsignedByte(); + clientEntityTypes.put(id, EntityUtil.getTypeFromID(type, false)); output.writeByte(type); + double x = input.readInt(); output.writeDouble(x / 32D); double y = input.readInt(); @@ -293,16 +347,8 @@ public class OutgoingTransformer { output.writeShort(vY); short vZ = input.readShort(); output.writeShort(vZ); - try { - Object dataWatcher = ReflectionUtil.get(info.getLastPacket(), "l", ReflectionUtil.nms("DataWatcher")); - transformMetadata(dataWatcher, output); - } catch (NoSuchFieldException e) { - e.printStackTrace(); - } catch (IllegalAccessException e) { - e.printStackTrace(); - } catch (ClassNotFoundException e) { - e.printStackTrace(); - } + + transformMetadata(id, input, output); return; } if (packet == PacketType.PLAY_UPDATE_SIGN) { @@ -310,21 +356,28 @@ public class OutgoingTransformer { output.writeLong(location); for (int i = 0; i < 4; i++) { String line = PacketUtil.readString(input); - if (line == null || line.equalsIgnoreCase("null")) { - line = "{\"text\":\"\"}"; - } else { - if (!line.startsWith("\"") && !line.startsWith("{")) - line = "\"" + line + "\""; - if (line.startsWith("\"")) - line = "{\"text\":" + line + "}"; - } - PacketUtil.writeString(line, output); + PacketUtil.writeString(fixJson(line), output); } } + if (packet == PacketType.PLAY_CHAT_MESSAGE) { + String chat = PacketUtil.readString(input); + PacketUtil.writeString(fixJson(chat), output); + + byte pos = input.readByte(); + output.writeByte(pos); + return; + } + if (packet == PacketType.PLAY_JOIN_GAME) { + int id = input.readInt(); + clientEntityTypes.put(id, EntityType.PLAYER); + output.writeInt(id); + output.writeBytes(input); + return; + } if (packet == PacketType.PLAY_SPAWN_PLAYER) { int id = PacketUtil.readVarInt(input); PacketUtil.writeVarInt(id, output); - + clientEntityTypes.put(id, EntityType.PLAYER); UUID playerUUID = PacketUtil.readUUID(input); PacketUtil.writeUUID(playerUUID, output); @@ -339,20 +392,15 @@ public class OutgoingTransformer { output.writeByte(pitch); byte yaw = input.readByte(); output.writeByte(yaw); - try { - Object dataWatcher = ReflectionUtil.get(info.getLastPacket(), "i", ReflectionUtil.nms("DataWatcher")); - transformMetadata(dataWatcher, output); - } catch (NoSuchFieldException e) { - e.printStackTrace(); - } catch (IllegalAccessException e) { - e.printStackTrace(); - } catch (ClassNotFoundException e) { - e.printStackTrace(); - } + + // next field is Current Item, this was removed in 1.9 so we'll ignore it + input.readShort(); + + transformMetadata(id, input, output); return; } - if(packet == PacketType.PLAY_MAP) { + if (packet == PacketType.PLAY_MAP) { int damage = PacketUtil.readVarInt(input); PacketUtil.writeVarInt(damage, output); byte scale = input.readByte(); @@ -430,141 +478,27 @@ public class OutgoingTransformer { output.writeBytes(input); } - private void transformMetadata(Object dw, ByteBuf output) { - // get entity - try { - Class nmsClass = ReflectionUtil.nms("Entity"); - Object nmsEntity = ReflectionUtil.get(dw, "a", nmsClass); - Class craftClass = ReflectionUtil.obc("entity.CraftEntity"); - Method bukkitMethod = craftClass.getDeclaredMethod("getEntity", ReflectionUtil.obc("CraftServer"), nmsClass); - - Object entity = bukkitMethod.invoke(null, Bukkit.getServer(), nmsEntity); - transformMetadata((Entity) entity, (List) ReflectionUtil.invoke(dw, "b"), output); - } catch (NoSuchFieldException e) { - e.printStackTrace(); - } catch (IllegalAccessException e) { - e.printStackTrace(); - } catch (NoSuchMethodException e) { - e.printStackTrace(); - } catch (InvocationTargetException e) { - e.printStackTrace(); - } catch (ClassNotFoundException e) { - e.printStackTrace(); + private String fixJson(String line) { + if (line == null || line.equalsIgnoreCase("null")) { + line = "{\"text\":\"\"}"; + } else { + if (!line.startsWith("\"") && !line.startsWith("{")) + line = "\"" + line + "\""; + if (line.startsWith("\"")) + line = "{\"text\":" + line + "}"; } + return line; } - private void transformMetadata(Entity entity, List dw, ByteBuf output) { - if (dw != null) { - short id = -1; - int data = -1; - - Iterator iterator = dw.iterator(); - while (iterator.hasNext()) { - Object watchableObj = iterator.next(); // - MetaIndex metaIndex = null; - try { - metaIndex = MetaIndex.getIndex(entity, (int) ReflectionUtil.invoke(watchableObj, "a")); - } catch (NoSuchMethodException e) { - e.printStackTrace(); - } catch (InvocationTargetException e) { - e.printStackTrace(); - } catch (IllegalAccessException e) { - e.printStackTrace(); - } - try { - if (metaIndex.getNewType() != NewType.Discontinued) { - if (metaIndex.getNewType() != NewType.BlockID || id != -1 && data == -1 || id == -1 && data != -1) { // block ID is only written if we have both parts - output.writeByte(metaIndex.getNewIndex()); - output.writeByte(metaIndex.getNewType().getTypeID()); - } - Object value = ReflectionUtil.invoke(watchableObj, "b"); - switch (metaIndex.getNewType()) { - case Byte: - // convert from int, byte - if (metaIndex.getOldType() == Type.Byte) { - output.writeByte(((Byte) value).byteValue()); - } - if (metaIndex.getOldType() == Type.Int) { - output.writeByte(((Integer) value).byteValue()); - } - break; - case OptUUID: - String owner = (String) value; - UUID toWrite = null; - if (owner.length() != 0) { - try { - toWrite = UUID.fromString(owner); - } catch (Exception ignored) { - } - } - output.writeBoolean(toWrite != null); - if (toWrite != null) - PacketUtil.writeUUID((UUID) toWrite, output); - break; - case BlockID: - // if we have both sources :)) - if (metaIndex.getOldType() == Type.Byte) { - data = ((Byte) value).byteValue(); - } - if (metaIndex.getOldType() == Type.Short) { - id = ((Short) value).shortValue(); - } - if (id != -1 && data != -1) { - int combined = id << 4 | data; - data = -1; - id = -1; - PacketUtil.writeVarInt(combined, output); - } - break; - case VarInt: - // convert from int, short, byte - if (metaIndex.getOldType() == Type.Byte) { - PacketUtil.writeVarInt(((Byte) value).intValue(), output); - } - if (metaIndex.getOldType() == Type.Short) { - PacketUtil.writeVarInt(((Short) value).intValue(), output); - } - if (metaIndex.getOldType() == Type.Int) { - PacketUtil.writeVarInt(((Integer) value).intValue(), output); - } - break; - case Float: - output.writeFloat(((Float) value).floatValue()); - break; - case String: - PacketUtil.writeString((String) value, output); - break; - case Boolean: - output.writeBoolean(((Byte) value).byteValue() != 0); - break; - case Slot: - PacketUtil.writeItem(value, output); - break; - case Position: - output.writeInt((int) ReflectionUtil.invoke(value, "getX")); - output.writeInt((int) ReflectionUtil.invoke(value, "getY")); - output.writeInt((int) ReflectionUtil.invoke(value, "getZ")); - break; - case Vector3F: - output.writeFloat((float) ReflectionUtil.invoke(value, "getX")); - output.writeFloat((float) ReflectionUtil.invoke(value, "getY")); - output.writeFloat((float) ReflectionUtil.invoke(value, "getZ")); - } - - } - } catch (Exception e) { - if (entity != null) { - System.out.println("An error occurred with entity meta data for " + entity.getType()); - System.out.println("Old ID: " + metaIndex.getIndex() + " New ID: " + metaIndex.getNewIndex()); - System.out.println("Old Type: " + metaIndex.getOldType() + " New Type: " + metaIndex.getNewType()); - } - e.printStackTrace(); - } - } + private void transformMetadata(int entityID, ByteBuf input, ByteBuf output) throws CancelException { + EntityType type = clientEntityTypes.get(entityID); + if (type == null) { + System.out.println("Unable to get entity for ID: " + entityID); + output.writeByte(255); + return; } - output.writeByte(255); - - + List list = MetadataRewriter.readMetadata1_8(type, input); + MetadataRewriter.writeMetadata1_9(type, list, output); } diff --git a/src/main/java/us/myles/ViaVersion/util/EntityUtil.java b/src/main/java/us/myles/ViaVersion/util/EntityUtil.java new file mode 100644 index 000000000..8095e4722 --- /dev/null +++ b/src/main/java/us/myles/ViaVersion/util/EntityUtil.java @@ -0,0 +1,91 @@ +package us.myles.ViaVersion.util; + +import org.bukkit.entity.EntityType; + +public class EntityUtil { + public static EntityType getTypeFromID(int typeID, boolean isObject) { + if (isObject) { + return getObjectFromID(typeID); + } else { + return EntityType.fromId(typeID); + } + } + + // based on http://wiki.vg/index.php?title=Entities + public static EntityType getObjectFromID(int objectID) { + EntityType type; + switch (objectID) { + case 2: + type = EntityType.DROPPED_ITEM; + break; + case 77: + type = EntityType.LEASH_HITCH; + break; + case 60: + type = EntityType.ARROW; + break; + case 61: + type = EntityType.SNOWBALL; + break; + case 63: + type = EntityType.FIREBALL; + break; + case 64: + type = EntityType.SMALL_FIREBALL; + break; + case 65: + type = EntityType.ENDER_PEARL; + break; + case 72: + type = EntityType.ENDER_SIGNAL; + break; + case 75: + type = EntityType.THROWN_EXP_BOTTLE; + break; + case 71: + type = EntityType.ITEM_FRAME; + break; + case 66: + type = EntityType.WITHER_SKULL; + break; + case 50: + type = EntityType.PRIMED_TNT; + break; + case 70: + type = EntityType.FALLING_BLOCK; + break; + case 76: + type = EntityType.FIREWORK; + break; + case 78: + type = EntityType.ARMOR_STAND; + break; + case 1: + type = EntityType.BOAT; + break; + case 10: + type = EntityType.MINECART; + break; + case 51: + type = EntityType.ENDER_CRYSTAL; + break; + case 73: + type = EntityType.SPLASH_POTION; + break; + case 62: + type = EntityType.EGG; + break; + case 90: + type = EntityType.FISHING_HOOK; + break; + default: + type = EntityType.fromId(objectID); + if (type == null) { + System.out.println("Unable to find entity type for " + objectID); + type = EntityType.UNKNOWN; + } + break; + } + return type; + } +} diff --git a/src/main/java/us/myles/ViaVersion/PacketUtil.java b/src/main/java/us/myles/ViaVersion/util/PacketUtil.java similarity index 85% rename from src/main/java/us/myles/ViaVersion/PacketUtil.java rename to src/main/java/us/myles/ViaVersion/util/PacketUtil.java index cacd6929a..1c2dde784 100644 --- a/src/main/java/us/myles/ViaVersion/PacketUtil.java +++ b/src/main/java/us/myles/ViaVersion/util/PacketUtil.java @@ -1,4 +1,4 @@ -package us.myles.ViaVersion; +package us.myles.ViaVersion.util; import com.google.common.base.Charsets; import com.google.common.base.Preconditions; @@ -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); } } @@ -363,4 +372,46 @@ public class PacketUtil { e.printStackTrace(); } } + + public static Object readItem(ByteBuf output) { + try { + Class serializer = ReflectionUtil.nms("PacketDataSerializer"); + Object init = serializer.getDeclaredConstructor(ByteBuf.class).newInstance(output); + Method toCall = init.getClass().getDeclaredMethod("i"); + return toCall.invoke(init); + } catch (ClassNotFoundException e) { + e.printStackTrace(); + } catch (NoSuchMethodException e) { + e.printStackTrace(); + } catch (InstantiationException e) { + e.printStackTrace(); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } catch (InvocationTargetException e) { + e.printStackTrace(); + } + return null; + } + + public static long[] readBlockPosition(ByteBuf buf) { + long val = buf.readLong(); + long x = (val >> 38); // signed + long y = (val >> 26) & 0xfff; // unsigned + // this shifting madness is used to preserve sign + long z = (val << 38) >> 38; // signed + return new long[]{x, y, z}; + } + + public static void writeBlockPosition(ByteBuf buf, long x, long y, long z) { + buf.writeLong(((x & 0x3ffffff) << 38) | ((y & 0xfff) << 26) | (z & 0x3ffffff)); + } + + public static int[] readVarInts(int amount, ByteBuf input) { + int data[] = new int[amount]; + for (int index = 0; index < amount; index++) { + data[index] = PacketUtil.readVarInt(input); + } + + return data; + } } diff --git a/src/main/java/us/myles/ViaVersion/ReflectionUtil.java b/src/main/java/us/myles/ViaVersion/util/ReflectionUtil.java similarity index 97% rename from src/main/java/us/myles/ViaVersion/ReflectionUtil.java rename to src/main/java/us/myles/ViaVersion/util/ReflectionUtil.java index a77c32ed0..e5f19187e 100644 --- a/src/main/java/us/myles/ViaVersion/ReflectionUtil.java +++ b/src/main/java/us/myles/ViaVersion/util/ReflectionUtil.java @@ -1,4 +1,4 @@ -package us.myles.ViaVersion; +package us.myles.ViaVersion.util; import org.bukkit.Bukkit; diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 4dcca77dc..59982eef5 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -1,4 +1,6 @@ name: ViaVersion -main: us.myles.ViaVersion.Core +main: us.myles.ViaVersion.ViaVersionPlugin author: _MylesC -version: 0.3.3 \ No newline at end of file +version: 0.4.5 +load: startup +loadbefore: [ProtocolLib, ProxyPipe] \ No newline at end of file