Skip to content

Commit c7dba8c

Browse files
committed
Added support for joining Bedrock NetherNet Realms and LAN worlds
1 parent 52aab5f commit c7dba8c

File tree

9 files changed

+198
-96
lines changed

9 files changed

+198
-96
lines changed

build.gradle

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ repositories {
4545

4646
content {
4747
includeGroupByRegex "com\\.github\\..+"
48+
includeGroupByRegex "dev\\.kastle\\..+"
4849
}
4950
}
5051
}
@@ -80,8 +81,9 @@ dependencies {
8081
includeInJar("net.lenni0451:MCPing:1.4.4") {
8182
exclude group: "com.google.code.gson", module: "gson"
8283
}
83-
includeInJar "net.lenni0451.commons:swing:1.9.0"
84-
includeInJar "net.lenni0451.commons:brigadier:1.9.0"
84+
includeInJar "net.lenni0451.commons:swing:1.9.1"
85+
includeInJar "net.lenni0451.commons:brigadier:1.9.1"
86+
includeInJar "net.lenni0451.commons:unchecked:1.9.1"
8587
includeInJar("net.raphimc.netminecraft:all:3.1.5-SNAPSHOT") {
8688
exclude group: "com.google.code.gson", module: "gson"
8789
}
@@ -100,6 +102,12 @@ dependencies {
100102
includeInJar("dev.kastle.netty:netty-transport-raknet:1.5.2") {
101103
exclude group: "io.netty"
102104
}
105+
includeInJar("dev.kastle.NetworkCompatible:netty-transport-nethernet:6a8915db93") {
106+
exclude group: "io.netty"
107+
}
108+
["windows-x86_64", "windows-aarch64", "linux-x86_64", "linux-aarch64", "macos-aarch64"].each {
109+
includeInJar "dev.kastle.webrtc:webrtc-java:1.0.3:$it"
110+
}
103111
includeInJar "gs.mclo:api:5.0.2"
104112
includeInJar "net.lenni0451:optconfig:1.1.1"
105113

src/main/java/net/raphimc/viaproxy/protocoltranslator/providers/ViaProxyNettyPipelineProvider.java

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import com.viaversion.vialoader.netty.VLPipeline;
2121
import com.viaversion.viaversion.api.connection.UserConnection;
2222
import io.netty.channel.Channel;
23+
import net.lenni0451.commons.unchecked.Sneaky;
2324
import net.raphimc.netminecraft.constants.MCPipeline;
2425
import net.raphimc.viabedrock.api.io.compression.ProtocolCompression;
2526
import net.raphimc.viabedrock.netty.CompressionCodec;
@@ -40,11 +41,7 @@ public void enableCompression(UserConnection user, ProtocolCompression protocolC
4041
throw new IllegalStateException("Compression already enabled");
4142
}
4243

43-
try {
44-
channel.pipeline().addBefore(MCPipeline.SIZER_HANDLER_NAME, MCPipeline.COMPRESSION_HANDLER_NAME, new CompressionCodec(protocolCompression));
45-
} catch (Throwable e) {
46-
throw new RuntimeException(e);
47-
}
44+
channel.pipeline().addBefore(MCPipeline.SIZER_HANDLER_NAME, MCPipeline.COMPRESSION_HANDLER_NAME, new CompressionCodec(protocolCompression));
4845
}
4946

5047
@Override
@@ -56,10 +53,12 @@ public void enableEncryption(UserConnection user, SecretKey key) {
5653
throw new IllegalStateException("Encryption already enabled");
5754
}
5855

59-
try {
60-
channel.pipeline().addAfter(VLPipeline.VIABEDROCK_RAKNET_MESSAGE_CODEC_NAME, MCPipeline.ENCRYPTION_HANDLER_NAME, new AesEncryptionCodec(key));
61-
} catch (Throwable e) {
62-
throw new RuntimeException(e);
56+
if (channel.pipeline().get(VLPipeline.VIABEDROCK_RAKNET_MESSAGE_CODEC_NAME) != null) { // Only enable encryption for RakNet connections
57+
try {
58+
channel.pipeline().addAfter(VLPipeline.VIABEDROCK_RAKNET_MESSAGE_CODEC_NAME, MCPipeline.ENCRYPTION_HANDLER_NAME, new AesEncryptionCodec(key));
59+
} catch (Throwable e) {
60+
Sneaky.sneakyThrow(e);
61+
}
6362
}
6463
}
6564

src/main/java/net/raphimc/viaproxy/proxy/client2proxy/Client2ProxyHandler.java

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import com.viaversion.viaversion.api.connection.UserConnection;
2424
import com.viaversion.viaversion.api.protocol.version.ProtocolVersion;
2525
import com.viaversion.viaversion.api.protocol.version.VersionType;
26+
import dev.kastle.netty.channel.nethernet.config.NetherNetAddress;
2627
import io.netty.channel.ChannelFutureListener;
2728
import io.netty.channel.ChannelHandler;
2829
import io.netty.channel.ChannelHandlerContext;
@@ -43,7 +44,10 @@
4344
import net.raphimc.viaproxy.proxy.packethandler.*;
4445
import net.raphimc.viaproxy.proxy.proxy2server.Proxy2ServerChannelInitializer;
4546
import net.raphimc.viaproxy.proxy.proxy2server.Proxy2ServerHandler;
46-
import net.raphimc.viaproxy.proxy.session.*;
47+
import net.raphimc.viaproxy.proxy.session.BedrockProxyConnection;
48+
import net.raphimc.viaproxy.proxy.session.DummyProxyConnection;
49+
import net.raphimc.viaproxy.proxy.session.ProxyConnection;
50+
import net.raphimc.viaproxy.proxy.session.UserOptions;
4751
import net.raphimc.viaproxy.proxy.util.*;
4852
import net.raphimc.viaproxy.saves.impl.accounts.ClassicAccount;
4953
import net.raphimc.viaproxy.util.*;
@@ -166,13 +170,6 @@ private void handleHandshake(final C2SHandshakingClientIntentionPacket packet) {
166170
}
167171
}
168172

169-
if (packet.intendedState.getConnectionState() == ConnectionState.STATUS && !ViaProxy.getConfig().shouldAllowBetaPinging() && serverVersion.olderThanOrEqualTo(LegacyProtocolVersion.b1_7tob1_7_3)) {
170-
if (!ViaProxy.getConfig().getCustomMotd().isBlank()) {
171-
this.proxyConnection.kickClient(ViaProxy.getConfig().getCustomMotd());
172-
}
173-
this.proxyConnection.kickClient("§7ViaProxy is working!\n§7Connect to join the configured server");
174-
}
175-
176173
if (packet.intendedState.getConnectionState() == ConnectionState.LOGIN && TransferDataHolder.hasTempRedirect(this.proxyConnection.getC2P())) {
177174
serverAddress = TransferDataHolder.removeTempRedirect(this.proxyConnection.getC2P());
178175
if (clientVersion.olderThan(ProtocolVersion.v1_20_5)) {
@@ -194,6 +191,15 @@ private void handleHandshake(final C2SHandshakingClientIntentionPacket packet) {
194191
serverAddress = preConnectEvent.getServerAddress();
195192
serverVersion = preConnectEvent.getServerVersion();
196193

194+
final boolean isJavaBetaPing = packet.intendedState.getConnectionState() == ConnectionState.STATUS && serverVersion.olderThanOrEqualTo(LegacyProtocolVersion.b1_7tob1_7_3) && !ViaProxy.getConfig().shouldAllowBetaPinging();
195+
final boolean isBedrockNetherNetPing = packet.intendedState.getConnectionState() == ConnectionState.STATUS && serverVersion.equals(BedrockProtocolVersion.bedrockLatest) && serverAddress instanceof NetherNetAddress;
196+
if (isJavaBetaPing || isBedrockNetherNetPing) {
197+
if (!ViaProxy.getConfig().getCustomMotd().isBlank()) {
198+
this.proxyConnection.kickClient(ViaProxy.getConfig().getCustomMotd());
199+
}
200+
this.proxyConnection.kickClient("§7ViaProxy is working!\n§7Connect to join the configured server");
201+
}
202+
197203
final UserOptions userOptions = new UserOptions(classicMpPass, ViaProxy.getConfig().getAccount());
198204
ChannelUtil.disableAutoRead(this.proxyConnection.getC2P());
199205

@@ -220,11 +226,7 @@ private void connect(final SocketAddress serverAddress, final ProtocolVersion se
220226
final Supplier<ChannelHandler> handlerSupplier = () -> ViaProxy.EVENT_MANAGER.call(new Proxy2ServerHandlerCreationEvent(new Proxy2ServerHandler(), false)).getHandler();
221227
final ProxyConnection proxyConnection;
222228
if (serverVersion.equals(BedrockProtocolVersion.bedrockLatest)) {
223-
if (intendedState != IntendedState.STATUS) {
224-
proxyConnection = new BedrockProxyConnection(new Proxy2ServerChannelInitializer(handlerSupplier), this.proxyConnection.getC2P());
225-
} else {
226-
proxyConnection = new BedrockStatusProxyConnection(new Proxy2ServerChannelInitializer(handlerSupplier), this.proxyConnection.getC2P());
227-
}
229+
proxyConnection = new BedrockProxyConnection(new Proxy2ServerChannelInitializer(handlerSupplier), this.proxyConnection.getC2P());
228230
} else {
229231
proxyConnection = new ProxyConnection(new Proxy2ServerChannelInitializer(handlerSupplier), this.proxyConnection.getC2P());
230232
}

src/main/java/net/raphimc/viaproxy/proxy/proxy2server/Proxy2ServerChannelInitializer.java

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,16 @@
3131
import net.raphimc.netminecraft.packet.registry.DefaultPacketRegistry;
3232
import net.raphimc.viabedrock.api.BedrockProtocolVersion;
3333
import net.raphimc.viabedrock.netty.util.DatagramCodec;
34+
import net.raphimc.viabedrock.protocol.NetherNetStatusProtocol;
3435
import net.raphimc.viabedrock.protocol.RakNetStatusProtocol;
3536
import net.raphimc.viaproxy.ViaProxy;
3637
import net.raphimc.viaproxy.plugins.events.Proxy2ServerChannelInitializeEvent;
3738
import net.raphimc.viaproxy.plugins.events.types.ITyped;
3839
import net.raphimc.viaproxy.protocoltranslator.impl.ViaProxyVLPipeline;
3940
import net.raphimc.viaproxy.proxy.session.ProxyConnection;
41+
import net.raphimc.viaproxy.util.NetherNetInetSocketAddress;
4042

43+
import java.net.InetSocketAddress;
4144
import java.util.function.Supplier;
4245

4346
public class Proxy2ServerChannelInitializer extends MinecraftChannelInitializer {
@@ -81,7 +84,13 @@ protected void initChannel(Channel channel) {
8184
channel.pipeline().remove(MCPipeline.SIZER_HANDLER_NAME);
8285
channel.pipeline().remove(VLPipeline.VIABEDROCK_PACKET_CODEC_NAME);
8386
channel.pipeline().replace(VLPipeline.VIABEDROCK_RAKNET_MESSAGE_CODEC_NAME, "viabedrock-datagram-codec", new DatagramCodec());
84-
user.getProtocolInfo().getPipeline().add(RakNetStatusProtocol.INSTANCE);
87+
if (proxyConnection.getServerAddress() instanceof NetherNetInetSocketAddress) {
88+
user.getProtocolInfo().getPipeline().add(NetherNetStatusProtocol.INSTANCE);
89+
} else if (proxyConnection.getServerAddress() instanceof InetSocketAddress) {
90+
user.getProtocolInfo().getPipeline().add(RakNetStatusProtocol.INSTANCE);
91+
} else {
92+
throw new UnsupportedOperationException("Unsupported address type for Bedrock status: " + proxyConnection.getServerAddress().getClass().getName());
93+
}
8594
}
8695
}
8796

src/main/java/net/raphimc/viaproxy/proxy/session/BedrockProxyConnection.java

Lines changed: 86 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -17,54 +17,127 @@
1717
*/
1818
package net.raphimc.viaproxy.proxy.session;
1919

20+
import com.viaversion.vialoader.netty.VLPipeline;
21+
import com.viaversion.viaversion.api.protocol.version.ProtocolVersion;
22+
import dev.kastle.netty.channel.nethernet.NetherNetChannelFactory;
23+
import dev.kastle.netty.channel.nethernet.config.NetherChannelOption;
24+
import dev.kastle.netty.channel.nethernet.config.NetherNetAddress;
25+
import dev.kastle.netty.channel.nethernet.signaling.NetherNetClientSignaling;
26+
import dev.kastle.netty.channel.nethernet.signaling.NetherNetDiscoverySignaling;
27+
import dev.kastle.netty.channel.nethernet.signaling.NetherNetXboxSignaling;
28+
import dev.kastle.webrtc.PeerConnectionFactory;
2029
import io.netty.bootstrap.Bootstrap;
21-
import io.netty.channel.Channel;
22-
import io.netty.channel.ChannelInitializer;
23-
import io.netty.channel.ChannelOption;
30+
import io.netty.channel.*;
2431
import io.netty.channel.socket.DatagramChannel;
32+
import net.raphimc.netminecraft.constants.ConnectionState;
2533
import net.raphimc.netminecraft.util.EventLoops;
2634
import net.raphimc.netminecraft.util.TransportType;
2735
import net.raphimc.viabedrock.protocol.data.ProtocolConstants;
2836
import net.raphimc.viaproxy.ViaProxy;
37+
import net.raphimc.viaproxy.saves.impl.accounts.BedrockAccount;
38+
import net.raphimc.viaproxy.util.NetherNetInetSocketAddress;
2939
import org.cloudburstmc.netty.channel.raknet.RakChannelFactory;
40+
import org.cloudburstmc.netty.channel.raknet.RakClientChannel;
3041
import org.cloudburstmc.netty.channel.raknet.config.RakChannelOption;
3142

43+
import java.net.SocketAddress;
3244
import java.util.concurrent.ThreadLocalRandom;
3345

3446
public class BedrockProxyConnection extends ProxyConnection {
3547

48+
private boolean useNetherNetDiscovery;
49+
private boolean useNetherNetXbox;
50+
3651
public BedrockProxyConnection(final ChannelInitializer<Channel> channelInitializer, final Channel c2p) {
3752
super(channelInitializer, c2p);
3853
}
3954

4055
@Override
41-
public void initialize(TransportType transportType, final Bootstrap bootstrap) {
56+
public void initialize(final TransportType transportType, final Bootstrap bootstrap) {
57+
bootstrap
58+
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, ViaProxy.getConfig().getConnectTimeout())
59+
.attr(ProxyConnection.PROXY_CONNECTION_ATTRIBUTE_KEY, this)
60+
.handler(this.channelInitializer);
61+
62+
if (this.getC2pConnectionState() == ConnectionState.STATUS) {
63+
this.initializeRaw(transportType, bootstrap);
64+
} else if (this.useNetherNetDiscovery || this.useNetherNetXbox) {
65+
this.initializeNetherNet(transportType, bootstrap);
66+
} else {
67+
this.initializeRakNet(transportType, bootstrap);
68+
}
69+
70+
this.channelFuture = bootstrap.register().syncUninterruptibly();
71+
}
72+
73+
@Override
74+
public ChannelFuture connectToServer(final SocketAddress serverAddress, final ProtocolVersion targetVersion) {
75+
this.useNetherNetDiscovery = serverAddress instanceof NetherNetInetSocketAddress;
76+
this.useNetherNetXbox = serverAddress instanceof NetherNetAddress;
77+
return super.connectToServer(serverAddress, targetVersion);
78+
}
79+
80+
protected void initializeRakNet(TransportType transportType, final Bootstrap bootstrap) {
4281
if (!DatagramChannel.class.isAssignableFrom(transportType.udpClientChannelClass())) {
4382
throw new IllegalArgumentException("Channel type must be a DatagramChannel");
4483
}
4584
if (transportType == TransportType.KQUEUE) {
4685
transportType = TransportType.NIO; // KQueue doesn't work for some reason
4786
}
4887

88+
final RakChannelFactory<RakClientChannel> channelFactory = RakChannelFactory.client((Class<? extends DatagramChannel>) transportType.udpClientChannelClass());
4989
bootstrap
5090
.group(EventLoops.getClientEventLoop(transportType))
51-
.channelFactory(RakChannelFactory.client((Class<? extends DatagramChannel>) transportType.udpClientChannelClass()))
52-
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, ViaProxy.getConfig().getConnectTimeout())
91+
.channelFactory(() -> {
92+
final Channel channel = channelFactory.newChannel();
93+
if (channel.config().setOption(RakChannelOption.RAK_IP_DONT_FRAGMENT, true)) {
94+
channel.config().setOption(RakChannelOption.RAK_MTU_SIZES, new Integer[]{1492, 1200, 576});
95+
}
96+
return channel;
97+
})
5398
.option(RakChannelOption.RAK_PROTOCOL_VERSION, ProtocolConstants.BEDROCK_RAKNET_PROTOCOL_VERSION)
5499
.option(RakChannelOption.RAK_COMPATIBILITY_MODE, true)
55100
.option(RakChannelOption.RAK_CLIENT_INTERNAL_ADDRESSES, 20)
56101
.option(RakChannelOption.RAK_TIME_BETWEEN_SEND_CONNECTION_ATTEMPTS_MS, 500)
57102
.option(RakChannelOption.RAK_CONNECT_TIMEOUT, (long) ViaProxy.getConfig().getConnectTimeout())
58103
.option(RakChannelOption.RAK_SESSION_TIMEOUT, 30_000L)
59-
.option(RakChannelOption.RAK_GUID, ThreadLocalRandom.current().nextLong())
60-
.attr(ProxyConnection.PROXY_CONNECTION_ATTRIBUTE_KEY, this)
61-
.handler(this.channelInitializer);
104+
.option(RakChannelOption.RAK_GUID, ThreadLocalRandom.current().nextLong());
105+
}
62106

63-
this.channelFuture = bootstrap.register().syncUninterruptibly();
107+
protected void initializeNetherNet(final TransportType transportType, final Bootstrap bootstrap) {
108+
final NetherNetClientSignaling netherNetSignaling;
109+
if (this.useNetherNetDiscovery) {
110+
netherNetSignaling = new NetherNetDiscoverySignaling();
111+
} else {
112+
if (this.getUserOptions().account() instanceof BedrockAccount bedrockAccount) {
113+
netherNetSignaling = new NetherNetXboxSignaling(bedrockAccount.getAuthManager().getMinecraftSession().getUpToDateUnchecked().getAuthorizationHeader());
114+
} else {
115+
this.kickClient("§cThe configured target server requires Xbox signaling, but no Minecraft: Bedrock Edition account is selected.");
116+
return;
117+
}
118+
}
119+
final ChannelHandler channelHandler = bootstrap.config().handler();
120+
bootstrap
121+
.group(EventLoops.getClientEventLoop(TransportType.NIO))
122+
.channelFactory(NetherNetChannelFactory.client(new PeerConnectionFactory(), netherNetSignaling))
123+
.option(NetherChannelOption.NETHER_CLIENT_HANDSHAKE_TIMEOUT_MS, ViaProxy.getConfig().getConnectTimeout())
124+
.handler(new ChannelInitializer<>() {
125+
@Override
126+
protected void initChannel(final Channel channel) {
127+
channel.pipeline().addLast(channelHandler);
128+
channel.pipeline().remove(VLPipeline.VIABEDROCK_RAKNET_MESSAGE_CODEC_NAME);
129+
}
130+
});
131+
}
64132

65-
/*if (this.getChannel().config().setOption(RakChannelOption.RAK_IP_DONT_FRAGMENT, true)) {
66-
this.getChannel().config().setOption(RakChannelOption.RAK_MTU_SIZES, new Integer[]{1492, 1200, 576});
67-
}*/
133+
protected void initializeRaw(TransportType transportType, final Bootstrap bootstrap) {
134+
if (transportType == TransportType.KQUEUE) {
135+
transportType = TransportType.NIO; // KQueue doesn't work for some reason
136+
}
137+
138+
bootstrap
139+
.group(EventLoops.getClientEventLoop(transportType))
140+
.channel(transportType.udpClientChannelClass());
68141
}
69142

70143
}

src/main/java/net/raphimc/viaproxy/proxy/session/BedrockStatusProxyConnection.java

Lines changed: 0 additions & 47 deletions
This file was deleted.

0 commit comments

Comments
 (0)