Skip to content

Commit

Permalink
Fixed many issues and added a new RakNet backend
Browse files Browse the repository at this point in the history
  • Loading branch information
RaphiMC committed Dec 11, 2023
1 parent 02de496 commit e0cdcb2
Show file tree
Hide file tree
Showing 9 changed files with 173 additions and 53 deletions.
5 changes: 4 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,12 @@ repositories {
}

dependencies {
implementation "net.raphimc:ViaProxy:3.0.23-SNAPSHOT"
implementation "net.raphimc:ViaProxy:3.0.23"

include "net.java.dev.jna:jna:5.13.0"

// For dependency sources, see RakNetBackend class
include "com.github.RelativityMC.netty-raknet:netty-raknet-client:ae1f415e05"
}

processResources {
Expand Down
6 changes: 4 additions & 2 deletions src/main/java/net/raphimc/raknetproviders/RakNetBackend.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,10 @@

public enum RakNetBackend {

CLOUDBURST("Cloudburst Network (default)"),
NATIVE("Native RakNet (best)");
NATIVE_RAKNET("Native RakNet (best)"), // https://github.com/extremeheat/fb-raknet
CLOUDBURSTMC_NETWORK("CloudburstMC/Network (default)"), // https://github.com/CloudburstMC/Network
RELATIVITYMC_NETTY_RAKNET("RelativityMC/netty-raknet"), // https://github.com/RelativityMC/netty-raknet
;

private final String displayName;

Expand Down
31 changes: 17 additions & 14 deletions src/main/java/net/raphimc/raknetproviders/RakNetProviders.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,9 @@
import net.lenni0451.lambdaevents.EventHandler;
import net.lenni0451.reflect.Objects;
import net.lenni0451.reflect.stream.RStream;
import net.raphimc.raknetproviders.raknetnative.RakNetNative;
import net.raphimc.raknetproviders.raknetnative.RakNetNativeBedrockProxyConnection;
import net.raphimc.raknetproviders.native_raknet.NativeRakNet;
import net.raphimc.raknetproviders.native_raknet.NativeRakNetBedrockProxyConnection;
import net.raphimc.raknetproviders.relativitymc_netty_raknet.RelativityMcNettyRakNetBedrockProxyConnection;
import net.raphimc.viabedrock.protocol.data.ProtocolConstants;
import net.raphimc.viaproxy.ViaProxy;
import net.raphimc.viaproxy.plugins.ViaProxyPlugin;
Expand All @@ -47,8 +48,8 @@ public class RakNetProviders extends ViaProxyPlugin {
public void onEnable() {
ViaProxy.EVENT_MANAGER.register(this);

if (RakNetNative.isLoaded()) {
RakNetNative.INSTANCE.RN_SetRakNetProtocolVersion(ProtocolConstants.BEDROCK_RAKNET_PROTOCOL_VERSION);
if (NativeRakNet.isLoaded()) {
NativeRakNet.INSTANCE.RN_SetRakNetProtocolVersion(ProtocolConstants.BEDROCK_RAKNET_PROTOCOL_VERSION);
}
}

Expand All @@ -61,9 +62,8 @@ public void onViaProxyLoaded(final ViaProxyLoadedEvent event) {
final ViaProxyUI ui = ViaProxy.getUI();

ui.eventManager.register(() -> {
if (this.rakNetBackend.getSelectedItem() instanceof RakNetBackend backend) {
ViaProxy.getSaveManager().uiSave.put("rakNetBackend", backend.name());
}
ViaProxy.getSaveManager().uiSave.put("rakNetBackend", String.valueOf(this.rakNetBackend.getSelectedIndex()));
ViaProxy.getSaveManager().save();
}, UICloseEvent.class);

final JPanel advancedTabPanel = RStream.of(ui.advancedTab).withSuper().fields().by("contentPane").get();
Expand Down Expand Up @@ -99,16 +99,19 @@ public Component getListCellRendererComponent(JList<?> list, Object value, int i
public void onProxySessionCreation(final ProxySessionCreationEvent event) {
if (event.getProxySession() instanceof BedrockProxyConnection bedrockProxyConnection && this.rakNetBackend.getSelectedItem() instanceof RakNetBackend backend) {
switch (backend) {
case CLOUDBURST -> {
// it's already set to CloudNet by default
}
case NATIVE -> {
if (RakNetNative.isLoaded()) {
event.setProxySession(new RakNetNativeBedrockProxyConnection(Objects.cast(bedrockProxyConnection, RakNetNativeBedrockProxyConnection.class)));
case NATIVE_RAKNET -> {
if (NativeRakNet.isLoaded()) {
event.setProxySession(new NativeRakNetBedrockProxyConnection(Objects.cast(bedrockProxyConnection, NativeRakNetBedrockProxyConnection.class)));
} else {
Logger.LOGGER.warn("RakNetNative is not supported on this system, falling back to " + RakNetBackend.CLOUDBURST.getDisplayName());
Logger.LOGGER.warn("NATIVE_RAKNET is not supported on this system, falling back to CLOUDBURST_NETWORK");
}
}
case CLOUDBURSTMC_NETWORK -> {
// default implementation
}
case RELATIVITYMC_NETTY_RAKNET -> {
event.setProxySession(new RelativityMcNettyRakNetBedrockProxyConnection(Objects.cast(bedrockProxyConnection, RelativityMcNettyRakNetBedrockProxyConnection.class)));
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 net.raphimc.raknetproviders.raknetnative;
package net.raphimc.raknetproviders.native_raknet;

import com.sun.jna.Library;
import com.sun.jna.Native;
Expand All @@ -25,13 +25,13 @@
import java.nio.charset.StandardCharsets;
import java.util.Arrays;

public interface RakNetNative extends Library {
public interface NativeRakNet extends Library {

RakNetNative INSTANCE = loadNative();
NativeRakNet INSTANCE = loadNative();

private static RakNetNative loadNative() {
private static NativeRakNet loadNative() {
try {
return Native.load("raknet", RakNetNative.class);
return Native.load("raknet", NativeRakNet.class);
} catch (Throwable ignored) {
}
return null;
Expand Down Expand Up @@ -71,11 +71,9 @@ class RN_SocketDescriptor extends Structure {
public int extraSocketOptions;

public RN_SocketDescriptor() {
super();
}

public RN_SocketDescriptor(final short port, final String hostAddress, final short socketFamily, final short remotePortRakNetWasStartedOn_PS3, final int chromeInstance, final boolean blockingSocket, final int extraSocketOptions) {
super();
this.port = port;
this.hostAddress = Arrays.copyOf(hostAddress.getBytes(StandardCharsets.US_ASCII), 32);
this.socketFamily = socketFamily;
Expand Down Expand Up @@ -107,11 +105,9 @@ class RN_AddressOrGUID extends Structure {
public byte[] padding = new byte[20];

public RN_AddressOrGUID() {
super();
}

public RN_AddressOrGUID(final long guid, final short systemIndex) {
super();
this.guid = guid;
this.systemIndex = systemIndex;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,18 @@
* 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 net.raphimc.raknetproviders.raknetnative;
package net.raphimc.raknetproviders.native_raknet;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelOption;
import io.netty.channel.oio.OioEventLoopGroup;
import net.raphimc.netminecraft.constants.ConnectionState;
import net.raphimc.viaproxy.proxy.session.BedrockProxyConnection;
import net.raphimc.viaproxy.proxy.session.ProxyConnection;

public class RakNetNativeBedrockProxyConnection extends BedrockProxyConnection {
public class NativeRakNetBedrockProxyConnection extends BedrockProxyConnection {

public RakNetNativeBedrockProxyConnection(final RakNetNativeBedrockProxyConnection bedrockProxyConnection) {
public NativeRakNetBedrockProxyConnection(final NativeRakNetBedrockProxyConnection bedrockProxyConnection) {
super(bedrockProxyConnection.handlerSupplier, bedrockProxyConnection.channelInitializerSupplier, bedrockProxyConnection.getC2P());
}

Expand All @@ -34,7 +35,8 @@ public void initialize(Bootstrap bootstrap) {
if (this.getC2pConnectionState() == ConnectionState.LOGIN) {
bootstrap
.group(new OioEventLoopGroup())
.channel(RakNetNativeChannel.class)
.channel(NativeRakNetChannel.class)
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 4_000)
.attr(ProxyConnection.PROXY_CONNECTION_ATTRIBUTE_KEY, this)
.handler(this.channelInitializerSupplier.apply(this.handlerSupplier));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,23 +15,22 @@
* 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 net.raphimc.raknetproviders.raknetnative;
package net.raphimc.raknetproviders.native_raknet;

import com.sun.jna.Pointer;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelConfig;
import io.netty.channel.ChannelMetadata;
import io.netty.channel.ChannelOutboundBuffer;
import io.netty.channel.DefaultChannelConfig;
import io.netty.channel.*;
import io.netty.channel.oio.AbstractOioMessageChannel;
import io.netty.util.internal.StringUtil;
import org.cloudburstmc.netty.channel.raknet.RakConstants;
import org.cloudburstmc.netty.channel.raknet.packet.RakMessage;

import java.net.ConnectException;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.util.List;

public class RakNetNativeChannel extends AbstractOioMessageChannel {
public class NativeRakNetChannel extends AbstractOioMessageChannel {

private static final ChannelMetadata METADATA = new ChannelMetadata(false);
private final ChannelConfig config = new DefaultChannelConfig(this);
Expand All @@ -44,37 +43,60 @@ public class RakNetNativeChannel extends AbstractOioMessageChannel {
private boolean open = true;
private boolean active = false;

public RakNetNativeChannel() {
public NativeRakNetChannel() {
super(null);
}

@Override
protected void doConnect(SocketAddress remoteAddress, SocketAddress localAddress) {
protected void doConnect(SocketAddress remoteAddress, SocketAddress localAddress) throws InterruptedException, ConnectException {
this.remoteAddress = remoteAddress;
this.localAddress = localAddress;

this.rakPeer = RakNetNative.INSTANCE.RN_RakPeerGetInstance();
if (RakNetNative.INSTANCE.RN_RakPeerStartup(this.rakPeer, 1, new RakNetNative.RN_SocketDescriptor[]{new RakNetNative.RN_SocketDescriptor((short) 0, "0.0.0.0", (short) 2, (short) 0, 0, false, 0)}, 1, -99999) != 0) {
throw new RuntimeException("Failed to start RakPeer");
this.rakPeer = NativeRakNet.INSTANCE.RN_RakPeerGetInstance();
if (NativeRakNet.INSTANCE.RN_RakPeerStartup(this.rakPeer, 1, new NativeRakNet.RN_SocketDescriptor[]{new NativeRakNet.RN_SocketDescriptor((short) 0, "0.0.0.0", (short) 2, (short) 0, 0, false, 0)}, 1, -99999) != 0) {
throw new ConnectException("Failed to start RakPeer");
}
if (RakNetNative.INSTANCE.RN_RakPeerConnect(this.rakPeer, ((InetSocketAddress) remoteAddress).getHostString(), (short) ((InetSocketAddress) remoteAddress).getPort(), null, 0, null, 0, 12, 500, 0) != 0) {
throw new RuntimeException("Failed to connect to server");
if (NativeRakNet.INSTANCE.RN_RakPeerConnect(this.rakPeer, ((InetSocketAddress) remoteAddress).getHostString(), (short) ((InetSocketAddress) remoteAddress).getPort(), null, 0, null, 0, 12, 500, 0) != 0) {
throw new ConnectException("Failed to connect to server");
}

this.active = true;
final long start = System.currentTimeMillis();
final long timeout = this.config().getConnectTimeoutMillis();
while (System.currentTimeMillis() - start < timeout) {
final NativeRakNet.RN_Packet packet = NativeRakNet.INSTANCE.RN_RakPeerReceive(this.rakPeer);
if (packet == null) {
Thread.sleep(50);
continue;
}
try {
if (packet.length <= 0) throw new ConnectException("Received empty packet");
final int packetId = packet.data.getByte(0) & 0xFF;

if (packetId == RakConstants.ID_CONNECTION_REQUEST_ACCEPTED) {
this.active = true;
break;
} else {
throw new ConnectException("Received unexpected packet while connecting: " + packetId);
}
} finally {
NativeRakNet.INSTANCE.RN_RakPeerDeallocatePacket(this.rakPeer, packet);
}
}

if (!this.active) {
throw new ConnectTimeoutException("Connect timed out");
}
}

@Override
protected int doReadMessages(List<Object> list) {
final RakNetNative.RN_Packet packet = RakNetNative.INSTANCE.RN_RakPeerReceive(this.rakPeer);
final NativeRakNet.RN_Packet packet = NativeRakNet.INSTANCE.RN_RakPeerReceive(this.rakPeer);
if (packet == null) {
return 0;
}
final byte[] bytes = packet.data.getByteArray(0, packet.length);
RakNetNative.INSTANCE.RN_RakPeerDeallocatePacket(this.rakPeer, packet);

NativeRakNet.INSTANCE.RN_RakPeerDeallocatePacket(this.rakPeer, packet);
list.add(new RakMessage(this.alloc().buffer(bytes.length).writeBytes(bytes)));

return bytes.length;
}

Expand All @@ -84,13 +106,14 @@ protected void doWrite(ChannelOutboundBuffer channelOutboundBuffer) {
final Object msg = channelOutboundBuffer.current();
if (msg == null) {
return;
} else if (msg instanceof ByteBuf buf) {
} else if (msg instanceof RakMessage rakMessage) {
final ByteBuf buf = rakMessage.content();
final byte[] bytes = new byte[buf.readableBytes()];
buf.readBytes(bytes);

final RakNetNative.RN_AddressOrGUID.ByValue address = new RakNetNative.RN_AddressOrGUID.ByValue();
final NativeRakNet.RN_AddressOrGUID.ByValue address = new NativeRakNet.RN_AddressOrGUID.ByValue();
address.guid = -1;
if (RakNetNative.INSTANCE.RN_RakPeerSend(this.rakPeer, bytes, bytes.length, 2, 3, (byte) 0, address, true, 0) == 0) {
if (NativeRakNet.INSTANCE.RN_RakPeerSend(this.rakPeer, bytes, bytes.length, rakMessage.priority().ordinal(), rakMessage.reliability().ordinal(), (byte) rakMessage.channel(), address, true, 0) == 0) {
throw new RuntimeException("Failed to send packet");
}

Expand Down Expand Up @@ -125,8 +148,8 @@ protected void doDisconnect() {
protected void doClose() {
this.open = false;

RakNetNative.INSTANCE.RN_RakPeerShutdown(this.rakPeer, 500, 0, 2);
RakNetNative.INSTANCE.RN_RakPeerDestroyInstance(this.rakPeer);
NativeRakNet.INSTANCE.RN_RakPeerShutdown(this.rakPeer, 500, 0, 2);
NativeRakNet.INSTANCE.RN_RakPeerDestroyInstance(this.rakPeer);
this.rakPeer = null;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
/*
* This file is part of ViaProxyRakNetProviders - https://github.com/ViaVersionAddons/ViaProxyRakNetProviders
* 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 net.raphimc.raknetproviders.relativitymc_netty_raknet;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.handler.codec.MessageToMessageCodec;
import net.raphimc.netminecraft.constants.ConnectionState;
import net.raphimc.netminecraft.util.LazyLoadBase;
import net.raphimc.viabedrock.protocol.data.ProtocolConstants;
import net.raphimc.vialoader.netty.VLPipeline;
import net.raphimc.viaproxy.proxy.session.BedrockProxyConnection;
import net.raphimc.viaproxy.proxy.session.ProxyConnection;
import network.ycc.raknet.RakNet;
import network.ycc.raknet.client.RakNetClient;
import network.ycc.raknet.client.channel.RakNetClientThreadedChannel;
import network.ycc.raknet.frame.FrameData;
import network.ycc.raknet.packet.FramedPacket;
import org.cloudburstmc.netty.channel.raknet.RakPriority;
import org.cloudburstmc.netty.channel.raknet.RakReliability;
import org.cloudburstmc.netty.channel.raknet.packet.RakMessage;

import java.util.List;

public class RelativityMcNettyRakNetBedrockProxyConnection extends BedrockProxyConnection {

public RelativityMcNettyRakNetBedrockProxyConnection(final RelativityMcNettyRakNetBedrockProxyConnection bedrockProxyConnection) {
super(bedrockProxyConnection.handlerSupplier, bedrockProxyConnection.channelInitializerSupplier, bedrockProxyConnection.getC2P());
}

@Override
public void initialize(Bootstrap bootstrap) {
if (this.getC2pConnectionState() == ConnectionState.LOGIN) {
bootstrap
.group(LazyLoadBase.CLIENT_NIO_EVENTLOOP.getValue())
.channel(RakNetClient.THREADED_CHANNEL)
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 4_000)
.option(ChannelOption.IP_TOS, 0x18)
.option(RakNet.PROTOCOL_VERSION, ProtocolConstants.BEDROCK_RAKNET_PROTOCOL_VERSION)
.attr(ProxyConnection.PROXY_CONNECTION_ATTRIBUTE_KEY, this)
.handler(new ChannelInitializer<>() {

@Override
protected void initChannel(Channel channel) {
final RakNetClientThreadedChannel rakChannel = (RakNetClientThreadedChannel) channel;
rakChannel.config().setprotocolVersions(new int[]{11});
channel.pipeline().addLast(channelInitializerSupplier.apply(handlerSupplier));

channel.pipeline().addBefore(VLPipeline.VIABEDROCK_FRAME_ENCAPSULATION_HANDLER_NAME, "viabedrock-frame-converter", new MessageToMessageCodec<FrameData, RakMessage>() {
@Override
protected void encode(ChannelHandlerContext channelHandlerContext, RakMessage rakMessage, List<Object> list) {
final FrameData frameData = FrameData.read(rakMessage.content(), rakMessage.content().readableBytes(), false);
frameData.setReliability(FramedPacket.Reliability.get(rakMessage.reliability().ordinal()));
frameData.setOrderChannel(rakMessage.channel());
list.add(frameData);
}

@Override
protected void decode(ChannelHandlerContext channelHandlerContext, FrameData frameData, List<Object> list) {
final RakReliability reliability = RakReliability.fromId(frameData.getReliability().ordinal());
list.add(new RakMessage(frameData.createData(), reliability, RakPriority.NORMAL, frameData.getOrderChannel()));
}
});
}
});

this.channelFuture = bootstrap.register().syncUninterruptibly();
} else {
super.initialize(bootstrap);
}
}

}
Binary file modified src/main/resources/win32-x86-64/raknet.dll
Binary file not shown.
Binary file modified src/main/resources/win32-x86/raknet.dll
Binary file not shown.

0 comments on commit e0cdcb2

Please sign in to comment.