diff --git a/common/src/main/java/us/myles/ViaVersion/api/PacketWrapper.java b/common/src/main/java/us/myles/ViaVersion/api/PacketWrapper.java index 7dfba2c7b..29f583064 100644 --- a/common/src/main/java/us/myles/ViaVersion/api/PacketWrapper.java +++ b/common/src/main/java/us/myles/ViaVersion/api/PacketWrapper.java @@ -10,10 +10,12 @@ import us.myles.ViaVersion.api.protocol.Protocol; import us.myles.ViaVersion.api.remapper.ValueCreator; import us.myles.ViaVersion.api.type.Type; import us.myles.ViaVersion.api.type.TypeConverter; +import us.myles.ViaVersion.exception.CancelException; import us.myles.ViaVersion.exception.InformativeException; import us.myles.ViaVersion.packets.Direction; import us.myles.ViaVersion.packets.State; import us.myles.ViaVersion.protocols.base.ProtocolInfo; +import us.myles.ViaVersion.util.PipelineUtil; import java.io.IOException; import java.util.ArrayList; @@ -298,8 +300,14 @@ public class PacketWrapper { */ public void send(Class packetProtocol, boolean skipCurrentPipeline, boolean currentThread) throws Exception { if (!isCancelled()) { - ByteBuf output = constructPacket(packetProtocol, skipCurrentPipeline, Direction.OUTGOING); - user().sendRawPacket(output, currentThread); + try { + ByteBuf output = constructPacket(packetProtocol, skipCurrentPipeline, Direction.OUTGOING); + user().sendRawPacket(output, currentThread); + } catch (Exception e) { + if (!PipelineUtil.containsCause(e, CancelException.class)) { + throw e; + } + } } } @@ -493,8 +501,14 @@ public class PacketWrapper { */ public void sendToServer(Class packetProtocol, boolean skipCurrentPipeline, boolean currentThread) throws Exception { if (!isCancelled()) { - ByteBuf output = constructPacket(packetProtocol, skipCurrentPipeline, Direction.INCOMING); - user().sendRawPacketToServer(output, currentThread); + try { + ByteBuf output = constructPacket(packetProtocol, skipCurrentPipeline, Direction.INCOMING); + user().sendRawPacketToServer(output, currentThread); + } catch (Exception e) { + if (!PipelineUtil.containsCause(e, CancelException.class)) { + throw e; + } + } } } diff --git a/common/src/main/java/us/myles/ViaVersion/api/data/UserConnection.java b/common/src/main/java/us/myles/ViaVersion/api/data/UserConnection.java index 566240937..1f5e85ee9 100644 --- a/common/src/main/java/us/myles/ViaVersion/api/data/UserConnection.java +++ b/common/src/main/java/us/myles/ViaVersion/api/data/UserConnection.java @@ -214,32 +214,41 @@ public class UserConnection { public void sendRawPacketToServer(final ByteBuf packet, boolean currentThread) { final ByteBuf buf = packet.alloc().buffer(); try { - Type.VAR_INT.write(buf, PacketWrapper.PASSTHROUGH_ID); - } catch (Exception e) { - // Should not happen - Via.getPlatform().getLogger().warning("Type.VAR_INT.write thrown an exception: " + e); - } - buf.writeBytes(packet); - packet.release(); - final ChannelHandlerContext context = PipelineUtil - .getPreviousContext(Via.getManager().getInjector().getDecoderName(), getChannel().pipeline()); - if (currentThread) { - if (context != null) { - context.fireChannelRead(buf); - } else { - getChannel().pipeline().fireChannelRead(buf); + try { + Type.VAR_INT.write(buf, PacketWrapper.PASSTHROUGH_ID); + } catch (Exception e) { + // Should not happen + Via.getPlatform().getLogger().warning("Type.VAR_INT.write thrown an exception: " + e); } - } else { - channel.eventLoop().submit(new Runnable() { - @Override - public void run() { - if (context != null) { - context.fireChannelRead(buf); - } else { - getChannel().pipeline().fireChannelRead(buf); - } + buf.writeBytes(packet); + final ChannelHandlerContext context = PipelineUtil + .getPreviousContext(Via.getManager().getInjector().getDecoderName(), getChannel().pipeline()); + if (currentThread) { + if (context != null) { + context.fireChannelRead(buf); + } else { + getChannel().pipeline().fireChannelRead(buf); } - }); + } else { + try { + channel.eventLoop().submit(new Runnable() { + @Override + public void run() { + if (context != null) { + context.fireChannelRead(buf); + } else { + getChannel().pipeline().fireChannelRead(buf); + } + } + }); + } catch (Throwable t) { + // Couldn't schedule + buf.release(); + throw t; + } + } + } finally { + packet.release(); } } diff --git a/common/src/main/java/us/myles/ViaVersion/protocols/protocol1_13to1_12_2/types/Chunk1_13Type.java b/common/src/main/java/us/myles/ViaVersion/protocols/protocol1_13to1_12_2/types/Chunk1_13Type.java index ac030d099..804d69531 100644 --- a/common/src/main/java/us/myles/ViaVersion/protocols/protocol1_13to1_12_2/types/Chunk1_13Type.java +++ b/common/src/main/java/us/myles/ViaVersion/protocols/protocol1_13to1_12_2/types/Chunk1_13Type.java @@ -81,20 +81,23 @@ public class Chunk1_13Type extends PartialType { Type.VAR_INT.write(output, chunk.getBitmask()); ByteBuf buf = output.alloc().buffer(); - for (int i = 0; i < 16; i++) { - ChunkSection section = chunk.getSections()[i]; - if (section == null) continue; // Section not set - Types1_13.CHUNK_SECTION.write(buf, section); - section.writeBlockLight(buf); + try { + for (int i = 0; i < 16; i++) { + ChunkSection section = chunk.getSections()[i]; + if (section == null) continue; // Section not set + Types1_13.CHUNK_SECTION.write(buf, section); + section.writeBlockLight(buf); - if (!section.hasSkyLight()) continue; // No sky light, we're done here. - section.writeSkyLight(buf); + if (!section.hasSkyLight()) continue; // No sky light, we're done here. + section.writeSkyLight(buf); + } + buf.readerIndex(0); + Type.VAR_INT.write(output, buf.readableBytes() + (chunk.isBiomeData() ? 256 * 4 : 0)); + output.writeBytes(buf); + } finally { + buf.release(); // release buffer } - buf.readerIndex(0); - Type.VAR_INT.write(output, buf.readableBytes() + (chunk.isBiomeData() ? 256 * 4 : 0)); - output.writeBytes(buf); - buf.release(); // release buffer // Write biome data if (chunk.isBiomeData()) { diff --git a/common/src/main/java/us/myles/ViaVersion/protocols/protocol1_14to1_13_2/packets/WorldPackets.java b/common/src/main/java/us/myles/ViaVersion/protocols/protocol1_14to1_13_2/packets/WorldPackets.java index ee73fabd5..43b1e9156 100644 --- a/common/src/main/java/us/myles/ViaVersion/protocols/protocol1_14to1_13_2/packets/WorldPackets.java +++ b/common/src/main/java/us/myles/ViaVersion/protocols/protocol1_14to1_13_2/packets/WorldPackets.java @@ -184,8 +184,8 @@ public class WorldPackets { } lightPacket.write(Type.VAR_INT, skyLightMask); lightPacket.write(Type.VAR_INT, blockLightMask); - lightPacket.write(Type.VAR_INT, 0); //TODO find out what these two bitmasks mean - lightPacket.write(Type.VAR_INT, 0); //TODO + lightPacket.write(Type.VAR_INT, 0); // empty sky light mask + lightPacket.write(Type.VAR_INT, 0); // empty block light mask for (ChunkSection section : chunk.getSections()) { if (section == null || !section.hasSkyLight()) continue; lightPacket.write(Type.BYTE_ARRAY, Bytes.asList(section.getSkyLight()).toArray(new Byte[0])); @@ -209,7 +209,7 @@ public class WorldPackets { entityTracker.setChunkCenterZ(chunk.getZ()); } - lightPacket.send(Protocol1_14To1_13_2.class, true, false); + lightPacket.send(Protocol1_14To1_13_2.class, true, true); } }); } diff --git a/common/src/main/java/us/myles/ViaVersion/protocols/protocol1_14to1_13_2/types/Chunk1_14Type.java b/common/src/main/java/us/myles/ViaVersion/protocols/protocol1_14to1_13_2/types/Chunk1_14Type.java index 6c50ee7f9..b9e4e4f2e 100644 --- a/common/src/main/java/us/myles/ViaVersion/protocols/protocol1_14to1_13_2/types/Chunk1_14Type.java +++ b/common/src/main/java/us/myles/ViaVersion/protocols/protocol1_14to1_13_2/types/Chunk1_14Type.java @@ -81,16 +81,19 @@ public class Chunk1_14Type extends PartialType { Type.NBT.write(output, chunk.getHeightMap()); ByteBuf buf = output.alloc().buffer(); - for (int i = 0; i < 16; i++) { - ChunkSection section = chunk.getSections()[i]; - if (section == null) continue; // Section not set - buf.writeShort(section.getNonAirBlocksCount()); - Types1_13.CHUNK_SECTION.write(buf, section); + try { + for (int i = 0; i < 16; i++) { + ChunkSection section = chunk.getSections()[i]; + if (section == null) continue; // Section not set + buf.writeShort(section.getNonAirBlocksCount()); + Types1_13.CHUNK_SECTION.write(buf, section); + } + buf.readerIndex(0); + Type.VAR_INT.write(output, buf.readableBytes() + (chunk.isBiomeData() ? 256 * 4 : 0)); + output.writeBytes(buf); + } finally { + buf.release(); // release buffer } - buf.readerIndex(0); - Type.VAR_INT.write(output, buf.readableBytes() + (chunk.isBiomeData() ? 256 * 4 : 0)); - output.writeBytes(buf); - buf.release(); // release buffer // Write biome data if (chunk.isBiomeData()) { diff --git a/common/src/main/java/us/myles/ViaVersion/protocols/protocol1_9_1_2to1_9_3_4/types/Chunk1_9_3_4Type.java b/common/src/main/java/us/myles/ViaVersion/protocols/protocol1_9_1_2to1_9_3_4/types/Chunk1_9_3_4Type.java index f0ea88144..2284b92ae 100644 --- a/common/src/main/java/us/myles/ViaVersion/protocols/protocol1_9_1_2to1_9_3_4/types/Chunk1_9_3_4Type.java +++ b/common/src/main/java/us/myles/ViaVersion/protocols/protocol1_9_1_2to1_9_3_4/types/Chunk1_9_3_4Type.java @@ -82,20 +82,23 @@ public class Chunk1_9_3_4Type extends PartialType { Type.VAR_INT.write(output, chunk.getBitmask()); ByteBuf buf = output.alloc().buffer(); - for (int i = 0; i < 16; i++) { - ChunkSection section = chunk.getSections()[i]; - if (section == null) continue; // Section not set - Types1_9.CHUNK_SECTION.write(buf, section); - section.writeBlockLight(buf); + try { + for (int i = 0; i < 16; i++) { + ChunkSection section = chunk.getSections()[i]; + if (section == null) continue; // Section not set + Types1_9.CHUNK_SECTION.write(buf, section); + section.writeBlockLight(buf); - if (!section.hasSkyLight()) continue; // No sky light, we're done here. - section.writeSkyLight(buf); + if (!section.hasSkyLight()) continue; // No sky light, we're done here. + section.writeSkyLight(buf); + } + buf.readerIndex(0); + Type.VAR_INT.write(output, buf.readableBytes() + (chunk.isBiomeData() ? 256 : 0)); + output.writeBytes(buf); + } finally { + buf.release(); // release buffer } - buf.readerIndex(0); - Type.VAR_INT.write(output, buf.readableBytes() + (chunk.isBiomeData() ? 256 : 0)); - output.writeBytes(buf); - buf.release(); // release buffer // Write biome data if (chunk.isBiomeData()) { diff --git a/common/src/main/java/us/myles/ViaVersion/protocols/protocol1_9_3to1_9_1_2/types/Chunk1_9_1_2Type.java b/common/src/main/java/us/myles/ViaVersion/protocols/protocol1_9_3to1_9_1_2/types/Chunk1_9_1_2Type.java index 9dcc6e5e6..1a0e05951 100644 --- a/common/src/main/java/us/myles/ViaVersion/protocols/protocol1_9_3to1_9_1_2/types/Chunk1_9_1_2Type.java +++ b/common/src/main/java/us/myles/ViaVersion/protocols/protocol1_9_3to1_9_1_2/types/Chunk1_9_1_2Type.java @@ -78,20 +78,23 @@ public class Chunk1_9_1_2Type extends PartialType { Type.VAR_INT.write(output, chunk.getBitmask()); ByteBuf buf = output.alloc().buffer(); - for (int i = 0; i < 16; i++) { - ChunkSection section = chunk.getSections()[i]; - if (section == null) continue; // Section not set - Types1_9.CHUNK_SECTION.write(buf, section); - section.writeBlockLight(buf); + try { + for (int i = 0; i < 16; i++) { + ChunkSection section = chunk.getSections()[i]; + if (section == null) continue; // Section not set + Types1_9.CHUNK_SECTION.write(buf, section); + section.writeBlockLight(buf); - if (!section.hasSkyLight()) continue; // No sky light, we're done here. - section.writeSkyLight(buf); + if (!section.hasSkyLight()) continue; // No sky light, we're done here. + section.writeSkyLight(buf); + } + buf.readerIndex(0); + Type.VAR_INT.write(output, buf.readableBytes() + (chunk.isBiomeData() ? 256 : 0)); + output.writeBytes(buf); + } finally { + buf.release(); // release buffer } - buf.readerIndex(0); - Type.VAR_INT.write(output, buf.readableBytes() + (chunk.isBiomeData() ? 256 : 0)); - output.writeBytes(buf); - buf.release(); // release buffer // Write biome data if (chunk.isBiomeData()) { diff --git a/common/src/main/java/us/myles/ViaVersion/protocols/protocol1_9to1_8/packets/WorldPackets.java b/common/src/main/java/us/myles/ViaVersion/protocols/protocol1_9to1_8/packets/WorldPackets.java index 2ced7a240..9fcc90631 100644 --- a/common/src/main/java/us/myles/ViaVersion/protocols/protocol1_9to1_8/packets/WorldPackets.java +++ b/common/src/main/java/us/myles/ViaVersion/protocols/protocol1_9to1_8/packets/WorldPackets.java @@ -17,7 +17,6 @@ import us.myles.ViaVersion.api.type.Type; import us.myles.ViaVersion.packets.State; import us.myles.ViaVersion.protocols.protocol1_9to1_8.ItemRewriter; import us.myles.ViaVersion.protocols.protocol1_9to1_8.Protocol1_9To1_8; -import us.myles.ViaVersion.api.minecraft.chunks.Chunk1_8; import us.myles.ViaVersion.protocols.protocol1_9to1_8.providers.BulkChunkTranslatorProvider; import us.myles.ViaVersion.protocols.protocol1_9to1_8.providers.CommandBlockProvider; import us.myles.ViaVersion.protocols.protocol1_9to1_8.sounds.Effect; @@ -163,11 +162,14 @@ public class WorldPackets { PacketWrapper output = (PacketWrapper) obj; ByteBuf buffer = wrapper.user().getChannel().alloc().buffer(); - output.setId(-1); // -1 for no writing of id - output.writeToBuffer(buffer); - PacketWrapper chunkPacket = new PacketWrapper(0x21, buffer, wrapper.user()); - chunkPacket.send(Protocol1_9To1_8.class, false, true); - buffer.release(); + try { + output.setId(-1); // -1 for no writing of id + output.writeToBuffer(buffer); + PacketWrapper chunkPacket = new PacketWrapper(0x21, buffer, wrapper.user()); + chunkPacket.send(Protocol1_9To1_8.class, false, true); + } finally { + buffer.release(); + } } } }); diff --git a/common/src/main/java/us/myles/ViaVersion/protocols/protocol1_9to1_8/types/Chunk1_9to1_8Type.java b/common/src/main/java/us/myles/ViaVersion/protocols/protocol1_9to1_8/types/Chunk1_9to1_8Type.java index 86f8ef97f..d76541bb7 100644 --- a/common/src/main/java/us/myles/ViaVersion/protocols/protocol1_9to1_8/types/Chunk1_9to1_8Type.java +++ b/common/src/main/java/us/myles/ViaVersion/protocols/protocol1_9to1_8/types/Chunk1_9to1_8Type.java @@ -141,20 +141,23 @@ public class Chunk1_9to1_8Type extends PartialType { Type.VAR_INT.write(output, chunk.getBitmask()); ByteBuf buf = output.alloc().buffer(); - for (int i = 0; i < SECTION_COUNT; i++) { - ChunkSection section = chunk.getSections()[i]; - if (section == null) continue; // Section not set - Types1_9.CHUNK_SECTION.write(buf, section); - section.writeBlockLight(buf); + try { + for (int i = 0; i < SECTION_COUNT; i++) { + ChunkSection section = chunk.getSections()[i]; + if (section == null) continue; // Section not set + Types1_9.CHUNK_SECTION.write(buf, section); + section.writeBlockLight(buf); - if (!section.hasSkyLight()) continue; // No sky light, we're done here. - section.writeSkyLight(buf); + if (!section.hasSkyLight()) continue; // No sky light, we're done here. + section.writeSkyLight(buf); + } + buf.readerIndex(0); + Type.VAR_INT.write(output, buf.readableBytes() + (chunk.hasBiomeData() ? 256 : 0)); + output.writeBytes(buf); + } finally { + buf.release(); // release buffer } - buf.readerIndex(0); - Type.VAR_INT.write(output, buf.readableBytes() + (chunk.hasBiomeData() ? 256 : 0)); - output.writeBytes(buf); - buf.release(); // release buffer // Write biome data if (chunk.hasBiomeData()) { diff --git a/common/src/main/java/us/myles/ViaVersion/util/PipelineUtil.java b/common/src/main/java/us/myles/ViaVersion/util/PipelineUtil.java index 6be8ac0b8..bf5f63cb2 100644 --- a/common/src/main/java/us/myles/ViaVersion/util/PipelineUtil.java +++ b/common/src/main/java/us/myles/ViaVersion/util/PipelineUtil.java @@ -6,7 +6,6 @@ import io.netty.channel.ChannelPipeline; import io.netty.handler.codec.ByteToMessageDecoder; import io.netty.handler.codec.MessageToByteEncoder; import io.netty.handler.codec.MessageToMessageDecoder; -import io.netty.handler.codec.MessageToMessageEncoder; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; @@ -90,14 +89,15 @@ public class PipelineUtil { * * @param t The throwable * @param c The exception to look for - * @return True if the stack trace contained it as its cause. + * @return True if the stack trace contained it as its cause or if t is an instance of c. */ public static boolean containsCause(Throwable t, Class c) { - while (t != null) { - t = t.getCause(); - if (t != null) + do { + if (t != null) { if (c.isAssignableFrom(t.getClass())) return true; - } + t = t.getCause(); + } + } while (t != null); return false; } diff --git a/velocity/src/main/java/us/myles/ViaVersion/velocity/handlers/VelocityEncodeHandler.java b/velocity/src/main/java/us/myles/ViaVersion/velocity/handlers/VelocityEncodeHandler.java index 2ec4e2525..eed1d02f3 100644 --- a/velocity/src/main/java/us/myles/ViaVersion/velocity/handlers/VelocityEncodeHandler.java +++ b/velocity/src/main/java/us/myles/ViaVersion/velocity/handlers/VelocityEncodeHandler.java @@ -77,8 +77,11 @@ public class VelocityEncodeHandler extends MessageToMessageEncoder { if (needsCompress) { ByteBuf old = bytebuf; bytebuf = ctx.alloc().buffer(); - PipelineUtil.callEncode((MessageToByteEncoder) ctx.pipeline().get("compression-encoder"), ctx, old, bytebuf); - old.release(); + try { + PipelineUtil.callEncode((MessageToByteEncoder) ctx.pipeline().get("compression-encoder"), ctx, old, bytebuf); + } finally { + old.release(); + } } out.add(bytebuf); } diff --git a/velocity/src/main/java/us/myles/ViaVersion/velocity/handlers/VelocityServerHandler.java b/velocity/src/main/java/us/myles/ViaVersion/velocity/handlers/VelocityServerHandler.java index 35f85d042..39fbc44da 100644 --- a/velocity/src/main/java/us/myles/ViaVersion/velocity/handlers/VelocityServerHandler.java +++ b/velocity/src/main/java/us/myles/ViaVersion/velocity/handlers/VelocityServerHandler.java @@ -100,99 +100,102 @@ public class VelocityServerHandler { user.getVelocityLock().writeLock().lock(); - VelocityStorage storage = user.get(VelocityStorage.class); + try { + VelocityStorage storage = user.get(VelocityStorage.class); - if (e.getServer() != null) { - if (!e.getServer().getServerInfo().getName().equals(storage.getCurrentServer())) { - String serverName = e.getServer().getServerInfo().getName(); + if (e.getServer() != null) { + if (!e.getServer().getServerInfo().getName().equals(storage.getCurrentServer())) { + String serverName = e.getServer().getServerInfo().getName(); - storage.setCurrentServer(serverName); + storage.setCurrentServer(serverName); - int protocolId = ProtocolDetectorService.getProtocolId(serverName); + int protocolId = ProtocolDetectorService.getProtocolId(serverName); - if (protocolId <= ProtocolVersion.MINECRAFT_1_8.getProtocol()) { // 1.8 doesn't have BossBar packet - if (storage.getBossbar() != null) { - for (UUID uuid : storage.getBossbar()) { - PacketWrapper wrapper = new PacketWrapper(0x0C, null, user); - wrapper.write(Type.UUID, uuid); - wrapper.write(Type.VAR_INT, 1); // remove - wrapper.send(Protocol1_9To1_8.class, true, true); - } - storage.getBossbar().clear(); - } - } - - ProtocolInfo info = user.get(ProtocolInfo.class); - int previousServerProtocol = info.getServerProtocolVersion(); - - // Refresh the pipes - List> protocols = ProtocolRegistry.getProtocolPath(info.getProtocolVersion(), protocolId); - ProtocolPipeline pipeline = user.get(ProtocolInfo.class).getPipeline(); - user.clearStoredObjects(); - pipeline.cleanPipes(); - if (protocols == null) { - // TODO Check Bungee Supported Protocols? *shrugs* - protocolId = info.getProtocolVersion(); - } else { - for (Pair prot : protocols) { - pipeline.add(prot.getValue()); - } - } - - info.setServerProtocolVersion(protocolId); - // Add version-specific base Protocol - pipeline.add(ProtocolRegistry.getBaseProtocol(protocolId)); - - // Workaround 1.13 server change - Object sessionHandler = ReflectionUtil.invoke( - getMinecraftConnection.invoke(e.getPlayer()), - "getSessionHandler" - ); - - if (clientPlaySessionHandler.isInstance(sessionHandler)) { // It may be InitialConnectSessionHandler on the first server connection - Set knownChannels = (Set) getKnownChannels.invoke(sessionHandler); - if (previousServerProtocol != -1) { - int id1_13 = ProtocolVersion.MINECRAFT_1_13.getProtocol(); - if (previousServerProtocol < id1_13 && protocolId >= id1_13) { - ArrayList newChannels = new ArrayList<>(); - for (String oldChannel : knownChannels) { - String transformed = InventoryPackets.getNewPluginChannelId(oldChannel); - if (transformed != null) { - newChannels.add(transformed); - } + if (protocolId <= ProtocolVersion.MINECRAFT_1_8.getProtocol()) { // 1.8 doesn't have BossBar packet + if (storage.getBossbar() != null) { + for (UUID uuid : storage.getBossbar()) { + PacketWrapper wrapper = new PacketWrapper(0x0C, null, user); + wrapper.write(Type.UUID, uuid); + wrapper.write(Type.VAR_INT, 1); // remove + wrapper.send(Protocol1_9To1_8.class, true, true); } - knownChannels.clear(); - knownChannels.addAll(newChannels); - } else if (previousServerProtocol >= id1_13 && protocolId < id1_13) { - ArrayList newChannels = new ArrayList<>(); - for (String oldChannel : knownChannels) { - String transformed = InventoryPackets.getOldPluginChannelId(oldChannel); - if (transformed != null) { - newChannels.add(transformed); - } - } - knownChannels.clear(); - knownChannels.addAll(newChannels); + storage.getBossbar().clear(); } } + + ProtocolInfo info = user.get(ProtocolInfo.class); + int previousServerProtocol = info.getServerProtocolVersion(); + + // Refresh the pipes + List> protocols = ProtocolRegistry.getProtocolPath(info.getProtocolVersion(), protocolId); + ProtocolPipeline pipeline = user.get(ProtocolInfo.class).getPipeline(); + user.clearStoredObjects(); + pipeline.cleanPipes(); + if (protocols == null) { + // TODO Check Bungee Supported Protocols? *shrugs* + protocolId = info.getProtocolVersion(); + } else { + for (Pair prot : protocols) { + pipeline.add(prot.getValue()); + } + } + + info.setServerProtocolVersion(protocolId); + // Add version-specific base Protocol + pipeline.add(ProtocolRegistry.getBaseProtocol(protocolId)); + + // Workaround 1.13 server change + Object sessionHandler = ReflectionUtil.invoke( + getMinecraftConnection.invoke(e.getPlayer()), + "getSessionHandler" + ); + + if (clientPlaySessionHandler.isInstance(sessionHandler)) { // It may be InitialConnectSessionHandler on the first server connection + Set knownChannels = (Set) getKnownChannels.invoke(sessionHandler); + if (previousServerProtocol != -1) { + int id1_13 = ProtocolVersion.MINECRAFT_1_13.getProtocol(); + if (previousServerProtocol < id1_13 && protocolId >= id1_13) { + ArrayList newChannels = new ArrayList<>(); + for (String oldChannel : knownChannels) { + String transformed = InventoryPackets.getNewPluginChannelId(oldChannel); + if (transformed != null) { + newChannels.add(transformed); + } + } + knownChannels.clear(); + knownChannels.addAll(newChannels); + } else if (previousServerProtocol >= id1_13 && protocolId < id1_13) { + ArrayList newChannels = new ArrayList<>(); + for (String oldChannel : knownChannels) { + String transformed = InventoryPackets.getOldPluginChannelId(oldChannel); + if (transformed != null) { + newChannels.add(transformed); + } + } + knownChannels.clear(); + knownChannels.addAll(newChannels); + } + } + } + + user.put(info); + user.put(storage); + + user.setActive(protocols != null); + + // Init all protocols TODO check if this can get moved up to the previous for loop, and doesn't require the pipeline to already exist. + for (Protocol protocol : pipeline.pipes()) { + protocol.init(user); + } + + Object connection = getMinecraftConnection.invoke(e.getPlayer()); + ProtocolVersion version = (ProtocolVersion) getNextProtocolVersion.invoke(connection); + setProtocolVersion.invoke(connection, version); } - - user.put(info); - user.put(storage); - - user.setActive(protocols != null); - - // Init all protocols TODO check if this can get moved up to the previous for loop, and doesn't require the pipeline to already exist. - for (Protocol protocol : pipeline.pipes()) { - protocol.init(user); - } - - Object connection = getMinecraftConnection.invoke(e.getPlayer()); - ProtocolVersion version = (ProtocolVersion) getNextProtocolVersion.invoke(connection); - setProtocolVersion.invoke(connection, version); } + } finally { + user.getVelocityLock().writeLock().unlock(); } - user.getVelocityLock().writeLock().unlock(); } } }