diff --git a/README.md b/README.md index ff28bd3..0eade98 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,6 @@ You can join the [Discord](https://discord.gg/5z4GuSnqmQ) for help with this for - New incoming connection batches additional packets to more closely imitate the vanilla client: - A `Connected Ping` - The first game packet, `Request Network Settings Packet` - - Attempts to detect it from the connection, but uses `RakChannelOption.RAK_CLIENT_BEDROCK_PROTOCOL_VERSION` if it is not detected in the pipeline - Allows for resetting security state if `Open Connection Reply 1` is resent by the server - Only do retries with `Open Connection Request 1`, and reserve `Open Connection Request 2` only as a direct response to `Open Connection Reply 1` - Allows using datagram channel factories for raknet (from [@AlexProgrammerDE](https://github.com/AlexProgrammerDE)) @@ -23,4 +22,4 @@ The library is published to Maven Central. See the [latest release](https://gith ### Snapshots [![](https://jitpack.io/v/dev.kastle/NetworkCompatible.svg)](https://jitpack.io/#dev.kastle/NetworkCompatible) -Snapshots are avaible from [jitpack](https://jitpack.io/#dev.kastle/NetworkCompatible). \ No newline at end of file +Snapshots are avaible from [jitpack](https://jitpack.io/#dev.kastle/NetworkCompatible). diff --git a/transport-raknet/src/main/java/org/cloudburstmc/netty/channel/raknet/config/DefaultRakClientConfig.java b/transport-raknet/src/main/java/org/cloudburstmc/netty/channel/raknet/config/DefaultRakClientConfig.java index 929fe1a..7418e6c 100644 --- a/transport-raknet/src/main/java/org/cloudburstmc/netty/channel/raknet/config/DefaultRakClientConfig.java +++ b/transport-raknet/src/main/java/org/cloudburstmc/netty/channel/raknet/config/DefaultRakClientConfig.java @@ -24,10 +24,7 @@ import java.util.Map; -import static org.cloudburstmc.netty.channel.raknet.RakConstants.DEFAULT_UNCONNECTED_MAGIC; -import static org.cloudburstmc.netty.channel.raknet.RakConstants.MTU_SIZES; -import static org.cloudburstmc.netty.channel.raknet.RakConstants.SESSION_TIMEOUT_MS; -import static org.cloudburstmc.netty.channel.raknet.RakConstants.TIME_BETWEEN_SEND_CONNECTION_ATTEMPTS_MS; +import static org.cloudburstmc.netty.channel.raknet.RakConstants.*; /** * The extended implementation of {@link RakChannelConfig} based on {@link DefaultRakSessionConfig} used by client. @@ -43,7 +40,6 @@ public class DefaultRakClientConfig extends DefaultRakSessionConfig { private volatile boolean ipDontFragment = false; private volatile int clientInternalAddresses = 10; private volatile int timeBetweenSendConnectionAttemptsMS = TIME_BETWEEN_SEND_CONNECTION_ATTEMPTS_MS; - private volatile int bedrockProtocolVersion; public DefaultRakClientConfig(Channel channel) { super(channel); @@ -52,10 +48,9 @@ public DefaultRakClientConfig(Channel channel) { @Override public Map, Object> getOptions() { return this.getOptions( - super.getOptions(), + super.getOptions(), RakChannelOption.RAK_UNCONNECTED_MAGIC, RakChannelOption.RAK_CONNECT_TIMEOUT, RakChannelOption.RAK_REMOTE_GUID, RakChannelOption.RAK_SESSION_TIMEOUT, RakChannelOption.RAK_COMPATIBILITY_MODE, - RakChannelOption.RAK_MTU_SIZES, RakChannelOption.RAK_IP_DONT_FRAGMENT, RakChannelOption.RAK_CLIENT_INTERNAL_ADDRESSES, RakChannelOption.RAK_TIME_BETWEEN_SEND_CONNECTION_ATTEMPTS_MS, - RakChannelOption.RAK_CLIENT_BEDROCK_PROTOCOL_VERSION); + RakChannelOption.RAK_MTU_SIZES, RakChannelOption.RAK_IP_DONT_FRAGMENT, RakChannelOption.RAK_CLIENT_INTERNAL_ADDRESSES, RakChannelOption.RAK_TIME_BETWEEN_SEND_CONNECTION_ATTEMPTS_MS); } @SuppressWarnings("unchecked") @@ -79,8 +74,6 @@ public T getOption(ChannelOption option) { return (T) Integer.valueOf(this.clientInternalAddresses); } else if (option == RakChannelOption.RAK_TIME_BETWEEN_SEND_CONNECTION_ATTEMPTS_MS) { return (T) Integer.valueOf(this.timeBetweenSendConnectionAttemptsMS); - } else if (option == RakChannelOption.RAK_CLIENT_BEDROCK_PROTOCOL_VERSION) { - return (T) Integer.valueOf(this.bedrockProtocolVersion); } return super.getOption(option); } @@ -116,9 +109,6 @@ public boolean setOption(ChannelOption option, T value) { } else if (option == RakChannelOption.RAK_TIME_BETWEEN_SEND_CONNECTION_ATTEMPTS_MS) { this.setTimeBetweenSendConnectionAttemptsMS((Integer) value); return true; - } else if (option == RakChannelOption.RAK_CLIENT_BEDROCK_PROTOCOL_VERSION) { - this.setBedrockProtocolVersion((Integer) value); - return true; } return super.setOption(option, value); } @@ -203,12 +193,4 @@ public int getTimeBetweenSendConnectionAttemptsMS() { public void setTimeBetweenSendConnectionAttemptsMS(int timeBetweenSendConnectionAttemptsMS) { this.timeBetweenSendConnectionAttemptsMS = timeBetweenSendConnectionAttemptsMS; } - - public int getBedrockProtocolVersion() { - return this.bedrockProtocolVersion; - } - - public void setBedrockProtocolVersion(int bedrockProtocolVersion) { - this.bedrockProtocolVersion = bedrockProtocolVersion; - } } diff --git a/transport-raknet/src/main/java/org/cloudburstmc/netty/channel/raknet/config/RakChannelOption.java b/transport-raknet/src/main/java/org/cloudburstmc/netty/channel/raknet/config/RakChannelOption.java index aac6cb2..fc8e25b 100644 --- a/transport-raknet/src/main/java/org/cloudburstmc/netty/channel/raknet/config/RakChannelOption.java +++ b/transport-raknet/src/main/java/org/cloudburstmc/netty/channel/raknet/config/RakChannelOption.java @@ -189,12 +189,6 @@ public class RakChannelOption extends ChannelOption { public static final ChannelOption RAK_TIME_BETWEEN_SEND_CONNECTION_ATTEMPTS_MS = valueOf(RakChannelOption.class, "RAK_TIME_BETWEEN_SEND_CONNECTION_ATTEMPTS_MS"); - /** - * The fllback protocol version of the RakNet client for sending RequestNetworkSettingsPacket in compatibility mode if one is not found in the pipeline. - */ - public static final ChannelOption RAK_CLIENT_BEDROCK_PROTOCOL_VERSION = - valueOf(RakChannelOption.class, "RAK_CLIENT_BEDROCK_PROTOCOL_VERSION"); - @SuppressWarnings("deprecation") protected RakChannelOption() { super(null); diff --git a/transport-raknet/src/main/java/org/cloudburstmc/netty/handler/codec/raknet/client/RakClientNetworkSettingsHandler.java b/transport-raknet/src/main/java/org/cloudburstmc/netty/handler/codec/raknet/client/RakClientNetworkSettingsHandler.java index cf1f513..3a8755e 100644 --- a/transport-raknet/src/main/java/org/cloudburstmc/netty/handler/codec/raknet/client/RakClientNetworkSettingsHandler.java +++ b/transport-raknet/src/main/java/org/cloudburstmc/netty/handler/codec/raknet/client/RakClientNetworkSettingsHandler.java @@ -4,42 +4,50 @@ import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelOutboundHandlerAdapter; import io.netty.channel.ChannelPromise; -import io.netty.util.AttributeKey; -import io.netty.util.internal.logging.InternalLogger; -import io.netty.util.internal.logging.InternalLoggerFactory; +import io.netty.util.concurrent.Promise; import org.cloudburstmc.netty.channel.raknet.RakChannel; +import org.cloudburstmc.netty.channel.raknet.RakPriority; +import org.cloudburstmc.netty.channel.raknet.RakReliability; import org.cloudburstmc.netty.channel.raknet.config.RakChannelOption; +import org.cloudburstmc.netty.channel.raknet.packet.RakMessage; import static org.cloudburstmc.netty.channel.raknet.RakConstants.ID_GAME_PACKET; public class RakClientNetworkSettingsHandler extends ChannelOutboundHandlerAdapter { public static final String NAME = "rak-client-network-settings-handler"; - public static final AttributeKey NETWORK_SETTINGS_PAYLOAD = AttributeKey.valueOf("network-settings-payload"); - private static final InternalLogger log = InternalLoggerFactory.getInstance(RakClientNetworkSettingsHandler.class); private final RakChannel channel; + private final Promise networkSettingsPacketPromise; - public RakClientNetworkSettingsHandler(RakChannel channel) { + public RakClientNetworkSettingsHandler(RakChannel channel, Promise networkSettingsPacketPromise) { this.channel = channel; + this.networkSettingsPacketPromise = networkSettingsPacketPromise; } @Override public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception { - if (!(msg instanceof ByteBuf)) { - ctx.write(msg, promise); - return; + ctx.channel().pipeline().remove(RakClientNetworkSettingsHandler.NAME); + + RakMessage packet; + + if (!(msg instanceof RakMessage)) { + if (msg instanceof ByteBuf) { + packet = new RakMessage((ByteBuf) msg, RakReliability.RELIABLE_ORDERED, RakPriority.NORMAL); + } else { + throw new IllegalStateException("First packet was not an instance of RakMessage or ByteBuf: class " + msg.getClass().getName()); + } + } else { + packet = (RakMessage) msg; } - ByteBuf packet = (ByteBuf) msg; + ByteBuf content = packet.content(); - if (packet.capacity() < 4) { - ctx.write(msg, promise); - return; + if (content.capacity() < 4) { + throw new IllegalStateException("First packet was not a RequestNetworkSettings packet: Content less than 4 bytes"); } - if (packet.getByte(0) != (byte) ID_GAME_PACKET) { - ctx.write(msg, promise); - return; + if (content.getByte(0) != (byte) ID_GAME_PACKET) { + throw new IllegalStateException("First packet was not a RequestNetworkSettings packet: Expected RakNet game packet ID (" + ID_GAME_PACKET + "), but got " + content.getByte(0)); } int rakVersion = this.channel.config().getOption(RakChannelOption.RAK_PROTOCOL_VERSION); @@ -48,31 +56,25 @@ public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) case 11: case 10: case 9: - if (packet.getByte(1) != (byte) 0x06) break; - if (packet.getByte(2) != (byte) 0xc1) break; - if ((packet.getByte(3) & (byte) 0b10000111) != (byte) 0b00000001) break; - onNetworkSettings(ctx, packet); + if (content.getByte(1) != (byte) 0x06) break; + if (content.getByte(2) != (byte) 0xc1) break; + if ((content.getByte(3) & (byte) 0b10000111) != (byte) 0b00000001) break; + this.networkSettingsPacketPromise.setSuccess(packet); return; case 8: - if (packet.getByte(1) != (byte) 0x07) break; - if (packet.getByte(2) != 0xc1) break; - onNetworkSettings(ctx, packet); + if (content.getByte(1) != (byte) 0x07) break; + if (content.getByte(2) != (byte) 0xc1) break; + this.networkSettingsPacketPromise.setSuccess(packet); return; case 7: - if (packet.getByte(1) != (byte) 0x05) break; - if (packet.getByte(2) != (byte) 0xc1) break; - onNetworkSettings(ctx, packet); + if (content.getByte(1) != (byte) 0x05) break; + if (content.getByte(2) != (byte) 0xc1) break; + this.networkSettingsPacketPromise.setSuccess(packet); return; default: throw new UnsupportedOperationException("Unsupported protocol version: " + rakVersion); } - ctx.write(msg, promise); - } - - private void onNetworkSettings(ChannelHandlerContext ctx, ByteBuf packet) { - log.info("Detected network settings packet, removing handler"); - ctx.channel().attr(NETWORK_SETTINGS_PAYLOAD).set(packet.retain()); - ctx.channel().pipeline().remove(RakClientNetworkSettingsHandler.NAME); + throw new IllegalStateException("First packet was not a RequestNetworkSettings packet: Invalid Bedrock packet ID"); } -} \ No newline at end of file +} diff --git a/transport-raknet/src/main/java/org/cloudburstmc/netty/handler/codec/raknet/client/RakClientOfflineHandlerCompatible.java b/transport-raknet/src/main/java/org/cloudburstmc/netty/handler/codec/raknet/client/RakClientOfflineHandlerCompatible.java index b41b2ae..0ef64d6 100644 --- a/transport-raknet/src/main/java/org/cloudburstmc/netty/handler/codec/raknet/client/RakClientOfflineHandlerCompatible.java +++ b/transport-raknet/src/main/java/org/cloudburstmc/netty/handler/codec/raknet/client/RakClientOfflineHandlerCompatible.java @@ -4,17 +4,12 @@ import io.netty.channel.Channel; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelPromise; +import io.netty.util.concurrent.Promise; import org.cloudburstmc.netty.channel.raknet.RakChannel; import org.cloudburstmc.netty.channel.raknet.RakOfflineState; import org.cloudburstmc.netty.channel.raknet.config.RakChannelOption; -import org.cloudburstmc.netty.handler.codec.raknet.common.ConnectedPingHandler; -import org.cloudburstmc.netty.handler.codec.raknet.common.ConnectedPongHandler; -import org.cloudburstmc.netty.handler.codec.raknet.common.DisconnectNotificationHandler; -import org.cloudburstmc.netty.handler.codec.raknet.common.EncapsulatedToMessageHandler; -import org.cloudburstmc.netty.handler.codec.raknet.common.RakAcknowledgeHandler; -import org.cloudburstmc.netty.handler.codec.raknet.common.RakDatagramCodec; -import org.cloudburstmc.netty.handler.codec.raknet.common.RakSessionCodec; -import org.cloudburstmc.netty.handler.codec.raknet.common.RakSessionCodecCompatible; +import org.cloudburstmc.netty.channel.raknet.packet.RakMessage; +import org.cloudburstmc.netty.handler.codec.raknet.common.*; import org.cloudburstmc.netty.util.RakUtils; public class RakClientOfflineHandlerCompatible extends RakClientOfflineHandler { @@ -36,17 +31,18 @@ void onRetryAttempt(Channel channel) { @Override void onSuccess(ChannelHandlerContext ctx) { RakSessionCodec sessionCodec = new RakSessionCodecCompatible(this.rakChannel()); + Promise networkSettingsPacketPromise = ctx.executor().newPromise(); ctx.pipeline().addAfter(NAME, RakDatagramCodec.NAME, new RakDatagramCodec()); ctx.pipeline().addAfter(RakDatagramCodec.NAME, RakAcknowledgeHandler.NAME, new RakAcknowledgeHandler(sessionCodec)); ctx.pipeline().addAfter(RakAcknowledgeHandler.NAME, RakSessionCodec.NAME, sessionCodec); // Ensure new incoming connection batches with request network settings game packet - ctx.pipeline().addAfter(RakSessionCodec.NAME, RakClientNetworkSettingsHandler.NAME, new RakClientNetworkSettingsHandler(this.rakChannel())); + ctx.pipeline().addAfter(RakSessionCodec.NAME, RakClientNetworkSettingsHandler.NAME, new RakClientNetworkSettingsHandler(this.rakChannel(), networkSettingsPacketPromise)); ctx.pipeline().addAfter(RakSessionCodec.NAME, ConnectedPingHandler.NAME, new ConnectedPingHandler()); ctx.pipeline().addAfter(ConnectedPingHandler.NAME, ConnectedPongHandler.NAME, new ConnectedPongHandler(sessionCodec)); ctx.pipeline().addAfter(ConnectedPongHandler.NAME, DisconnectNotificationHandler.NAME, DisconnectNotificationHandler.INSTANCE); // Replicate server behavior, and transform unhandled encapsulated packets to rakMessage ctx.pipeline().addAfter(DisconnectNotificationHandler.NAME, EncapsulatedToMessageHandler.NAME, EncapsulatedToMessageHandler.INSTANCE); - ctx.pipeline().addAfter(DisconnectNotificationHandler.NAME, RakClientOnlineInitialHandlerCompatible.NAME, new RakClientOnlineInitialHandlerCompatible(this.rakChannel(), this.successPromise())); + ctx.pipeline().addAfter(DisconnectNotificationHandler.NAME, RakClientOnlineInitialHandlerCompatible.NAME, new RakClientOnlineInitialHandlerCompatible(this.rakChannel(), this.successPromise(), networkSettingsPacketPromise)); } @Override diff --git a/transport-raknet/src/main/java/org/cloudburstmc/netty/handler/codec/raknet/client/RakClientOnlineInitialHandlerCompatible.java b/transport-raknet/src/main/java/org/cloudburstmc/netty/handler/codec/raknet/client/RakClientOnlineInitialHandlerCompatible.java index 1c93cbb..4a831a8 100644 --- a/transport-raknet/src/main/java/org/cloudburstmc/netty/handler/codec/raknet/client/RakClientOnlineInitialHandlerCompatible.java +++ b/transport-raknet/src/main/java/org/cloudburstmc/netty/handler/codec/raknet/client/RakClientOnlineInitialHandlerCompatible.java @@ -3,65 +3,46 @@ import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelPromise; +import io.netty.util.concurrent.Promise; import org.cloudburstmc.netty.channel.raknet.RakChannel; import org.cloudburstmc.netty.channel.raknet.RakPriority; import org.cloudburstmc.netty.channel.raknet.RakReliability; -import org.cloudburstmc.netty.channel.raknet.config.RakChannelOption; import org.cloudburstmc.netty.channel.raknet.packet.RakMessage; import org.cloudburstmc.netty.util.RakUtils; -import static org.cloudburstmc.netty.channel.raknet.RakConstants.*; +import static org.cloudburstmc.netty.channel.raknet.RakConstants.ID_CONNECTED_PING; +import static org.cloudburstmc.netty.channel.raknet.RakConstants.IPV4_MESSAGE_SIZE; public class RakClientOnlineInitialHandlerCompatible extends RakClientOnlineInitialHandler { public static final String NAME = "rak-client-online-initial-handler"; + private final Promise networkSettingsPacketPromise; private long pingTime = 0; - public RakClientOnlineInitialHandlerCompatible(RakChannel rakChannel, ChannelPromise promise) { - super(rakChannel, promise); + public RakClientOnlineInitialHandlerCompatible(RakChannel rakChannel, ChannelPromise successPromise, Promise networkSettingsPacketPromise) { + super(rakChannel, successPromise); + this.networkSettingsPacketPromise = networkSettingsPacketPromise; } @Override void onSuccess(ChannelHandlerContext ctx) { super.onSuccess(ctx); - ByteBuf incomingBuffer = ctx.alloc().ioBuffer(); - this.writeIncomingConnection(ctx, incomingBuffer, pingTime); - ctx.write(new RakMessage(incomingBuffer, RakReliability.RELIABLE_ORDERED, RakPriority.NORMAL)); + // Wait for the RequestNetworkSettings packet before sending the final batch + this.networkSettingsPacketPromise.addListener(future -> { + ByteBuf incomingBuffer = ctx.alloc().ioBuffer(); + this.writeIncomingConnection(ctx, incomingBuffer, pingTime); + ctx.write(new RakMessage(incomingBuffer, RakReliability.RELIABLE_ORDERED, RakPriority.NORMAL)); - ByteBuf pingBuffer = ctx.alloc().ioBuffer(); - pingBuffer.writeByte(ID_CONNECTED_PING); - pingBuffer.writeLong(System.currentTimeMillis()); - ctx.write(new RakMessage(pingBuffer, RakReliability.UNRELIABLE, RakPriority.NORMAL)); + ByteBuf pingBuffer = ctx.alloc().ioBuffer(); + pingBuffer.writeByte(ID_CONNECTED_PING); + pingBuffer.writeLong(System.currentTimeMillis()); + ctx.write(new RakMessage(pingBuffer, RakReliability.UNRELIABLE, RakPriority.NORMAL)); - ByteBuf netSettingsBuffer = ctx.channel().attr(RakClientNetworkSettingsHandler.NETWORK_SETTINGS_PAYLOAD).get(); - if (netSettingsBuffer == null) { - netSettingsBuffer = ctx.alloc().ioBuffer(); - netSettingsBuffer.writeByte(ID_GAME_PACKET); - int rakVersion = this.rakChannel().config().getOption(RakChannelOption.RAK_PROTOCOL_VERSION); - switch (rakVersion) { - case 11: - case 10: - case 9: - netSettingsBuffer.writeByte(0x06); // length - netSettingsBuffer.writeByte(0xc1).writeByte(0x01); // header - break; - case 8: - netSettingsBuffer.writeByte(0x07); // length - netSettingsBuffer.writeByte(0xc1).writeByte(0x00).writeByte(0x00); // header - break; - case 7: - netSettingsBuffer.writeByte(0x05); // length - netSettingsBuffer.writeByte(0xc1); // header - break; - default: - throw new UnsupportedOperationException("Unsupported protocol version: " + rakVersion); - } - netSettingsBuffer.writeInt(this.rakChannel().config().getOption(RakChannelOption.RAK_CLIENT_BEDROCK_PROTOCOL_VERSION)); - } - ctx.write(new RakMessage(netSettingsBuffer, RakReliability.RELIABLE_ORDERED, RakPriority.NORMAL)); + ctx.write(future.get()); - ctx.flush(); + ctx.flush(); + }); } @Override