From 42eb7e46e417566b24a572d229b5f925c8957fb8 Mon Sep 17 00:00:00 2001 From: FlorianMichael <60033407+FlorianMichael@users.noreply.github.com> Date: Tue, 14 Mar 2023 21:07:55 +0100 Subject: [PATCH] (WIP) ViaBedrock Implementation --- README.md | 6 +- build.gradle | 11 ++ gradle.properties | 1 + .../viafabricplus/ViaFabricPlus.java | 7 ++ .../injection/access/IClientConnection.java | 14 +++ .../mixin/base/MixinClientConnection.java | 101 +++++++++++++++++- .../mixin/base/MixinClientConnection_1.java | 77 ++++++++++++- .../MixinMultiplayerServerListPinger.java | 42 ++++++++ .../mixin/fixes/MixinServerAddress.java | 3 +- .../fixes/screen/MixinConnectScreen_1.java | 2 +- .../MixinWorldPackets1_13.java | 1 - .../platform/ViaBedrockPlatformImpl.java | 44 ++++++++ .../{ => pre_netty}/PreNettyConstants.java | 2 +- .../{ => pre_netty}/VFPPreNettyDecoder.java | 2 +- .../{ => pre_netty}/VFPPreNettyEncoder.java | 2 +- .../raknet/BedrockRakNetConstants.java | 29 +++++ .../platform/raknet/DisconnectHandler.java | 38 +++++++ .../raknet/PingEncapsulationCodec.java | 64 +++++++++++ .../raknet/RakMessageEncapsulationCodec.java | 61 +++++++++++ .../platform/raknet/RakNetPingSessions.java | 34 ++++++ .../FixedUnconnectedPingEncoder.java | 60 +++++++++++ .../FixedUnconnectedPongDecoder.java | 74 +++++++++++++ .../ViaFabricPlusNettyPipelineProvider.java | 59 ++++++++++ src/main/resources/viafabricplus.mixins.json | 6 +- 24 files changed, 723 insertions(+), 17 deletions(-) create mode 100644 src/main/java/de/florianmichael/viafabricplus/injection/mixin/base/MixinMultiplayerServerListPinger.java create mode 100644 src/main/java/de/florianmichael/viafabricplus/platform/ViaBedrockPlatformImpl.java rename src/main/java/de/florianmichael/viafabricplus/platform/{ => pre_netty}/PreNettyConstants.java (94%) rename src/main/java/de/florianmichael/viafabricplus/platform/{ => pre_netty}/VFPPreNettyDecoder.java (96%) rename src/main/java/de/florianmichael/viafabricplus/platform/{ => pre_netty}/VFPPreNettyEncoder.java (96%) create mode 100644 src/main/java/de/florianmichael/viafabricplus/platform/raknet/BedrockRakNetConstants.java create mode 100644 src/main/java/de/florianmichael/viafabricplus/platform/raknet/DisconnectHandler.java create mode 100644 src/main/java/de/florianmichael/viafabricplus/platform/raknet/PingEncapsulationCodec.java create mode 100644 src/main/java/de/florianmichael/viafabricplus/platform/raknet/RakMessageEncapsulationCodec.java create mode 100644 src/main/java/de/florianmichael/viafabricplus/platform/raknet/RakNetPingSessions.java create mode 100644 src/main/java/de/florianmichael/viafabricplus/platform/raknet/library_fix/FixedUnconnectedPingEncoder.java create mode 100644 src/main/java/de/florianmichael/viafabricplus/platform/raknet/library_fix/FixedUnconnectedPongDecoder.java create mode 100644 src/main/java/de/florianmichael/viafabricplus/provider/ViaFabricPlusNettyPipelineProvider.java diff --git a/README.md b/README.md index f4280aee..e4f90934 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # ViaFabricPlus -Clientside ViaVersion, ViaLegacy and ViaAprilFools implementation with clientside fixes for Fabric +Clientside ViaVersion, ViaLegacy, ViaBedrock and ViaAprilFools implementation with clientside fixes for Fabric ### This project has nothing to do with the original ViaFabric and is therefore also not compact ## Contact @@ -13,6 +13,7 @@ If you just want to talk or need help with ViaFabricPlus feel free to join my - [x] ViaBackwards implementation - [x] ViaLegacy implementation - [x] ViaAprilFools implementation +- [x] (Beta) ViaBedrock implementation - [x] BetaCraft implementation for MP Pass ## Clientside related Fixes @@ -36,7 +37,7 @@ If you just want to talk or need help with ViaFabricPlus feel free to join my - [x] Fixed clientside packet handling (1.16.5 transactions, 1.19.0 tablist, ...) ## TODO -- [ ] ViaBedrock implementation +- [ ] Bedrock account auth for ViaBedrock - [ ] ClassiCube implementation for MP Pass - [ ] BetaCraft server list screen - [ ] More extensions for Classic Protocol Extensions protocol @@ -64,6 +65,7 @@ as does [multiconnect](https://github.com/Earthcomputer/multiconnect) from Earth | Snake YAML | https://mvnrepository.com/artifact/org.yaml/snakeyaml/1.33 | | ViaLegacy | https://github.com/RaphiMC/ViaLegacy | | ViaAprilFools | https://github.com/RaphiMC/ViaAprilFools | +| ViaBedrock | https://github.com/RaphiMC/ViaBedrock | | MC-Structs | https://github.com/Lenni0451/MCStructs | | Reflect | https://github.com/Lenni0451/Reflect | | ViaLoadingBase | https://github.com/FlorianMichael/ViaLoadingBase | diff --git a/build.gradle b/build.gradle index c73b3041..dfb4cae9 100644 --- a/build.gradle +++ b/build.gradle @@ -37,6 +37,10 @@ allprojects { name = "TerraformersMC" url = "https://maven.terraformersmc.com/releases" } + maven { + name = "OpenCollab Snapshots" + url = "https://repo.opencollab.dev/maven-snapshots/" + } } } @@ -59,9 +63,16 @@ dependencies { libs "net.raphimc:ViaLegacy:${project.vialegacy_version}" libs "net.raphimc:ViaAprilFools:${project.viaaprilfools_version}" + libs "net.raphimc:ViaBedrock:${project.viabedrock_version}" libs "net.lenni0451.mcstructs:text:${project.mcstructs_text_version}" libs "net.lenni0451:Reflect:${project.reflect_version}" + libs("org.cloudburstmc.netty:netty-transport-raknet:1.0.0.CR1-SNAPSHOT") { + exclude group: "io.netty", module: "netty-common" + exclude group: "io.netty", module: "netty-buffer" + exclude group: "io.netty", module: "netty-codec" + exclude group: "io.netty", module: "netty-transport" + } } processResources { diff --git a/gradle.properties b/gradle.properties index ebeffabf..36fa6db2 100644 --- a/gradle.properties +++ b/gradle.properties @@ -23,6 +23,7 @@ snake_yml_version=2.0 # raphimc libs vialegacy_version=2.2.9-SNAPSHOT viaaprilfools_version=2.0.6 +viabedrock_version=0.0.1-SNAPSHOT # lenni0451 libs mcstructs_text_version=2.2.0 diff --git a/src/main/java/de/florianmichael/viafabricplus/ViaFabricPlus.java b/src/main/java/de/florianmichael/viafabricplus/ViaFabricPlus.java index e6ffe173..e36ed20e 100644 --- a/src/main/java/de/florianmichael/viafabricplus/ViaFabricPlus.java +++ b/src/main/java/de/florianmichael/viafabricplus/ViaFabricPlus.java @@ -30,6 +30,7 @@ import de.florianmichael.viafabricplus.definition.c0_30.command.ClassicProtocolC import de.florianmichael.viafabricplus.definition.v1_19_0.provider.CommandArgumentsProvider; import de.florianmichael.viafabricplus.definition.v1_8_x.ArmorPointsDefinition; import de.florianmichael.viafabricplus.platform.ViaAprilFoolsPlatformImpl; +import de.florianmichael.viafabricplus.platform.ViaBedrockPlatformImpl; import de.florianmichael.viafabricplus.platform.ViaLegacyPlatformImpl; import de.florianmichael.viafabricplus.provider.*; import de.florianmichael.viafabricplus.settings.SettingGroup; @@ -43,6 +44,8 @@ import net.minecraft.SharedConstants; import net.minecraft.client.MinecraftClient; import net.minecraft.network.ClientConnection; import net.raphimc.viaaprilfools.api.AprilFoolsProtocolVersion; +import net.raphimc.viabedrock.api.BedrockProtocolVersion; +import net.raphimc.viabedrock.protocol.providers.NettyPipelineProvider; import net.raphimc.vialegacy.api.LegacyProtocolVersion; import net.raphimc.vialegacy.protocols.classic.protocola1_0_15toc0_28_30.providers.ClassicCustomCommandProvider; import net.raphimc.vialegacy.protocols.classic.protocola1_0_15toc0_28_30.providers.ClassicMPPassProvider; @@ -52,6 +55,7 @@ import net.raphimc.vialegacy.protocols.release.protocol1_7_2_5to1_6_4.providers. import net.raphimc.vialegacy.protocols.release.protocol1_8to1_7_6_10.providers.GameProfileFetcher; import java.io.File; +import java.net.InetAddress; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -80,12 +84,14 @@ public class ViaFabricPlus { protocolVersions.add(protocolVersions.indexOf(ProtocolVersion.v1_16) + 1, AprilFoolsProtocolVersion.s20w14infinite); protocolVersions.add(protocolVersions.indexOf(ProtocolVersion.v1_16_2) + 1, AprilFoolsProtocolVersion.sCombatTest8c); }); + private final SubPlatform SUB_PLATFORM_VIA_BEDROCK = new SubPlatform("ViaBedrock", () -> true, ViaBedrockPlatformImpl::new, protocolVersions -> protocolVersions.add(BedrockProtocolVersion.bedrockLatest)); public void preLoad() { CustomClassicProtocolExtensions.reflect(); ViaLoadingBase.ViaLoadingBaseBuilder builder = ViaLoadingBase.ViaLoadingBaseBuilder.create(); + builder = builder.subPlatform(SUB_PLATFORM_VIA_BEDROCK, 0); builder = builder.subPlatform(SUB_PLATFORM_VIA_LEGACY); builder = builder.subPlatform(SUB_PLATFORM_VIA_APRIL_FOOLS); @@ -108,6 +114,7 @@ public class ViaFabricPlus { providers.use(GameProfileFetcher.class, new ViaFabricPlusGameProfileFetcher()); providers.use(ClassicMPPassProvider.class, new ViaFabricPlusClassicMPPassProvider()); providers.use(ClassicCustomCommandProvider.class, new ViaFabricPlusClassicCustomCommandProvider()); + providers.use(NettyPipelineProvider.class, new ViaFabricPlusNettyPipelineProvider()); }); builder = builder.onProtocolReload(protocolVersion -> { FabricLoader.getInstance().getEntrypoints("viafabricplus", ViaFabricPlusAddon.class).forEach(viaFabricPlusAddon -> viaFabricPlusAddon.onChangeVersion(protocolVersion)); diff --git a/src/main/java/de/florianmichael/viafabricplus/injection/access/IClientConnection.java b/src/main/java/de/florianmichael/viafabricplus/injection/access/IClientConnection.java index 6a5e9c64..f9032b3c 100644 --- a/src/main/java/de/florianmichael/viafabricplus/injection/access/IClientConnection.java +++ b/src/main/java/de/florianmichael/viafabricplus/injection/access/IClientConnection.java @@ -17,7 +17,21 @@ */ package de.florianmichael.viafabricplus.injection.access; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.SecretKey; +import java.net.InetSocketAddress; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; + public interface IClientConnection { void viafabricplus_setupPreNettyEncryption(); + + void viafabricplus_captureAddress(final InetSocketAddress address); + InetSocketAddress viafabricplus_capturedAddress(); + + void viafabricplus_enableZLibCompression(); + void viafabricplus_enableSnappyCompression(); + void viafabricplus_enableAesGcmEncryption(final SecretKey secretKey) throws InvalidAlgorithmParameterException, NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException; } diff --git a/src/main/java/de/florianmichael/viafabricplus/injection/mixin/base/MixinClientConnection.java b/src/main/java/de/florianmichael/viafabricplus/injection/mixin/base/MixinClientConnection.java index 70472c6b..c39aca23 100644 --- a/src/main/java/de/florianmichael/viafabricplus/injection/mixin/base/MixinClientConnection.java +++ b/src/main/java/de/florianmichael/viafabricplus/injection/mixin/base/MixinClientConnection.java @@ -18,35 +18,67 @@ package de.florianmichael.viafabricplus.injection.mixin.base; import de.florianmichael.viafabricplus.injection.access.IClientConnection; -import de.florianmichael.viafabricplus.platform.PreNettyConstants; +import de.florianmichael.viafabricplus.platform.pre_netty.PreNettyConstants; +import de.florianmichael.viafabricplus.platform.raknet.BedrockRakNetConstants; +import de.florianmichael.viafabricplus.platform.raknet.RakNetPingSessions; import de.florianmichael.vialoadingbase.ViaLoadingBase; import de.florianmichael.vialoadingbase.event.PipelineReorderEvent; -import io.netty.channel.Channel; +import io.netty.bootstrap.AbstractBootstrap; +import io.netty.bootstrap.Bootstrap; +import io.netty.channel.*; +import io.netty.channel.epoll.EpollDatagramChannel; +import io.netty.channel.epoll.EpollSocketChannel; +import io.netty.channel.socket.nio.NioDatagramChannel; import net.minecraft.network.ClientConnection; +import net.minecraft.network.Packet; import net.minecraft.network.encryption.PacketDecryptor; import net.minecraft.network.encryption.PacketEncryptor; +import net.raphimc.viabedrock.api.BedrockProtocolVersion; +import net.raphimc.viabedrock.netty.AesGcmEncryption; +import net.raphimc.viabedrock.netty.SnappyCompression; +import net.raphimc.viabedrock.netty.ZLibCompression; import net.raphimc.vialegacy.api.LegacyProtocolVersion; +import org.cloudburstmc.netty.channel.raknet.RakChannelFactory; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.Unique; 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; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import org.spongepowered.asm.mixin.injection.callback.LocalCapture; import javax.crypto.Cipher; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.SecretKey; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; @Mixin(ClientConnection.class) -public class MixinClientConnection implements IClientConnection { +public abstract class MixinClientConnection extends SimpleChannelInboundHandler> implements IClientConnection { @Shadow private Channel channel; @Shadow private boolean encrypted; + + @Shadow public abstract void channelActive(ChannelHandlerContext context) throws Exception; + @Unique private Cipher viafabricplus_decryptionCipher; @Unique private Cipher viafabricplus_encryptionCipher; + @Unique + private boolean viafabricplus_compressionEnabled = false; + + @Unique + private InetSocketAddress viafabricplus_capturedAddress; + @Inject(method = "setCompressionThreshold", at = @At("RETURN")) private void reorderCompression(int compressionThreshold, boolean rejectBad, CallbackInfo ci) { channel.pipeline().fireUserEventTriggered(new PipelineReorderEvent()); @@ -61,10 +93,73 @@ public class MixinClientConnection implements IClientConnection { } } + @Inject(method = "connect", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/Lazy;get()Ljava/lang/Object;"), locals = LocalCapture.CAPTURE_FAILHARD) + private static void captureAddress(InetSocketAddress address, boolean useEpoll, CallbackInfoReturnable cir, final ClientConnection clientConnection) { + ((IClientConnection) clientConnection).viafabricplus_captureAddress(address); + } + + @Redirect(method = "connect", at = @At(value = "INVOKE", target = "Lio/netty/bootstrap/Bootstrap;channel(Ljava/lang/Class;)Lio/netty/bootstrap/AbstractBootstrap;")) + private static AbstractBootstrap applyRakNetChannelFactory(Bootstrap instance, Class aClass) { + if (ViaLoadingBase.getClassWrapper().getTargetVersion().isEqualTo(BedrockProtocolVersion.bedrockLatest)) { + return instance.channelFactory(aClass == EpollSocketChannel.class ? RakChannelFactory.client(EpollDatagramChannel.class) : RakChannelFactory.client(NioDatagramChannel.class)); + } + return instance.channel(aClass); + } + + @Redirect(method = "connect", at = @At(value = "INVOKE", target = "Lio/netty/bootstrap/Bootstrap;connect(Ljava/net/InetAddress;I)Lio/netty/channel/ChannelFuture;")) + private static ChannelFuture applyRakNetPing(Bootstrap instance, InetAddress inetHost, int inetPort) { + if (RakNetPingSessions.isPingSession(inetHost)) { + return instance.bind(new InetSocketAddress(0)).addListener(ChannelFutureListener.FIRE_EXCEPTION_ON_FAILURE); + } + return instance.connect(inetHost, inetPort); + } + + @Override + public void channelRegistered(ChannelHandlerContext ctx) throws Exception { + super.channelRegistered(ctx); + if (ViaLoadingBase.getClassWrapper().getTargetVersion().isEqualTo(BedrockProtocolVersion.bedrockLatest)) { + channelActive(ctx); + } + } + @Override public void viafabricplus_setupPreNettyEncryption() { this.encrypted = true; this.channel.pipeline().addBefore(PreNettyConstants.HANDLER_DECODER_NAME, "decrypt", new PacketDecryptor(this.viafabricplus_decryptionCipher)); this.channel.pipeline().addBefore(PreNettyConstants.HANDLER_ENCODER_NAME, "encrypt", new PacketEncryptor(this.viafabricplus_encryptionCipher)); } + + @Override + public void viafabricplus_captureAddress(InetSocketAddress address) { + this.viafabricplus_capturedAddress = address; + } + + @Override + public InetSocketAddress viafabricplus_capturedAddress() { + return this.viafabricplus_capturedAddress; + } + + @Override + public void viafabricplus_enableZLibCompression() { + if (this.viafabricplus_compressionEnabled) throw new IllegalStateException("Compression is already enabled"); + this.viafabricplus_compressionEnabled = true; + + this.channel.pipeline().addBefore(BedrockRakNetConstants.BATCH_LENGTH_HANDLER_NAME, BedrockRakNetConstants.COMPRESSION_HANDLER_NAME, new ZLibCompression()); + } + + @Override + public void viafabricplus_enableSnappyCompression() { + if (this.viafabricplus_compressionEnabled) throw new IllegalStateException("Compression is already enabled"); + this.viafabricplus_compressionEnabled = true; + + this.channel.pipeline().addBefore(BedrockRakNetConstants.BATCH_LENGTH_HANDLER_NAME, BedrockRakNetConstants.COMPRESSION_HANDLER_NAME, new SnappyCompression()); + } + + @Override + public void viafabricplus_enableAesGcmEncryption(final SecretKey secretKey) throws InvalidAlgorithmParameterException, NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException { + if (this.encrypted) throw new IllegalStateException("Encryption is already enabled"); + this.encrypted = true; + + this.channel.pipeline().addAfter(BedrockRakNetConstants.FRAME_ENCAPSULATION_HANDLER_NAME, BedrockRakNetConstants.ENCRYPTION_HANDLER_NAME, new AesGcmEncryption(secretKey)); + } } diff --git a/src/main/java/de/florianmichael/viafabricplus/injection/mixin/base/MixinClientConnection_1.java b/src/main/java/de/florianmichael/viafabricplus/injection/mixin/base/MixinClientConnection_1.java index 16b2ce91..032062f7 100644 --- a/src/main/java/de/florianmichael/viafabricplus/injection/mixin/base/MixinClientConnection_1.java +++ b/src/main/java/de/florianmichael/viafabricplus/injection/mixin/base/MixinClientConnection_1.java @@ -17,29 +17,48 @@ */ package de.florianmichael.viafabricplus.injection.mixin.base; +import com.viaversion.viaversion.api.Via; import com.viaversion.viaversion.api.connection.UserConnection; import com.viaversion.viaversion.connection.UserConnectionImpl; import com.viaversion.viaversion.protocol.ProtocolPipelineImpl; import de.florianmichael.viafabricplus.ViaFabricPlus; -import de.florianmichael.viafabricplus.platform.PreNettyConstants; -import de.florianmichael.viafabricplus.platform.VFPPreNettyDecoder; -import de.florianmichael.viafabricplus.platform.VFPPreNettyEncoder; +import de.florianmichael.viafabricplus.injection.access.IClientConnection; +import de.florianmichael.viafabricplus.platform.pre_netty.PreNettyConstants; +import de.florianmichael.viafabricplus.platform.pre_netty.VFPPreNettyDecoder; +import de.florianmichael.viafabricplus.platform.pre_netty.VFPPreNettyEncoder; import de.florianmichael.viafabricplus.platform.VFPVLBViaDecodeHandler; +import de.florianmichael.viafabricplus.platform.raknet.*; +import de.florianmichael.viafabricplus.platform.raknet.library_fix.FixedUnconnectedPingEncoder; +import de.florianmichael.viafabricplus.platform.raknet.library_fix.FixedUnconnectedPongDecoder; import de.florianmichael.vialoadingbase.ViaLoadingBase; import de.florianmichael.vialoadingbase.netty.VLBViaEncodeHandler; import de.florianmichael.vialoadingbase.netty.NettyConstants; import io.netty.channel.Channel; +import io.netty.channel.ChannelConfig; +import io.netty.channel.ChannelOption; import io.netty.channel.socket.SocketChannel; import net.minecraft.network.ClientConnection; +import net.raphimc.viabedrock.api.BedrockProtocolVersion; +import net.raphimc.viabedrock.netty.BatchLengthCodec; +import net.raphimc.viabedrock.netty.PacketEncapsulationCodec; +import net.raphimc.viabedrock.protocol.BedrockBaseProtocol; import net.raphimc.vialegacy.api.LegacyProtocolVersion; import net.raphimc.vialegacy.protocols.release.protocol1_7_2_5to1_6_4.baseprotocols.PreNettyBaseProtocol; +import org.cloudburstmc.netty.channel.raknet.RakClientChannel; +import org.cloudburstmc.netty.channel.raknet.config.RakChannelOption; +import org.cloudburstmc.netty.handler.codec.raknet.common.UnconnectedPingEncoder; +import org.cloudburstmc.netty.handler.codec.raknet.common.UnconnectedPongDecoder; import org.spongepowered.asm.mixin.Final; 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; +import java.net.InetSocketAddress; +import java.util.concurrent.ThreadLocalRandom; + @Mixin(targets = "net.minecraft.network.ClientConnection$1") public class MixinClientConnection_1 { @@ -47,9 +66,22 @@ public class MixinClientConnection_1 { @Shadow ClientConnection field_11663; + @Redirect(method = "initChannel", at = @At(value = "INVOKE", target = "Lio/netty/channel/ChannelConfig;setOption(Lio/netty/channel/ChannelOption;Ljava/lang/Object;)Z")) + public boolean applyRakNetOptions(ChannelConfig instance, ChannelOption tChannelOption, Object t) { + if (ViaLoadingBase.getClassWrapper().getTargetVersion().isEqualTo(BedrockProtocolVersion.bedrockLatest)) { + instance.setOption(RakChannelOption.RAK_PROTOCOL_VERSION, 11); + instance.setOption(RakChannelOption.RAK_CONNECT_TIMEOUT, 4_000L); + instance.setOption(RakChannelOption.RAK_SESSION_TIMEOUT, 30_000L); + return instance.setOption(RakChannelOption.RAK_GUID, ThreadLocalRandom.current().nextLong()); + } + return instance.setOption(tChannelOption, t); + } + @Inject(method = "initChannel", at = @At("TAIL")) public void hackNettyPipeline(Channel channel, CallbackInfo ci) { - if (channel instanceof SocketChannel) { + final boolean isBedrock = ViaLoadingBase.getClassWrapper().getTargetVersion().isEqualTo(BedrockProtocolVersion.bedrockLatest); + + if (channel instanceof SocketChannel || isBedrock) { final UserConnection user = new UserConnectionImpl(channel, true); channel.attr(ViaFabricPlus.LOCAL_VIA_CONNECTION).set(user); channel.attr(ViaFabricPlus.LOCAL_MINECRAFT_CONNECTION).set(field_11663); @@ -59,12 +91,49 @@ public class MixinClientConnection_1 { channel.pipeline().addBefore("encoder", NettyConstants.HANDLER_ENCODER_NAME, new VLBViaEncodeHandler(user)); channel.pipeline().addBefore("decoder", NettyConstants.HANDLER_DECODER_NAME, new VFPVLBViaDecodeHandler(user)); + if (isBedrock) { + user.getProtocolInfo().getPipeline().add(BedrockBaseProtocol.INSTANCE); + + channel.pipeline().replace("splitter", BedrockRakNetConstants.BATCH_LENGTH_HANDLER_NAME, new BatchLengthCodec()); + + channel.pipeline().addBefore(BedrockRakNetConstants.BATCH_LENGTH_HANDLER_NAME, BedrockRakNetConstants.DISCONNECT_HANDLER_NAME, new DisconnectHandler()); + channel.pipeline().addAfter(BedrockRakNetConstants.DISCONNECT_HANDLER_NAME, BedrockRakNetConstants.FRAME_ENCAPSULATION_HANDLER_NAME, new RakMessageEncapsulationCodec()); + channel.pipeline().addAfter(BedrockRakNetConstants.BATCH_LENGTH_HANDLER_NAME, BedrockRakNetConstants.PACKET_ENCAPSULATION_HANDLER_NAME, new PacketEncapsulationCodec()); + + channel.pipeline().remove("prepender"); + channel.pipeline().remove("timeout"); + + final InetSocketAddress address = ((IClientConnection) field_11663).viafabricplus_capturedAddress(); + + // Pinging in RakNet is something different + if (RakNetPingSessions.isPingSession(address.getAddress())) { + RakNetPingSessions.pingSessions.remove(address.getAddress()); + Via.getPlatform().getLogger().info("ViaFabricPlus is capturing RakNet/Ping protocol"); + + { // Temporary fix for the ping encoder + final RakClientChannel rakChannel = (RakClientChannel) channel; + + rakChannel.parent().pipeline().replace(UnconnectedPingEncoder.NAME, UnconnectedPingEncoder.NAME, new FixedUnconnectedPingEncoder(rakChannel)); + rakChannel.parent().pipeline().replace(UnconnectedPongDecoder.NAME, UnconnectedPongDecoder.NAME, new FixedUnconnectedPongDecoder(rakChannel)); + } + + channel.pipeline().replace(BedrockRakNetConstants.FRAME_ENCAPSULATION_HANDLER_NAME, BedrockRakNetConstants.PING_ENCAPSULATION_HANDLER_NAME, new PingEncapsulationCodec(address)); + + channel.pipeline().remove(BedrockRakNetConstants.PACKET_ENCAPSULATION_HANDLER_NAME); + channel.pipeline().remove(BedrockRakNetConstants.BATCH_LENGTH_HANDLER_NAME); + } + } + if (ViaLoadingBase.getClassWrapper().getTargetVersion().isOlderThanOrEqualTo(LegacyProtocolVersion.r1_6_4)) { user.getProtocolInfo().getPipeline().add(PreNettyBaseProtocol.INSTANCE); channel.pipeline().addBefore("prepender", PreNettyConstants.HANDLER_ENCODER_NAME, new VFPPreNettyEncoder(user)); channel.pipeline().addBefore("splitter", PreNettyConstants.HANDLER_DECODER_NAME, new VFPPreNettyDecoder(user)); } + + for (String name : channel.pipeline().names()) { + System.out.println(name); + } } } } diff --git a/src/main/java/de/florianmichael/viafabricplus/injection/mixin/base/MixinMultiplayerServerListPinger.java b/src/main/java/de/florianmichael/viafabricplus/injection/mixin/base/MixinMultiplayerServerListPinger.java new file mode 100644 index 00000000..b909f3e9 --- /dev/null +++ b/src/main/java/de/florianmichael/viafabricplus/injection/mixin/base/MixinMultiplayerServerListPinger.java @@ -0,0 +1,42 @@ +/* + * This file is part of ViaFabricPlus - https://github.com/FlorianMichael/ViaFabricPlus + * Copyright (C) 2021-2023 FlorianMichael/MrLookAtMe (EnZaXD) and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package de.florianmichael.viafabricplus.injection.mixin.base; + +import de.florianmichael.viafabricplus.platform.raknet.RakNetPingSessions; +import de.florianmichael.vialoadingbase.ViaLoadingBase; +import net.minecraft.client.network.MultiplayerServerListPinger; +import net.raphimc.viabedrock.api.BedrockProtocolVersion; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +import java.net.InetSocketAddress; +import java.util.Optional; + +@Mixin(MultiplayerServerListPinger.class) +public class MixinMultiplayerServerListPinger { + + @Redirect(method = "add", at = @At(value = "INVOKE", target = "Ljava/util/Optional;get()Ljava/lang/Object;")) + public Object mapSocketAddress(Optional instance) { + final InetSocketAddress address = instance.get(); + if (ViaLoadingBase.getClassWrapper().getTargetVersion().isEqualTo(BedrockProtocolVersion.bedrockLatest)) { + RakNetPingSessions.storePingSession(address.getAddress()); + } + return address; + } +} diff --git a/src/main/java/de/florianmichael/viafabricplus/injection/mixin/fixes/MixinServerAddress.java b/src/main/java/de/florianmichael/viafabricplus/injection/mixin/fixes/MixinServerAddress.java index 8c9810a6..6d8b36b0 100644 --- a/src/main/java/de/florianmichael/viafabricplus/injection/mixin/fixes/MixinServerAddress.java +++ b/src/main/java/de/florianmichael/viafabricplus/injection/mixin/fixes/MixinServerAddress.java @@ -22,6 +22,7 @@ import de.florianmichael.vialoadingbase.ViaLoadingBase; import de.florianmichael.vialoadingbase.platform.ProtocolRange; import net.minecraft.client.network.AllowedAddressResolver; import net.minecraft.client.network.ServerAddress; +import net.raphimc.viabedrock.api.BedrockProtocolVersion; import net.raphimc.vialegacy.api.LegacyProtocolVersion; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; @@ -41,7 +42,7 @@ public class MixinServerAddress { @Inject(method = "parse", at = @At("RETURN"), cancellable = true) private static void fixAddress(String address, CallbackInfoReturnable cir) { - if (!cir.getReturnValue().equals(INVALID) && viafabricplus_srvRange.contains(ViaLoadingBase.getClassWrapper().getTargetVersion())) { + if (!cir.getReturnValue().equals(INVALID) && (viafabricplus_srvRange.contains(ViaLoadingBase.getClassWrapper().getTargetVersion()) || ViaLoadingBase.getClassWrapper().getTargetVersion().isEqualTo(BedrockProtocolVersion.bedrockLatest))) { cir.setReturnValue(AllowedAddressResolver.DEFAULT.redirectResolver.lookupRedirect(cir.getReturnValue()).orElse(cir.getReturnValue())); } } diff --git a/src/main/java/de/florianmichael/viafabricplus/injection/mixin/fixes/screen/MixinConnectScreen_1.java b/src/main/java/de/florianmichael/viafabricplus/injection/mixin/fixes/screen/MixinConnectScreen_1.java index 2b0121be..823e72b8 100644 --- a/src/main/java/de/florianmichael/viafabricplus/injection/mixin/fixes/screen/MixinConnectScreen_1.java +++ b/src/main/java/de/florianmichael/viafabricplus/injection/mixin/fixes/screen/MixinConnectScreen_1.java @@ -75,7 +75,7 @@ public class MixinConnectScreen_1 { } final ClientConnection connection = field_2416.connection; - if (connection == null) return; + if (connection == null || connection.channel == null) return; final UserConnection userConnection = connection.channel.attr(ViaFabricPlus.LOCAL_VIA_CONNECTION).get(); if (userConnection == null) { diff --git a/src/main/java/de/florianmichael/viafabricplus/injection/mixin/fixes/viaversion/protocol1_13to1_12_2/MixinWorldPackets1_13.java b/src/main/java/de/florianmichael/viafabricplus/injection/mixin/fixes/viaversion/protocol1_13to1_12_2/MixinWorldPackets1_13.java index edac333f..df82576f 100644 --- a/src/main/java/de/florianmichael/viafabricplus/injection/mixin/fixes/viaversion/protocol1_13to1_12_2/MixinWorldPackets1_13.java +++ b/src/main/java/de/florianmichael/viafabricplus/injection/mixin/fixes/viaversion/protocol1_13to1_12_2/MixinWorldPackets1_13.java @@ -31,5 +31,4 @@ public abstract class MixinWorldPackets1_13 { private static void returnAirDefault(int oldId, CallbackInfoReturnable cir) { cir.setReturnValue(0); } - } diff --git a/src/main/java/de/florianmichael/viafabricplus/platform/ViaBedrockPlatformImpl.java b/src/main/java/de/florianmichael/viafabricplus/platform/ViaBedrockPlatformImpl.java new file mode 100644 index 00000000..01ab853e --- /dev/null +++ b/src/main/java/de/florianmichael/viafabricplus/platform/ViaBedrockPlatformImpl.java @@ -0,0 +1,44 @@ +/* + * This file is part of ViaFabricPlus - https://github.com/FlorianMichael/ViaFabricPlus + * Copyright (C) 2021-2023 FlorianMichael/MrLookAtMe (EnZaXD) and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package de.florianmichael.viafabricplus.platform; + +import com.viaversion.viaversion.api.Via; +import de.florianmichael.vialoadingbase.util.JLoggerToLog4j; +import net.raphimc.viabedrock.platform.ViaBedrockPlatform; +import org.apache.logging.log4j.LogManager; + +import java.io.File; +import java.util.logging.Logger; + +public class ViaBedrockPlatformImpl implements ViaBedrockPlatform { + private static final Logger LOGGER = new JLoggerToLog4j(LogManager.getLogger("ViaBedrock")); + + public ViaBedrockPlatformImpl() { + this.init(this.getDataFolder()); + } + + @Override + public Logger getLogger() { + return LOGGER; + } + + @Override + public File getDataFolder() { + return Via.getPlatform().getDataFolder(); + } +} diff --git a/src/main/java/de/florianmichael/viafabricplus/platform/PreNettyConstants.java b/src/main/java/de/florianmichael/viafabricplus/platform/pre_netty/PreNettyConstants.java similarity index 94% rename from src/main/java/de/florianmichael/viafabricplus/platform/PreNettyConstants.java rename to src/main/java/de/florianmichael/viafabricplus/platform/pre_netty/PreNettyConstants.java index b31aa487..63225064 100644 --- a/src/main/java/de/florianmichael/viafabricplus/platform/PreNettyConstants.java +++ b/src/main/java/de/florianmichael/viafabricplus/platform/pre_netty/PreNettyConstants.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package de.florianmichael.viafabricplus.platform; +package de.florianmichael.viafabricplus.platform.pre_netty; public class PreNettyConstants { diff --git a/src/main/java/de/florianmichael/viafabricplus/platform/VFPPreNettyDecoder.java b/src/main/java/de/florianmichael/viafabricplus/platform/pre_netty/VFPPreNettyDecoder.java similarity index 96% rename from src/main/java/de/florianmichael/viafabricplus/platform/VFPPreNettyDecoder.java rename to src/main/java/de/florianmichael/viafabricplus/platform/pre_netty/VFPPreNettyDecoder.java index ee685f12..bf59aa16 100644 --- a/src/main/java/de/florianmichael/viafabricplus/platform/VFPPreNettyDecoder.java +++ b/src/main/java/de/florianmichael/viafabricplus/platform/pre_netty/VFPPreNettyDecoder.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package de.florianmichael.viafabricplus.platform; +package de.florianmichael.viafabricplus.platform.pre_netty; import com.viaversion.viaversion.api.Via; import com.viaversion.viaversion.api.connection.UserConnection; diff --git a/src/main/java/de/florianmichael/viafabricplus/platform/VFPPreNettyEncoder.java b/src/main/java/de/florianmichael/viafabricplus/platform/pre_netty/VFPPreNettyEncoder.java similarity index 96% rename from src/main/java/de/florianmichael/viafabricplus/platform/VFPPreNettyEncoder.java rename to src/main/java/de/florianmichael/viafabricplus/platform/pre_netty/VFPPreNettyEncoder.java index 4aca4961..3b9997de 100644 --- a/src/main/java/de/florianmichael/viafabricplus/platform/VFPPreNettyEncoder.java +++ b/src/main/java/de/florianmichael/viafabricplus/platform/pre_netty/VFPPreNettyEncoder.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package de.florianmichael.viafabricplus.platform; +package de.florianmichael.viafabricplus.platform.pre_netty; import com.viaversion.viaversion.api.Via; import com.viaversion.viaversion.api.connection.UserConnection; diff --git a/src/main/java/de/florianmichael/viafabricplus/platform/raknet/BedrockRakNetConstants.java b/src/main/java/de/florianmichael/viafabricplus/platform/raknet/BedrockRakNetConstants.java new file mode 100644 index 00000000..1e8a1110 --- /dev/null +++ b/src/main/java/de/florianmichael/viafabricplus/platform/raknet/BedrockRakNetConstants.java @@ -0,0 +1,29 @@ +/* + * This file is part of ViaFabricPlus - https://github.com/FlorianMichael/ViaFabricPlus + * Copyright (C) 2021-2023 FlorianMichael/MrLookAtMe (EnZaXD) and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package de.florianmichael.viafabricplus.platform.raknet; + +public class BedrockRakNetConstants { + + public static final String DISCONNECT_HANDLER_NAME = "disconnect_handler"; + public static final String FRAME_ENCAPSULATION_HANDLER_NAME = "frame_encapsulation"; + public static final String PING_ENCAPSULATION_HANDLER_NAME = "ping_encapsulation"; + public static final String PACKET_ENCAPSULATION_HANDLER_NAME = "packet_encapsulation"; + public static final String BATCH_LENGTH_HANDLER_NAME = "batch_length"; + public static final String COMPRESSION_HANDLER_NAME = "compression"; + public static final String ENCRYPTION_HANDLER_NAME = "encryption"; +} diff --git a/src/main/java/de/florianmichael/viafabricplus/platform/raknet/DisconnectHandler.java b/src/main/java/de/florianmichael/viafabricplus/platform/raknet/DisconnectHandler.java new file mode 100644 index 00000000..37e3d973 --- /dev/null +++ b/src/main/java/de/florianmichael/viafabricplus/platform/raknet/DisconnectHandler.java @@ -0,0 +1,38 @@ +/* + * This file is part of ViaBedrock - https://github.com/RaphiMC/ViaBedrock + * Copyright (C) 2023 RK_01/RaphiMC and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package de.florianmichael.viafabricplus.platform.raknet; + +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelOutboundHandlerAdapter; +import io.netty.channel.ChannelPromise; + +public class DisconnectHandler extends ChannelOutboundHandlerAdapter { + + private boolean calledDisconnect = false; + + @Override + public void close(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception { + if (ctx.channel().isActive() && !this.calledDisconnect) { + this.calledDisconnect = true; + ctx.disconnect(promise); // Send disconnect notification to the server and close the channel + } else { + super.close(ctx, promise); + } + } + +} diff --git a/src/main/java/de/florianmichael/viafabricplus/platform/raknet/PingEncapsulationCodec.java b/src/main/java/de/florianmichael/viafabricplus/platform/raknet/PingEncapsulationCodec.java new file mode 100644 index 00000000..d4c15030 --- /dev/null +++ b/src/main/java/de/florianmichael/viafabricplus/platform/raknet/PingEncapsulationCodec.java @@ -0,0 +1,64 @@ +/* + * This file is part of ViaBedrock - https://github.com/RaphiMC/ViaBedrock + * Copyright (C) 2023 RK_01/RaphiMC and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package de.florianmichael.viafabricplus.platform.raknet; + +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.MessageToMessageCodec; +import org.cloudburstmc.netty.channel.raknet.RakConstants; +import org.cloudburstmc.netty.channel.raknet.RakPing; +import org.cloudburstmc.netty.channel.raknet.RakPong; + +import java.net.InetSocketAddress; +import java.util.List; + +public class PingEncapsulationCodec extends MessageToMessageCodec { + + private final InetSocketAddress remoteAddress; + + public PingEncapsulationCodec(final InetSocketAddress remoteAddress) { + this.remoteAddress = remoteAddress; + } + + @Override + protected void encode(ChannelHandlerContext ctx, ByteBuf in, List out) { + final int packetId = in.readUnsignedByte(); + + if (packetId == RakConstants.ID_UNCONNECTED_PING) { + out.add(new RakPing(in.readLong(), this.remoteAddress)); + } else { + ctx.close(); + throw new IllegalStateException("Unexpected packet ID: " + packetId); + } + } + + @Override + protected void decode(ChannelHandlerContext ctx, RakPong in, List out) { + if (!this.remoteAddress.equals(in.getSender())) { + ctx.close(); + throw new IllegalStateException("Received pong from unexpected address: " + in.getSender()); + } + + final ByteBuf buf = ctx.alloc().buffer(); + buf.writeByte(RakConstants.ID_UNCONNECTED_PONG); + buf.writeLong(in.getPingTime()); + buf.writeBytes(in.getPongData()); + out.add(buf); + } + +} diff --git a/src/main/java/de/florianmichael/viafabricplus/platform/raknet/RakMessageEncapsulationCodec.java b/src/main/java/de/florianmichael/viafabricplus/platform/raknet/RakMessageEncapsulationCodec.java new file mode 100644 index 00000000..2aa3aece --- /dev/null +++ b/src/main/java/de/florianmichael/viafabricplus/platform/raknet/RakMessageEncapsulationCodec.java @@ -0,0 +1,61 @@ +/* + * This file is part of ViaBedrock - https://github.com/RaphiMC/ViaBedrock + * Copyright (C) 2023 RK_01/RaphiMC and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package de.florianmichael.viafabricplus.platform.raknet; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.CompositeByteBuf; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.MessageToMessageCodec; +import org.cloudburstmc.netty.channel.raknet.RakReliability; +import org.cloudburstmc.netty.channel.raknet.packet.RakMessage; + +import java.util.List; + +public class RakMessageEncapsulationCodec extends MessageToMessageCodec { + + private static final int FRAME_ID = 0xFE; + + @Override + protected void encode(ChannelHandlerContext ctx, ByteBuf msg, List out) { + final CompositeByteBuf buf = ctx.alloc().compositeBuffer(2); + try { + buf.addComponent(true, ctx.alloc().ioBuffer(1).writeByte(FRAME_ID)); + buf.addComponent(true, msg.retainedSlice()); + out.add(buf.retain()); + } finally { + buf.release(); + } + } + + @Override + protected void decode(ChannelHandlerContext ctx, RakMessage msg, List out) { + if (msg.channel() != 0 && msg.reliability() != RakReliability.RELIABLE_ORDERED) { + return; + } + final ByteBuf in = msg.content(); + if (!in.isReadable()) { + return; + } + final int id = in.readUnsignedByte(); + if (id != FRAME_ID) { + throw new IllegalStateException("Invalid frame ID: " + id); + } + out.add(in.readRetainedSlice(in.readableBytes())); + } + +} diff --git a/src/main/java/de/florianmichael/viafabricplus/platform/raknet/RakNetPingSessions.java b/src/main/java/de/florianmichael/viafabricplus/platform/raknet/RakNetPingSessions.java new file mode 100644 index 00000000..efba61b7 --- /dev/null +++ b/src/main/java/de/florianmichael/viafabricplus/platform/raknet/RakNetPingSessions.java @@ -0,0 +1,34 @@ +/* + * This file is part of ViaFabricPlus - https://github.com/FlorianMichael/ViaFabricPlus + * Copyright (C) 2021-2023 FlorianMichael/MrLookAtMe (EnZaXD) and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package de.florianmichael.viafabricplus.platform.raknet; + +import java.net.InetAddress; +import java.util.ArrayList; +import java.util.List; + +public class RakNetPingSessions { + public final static List pingSessions = new ArrayList<>(); + + public static void storePingSession(final InetAddress address) { + pingSessions.add(address); + } + + public static boolean isPingSession(final InetAddress address) { + return pingSessions.contains(address); + } +} diff --git a/src/main/java/de/florianmichael/viafabricplus/platform/raknet/library_fix/FixedUnconnectedPingEncoder.java b/src/main/java/de/florianmichael/viafabricplus/platform/raknet/library_fix/FixedUnconnectedPingEncoder.java new file mode 100644 index 00000000..d87aa1db --- /dev/null +++ b/src/main/java/de/florianmichael/viafabricplus/platform/raknet/library_fix/FixedUnconnectedPingEncoder.java @@ -0,0 +1,60 @@ +/* + * This file is part of ViaBedrock - https://github.com/RaphiMC/ViaBedrock + * Copyright (C) 2023 RK_01/RaphiMC and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package de.florianmichael.viafabricplus.platform.raknet.library_fix; + +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelOutboundHandlerAdapter; +import io.netty.channel.ChannelPromise; +import io.netty.channel.socket.DatagramPacket; +import org.cloudburstmc.netty.channel.raknet.RakClientChannel; +import org.cloudburstmc.netty.channel.raknet.RakPing; +import org.cloudburstmc.netty.channel.raknet.config.RakChannelOption; + +import static org.cloudburstmc.netty.channel.raknet.RakConstants.ID_UNCONNECTED_PING; + +// Temporary fix until the library fixes the issue +public class FixedUnconnectedPingEncoder extends ChannelOutboundHandlerAdapter { + + private final RakClientChannel rakClientChannel; + + public FixedUnconnectedPingEncoder(final RakClientChannel rakClientChannel) { + this.rakClientChannel = rakClientChannel; + } + + @Override + public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception { + if (!(msg instanceof RakPing)) { + ctx.write(msg, promise); + return; + } + + RakPing ping = (RakPing) msg; + ByteBuf magicBuf = this.rakClientChannel.config().getOption(RakChannelOption.RAK_UNCONNECTED_MAGIC); + long guid = this.rakClientChannel.config().getOption(RakChannelOption.RAK_GUID); + + ByteBuf pingBuffer = ctx.alloc().ioBuffer(magicBuf.readableBytes() + 17); + pingBuffer.writeByte(ID_UNCONNECTED_PING); + pingBuffer.writeLong(ping.getPingTime()); + pingBuffer.writeBytes(magicBuf, magicBuf.readerIndex(), magicBuf.readableBytes()); + pingBuffer.writeLong(guid); + ctx.write(new DatagramPacket(pingBuffer, ping.getSender())); + } + +} diff --git a/src/main/java/de/florianmichael/viafabricplus/platform/raknet/library_fix/FixedUnconnectedPongDecoder.java b/src/main/java/de/florianmichael/viafabricplus/platform/raknet/library_fix/FixedUnconnectedPongDecoder.java new file mode 100644 index 00000000..d5df8d4e --- /dev/null +++ b/src/main/java/de/florianmichael/viafabricplus/platform/raknet/library_fix/FixedUnconnectedPongDecoder.java @@ -0,0 +1,74 @@ +/* + * This file is part of ViaBedrock - https://github.com/RaphiMC/ViaBedrock + * Copyright (C) 2023 RK_01/RaphiMC and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package de.florianmichael.viafabricplus.platform.raknet.library_fix; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufUtil; +import io.netty.buffer.Unpooled; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.socket.DatagramPacket; +import org.cloudburstmc.netty.channel.raknet.RakClientChannel; +import org.cloudburstmc.netty.channel.raknet.RakPong; +import org.cloudburstmc.netty.channel.raknet.config.RakChannelOption; +import org.cloudburstmc.netty.handler.codec.raknet.AdvancedChannelInboundHandler; + +import static org.cloudburstmc.netty.channel.raknet.RakConstants.ID_UNCONNECTED_PONG; + +// Temporary fix until the library fixes the issue +public class FixedUnconnectedPongDecoder extends AdvancedChannelInboundHandler { + + private final RakClientChannel rakClientChannel; + + public FixedUnconnectedPongDecoder(final RakClientChannel rakClientChannel) { + this.rakClientChannel = rakClientChannel; + } + + @Override + protected boolean acceptInboundMessage(ChannelHandlerContext ctx, Object msg) throws Exception { + if (!super.acceptInboundMessage(ctx, msg)) { + return false; + } + + DatagramPacket packet = (DatagramPacket) msg; + ByteBuf buf = packet.content(); + return buf.isReadable() && buf.getUnsignedByte(buf.readerIndex()) == ID_UNCONNECTED_PONG; + } + + @Override + protected void channelRead0(ChannelHandlerContext ctx, DatagramPacket packet) throws Exception { + ByteBuf buf = packet.content(); + buf.readUnsignedByte(); // Packet ID + + long pingTime = buf.readLong(); + long guid = buf.readLong(); + + ByteBuf magicBuf = this.rakClientChannel.config().getOption(RakChannelOption.RAK_UNCONNECTED_MAGIC); + if (!buf.isReadable(magicBuf.readableBytes()) || !ByteBufUtil.equals(buf.readSlice(magicBuf.readableBytes()), magicBuf)) { + // Magic does not match + return; + } + + ByteBuf pongData = Unpooled.EMPTY_BUFFER; + if (buf.isReadable(2)) { // Length + pongData = buf.readRetainedSlice(buf.readUnsignedShort()); + } + ctx.fireChannelRead(new RakPong(pingTime, guid, pongData, packet.sender())); + } + +} diff --git a/src/main/java/de/florianmichael/viafabricplus/provider/ViaFabricPlusNettyPipelineProvider.java b/src/main/java/de/florianmichael/viafabricplus/provider/ViaFabricPlusNettyPipelineProvider.java new file mode 100644 index 00000000..c3d35407 --- /dev/null +++ b/src/main/java/de/florianmichael/viafabricplus/provider/ViaFabricPlusNettyPipelineProvider.java @@ -0,0 +1,59 @@ +/* + * This file is part of ViaFabricPlus - https://github.com/FlorianMichael/ViaFabricPlus + * Copyright (C) 2021-2023 FlorianMichael/MrLookAtMe (EnZaXD) and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package de.florianmichael.viafabricplus.provider; + +import com.viaversion.viaversion.api.connection.UserConnection; +import de.florianmichael.viafabricplus.ViaFabricPlus; +import de.florianmichael.viafabricplus.injection.access.IClientConnection; +import net.raphimc.viabedrock.protocol.providers.NettyPipelineProvider; + +import javax.crypto.NoSuchPaddingException; +import javax.crypto.SecretKey; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; + +public class ViaFabricPlusNettyPipelineProvider extends NettyPipelineProvider { + + @Override + public void enableCompression(UserConnection user, int threshold, int algorithm) { + final IClientConnection currentConnection = (IClientConnection) user.getChannel().attr(ViaFabricPlus.LOCAL_MINECRAFT_CONNECTION).get(); + + try { + switch (algorithm) { + case 0 -> currentConnection.viafabricplus_enableZLibCompression(); + case 1 -> currentConnection.viafabricplus_enableSnappyCompression(); + + default -> throw new IllegalStateException("Invalid compression algorithm: " + algorithm); + } + } catch (Throwable e) { + throw new RuntimeException(e); + } + } + + @Override + public void enableEncryption(UserConnection user, SecretKey key) { + final IClientConnection currentConnection = (IClientConnection) user.getChannel().attr(ViaFabricPlus.LOCAL_MINECRAFT_CONNECTION).get(); + + try { + currentConnection.viafabricplus_enableAesGcmEncryption(key); + } catch (InvalidAlgorithmParameterException | NoSuchPaddingException | NoSuchAlgorithmException | InvalidKeyException e) { + throw new RuntimeException(e); + } + } +} diff --git a/src/main/resources/viafabricplus.mixins.json b/src/main/resources/viafabricplus.mixins.json index 7d8697aa..54360c26 100644 --- a/src/main/resources/viafabricplus.mixins.json +++ b/src/main/resources/viafabricplus.mixins.json @@ -5,11 +5,11 @@ "compatibilityLevel": "JAVA_17", "client": [ "base.MixinClientConnection", - "base.MixinClientConnection_1", "base.MixinClientLoginNetworkHandler", "base.MixinMain", "base.MixinMinecraftClient", "base.MixinMultiplayerScreen", + "base.MixinMultiplayerServerListPinger", "bridge.MixinConnectScreen", "bridge.MixinDebugHud", "bridge.MixinDownloadingTerrainScreen", @@ -145,6 +145,8 @@ "defaultRequire": 1 }, "mixins": [ - "fixes.block.MixinAbstractBlock" + "base.MixinClientConnection_1", + "fixes.block.MixinAbstractBlock", + "fixes.vialegacy.MixinBedrockProtocol" ] }