diff --git a/README.md b/README.md index b062ad3..846fa4d 100644 --- a/README.md +++ b/README.md @@ -21,17 +21,19 @@ or registry synchronization (fabric-registry-sync mod). | Dependency | Download | | ----------------------------------------- | ------------------------------------------------------------------------------------------ | -| (Bundled 3.2.0 release) ViaVersion 3.2.0+ | https://ci.viaversion.com/job/ViaVersion/ or https://ci.viaversion.com/job/ViaVersion-DEV/ | +| (Bundled 3.2.1 release) ViaVersion 3.2.1+ | https://ci.viaversion.com/job/ViaVersion/ or https://ci.viaversion.com/job/ViaVersion-DEV/ | | (Bundled) Cotton Client Commands | https://www.curseforge.com/minecraft/mc-mods/cotton-client-commands | | (Optional) Fabric Command API v1/v0 | https://www.curseforge.com/minecraft/mc-mods/fabric-api | +| (Optional) Fabric Lifecycle Events v1/v0 | https://www.curseforge.com/minecraft/mc-mods/fabric-api | | Fabric Resource Loader v0 | https://www.curseforge.com/minecraft/mc-mods/fabric-api | **1.8.9 Dependencies:** | Dependency | Download | | ----------------------------------------- | ------------------------------------------------------------------------------------------ | -| (Bundled 3.2.0 release) ViaVersion 3.2.0+ | https://ci.viaversion.com/job/ViaVersion/ or https://ci.viaversion.com/job/ViaVersion-DEV/ | +| (Bundled 3.2.1 release) ViaVersion 3.2.1+ | https://ci.viaversion.com/job/ViaVersion/ or https://ci.viaversion.com/job/ViaVersion-DEV/ | | (Optional) Fabric Commands v0 | https://www.curseforge.com/minecraft/mc-mods/legacy-fabric-api | +| (Optional) Fabric Lifecycle Events v1 | https://www.curseforge.com/minecraft/mc-mods/legacy-fabric-api | | Fabric Resource Loader v0 | https://www.curseforge.com/minecraft/mc-mods/legacy-fabric-api | @@ -102,6 +104,12 @@ Adding [ViaBackwards](https://viaversion.com/backwards) (and optionally [ViaRewi - [ViaVersion](https://viaversion.com): ViaVersion can run as a plugin for BungeeCord, CraftBukkit, SpongeCommon and Velocity servers. +**Cool things to try:** +- [Geyser](https://geysermc.org/): Plugins, Fabric mod and a standalone proxy for Bedrock edition translation. +- [PolyMc](https://github.com/TheEpicBlock/PolyMc): Fabric mods which translates modded items and blocks, allowing + vanilla to connect using resource packs. + + **How can I disable client-side ViaFabric?:** - You can disable it in the menu or by setting global protocol version to -1 (this will keep per-server translations still enabled) diff --git a/src/main/java/com/github/creeper123123321/viafabric/handler/CommonTransformer.java b/src/main/java/com/github/creeper123123321/viafabric/handler/CommonTransformer.java index 4da17e8..fdd5156 100644 --- a/src/main/java/com/github/creeper123123321/viafabric/handler/CommonTransformer.java +++ b/src/main/java/com/github/creeper123123321/viafabric/handler/CommonTransformer.java @@ -25,7 +25,39 @@ package com.github.creeper123123321.viafabric.handler; +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandler; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.ByteToMessageDecoder; +import io.netty.handler.codec.MessageToByteEncoder; +import io.netty.handler.codec.MessageToMessageDecoder; +import us.myles.ViaVersion.util.PipelineUtil; + +import java.lang.reflect.InvocationTargetException; + public class CommonTransformer { public static final String HANDLER_DECODER_NAME = "via-decoder"; public static final String HANDLER_ENCODER_NAME = "via-encoder"; + + public static void decompress(ChannelHandlerContext ctx, ByteBuf buf) throws InvocationTargetException { + ChannelHandler handler = ctx.pipeline().get("decompress"); + ByteBuf decompressed = handler instanceof MessageToMessageDecoder + ? (ByteBuf) PipelineUtil.callDecode((MessageToMessageDecoder) handler, ctx, buf).get(0) + : (ByteBuf) PipelineUtil.callDecode((ByteToMessageDecoder) handler, ctx, buf).get(0); + try { + buf.clear().writeBytes(decompressed); + } finally { + decompressed.release(); + } + } + + public static void compress(ChannelHandlerContext ctx, ByteBuf buf) throws Exception { + ByteBuf compressed = ctx.alloc().buffer(); + try { + PipelineUtil.callEncode((MessageToByteEncoder) ctx.pipeline().get("compress"), ctx, buf, compressed); + buf.clear().writeBytes(compressed); + } finally { + compressed.release(); + } + } } \ No newline at end of file diff --git a/src/main/java/com/github/creeper123123321/viafabric/handler/FabricDecodeHandler.java b/src/main/java/com/github/creeper123123321/viafabric/handler/FabricDecodeHandler.java index 1ff7882..7037c6d 100644 --- a/src/main/java/com/github/creeper123123321/viafabric/handler/FabricDecodeHandler.java +++ b/src/main/java/com/github/creeper123123321/viafabric/handler/FabricDecodeHandler.java @@ -34,11 +34,14 @@ 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 FabricDecodeHandler extends MessageToMessageDecoder { private final UserConnection info; + private boolean handledCompression; + private boolean skipDoubleTransform; public FabricDecodeHandler(UserConnection info) { this.info = info; @@ -48,8 +51,15 @@ public class FabricDecodeHandler extends MessageToMessageDecoder { return info; } + // https://github.com/ViaVersion/ViaVersion/blob/master/velocity/src/main/java/us/myles/ViaVersion/velocity/handlers/VelocityDecodeHandler.java @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()); @@ -58,13 +68,40 @@ public class FabricDecodeHandler extends MessageToMessageDecoder { ByteBuf transformedBuf = ctx.alloc().buffer().writeBytes(bytebuf); try { + boolean needsCompress = handleCompressionOrder(ctx, transformedBuf); + info.transformIncoming(transformedBuf, CancelDecoderException::generate); + + if (needsCompress) { + CommonTransformer.compress(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("decompress"); + if (decoderIndex == -1) return false; + handledCompression = true; + if (decoderIndex > ctx.pipeline().names().indexOf("via-decoder")) { + // Need to decompress this packet due to bad order + CommonTransformer.decompress(ctx, buf); + 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("compress", "via-encoder", encoder); + ctx.pipeline().addAfter("decompress", "via-decoder", decoder); + return true; + } + return false; + } + @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { if (PipelineUtil.containsCause(cause, CancelCodecException.class)) return; diff --git a/src/main/java/com/github/creeper123123321/viafabric/handler/FabricEncodeHandler.java b/src/main/java/com/github/creeper123123321/viafabric/handler/FabricEncodeHandler.java index bb74e60..45461b1 100644 --- a/src/main/java/com/github/creeper123123321/viafabric/handler/FabricEncodeHandler.java +++ b/src/main/java/com/github/creeper123123321/viafabric/handler/FabricEncodeHandler.java @@ -34,18 +34,20 @@ import us.myles.ViaVersion.exception.CancelCodecException; import us.myles.ViaVersion.exception.CancelEncoderException; import us.myles.ViaVersion.util.PipelineUtil; +import java.lang.reflect.InvocationTargetException; import java.util.List; @ChannelHandler.Sharable public class FabricEncodeHandler extends MessageToMessageEncoder { private final UserConnection info; + private boolean handledCompression; public FabricEncodeHandler(UserConnection info) { this.info = info; } @Override - protected void encode(final ChannelHandlerContext ctx, ByteBuf bytebuf, final List out) throws Exception { + protected void encode(final ChannelHandlerContext ctx, ByteBuf bytebuf, List out) throws Exception { if (!info.checkOutgoingPacket()) throw CancelEncoderException.generate(null); if (!info.shouldTransformPacket()) { out.add(bytebuf.retain()); @@ -54,13 +56,39 @@ public class FabricEncodeHandler extends MessageToMessageEncoder { ByteBuf transformedBuf = ctx.alloc().buffer().writeBytes(bytebuf); try { + boolean needsCompress = handleCompressionOrder(ctx, transformedBuf); + info.transformOutgoing(transformedBuf, CancelEncoderException::generate); + + if (needsCompress) { + CommonTransformer.compress(ctx, transformedBuf); + } out.add(transformedBuf.retain()); } finally { transformedBuf.release(); } } + private boolean handleCompressionOrder(ChannelHandlerContext ctx, ByteBuf buf) throws InvocationTargetException { + if (handledCompression) return false; + + int encoderIndex = ctx.pipeline().names().indexOf("compress"); + if (encoderIndex == -1) return false; + handledCompression = true; + if (encoderIndex > ctx.pipeline().names().indexOf("via-encoder")) { + // Need to decompress this packet due to bad order + CommonTransformer.decompress(ctx, buf); + 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("compress", "via-encoder", encoder); + ctx.pipeline().addAfter("decompress", "via-decoder", decoder); + return true; + } + return false; + } + @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { if (PipelineUtil.containsCause(cause, CancelCodecException.class)) return; diff --git a/src/main/java/com/github/creeper123123321/viafabric/handler/clientside/ProtocolDetectionHandler.java b/src/main/java/com/github/creeper123123321/viafabric/handler/clientside/ProtocolDetectionHandler.java index 52d5124..f969dd7 100644 --- a/src/main/java/com/github/creeper123123321/viafabric/handler/clientside/ProtocolDetectionHandler.java +++ b/src/main/java/com/github/creeper123123321/viafabric/handler/clientside/ProtocolDetectionHandler.java @@ -35,7 +35,6 @@ import us.myles.ViaVersion.api.Pair; import java.net.InetSocketAddress; import java.util.ArrayDeque; import java.util.Queue; -import java.util.concurrent.ExecutionException; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; @@ -48,23 +47,19 @@ public class ProtocolDetectionHandler extends ChannelDuplexHandler { public void channelActive(ChannelHandlerContext ctx) throws Exception { super.channelActive(ctx); if (ctx.channel().remoteAddress() instanceof InetSocketAddress) { - try { - ScheduledFuture timeoutRun = ctx.executor().schedule(() -> { - ViaFabric.JLOGGER.warning("Timeout for protocol auto-detection in " - + ctx.channel().remoteAddress() + " server"); - hold = false; - drainQueue(ctx); - ctx.pipeline().remove(this); - }, 10, TimeUnit.SECONDS); - ProtocolAutoDetector.SERVER_VER.get(((InetSocketAddress) ctx.channel().remoteAddress())) - .whenComplete((obj, ex) -> { - ctx.pipeline().remove(this); - timeoutRun.cancel(false); - }); - // Let's cache it before we need it - } catch (ExecutionException e) { - ViaFabric.JLOGGER.warning("Protocol auto detector error: " + e); - } + ScheduledFuture timeoutRun = ctx.executor().schedule(() -> { + ViaFabric.JLOGGER.warning("Timeout for protocol auto-detection in " + + ctx.channel().remoteAddress() + " server"); + hold = false; + drainQueue(ctx); + ctx.pipeline().remove(this); + }, 10, TimeUnit.SECONDS); + ProtocolAutoDetector.detectVersion(((InetSocketAddress) ctx.channel().remoteAddress())) + .whenComplete((obj, ex) -> { + ctx.pipeline().remove(this); + timeoutRun.cancel(false); + }); + // Let's cache it before we need it } } diff --git a/src/main/java/com/github/creeper123123321/viafabric/mixin/pipeline/MixinClientConnection.java b/src/main/java/com/github/creeper123123321/viafabric/mixin/pipeline/MixinClientConnection.java index 8f531d8..c37df20 100644 --- a/src/main/java/com/github/creeper123123321/viafabric/mixin/pipeline/MixinClientConnection.java +++ b/src/main/java/com/github/creeper123123321/viafabric/mixin/pipeline/MixinClientConnection.java @@ -25,25 +25,19 @@ package com.github.creeper123123321.viafabric.mixin.pipeline; -import com.github.creeper123123321.viafabric.handler.CommonTransformer; -import com.github.creeper123123321.viafabric.handler.FabricDecodeHandler; -import com.github.creeper123123321.viafabric.handler.FabricEncodeHandler; import io.netty.channel.Channel; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelPipeline; import net.minecraft.network.ClientConnection; import org.apache.logging.log4j.Logger; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Redirect; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; @Mixin(ClientConnection.class) public class MixinClientConnection { - @Shadow private Channel channel; + @Shadow + private Channel channel; @Redirect( method = "exceptionCaught", @@ -59,21 +53,4 @@ public class MixinClientConnection { logger.debug(message, t); } } - - @Inject(method = "setCompressionThreshold", at = @At( - value = "RETURN", - remap = false - )) - private void fixCompressionOrder(int compressionThreshold, CallbackInfo ci) { - if (channel.pipeline().get(FabricEncodeHandler.class) == null) return; - if (channel.pipeline().names().indexOf("compress") - < channel.pipeline().names().indexOf(CommonTransformer.HANDLER_ENCODER_NAME)) { - return; // Order is correct or compression is disabled - } - // Fixes the handler order - FabricDecodeHandler decode = channel.pipeline().remove(FabricDecodeHandler.class); - FabricEncodeHandler encode = channel.pipeline().remove(FabricEncodeHandler.class); - channel.pipeline().addAfter("decompress", "via-decoder", decode); - channel.pipeline().addAfter("compress", "via-encoder", encode); - } } diff --git a/src/main/java/com/github/creeper123123321/viafabric/providers/VRVersionProvider.java b/src/main/java/com/github/creeper123123321/viafabric/providers/VRVersionProvider.java index 806422d..7b9422d 100644 --- a/src/main/java/com/github/creeper123123321/viafabric/providers/VRVersionProvider.java +++ b/src/main/java/com/github/creeper123123321/viafabric/providers/VRVersionProvider.java @@ -102,7 +102,7 @@ public class VRVersionProvider extends VersionProvider { if (serverVer == -2) { // Hope protocol was autodetected ProtocolVersion autoVer = - ProtocolAutoDetector.SERVER_VER.get((InetSocketAddress) addr).getNow(null); + ProtocolAutoDetector.detectVersion((InetSocketAddress) addr).getNow(null); if (autoVer != null) { serverVer = autoVer.getId(); } diff --git a/src/main/java/com/github/creeper123123321/viafabric/service/ProtocolAutoDetector.java b/src/main/java/com/github/creeper123123321/viafabric/service/ProtocolAutoDetector.java index 362f9f7..08a56a5 100644 --- a/src/main/java/com/github/creeper123123321/viafabric/service/ProtocolAutoDetector.java +++ b/src/main/java/com/github/creeper123123321/viafabric/service/ProtocolAutoDetector.java @@ -47,13 +47,17 @@ import net.minecraft.text.LiteralText; import net.minecraft.text.Text; import us.myles.ViaVersion.api.protocol.ProtocolVersion; +import java.net.InetAddress; import java.net.InetSocketAddress; +import java.net.UnknownHostException; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; +import java.util.logging.Level; @Environment(EnvType.CLIENT) public class ProtocolAutoDetector { - public static LoadingCache> SERVER_VER = CacheBuilder.newBuilder() + private static LoadingCache> SERVER_VER = CacheBuilder.newBuilder() .expireAfterAccess(100, TimeUnit.SECONDS) .build(CacheLoader.from((address) -> { CompletableFuture future = new CompletableFuture<>(); @@ -61,8 +65,6 @@ public class ProtocolAutoDetector { try { final ClientConnection clientConnection = new ClientConnection(NetworkSide.CLIENTBOUND); - ViaFabricAddress viaAddr = new ViaFabricAddress().parse(address.getHostString()); - ChannelFuture ch = new Bootstrap() .group(ClientConnection.CLIENT_IO_GROUP.get()) .channel(NioSocketChannel.class) @@ -121,7 +123,7 @@ public class ProtocolAutoDetector { } }); - clientConnection.send(new HandshakeC2SPacket(viaAddr.realAddress, + clientConnection.send(new HandshakeC2SPacket(address.getHostString(), address.getPort(), NetworkState.STATUS)); clientConnection.send(new QueryRequestC2SPacket()); }); @@ -133,4 +135,16 @@ public class ProtocolAutoDetector { return future; })); + + public static CompletableFuture detectVersion(InetSocketAddress address) { + try { + InetSocketAddress real = new InetSocketAddress(InetAddress.getByAddress + (new ViaFabricAddress().parse(address.getHostString()).realAddress, + address.getAddress().getAddress()), address.getPort()); + return SERVER_VER.get(real); + } catch (UnknownHostException | ExecutionException e) { + ViaFabric.JLOGGER.log(Level.WARNING, "Protocol auto detector error: ", e); + return CompletableFuture.completedFuture(null); + } + } }