(WIP) ViaBedrock Implementation

This commit is contained in:
FlorianMichael 2023-03-14 21:07:55 +01:00
parent e5aa52f10f
commit 42eb7e46e4
24 changed files with 723 additions and 17 deletions

View File

@ -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 |

View File

@ -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 {

View File

@ -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

View File

@ -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));

View File

@ -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;
}

View File

@ -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<Packet<?>> 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<ClientConnection> 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));
}
}

View File

@ -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<Object> 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);
}
}
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
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<InetSocketAddress> instance) {
final InetSocketAddress address = instance.get();
if (ViaLoadingBase.getClassWrapper().getTargetVersion().isEqualTo(BedrockProtocolVersion.bedrockLatest)) {
RakNetPingSessions.storePingSession(address.getAddress());
}
return address;
}
}

View File

@ -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<ServerAddress> 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()));
}
}

View File

@ -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) {

View File

@ -31,5 +31,4 @@ public abstract class MixinWorldPackets1_13 {
private static void returnAirDefault(int oldId, CallbackInfoReturnable<Integer> cir) {
cir.setReturnValue(0);
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
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();
}
}

View File

@ -15,7 +15,7 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package de.florianmichael.viafabricplus.platform;
package de.florianmichael.viafabricplus.platform.pre_netty;
public class PreNettyConstants {

View File

@ -15,7 +15,7 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
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;

View File

@ -15,7 +15,7 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
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;

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
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";
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
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);
}
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
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<RakPong, ByteBuf> {
private final InetSocketAddress remoteAddress;
public PingEncapsulationCodec(final InetSocketAddress remoteAddress) {
this.remoteAddress = remoteAddress;
}
@Override
protected void encode(ChannelHandlerContext ctx, ByteBuf in, List<Object> 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<Object> 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);
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
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<RakMessage, ByteBuf> {
private static final int FRAME_ID = 0xFE;
@Override
protected void encode(ChannelHandlerContext ctx, ByteBuf msg, List<Object> 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<Object> 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()));
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
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<InetAddress> pingSessions = new ArrayList<>();
public static void storePingSession(final InetAddress address) {
pingSessions.add(address);
}
public static boolean isPingSession(final InetAddress address) {
return pingSessions.contains(address);
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
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()));
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
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<DatagramPacket> {
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()));
}
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
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);
}
}
}

View File

@ -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"
]
}