diff --git a/bukkit/src/main/java/us/myles/ViaVersion/bukkit/handlers/BukkitEncodeHandler.java b/bukkit/src/main/java/us/myles/ViaVersion/bukkit/handlers/BukkitEncodeHandler.java index 875583b8d..06bc19f20 100644 --- a/bukkit/src/main/java/us/myles/ViaVersion/bukkit/handlers/BukkitEncodeHandler.java +++ b/bukkit/src/main/java/us/myles/ViaVersion/bukkit/handlers/BukkitEncodeHandler.java @@ -61,7 +61,7 @@ public class BukkitEncodeHandler extends MessageToByteEncoder implements ViaHand @Override public void transform(ByteBuf bytebuf) throws Exception { - info.checkOutgoingPacket(); + if (!info.checkOutgoingPacket()) throw CancelEncoderException.generate(null); if (!info.shouldTransformPacket()) return; info.transformOutgoing(bytebuf, CancelEncoderException::generate); } diff --git a/bungee/src/main/java/us/myles/ViaVersion/bungee/handlers/BungeeEncodeHandler.java b/bungee/src/main/java/us/myles/ViaVersion/bungee/handlers/BungeeEncodeHandler.java index 914ce20e3..5c1df2399 100644 --- a/bungee/src/main/java/us/myles/ViaVersion/bungee/handlers/BungeeEncodeHandler.java +++ b/bungee/src/main/java/us/myles/ViaVersion/bungee/handlers/BungeeEncodeHandler.java @@ -22,7 +22,7 @@ public class BungeeEncodeHandler extends MessageToMessageEncoder { @Override protected void encode(final ChannelHandlerContext ctx, ByteBuf bytebuf, List out) throws Exception { - info.checkOutgoingPacket(); + if (!info.checkOutgoingPacket()) throw CancelEncoderException.generate(null); if (!info.shouldTransformPacket()) { out.add(bytebuf.retain()); return; diff --git a/common/pom.xml b/common/pom.xml index a7e7e39b1..51f1df82c 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -12,14 +12,6 @@ viaversion-common - - - org.yaml - snakeyaml - 1.18 - compile - - it.unimi.dsi diff --git a/common/src/main/java/us/myles/ViaVersion/ViaManager.java b/common/src/main/java/us/myles/ViaVersion/ViaManager.java index c78a588e0..a8306fee5 100644 --- a/common/src/main/java/us/myles/ViaVersion/ViaManager.java +++ b/common/src/main/java/us/myles/ViaVersion/ViaManager.java @@ -91,7 +91,7 @@ public class ViaManager { // Check if there are any pipes to this version if (ProtocolRegistry.SERVER_PROTOCOL != -1) { platform.getLogger().info("ViaVersion detected server version: " + ProtocolVersion.getProtocol(ProtocolRegistry.SERVER_PROTOCOL)); - if (!ProtocolRegistry.isWorkingPipe()) { + if (!ProtocolRegistry.isWorkingPipe() && !platform.isProxy()) { platform.getLogger().warning("ViaVersion does not have any compatible versions for this server version!"); platform.getLogger().warning("Please remember that ViaVersion only adds support for versions newer than the server version."); platform.getLogger().warning("If you need support for older versions you may need to use one or more ViaVersion addons too."); 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 3c0c97481..d37b39f69 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 @@ -3,7 +3,6 @@ package us.myles.ViaVersion.api.data; import io.netty.buffer.ByteBuf; import io.netty.channel.Channel; import io.netty.channel.ChannelFuture; -import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandlerContext; import net.md_5.bungee.api.ChatColor; import org.jetbrains.annotations.Nullable; @@ -18,7 +17,6 @@ import us.myles.ViaVersion.protocols.base.ProtocolInfo; import us.myles.ViaVersion.util.PipelineUtil; import java.util.Map; -import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicLong; import java.util.function.Function; @@ -27,8 +25,9 @@ public class UserConnection { private static final AtomicLong IDS = new AtomicLong(); private final long id = IDS.incrementAndGet(); private final Channel channel; - private ProtocolInfo protocolInfo; + private final boolean clientSide; Map storedObjects = new ConcurrentHashMap<>(); + private ProtocolInfo protocolInfo; private boolean active = true; private boolean pendingDisconnect; private Object lastPacket; @@ -42,8 +41,23 @@ public class UserConnection { private int secondsObserved; private int warnings; - public UserConnection(@Nullable Channel channel) { + /** + * Creates an UserConnection. When it's a client-side connection, some method behaviors are modified. + * + * @param channel netty channel. + * @param clientSide true if it's a client-side connection + */ + public UserConnection(@Nullable Channel channel, boolean clientSide) { this.channel = channel; + this.clientSide = clientSide; + } + + /** + * @param channel + * @see #UserConnection(Channel, boolean) + */ + public UserConnection(@Nullable Channel channel) { + this(channel, false); } /** @@ -91,12 +105,24 @@ public class UserConnection { * @param packet The raw packet to send * @param currentThread Should it run in the same thread */ - public void sendRawPacket(ByteBuf packet, boolean currentThread) { - ChannelHandler handler = channel.pipeline().get(Via.getManager().getInjector().getEncoderName()); - if (currentThread) { - channel.pipeline().context(handler).writeAndFlush(packet); + public void sendRawPacket(final ByteBuf packet, boolean currentThread) { + Runnable act; + if (clientSide) { + // We'll just assume that Via decoder isn't wrapping the original decoder + act = () -> getChannel().pipeline() + .context(Via.getManager().getInjector().getDecoderName()).fireChannelRead(packet); } else { - channel.eventLoop().submit(() -> channel.pipeline().context(handler).writeAndFlush(packet)); + act = () -> channel.pipeline().context(Via.getManager().getInjector().getEncoderName()).writeAndFlush(packet); + } + if (currentThread) { + act.run(); + } else { + try { + channel.eventLoop().submit(act); + } catch (Throwable e) { + packet.release(); // Couldn't schedule + e.printStackTrace(); + } } } @@ -106,9 +132,22 @@ public class UserConnection { * @param packet The raw packet to send * @return ChannelFuture of the packet being sent */ - public ChannelFuture sendRawPacketFuture(ByteBuf packet) { - ChannelHandler handler = channel.pipeline().get(Via.getManager().getInjector().getEncoderName()); - return channel.pipeline().context(handler).writeAndFlush(packet); + public ChannelFuture sendRawPacketFuture(final ByteBuf packet) { + if (clientSide) { + return sendRawPacketFutureClientSide(packet); + } else { + return sendRawPacketFutureServerSide(packet); + } + } + + private ChannelFuture sendRawPacketFutureServerSide(final ByteBuf packet) { + return channel.pipeline().context(Via.getManager().getInjector().getEncoderName()).writeAndFlush(packet); + } + + private ChannelFuture sendRawPacketFutureClientSide(final ByteBuf packet) { + // Assume that decoder isn't wrapping + getChannel().pipeline().context(Via.getManager().getInjector().getDecoderName()).fireChannelRead(packet); + return getChannel().newSucceededFuture(); } /** @@ -156,6 +195,7 @@ public class UserConnection { * @see #incrementReceived() */ public boolean exceedsMaxPPS() { + if (clientSide) return false; // Don't apply PPS limiting for client-side ViaVersionConfig conf = Via.getConfig(); // Max PPS Checker if (conf.getMaxPPS() > 0) { @@ -195,14 +235,8 @@ public class UserConnection { if (!channel.isOpen() || pendingDisconnect) return; pendingDisconnect = true; - UUID uuid = protocolInfo.getUuid(); - if (uuid == null) { - channel.close(); // Just disconnect, we don't know what the connection is - return; - } - Via.getPlatform().runSync(() -> { - if (!Via.getPlatform().kickPlayer(uuid, ChatColor.translateAlternateColorCodes('&', reason))) { + if (!Via.getPlatform().disconnect(this, ChatColor.translateAlternateColorCodes('&', reason))) { channel.close(); // =) } }); @@ -214,9 +248,20 @@ public class UserConnection { * @param packet Raw packet to be sent * @param currentThread If {@code true} executes immediately, {@code false} submits a task to EventLoop */ - public void sendRawPacketToServer(ByteBuf packet, boolean currentThread) { - ByteBuf buf = packet.alloc().buffer(); + public void sendRawPacketToServer(final ByteBuf packet, boolean currentThread) { + if (clientSide) { + sendRawPacketToServerClientSide(packet, currentThread); + } else { + sendRawPacketToServerServerSide(packet, currentThread); + } + } + + private void sendRawPacketToServerServerSide(final ByteBuf packet, boolean currentThread) { + final ByteBuf buf = packet.alloc().buffer(); try { + // We'll use passing through because there are some encoder wrappers + ChannelHandlerContext context = PipelineUtil + .getPreviousContext(Via.getManager().getInjector().getDecoderName(), channel.pipeline()); try { Type.VAR_INT.writePrimitive(buf, PacketWrapper.PASSTHROUGH_ID); } catch (Exception e) { @@ -224,23 +269,18 @@ public class UserConnection { Via.getPlatform().getLogger().warning("Type.VAR_INT.write thrown an exception: " + e); } buf.writeBytes(packet); - ChannelHandlerContext context = PipelineUtil - .getPreviousContext(Via.getManager().getInjector().getDecoderName(), channel.pipeline()); - if (currentThread) { + Runnable act = () -> { if (context != null) { context.fireChannelRead(buf); } else { channel.pipeline().fireChannelRead(buf); } + }; + if (currentThread) { + act.run(); } else { try { - channel.eventLoop().submit(() -> { - if (context != null) { - context.fireChannelRead(buf); - } else { - channel.pipeline().fireChannelRead(buf); - } - }); + channel.eventLoop().submit(act); } catch (Throwable t) { // Couldn't schedule buf.release(); @@ -252,6 +292,21 @@ public class UserConnection { } } + private void sendRawPacketToServerClientSide(final ByteBuf packet, boolean currentThread) { + Runnable act = () -> getChannel().pipeline() + .context(Via.getManager().getInjector().getEncoderName()).writeAndFlush(packet); + if (currentThread) { + act.run(); + } else { + try { + getChannel().eventLoop().submit(act); + } catch (Throwable e) { + e.printStackTrace(); + packet.release(); // Couldn't schedule + } + } + } + /** * Sends a raw packet to the server. It will submit a task to EventLoop. * @@ -262,11 +317,24 @@ public class UserConnection { } /** - * Monitors serverbound packets. + * Monitors incoming packets * * @return false if this packet should be cancelled */ public boolean checkIncomingPacket() { + if (clientSide) { + return checkClientBound(); + } else { + return checkServerBound(); + } + } + + private boolean checkClientBound() { + incrementSent(); + return true; + } + + private boolean checkServerBound() { // Ignore if pending disconnect if (pendingDisconnect) return false; // Increment received + Check PPS @@ -274,10 +342,16 @@ public class UserConnection { } /** - * Monitors clientbound packets. + * Monitors outgoing packets + * + * @return false if this packet should be cancelled */ - public void checkOutgoingPacket() { - incrementSent(); + public boolean checkOutgoingPacket() { + if (clientSide) { + return checkServerBound(); + } else { + return checkClientBound(); + } } /** @@ -290,7 +364,8 @@ public class UserConnection { } /** - * Transforms the clientbound packet contained in an outgoing ByteBuf. + * Transforms the outgoing packet contained in ByteBuf. When clientSide is true, this packet is considered + * serverbound. * * @param buf ByteBuf with packet id and packet contents * @param cancelSupplier Function called with original CancelException for generating the Exception used when @@ -301,11 +376,12 @@ public class UserConnection { */ public void transformOutgoing(ByteBuf buf, Function cancelSupplier) throws Exception { if (!buf.isReadable()) return; - transform(buf, Direction.OUTGOING, cancelSupplier); + transform(buf, clientSide ? Direction.INCOMING : Direction.OUTGOING, cancelSupplier); } /** - * Transforms the serverbound packet contained in an incoming ByteBuf. + * Transforms the incoming packet contained in ByteBuf. When clientSide is true, this packet is considered + * clientbound * * @param buf ByteBuf with packet id and packet contents * @param cancelSupplier Function called with original CancelException for generating the Exception used when @@ -316,7 +392,7 @@ public class UserConnection { */ public void transformIncoming(ByteBuf buf, Function cancelSupplier) throws Exception { if (!buf.isReadable()) return; - transform(buf, Direction.INCOMING, cancelSupplier); + transform(buf, clientSide ? Direction.OUTGOING : Direction.INCOMING, cancelSupplier); } private void transform(ByteBuf buf, Direction direction, Function cancelSupplier) throws Exception { @@ -459,4 +535,12 @@ public class UserConnection { public int hashCode() { return Long.hashCode(id); } + + public boolean isClientSide() { + return clientSide; + } + + public boolean shouldApplyBlockProtocol() { + return !clientSide; // Don't apply protocol blocking on client-side + } } diff --git a/common/src/main/java/us/myles/ViaVersion/api/platform/ViaConnectionManager.java b/common/src/main/java/us/myles/ViaVersion/api/platform/ViaConnectionManager.java index d26eabff1..f5ac95aed 100644 --- a/common/src/main/java/us/myles/ViaVersion/api/platform/ViaConnectionManager.java +++ b/common/src/main/java/us/myles/ViaVersion/api/platform/ViaConnectionManager.java @@ -46,7 +46,7 @@ public class ViaConnectionManager { * UUIDs can't be duplicate between frontend connections. */ public boolean isFrontEnd(UserConnection conn) { - return true; + return !conn.isClientSide(); } /** diff --git a/common/src/main/java/us/myles/ViaVersion/api/platform/ViaPlatform.java b/common/src/main/java/us/myles/ViaVersion/api/platform/ViaPlatform.java index 00a5950d3..29d4e4993 100644 --- a/common/src/main/java/us/myles/ViaVersion/api/platform/ViaPlatform.java +++ b/common/src/main/java/us/myles/ViaVersion/api/platform/ViaPlatform.java @@ -1,10 +1,13 @@ package us.myles.ViaVersion.api.platform; import com.google.gson.JsonObject; +import us.myles.ViaVersion.api.Via; import us.myles.ViaVersion.api.ViaAPI; import us.myles.ViaVersion.api.ViaVersionConfig; import us.myles.ViaVersion.api.command.ViaCommandSender; import us.myles.ViaVersion.api.configuration.ConfigurationProvider; +import us.myles.ViaVersion.api.data.UserConnection; +import us.myles.ViaVersion.protocols.base.ProtocolInfo; import java.io.File; import java.util.UUID; @@ -121,6 +124,20 @@ public interface ViaPlatform { */ boolean kickPlayer(UUID uuid, String message); + /** + * Disconnects an UserConnection for a reason + * + * @param connection The UserConnection + * @param message The message to kick them with + * @return True if it was successful + */ + default boolean disconnect(UserConnection connection, String message) { + if (connection.isClientSide()) return false; + UUID uuid = connection.get(ProtocolInfo.class).getUuid(); + if (uuid == null) return false; + return kickPlayer(uuid, message); + } + /** * Check if the plugin is enabled. * diff --git a/common/src/main/java/us/myles/ViaVersion/protocols/base/BaseProtocol1_7.java b/common/src/main/java/us/myles/ViaVersion/protocols/base/BaseProtocol1_7.java index 2948967f5..ee466c346 100644 --- a/common/src/main/java/us/myles/ViaVersion/protocols/base/BaseProtocol1_7.java +++ b/common/src/main/java/us/myles/ViaVersion/protocols/base/BaseProtocol1_7.java @@ -165,6 +165,7 @@ public class BaseProtocol1_7 extends SimpleProtocol { int protocol = wrapper.user().getProtocolInfo().getProtocolVersion(); if (Via.getConfig().getBlockedProtocols().contains(protocol)) { if (!wrapper.user().getChannel().isOpen()) return; + if (!wrapper.user().shouldApplyBlockProtocol()) return; PacketWrapper disconnectPacket = new PacketWrapper(0x00, null, wrapper.user()); // Disconnect Packet Protocol1_9To1_8.FIX_JSON.write(disconnectPacket, ChatColor.translateAlternateColorCodes('&', Via.getConfig().getBlockedDisconnectMsg())); diff --git a/jar/pom.xml b/jar/pom.xml index b8c1c7f66..140b0f106 100644 --- a/jar/pom.xml +++ b/jar/pom.xml @@ -123,10 +123,6 @@ javassist us.myles.viaversion.libs.javassist - - org.yaml.snakeyaml - us.myles.viaversion.libs.snakeyaml - net.md_5.bungee us.myles.viaversion.libs.bungeecordchat diff --git a/pom.xml b/pom.xml index 699b47081..3c0cbdfd9 100644 --- a/pom.xml +++ b/pom.xml @@ -70,6 +70,12 @@ viaversion-repo https://repo.viaversion.com + + + + mojang + https://libraries.minecraft.net + @@ -141,6 +147,14 @@ 5.3.1 test + + + + org.yaml + snakeyaml + 1.18 + provided + diff --git a/sponge/src/main/java/us/myles/ViaVersion/sponge/handlers/SpongeEncodeHandler.java b/sponge/src/main/java/us/myles/ViaVersion/sponge/handlers/SpongeEncodeHandler.java index cb6d90fae..6b643081c 100644 --- a/sponge/src/main/java/us/myles/ViaVersion/sponge/handlers/SpongeEncodeHandler.java +++ b/sponge/src/main/java/us/myles/ViaVersion/sponge/handlers/SpongeEncodeHandler.java @@ -42,7 +42,7 @@ public class SpongeEncodeHandler extends MessageToByteEncoder implements @Override public void transform(ByteBuf bytebuf) throws Exception { - info.checkOutgoingPacket(); + if (!info.checkOutgoingPacket()) throw CancelEncoderException.generate(null); if (!info.shouldTransformPacket()) return; info.transformOutgoing(bytebuf, CancelEncoderException::generate); } diff --git a/velocity/pom.xml b/velocity/pom.xml index 685ed2c7c..fdc2d5a40 100644 --- a/velocity/pom.xml +++ b/velocity/pom.xml @@ -42,7 +42,7 @@ com.velocitypowered velocity-api - 1.0.0-SNAPSHOT + 1.1.0-SNAPSHOT provided diff --git a/velocity/src/main/java/us/myles/ViaVersion/VelocityPlugin.java b/velocity/src/main/java/us/myles/ViaVersion/VelocityPlugin.java index 041f8b525..a5114b4dc 100644 --- a/velocity/src/main/java/us/myles/ViaVersion/VelocityPlugin.java +++ b/velocity/src/main/java/us/myles/ViaVersion/VelocityPlugin.java @@ -26,11 +26,7 @@ import us.myles.ViaVersion.util.GsonUtil; import us.myles.ViaVersion.velocity.VersionInfo; import us.myles.ViaVersion.velocity.command.VelocityCommandHandler; import us.myles.ViaVersion.velocity.command.VelocityCommandSender; -import us.myles.ViaVersion.velocity.platform.VelocityTaskId; -import us.myles.ViaVersion.velocity.platform.VelocityViaAPI; -import us.myles.ViaVersion.velocity.platform.VelocityViaConfig; -import us.myles.ViaVersion.velocity.platform.VelocityViaInjector; -import us.myles.ViaVersion.velocity.platform.VelocityViaLoader; +import us.myles.ViaVersion.velocity.platform.*; import us.myles.ViaVersion.velocity.service.ProtocolDetectorService; import us.myles.ViaVersion.velocity.util.LoggerWrapper; diff --git a/velocity/src/main/java/us/myles/ViaVersion/velocity/handlers/VelocityChannelInitializer.java b/velocity/src/main/java/us/myles/ViaVersion/velocity/handlers/VelocityChannelInitializer.java index bdd872b69..d4674ecfd 100644 --- a/velocity/src/main/java/us/myles/ViaVersion/velocity/handlers/VelocityChannelInitializer.java +++ b/velocity/src/main/java/us/myles/ViaVersion/velocity/handlers/VelocityChannelInitializer.java @@ -7,12 +7,14 @@ import us.myles.ViaVersion.api.protocol.ProtocolPipeline; import java.lang.reflect.Method; -public class VelocityChannelInitializer extends ChannelInitializer { - private final ChannelInitializer original; +public class VelocityChannelInitializer extends ChannelInitializer { + private final ChannelInitializer original; + private final boolean clientSide; private static Method initChannel; - public VelocityChannelInitializer(ChannelInitializer original) { + public VelocityChannelInitializer(ChannelInitializer original, boolean clientSide) { this.original = original; + this.clientSide = clientSide; } static { @@ -28,7 +30,7 @@ public class VelocityChannelInitializer extends ChannelInitializer { protected void initChannel(Channel channel) throws Exception { initChannel.invoke(original, channel); - UserConnection user = new UserConnection(channel); + UserConnection user = new UserConnection(channel, clientSide); new ProtocolPipeline(user); // We need to add a separated handler because Velocity uses pipeline().get(MINECRAFT_DECODER) diff --git a/velocity/src/main/java/us/myles/ViaVersion/velocity/handlers/VelocityDecodeHandler.java b/velocity/src/main/java/us/myles/ViaVersion/velocity/handlers/VelocityDecodeHandler.java index 7c140f56c..69e0f47f1 100644 --- a/velocity/src/main/java/us/myles/ViaVersion/velocity/handlers/VelocityDecodeHandler.java +++ b/velocity/src/main/java/us/myles/ViaVersion/velocity/handlers/VelocityDecodeHandler.java @@ -3,16 +3,21 @@ package us.myles.ViaVersion.velocity.handlers; import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.MessageToByteEncoder; import io.netty.handler.codec.MessageToMessageDecoder; import us.myles.ViaVersion.api.data.UserConnection; -import us.myles.ViaVersion.exception.CancelDecoderException; import us.myles.ViaVersion.exception.CancelCodecException; +import us.myles.ViaVersion.exception.CancelDecoderException; +import us.myles.ViaVersion.util.PipelineUtil; +import java.lang.reflect.InvocationTargetException; import java.util.List; @ChannelHandler.Sharable public class VelocityDecodeHandler extends MessageToMessageDecoder { private final UserConnection info; + private boolean handledCompression; + private boolean skipDoubleTransform; public VelocityDecodeHandler(UserConnection info) { this.info = info; @@ -20,6 +25,12 @@ public class VelocityDecodeHandler extends MessageToMessageDecoder { @Override protected void decode(ChannelHandlerContext ctx, ByteBuf bytebuf, List out) throws Exception { + if (skipDoubleTransform) { + skipDoubleTransform = false; + out.add(bytebuf.retain()); + return; + } + if (!info.checkIncomingPacket()) throw CancelDecoderException.generate(null); if (!info.shouldTransformPacket()) { out.add(bytebuf.retain()); @@ -28,13 +39,55 @@ public class VelocityDecodeHandler extends MessageToMessageDecoder { ByteBuf transformedBuf = ctx.alloc().buffer().writeBytes(bytebuf); try { + boolean needsCompress = handleCompressionOrder(ctx, transformedBuf); + info.transformIncoming(transformedBuf, CancelDecoderException::generate); + + if (needsCompress) { + recompress(ctx, transformedBuf); + skipDoubleTransform = true; + } out.add(transformedBuf.retain()); } finally { transformedBuf.release(); } } + private boolean handleCompressionOrder(ChannelHandlerContext ctx, ByteBuf buf) throws InvocationTargetException { + if (handledCompression) return false; + + int decoderIndex = ctx.pipeline().names().indexOf("compression-decoder"); + if (decoderIndex == -1) return false; + handledCompression = true; + if (decoderIndex > ctx.pipeline().names().indexOf("via-decoder")) { + // Need to decompress this packet due to bad order + ByteBuf decompressed = (ByteBuf) PipelineUtil.callDecode((MessageToMessageDecoder) ctx.pipeline().get("compression-decoder"), ctx, buf).get(0); + try { + buf.clear().writeBytes(decompressed); + } finally { + decompressed.release(); + } + ChannelHandler encoder = ctx.pipeline().get("via-encoder"); + ChannelHandler decoder = ctx.pipeline().get("via-decoder"); + ctx.pipeline().remove(encoder); + ctx.pipeline().remove(decoder); + ctx.pipeline().addAfter("compression-encoder", "via-encoder", encoder); + ctx.pipeline().addAfter("compression-decoder", "via-decoder", decoder); + return true; + } + return false; + } + + private void recompress(ChannelHandlerContext ctx, ByteBuf buf) throws Exception { + ByteBuf compressed = ctx.alloc().buffer(); + try { + PipelineUtil.callEncode((MessageToByteEncoder) ctx.pipeline().get("compression-encoder"), ctx, buf, compressed); + buf.clear().writeBytes(compressed); + } finally { + compressed.release(); + } + } + @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { if (cause instanceof CancelCodecException) return; 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 3edb7a1e9..0d8cf4276 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 @@ -25,7 +25,7 @@ public class VelocityEncodeHandler extends MessageToMessageEncoder { @Override protected void encode(final ChannelHandlerContext ctx, ByteBuf bytebuf, List out) throws Exception { - info.checkOutgoingPacket(); + if (!info.checkOutgoingPacket()) throw CancelEncoderException.generate(null); if (!info.shouldTransformPacket()) { out.add(bytebuf.retain()); return; @@ -47,9 +47,12 @@ public class VelocityEncodeHandler extends MessageToMessageEncoder { } private boolean handleCompressionOrder(ChannelHandlerContext ctx, ByteBuf buf) throws InvocationTargetException { - boolean needsCompress = false; - if (!handledCompression - && ctx.pipeline().names().indexOf("compression-encoder") > ctx.pipeline().names().indexOf("via-encoder")) { + if (handledCompression) return false; + + int encoderIndex = ctx.pipeline().names().indexOf("compression-encoder"); + if (encoderIndex == -1) return false; + handledCompression = true; + if (encoderIndex > ctx.pipeline().names().indexOf("via-encoder")) { // Need to decompress this packet due to bad order ByteBuf decompressed = (ByteBuf) PipelineUtil.callDecode((MessageToMessageDecoder) ctx.pipeline().get("compression-decoder"), ctx, buf).get(0); try { @@ -63,10 +66,9 @@ public class VelocityEncodeHandler extends MessageToMessageEncoder { ctx.pipeline().remove(decoder); ctx.pipeline().addAfter("compression-encoder", "via-encoder", encoder); ctx.pipeline().addAfter("compression-decoder", "via-decoder", decoder); - needsCompress = true; - handledCompression = true; + return true; } - return needsCompress; + return false; } private void recompress(ChannelHandlerContext ctx, ByteBuf buf) throws InvocationTargetException { 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 deleted file mode 100644 index e474e5c87..000000000 --- a/velocity/src/main/java/us/myles/ViaVersion/velocity/handlers/VelocityServerHandler.java +++ /dev/null @@ -1,182 +0,0 @@ -package us.myles.ViaVersion.velocity.handlers; - -import com.velocitypowered.api.event.PostOrder; -import com.velocitypowered.api.event.Subscribe; -import com.velocitypowered.api.event.player.ServerConnectedEvent; -import com.velocitypowered.api.event.player.ServerPreConnectEvent; -import com.velocitypowered.api.network.ProtocolVersion; -import us.myles.ViaVersion.api.PacketWrapper; -import us.myles.ViaVersion.api.Pair; -import us.myles.ViaVersion.api.Via; -import us.myles.ViaVersion.api.data.UserConnection; -import us.myles.ViaVersion.api.protocol.Protocol; -import us.myles.ViaVersion.api.protocol.ProtocolPipeline; -import us.myles.ViaVersion.api.protocol.ProtocolRegistry; -import us.myles.ViaVersion.api.type.Type; -import us.myles.ViaVersion.protocols.base.ProtocolInfo; -import us.myles.ViaVersion.protocols.protocol1_13to1_12_2.packets.InventoryPackets; -import us.myles.ViaVersion.protocols.protocol1_9to1_8.Protocol1_9To1_8; -import us.myles.ViaVersion.velocity.service.ProtocolDetectorService; -import us.myles.ViaVersion.velocity.storage.VelocityStorage; - -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.UUID; -import java.util.concurrent.CompletableFuture; - -public class VelocityServerHandler { - private static Method setProtocolVersion; - private static Method setNextProtocolVersion; - private static Method getMinecraftConnection; - private static Method getNextProtocolVersion; - private static Method getKnownChannels; - - static { - try { - setProtocolVersion = Class.forName("com.velocitypowered.proxy.connection.MinecraftConnection") - .getDeclaredMethod("setProtocolVersion", ProtocolVersion.class); - setNextProtocolVersion = Class.forName("com.velocitypowered.proxy.connection.MinecraftConnection") - .getDeclaredMethod("setNextProtocolVersion", ProtocolVersion.class); - Class connectedPlayer = Class.forName("com.velocitypowered.proxy.connection.client.ConnectedPlayer"); - getMinecraftConnection = connectedPlayer.getDeclaredMethod("getMinecraftConnection"); - getNextProtocolVersion = Class.forName("com.velocitypowered.proxy.connection.MinecraftConnection") - .getDeclaredMethod("getNextProtocolVersion"); - getKnownChannels = connectedPlayer.getDeclaredMethod("getKnownChannels"); - } catch (NoSuchMethodException | ClassNotFoundException e) { - e.printStackTrace(); - } - } - - @Subscribe - public void preServerConnect(ServerPreConnectEvent e) { - try { - UserConnection user = Via.getManager().getConnection(e.getPlayer().getUniqueId()); - if (user == null) return; - if (!user.has(VelocityStorage.class)) { - user.put(new VelocityStorage(user, e.getPlayer())); - } - - int protocolId = ProtocolDetectorService.getProtocolId(e.getOriginalServer().getServerInfo().getName()); - List> protocols = ProtocolRegistry.getProtocolPath(user.getProtocolInfo().getProtocolVersion(), protocolId); - - // Check if ViaVersion can support that version - Object connection = getMinecraftConnection.invoke(e.getPlayer()); - setNextProtocolVersion.invoke(connection, ProtocolVersion.getProtocolVersion(protocols == null - ? user.getProtocolInfo().getProtocolVersion() - : protocolId)); - - } catch (IllegalAccessException | InvocationTargetException e1) { - e1.printStackTrace(); - } - } - - @Subscribe(order = PostOrder.LATE) - public void connectedEvent(ServerConnectedEvent e) { - UserConnection user = Via.getManager().getConnection(e.getPlayer().getUniqueId()); - CompletableFuture.runAsync(() -> { - try { - checkServerChange(e, Via.getManager().getConnection(e.getPlayer().getUniqueId())); - } catch (Exception e1) { - e1.printStackTrace(); - } - }, user.getChannel().eventLoop()).join(); - } - - public void checkServerChange(ServerConnectedEvent e, UserConnection user) throws Exception { - if (user == null) return; - // Handle server/version change - if (user.has(VelocityStorage.class)) { - VelocityStorage storage = user.get(VelocityStorage.class); - - if (e.getServer() != null) { - if (!e.getServer().getServerInfo().getName().equals(storage.getCurrentServer())) { - String serverName = e.getServer().getServerInfo().getName(); - - storage.setCurrentServer(serverName); - - int protocolId = ProtocolDetectorService.getProtocolId(serverName); - - if (protocolId <= ProtocolVersion.MINECRAFT_1_8.getProtocol()) { // 1.8 doesn't have BossBar packet - if (storage.getBossbar() != null) { - // TODO: Verify whether this packet needs to be sent when 1.8 -> 1.9 protocol isn't present in the pipeline - // This ensures we can encode it properly as only the 1.9 protocol is currently implemented. - if (user.getProtocolInfo().getPipeline().contains(Protocol1_9To1_8.class)) { - 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.getProtocolInfo(); - int previousServerProtocol = info.getServerProtocolVersion(); - - // Refresh the pipes - List> protocols = ProtocolRegistry.getProtocolPath(info.getProtocolVersion(), protocolId); - ProtocolPipeline pipeline = info.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)); - - Collection knownChannels = (Collection) getKnownChannels.invoke(e.getPlayer()); - if (previousServerProtocol != -1) { - int id1_13 = ProtocolVersion.MINECRAFT_1_13.getProtocol(); - if (previousServerProtocol < id1_13 && protocolId >= id1_13) { - List 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) { - List 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); - } - } - } - } -} diff --git a/velocity/src/main/java/us/myles/ViaVersion/velocity/listeners/ElytraPatch.java b/velocity/src/main/java/us/myles/ViaVersion/velocity/listeners/ElytraPatch.java deleted file mode 100644 index 78cc94b9d..000000000 --- a/velocity/src/main/java/us/myles/ViaVersion/velocity/listeners/ElytraPatch.java +++ /dev/null @@ -1,44 +0,0 @@ -package us.myles.ViaVersion.velocity.listeners; - -import com.velocitypowered.api.event.PostOrder; -import com.velocitypowered.api.event.Subscribe; -import com.velocitypowered.api.event.player.ServerConnectedEvent; -import us.myles.ViaVersion.api.PacketWrapper; -import us.myles.ViaVersion.api.Via; -import us.myles.ViaVersion.api.data.UserConnection; -import us.myles.ViaVersion.api.minecraft.metadata.Metadata; -import us.myles.ViaVersion.api.minecraft.metadata.types.MetaType1_9; -import us.myles.ViaVersion.api.type.Type; -import us.myles.ViaVersion.api.type.types.version.Types1_9; -import us.myles.ViaVersion.protocols.base.ProtocolInfo; -import us.myles.ViaVersion.protocols.protocol1_9to1_8.Protocol1_9To1_8; -import us.myles.ViaVersion.protocols.protocol1_9to1_8.storage.EntityTracker1_9; - -import java.util.Collections; - -/* - * This patches https://github.com/ViaVersion/ViaVersion/issues/555 - */ -public class ElytraPatch { - - @Subscribe(order = PostOrder.LAST) - public void onServerConnected(ServerConnectedEvent event) { - UserConnection user = Via.getManager().getConnection(event.getPlayer().getUniqueId()); - if (user == null) return; - - try { - if (user.getProtocolInfo().getPipeline().contains(Protocol1_9To1_8.class)) { - int entityId = user.get(EntityTracker1_9.class).getProvidedEntityId(); - - PacketWrapper wrapper = new PacketWrapper(0x39, null, user); - - wrapper.write(Type.VAR_INT, entityId); - wrapper.write(Types1_9.METADATA_LIST, Collections.singletonList(new Metadata(0, MetaType1_9.Byte, (byte) 0))); - - wrapper.send(Protocol1_9To1_8.class); - } - } catch (Exception e) { - e.printStackTrace(); - } - } -} diff --git a/velocity/src/main/java/us/myles/ViaVersion/velocity/platform/VelocityViaInjector.java b/velocity/src/main/java/us/myles/ViaVersion/velocity/platform/VelocityViaInjector.java index 84df4af35..d9e0803ca 100644 --- a/velocity/src/main/java/us/myles/ViaVersion/velocity/platform/VelocityViaInjector.java +++ b/velocity/src/main/java/us/myles/ViaVersion/velocity/platform/VelocityViaInjector.java @@ -30,13 +30,25 @@ public class VelocityViaInjector implements ViaInjector { return (ChannelInitializer) ReflectionUtil.invoke(channelInitializerHolder, "get"); } + private ChannelInitializer getBackendInitializer() throws Exception { + Object connectionManager = ReflectionUtil.get(VelocityPlugin.PROXY, "cm", Object.class); + Object channelInitializerHolder = ReflectionUtil.invoke(connectionManager, "getBackendChannelInitializer"); + return (ChannelInitializer) ReflectionUtil.invoke(channelInitializerHolder, "get"); + } + @Override public void inject() throws Exception { Object connectionManager = ReflectionUtil.get(VelocityPlugin.PROXY, "cm", Object.class); Object channelInitializerHolder = ReflectionUtil.invoke(connectionManager, "getServerChannelInitializer"); ChannelInitializer originalInitializer = getInitializer(); channelInitializerHolder.getClass().getMethod("set", ChannelInitializer.class) - .invoke(channelInitializerHolder, new VelocityChannelInitializer(originalInitializer)); + .invoke(channelInitializerHolder, new VelocityChannelInitializer(originalInitializer, false)); + + + Object backendInitializerHolder = ReflectionUtil.invoke(connectionManager, "getBackendChannelInitializer"); + ChannelInitializer backendInitializer = getBackendInitializer(); + backendInitializerHolder.getClass().getMethod("set", ChannelInitializer.class) + .invoke(backendInitializerHolder, new VelocityChannelInitializer(backendInitializer, true)); } @Override diff --git a/velocity/src/main/java/us/myles/ViaVersion/velocity/platform/VelocityViaLoader.java b/velocity/src/main/java/us/myles/ViaVersion/velocity/platform/VelocityViaLoader.java index c65f3ead2..2d635fa7c 100644 --- a/velocity/src/main/java/us/myles/ViaVersion/velocity/platform/VelocityViaLoader.java +++ b/velocity/src/main/java/us/myles/ViaVersion/velocity/platform/VelocityViaLoader.java @@ -9,8 +9,6 @@ import us.myles.ViaVersion.api.protocol.ProtocolVersion; import us.myles.ViaVersion.protocols.base.VersionProvider; import us.myles.ViaVersion.protocols.protocol1_9to1_8.providers.BossBarProvider; import us.myles.ViaVersion.protocols.protocol1_9to1_8.providers.MovementTransmitterProvider; -import us.myles.ViaVersion.velocity.handlers.VelocityServerHandler; -import us.myles.ViaVersion.velocity.listeners.ElytraPatch; import us.myles.ViaVersion.velocity.listeners.UpdateListener; import us.myles.ViaVersion.velocity.providers.VelocityBossBarProvider; import us.myles.ViaVersion.velocity.providers.VelocityMovementTransmitter; @@ -26,7 +24,6 @@ public class VelocityViaLoader implements ViaPlatformLoader { if (ProtocolRegistry.SERVER_PROTOCOL < ProtocolVersion.v1_9.getVersion()) { Via.getManager().getProviders().use(MovementTransmitterProvider.class, new VelocityMovementTransmitter()); Via.getManager().getProviders().use(BossBarProvider.class, new VelocityBossBarProvider()); - VelocityPlugin.PROXY.getEventManager().register(plugin, new ElytraPatch()); } Via.getManager().getProviders().use(VersionProvider.class, new VelocityVersionProvider()); @@ -34,7 +31,6 @@ public class VelocityViaLoader implements ViaPlatformLoader { // We don't need main hand patch because Join Game packet makes client send hand data again VelocityPlugin.PROXY.getEventManager().register(plugin, new UpdateListener()); - VelocityPlugin.PROXY.getEventManager().register(plugin, new VelocityServerHandler()); int pingInterval = ((VelocityViaConfig) Via.getPlatform().getConf()).getVelocityPingInterval(); if (pingInterval > 0) { diff --git a/velocity/src/main/java/us/myles/ViaVersion/velocity/providers/VelocityVersionProvider.java b/velocity/src/main/java/us/myles/ViaVersion/velocity/providers/VelocityVersionProvider.java index df30ff650..0fde41837 100644 --- a/velocity/src/main/java/us/myles/ViaVersion/velocity/providers/VelocityVersionProvider.java +++ b/velocity/src/main/java/us/myles/ViaVersion/velocity/providers/VelocityVersionProvider.java @@ -1,19 +1,42 @@ package us.myles.ViaVersion.velocity.providers; +import com.velocitypowered.api.proxy.ServerConnection; +import io.netty.channel.ChannelHandler; import us.myles.ViaVersion.VelocityPlugin; import us.myles.ViaVersion.api.Via; import us.myles.ViaVersion.api.data.UserConnection; import us.myles.ViaVersion.api.protocol.ProtocolVersion; import us.myles.ViaVersion.protocols.base.VersionProvider; import us.myles.ViaVersion.velocity.platform.VelocityViaInjector; +import us.myles.ViaVersion.velocity.service.ProtocolDetectorService; +import java.lang.reflect.Method; import java.util.Arrays; import java.util.stream.IntStream; public class VelocityVersionProvider extends VersionProvider { + private static Method getAssociation; + + static { + try { + getAssociation = Class.forName("com.velocitypowered.proxy.connection.MinecraftConnection").getMethod("getAssociation"); + } catch (NoSuchMethodException | ClassNotFoundException e) { + e.printStackTrace(); + } + } @Override public int getServerProtocol(UserConnection user) throws Exception { + return user.isClientSide() ? getBackProtocol(user) : getFrontProtocol(user); + } + + private int getBackProtocol(UserConnection user) throws Exception { + ChannelHandler mcHandler = user.getChannel().pipeline().get("handler"); + return ProtocolDetectorService.getProtocolId( + ((ServerConnection) getAssociation.invoke(mcHandler)).getServerInfo().getName()); + } + + private int getFrontProtocol(UserConnection user) throws Exception { int playerVersion = user.getProtocolInfo().getProtocolVersion(); IntStream versions = com.velocitypowered.api.network.ProtocolVersion.SUPPORTED_VERSIONS.stream()