From 74798d80714bc155c560f8169018ced0fecb21bf Mon Sep 17 00:00:00 2001 From: RednedEpic Date: Sun, 30 Oct 2022 17:00:08 -0500 Subject: [PATCH] Remove old network code & use manual ping --- .../java/org/geysermc/geyser/GeyserImpl.java | 22 +-- .../level/block/BlockPositionIterator.java | 2 +- .../geyser/level/chunk/BlockStorage.java | 2 +- .../level/chunk/GeyserChunkSection.java | 2 +- .../geyser/level/chunk/bitarray/BitArray.java | 2 +- .../level/chunk/bitarray/PaddedBitArray.java | 2 +- .../level/chunk/bitarray/Pow2BitArray.java | 2 +- .../network/GeyserServerInitializer.java | 93 +--------- .../geyser/network/netty/GeyserServer.java | 167 ++++++++++++++++-- .../geyser/network/netty/RakPingHandler.java | 50 ++++++ .../geyser/registry/type/BlockMappings.java | 4 +- .../geyser/util/LoginEncryptionUtils.java | 2 +- gradle/libs.versions.toml | 4 +- 13 files changed, 215 insertions(+), 139 deletions(-) create mode 100644 core/src/main/java/org/geysermc/geyser/network/netty/RakPingHandler.java diff --git a/core/src/main/java/org/geysermc/geyser/GeyserImpl.java b/core/src/main/java/org/geysermc/geyser/GeyserImpl.java index ce4b6f30b..968af9b79 100644 --- a/core/src/main/java/org/geysermc/geyser/GeyserImpl.java +++ b/core/src/main/java/org/geysermc/geyser/GeyserImpl.java @@ -30,8 +30,6 @@ import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.github.steveice10.packetlib.tcp.TcpSession; -import com.nukkitx.network.raknet.RakNetConstants; -import com.nukkitx.network.util.EventLoops; import io.netty.channel.epoll.Epoll; import io.netty.channel.kqueue.KQueue; import io.netty.util.NettyRuntime; @@ -284,34 +282,16 @@ public class GeyserImpl implements GeyserApi { CooldownUtils.setDefaultShowCooldown(config.getShowCooldown()); DimensionUtils.changeBedrockNetherId(config.isAboveBedrockNetherBuilding()); // Apply End dimension ID workaround to Nether - // https://github.com/GeyserMC/Geyser/issues/957 - RakNetConstants.MAXIMUM_MTU_SIZE = (short) config.getMtu(); - logger.debug("Setting MTU to " + config.getMtu()); - Integer bedrockThreadCount = Integer.getInteger("Geyser.BedrockNetworkThreads"); if (bedrockThreadCount == null) { // Copy the code from Netty's default thread count fallback bedrockThreadCount = Math.max(1, SystemPropertyUtil.getInt("io.netty.eventLoopThreads", NettyRuntime.availableProcessors() * 2)); } - boolean enableProxyProtocol = config.getBedrock().isEnableProxyProtocol(); - if (config.isDebugMode()) { - logger.debug("EventLoop type: " + EventLoops.getChannelType()); - if (EventLoops.getChannelType() == EventLoops.ChannelType.NIO) { - if (System.getProperties().contains("disableNativeEventLoop")) { - logger.debug("EventLoop type is NIO because native event loops are disabled."); - } else { - logger.debug("Reason for no Epoll: " + Epoll.unavailabilityCause().toString()); - logger.debug("Reason for no KQueue: " + KQueue.unavailabilityCause().toString()); - } - } - } - if (shouldStartListener) { try { this.geyserServer = new GeyserServer(this, bedrockThreadCount); - this.geyserServer.bind(new InetSocketAddress(config.getBedrock().address(), config.getBedrock().port())) - .awaitUninterruptibly(); + this.geyserServer.bind(new InetSocketAddress(config.getBedrock().address(), config.getBedrock().port())); logger.info(GeyserLocale.getLocaleStringLog("geyser.core.start", config.getBedrock().address(), String.valueOf(config.getBedrock().port()))); diff --git a/core/src/main/java/org/geysermc/geyser/level/block/BlockPositionIterator.java b/core/src/main/java/org/geysermc/geyser/level/block/BlockPositionIterator.java index d22150ccf..7b94f751b 100644 --- a/core/src/main/java/org/geysermc/geyser/level/block/BlockPositionIterator.java +++ b/core/src/main/java/org/geysermc/geyser/level/block/BlockPositionIterator.java @@ -25,7 +25,7 @@ package org.geysermc.geyser.level.block; -import com.nukkitx.network.util.Preconditions; +import org.cloudburstmc.protocol.common.util.Preconditions; public class BlockPositionIterator { private final int minX; diff --git a/core/src/main/java/org/geysermc/geyser/level/chunk/BlockStorage.java b/core/src/main/java/org/geysermc/geyser/level/chunk/BlockStorage.java index fbcdfc3dc..8cf6d8091 100644 --- a/core/src/main/java/org/geysermc/geyser/level/chunk/BlockStorage.java +++ b/core/src/main/java/org/geysermc/geyser/level/chunk/BlockStorage.java @@ -25,11 +25,11 @@ package org.geysermc.geyser.level.chunk; -import com.nukkitx.network.VarInts; import io.netty.buffer.ByteBuf; import it.unimi.dsi.fastutil.ints.IntArrayList; import it.unimi.dsi.fastutil.ints.IntList; import lombok.Getter; +import org.cloudburstmc.protocol.common.util.VarInts; import org.geysermc.geyser.level.chunk.bitarray.BitArray; import org.geysermc.geyser.level.chunk.bitarray.BitArrayVersion; diff --git a/core/src/main/java/org/geysermc/geyser/level/chunk/GeyserChunkSection.java b/core/src/main/java/org/geysermc/geyser/level/chunk/GeyserChunkSection.java index 748bc8579..14a74fb99 100644 --- a/core/src/main/java/org/geysermc/geyser/level/chunk/GeyserChunkSection.java +++ b/core/src/main/java/org/geysermc/geyser/level/chunk/GeyserChunkSection.java @@ -25,8 +25,8 @@ package org.geysermc.geyser.level.chunk; -import com.nukkitx.network.util.Preconditions; import io.netty.buffer.ByteBuf; +import org.cloudburstmc.protocol.common.util.Preconditions; public class GeyserChunkSection { diff --git a/core/src/main/java/org/geysermc/geyser/level/chunk/bitarray/BitArray.java b/core/src/main/java/org/geysermc/geyser/level/chunk/bitarray/BitArray.java index e5eb44bee..776802cee 100644 --- a/core/src/main/java/org/geysermc/geyser/level/chunk/bitarray/BitArray.java +++ b/core/src/main/java/org/geysermc/geyser/level/chunk/bitarray/BitArray.java @@ -25,8 +25,8 @@ package org.geysermc.geyser.level.chunk.bitarray; -import com.nukkitx.network.VarInts; import io.netty.buffer.ByteBuf; +import org.cloudburstmc.protocol.common.util.VarInts; public interface BitArray { diff --git a/core/src/main/java/org/geysermc/geyser/level/chunk/bitarray/PaddedBitArray.java b/core/src/main/java/org/geysermc/geyser/level/chunk/bitarray/PaddedBitArray.java index 64c453bb3..54987d1d2 100644 --- a/core/src/main/java/org/geysermc/geyser/level/chunk/bitarray/PaddedBitArray.java +++ b/core/src/main/java/org/geysermc/geyser/level/chunk/bitarray/PaddedBitArray.java @@ -25,7 +25,7 @@ package org.geysermc.geyser.level.chunk.bitarray; -import com.nukkitx.network.util.Preconditions; +import org.cloudburstmc.protocol.common.util.Preconditions; import org.geysermc.geyser.util.MathUtils; import java.util.Arrays; diff --git a/core/src/main/java/org/geysermc/geyser/level/chunk/bitarray/Pow2BitArray.java b/core/src/main/java/org/geysermc/geyser/level/chunk/bitarray/Pow2BitArray.java index 0a925a184..c1f8d6f48 100644 --- a/core/src/main/java/org/geysermc/geyser/level/chunk/bitarray/Pow2BitArray.java +++ b/core/src/main/java/org/geysermc/geyser/level/chunk/bitarray/Pow2BitArray.java @@ -25,7 +25,7 @@ package org.geysermc.geyser.level.chunk.bitarray; -import com.nukkitx.network.util.Preconditions; +import org.cloudburstmc.protocol.common.util.Preconditions; import org.geysermc.geyser.util.MathUtils; import java.util.Arrays; diff --git a/core/src/main/java/org/geysermc/geyser/network/GeyserServerInitializer.java b/core/src/main/java/org/geysermc/geyser/network/GeyserServerInitializer.java index a646c0f93..dd29527af 100644 --- a/core/src/main/java/org/geysermc/geyser/network/GeyserServerInitializer.java +++ b/core/src/main/java/org/geysermc/geyser/network/GeyserServerInitializer.java @@ -35,21 +35,8 @@ import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.session.GeyserSession; import javax.annotation.Nonnull; -import java.nio.charset.StandardCharsets; public class GeyserServerInitializer extends BedrockServerInitializer { - private static final boolean PRINT_DEBUG_PINGS = Boolean.parseBoolean(System.getProperty("Geyser.PrintPingsInDebugMode", "true")); - - /* - The following constants are all used to ensure the ping does not reach a length where it is unparsable by the Bedrock client - */ - private static final int MINECRAFT_VERSION_BYTES_LENGTH = GameProtocol.DEFAULT_BEDROCK_CODEC.getMinecraftVersion().getBytes(StandardCharsets.UTF_8).length; - private static final int BRAND_BYTES_LENGTH = GeyserImpl.NAME.getBytes(StandardCharsets.UTF_8).length; - /** - * The MOTD, sub-MOTD and Minecraft version ({@link #MINECRAFT_VERSION_BYTES_LENGTH}) combined cannot reach this length. - */ - private static final int MAGIC_RAKNET_LENGTH = 338; - private final GeyserImpl geyser; // There is a constructor that doesn't require inputting threads, but older Netty versions don't have it private final DefaultEventLoopGroup eventLoopGroup = new DefaultEventLoopGroup(0, new DefaultThreadFactory("Geyser player thread")); @@ -81,89 +68,11 @@ public class GeyserServerInitializer extends BedrockServerInitializer { return true; } - @Override - public BedrockPong onQuery(InetSocketAddress inetSocketAddress) { - if (geyser.getConfig().isDebugMode() && PRINT_DEBUG_PINGS) { - String ip = geyser.getConfig().isLogPlayerIpAddresses() ? inetSocketAddress.toString() : ""; - geyser.getLogger().debug(GeyserLocale.getLocaleStringLog("geyser.network.pinged", ip)); - } - - GeyserConfiguration config = geyser.getConfig(); - - GeyserPingInfo pingInfo = null; - if (config.isPassthroughMotd() || config.isPassthroughPlayerCounts()) { - IGeyserPingPassthrough pingPassthrough = geyser.getBootstrap().getGeyserPingPassthrough(); - pingInfo = pingPassthrough.getPingInformation(inetSocketAddress); - } - - BedrockPong pong = new BedrockPong(); - pong.setEdition("MCPE"); - pong.setGameType("Survival"); // Can only be Survival or Creative as of 1.16.210.59 - pong.setNintendoLimited(false); - pong.setProtocolVersion(GameProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion()); - pong.setVersion(GameProtocol.DEFAULT_BEDROCK_CODEC.getMinecraftVersion()); // Required to not be empty as of 1.16.210.59. Can only contain . and numbers. - pong.setIpv4Port(config.getBedrock().port()); - - if (config.isPassthroughMotd() && pingInfo != null && pingInfo.getDescription() != null) { - String[] motd = MessageTranslator.convertMessageLenient(pingInfo.getDescription()).split("\n"); - String mainMotd = motd[0]; // First line of the motd. - String subMotd = (motd.length != 1) ? motd[1] : GeyserImpl.NAME; // Second line of the motd if present, otherwise default. - - pong.setMotd(mainMotd.trim()); - pong.setSubMotd(subMotd.trim()); // Trimmed to shift it to the left, prevents the universe from collapsing on us just because we went 2 characters over the text box's limit. - } else { - pong.setMotd(config.getBedrock().primaryMotd()); - pong.setSubMotd(config.getBedrock().secondaryMotd()); - } - - if (config.isPassthroughPlayerCounts() && pingInfo != null) { - pong.setPlayerCount(pingInfo.getPlayers().getOnline()); - pong.setMaximumPlayerCount(pingInfo.getPlayers().getMax()); - } else { - pong.setPlayerCount(geyser.getSessionManager().getSessions().size()); - pong.setMaximumPlayerCount(config.getMaxPlayers()); - } - - // Fallbacks to prevent errors and allow Bedrock to see the server - if (pong.getMotd() == null || pong.getMotd().isBlank()) { - pong.setMotd(GeyserImpl.NAME); - } - if (pong.getSubMotd() == null || pong.getSubMotd().isBlank()) { - // Sub-MOTD cannot be empty as of 1.16.210.59 - pong.setSubMotd(GeyserImpl.NAME); - } - - // The ping will not appear if the MOTD + sub-MOTD is of a certain length. - // We don't know why, though - byte[] motdArray = pong.getMotd().getBytes(StandardCharsets.UTF_8); - int subMotdLength = pong.getSubMotd().getBytes(StandardCharsets.UTF_8).length; - if (motdArray.length + subMotdLength > (MAGIC_RAKNET_LENGTH - MINECRAFT_VERSION_BYTES_LENGTH)) { - // Shorten the sub-MOTD first since that only appears locally - if (subMotdLength > BRAND_BYTES_LENGTH) { - pong.setSubMotd(GeyserImpl.NAME); - subMotdLength = BRAND_BYTES_LENGTH; - } - if (motdArray.length > (MAGIC_RAKNET_LENGTH - MINECRAFT_VERSION_BYTES_LENGTH - subMotdLength)) { - // If the top MOTD is still too long, we chop it down - byte[] newMotdArray = new byte[MAGIC_RAKNET_LENGTH - MINECRAFT_VERSION_BYTES_LENGTH - subMotdLength]; - System.arraycopy(motdArray, 0, newMotdArray, 0, newMotdArray.length); - pong.setMotd(new String(newMotdArray, StandardCharsets.UTF_8)); - } - } - - //Bedrock will not even attempt a connection if the client thinks the server is full - //so we have to fake it not being full - if (pong.getPlayerCount() >= pong.getMaximumPlayerCount()) { - pong.setMaximumPlayerCount(pong.getPlayerCount() + 1); - } - - return pong; - } - */ @Override public void initSession(@Nonnull BedrockServerSession bedrockServerSession) { + System.out.println("init session"); try { bedrockServerSession.setCodec(Bedrock_v554.CODEC); // Has the RequestNetworkSettingsPacket bedrockServerSession.setLogging(true); diff --git a/core/src/main/java/org/geysermc/geyser/network/netty/GeyserServer.java b/core/src/main/java/org/geysermc/geyser/network/netty/GeyserServer.java index fb1c9cc3a..4be10f896 100644 --- a/core/src/main/java/org/geysermc/geyser/network/netty/GeyserServer.java +++ b/core/src/main/java/org/geysermc/geyser/network/netty/GeyserServer.java @@ -25,36 +25,76 @@ package org.geysermc.geyser.network.netty; -import com.nukkitx.network.util.EventLoops; +import com.github.steveice10.packetlib.helper.TransportHelper; import io.netty.bootstrap.ServerBootstrap; +import io.netty.channel.Channel; import io.netty.channel.ChannelFuture; import io.netty.channel.EventLoopGroup; +import io.netty.channel.epoll.Epoll; +import io.netty.channel.epoll.EpollDatagramChannel; +import io.netty.channel.epoll.EpollEventLoopGroup; +import io.netty.channel.kqueue.KQueue; +import io.netty.channel.kqueue.KQueueDatagramChannel; +import io.netty.channel.kqueue.KQueueEventLoopGroup; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.socket.DatagramChannel; import io.netty.channel.socket.nio.NioDatagramChannel; import org.cloudburstmc.netty.channel.raknet.RakChannelFactory; 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.cloudburstmc.netty.handler.codec.raknet.common.UnconnectedPongEncoder; +import org.cloudburstmc.netty.handler.codec.raknet.server.RakServerOfflineHandler; import org.cloudburstmc.protocol.bedrock.BedrockPong; import org.geysermc.geyser.GeyserImpl; +import org.geysermc.geyser.configuration.GeyserConfiguration; import org.geysermc.geyser.network.GameProtocol; import org.geysermc.geyser.network.GeyserServerInitializer; +import org.geysermc.geyser.ping.GeyserPingInfo; +import org.geysermc.geyser.ping.IGeyserPingPassthrough; +import org.geysermc.geyser.text.GeyserLocale; +import org.geysermc.geyser.translator.text.MessageTranslator; import java.net.InetSocketAddress; +import java.nio.charset.StandardCharsets; +import java.util.Random; +import java.util.function.Function; public final class GeyserServer { + private static final boolean PRINT_DEBUG_PINGS = Boolean.parseBoolean(System.getProperty("Geyser.PrintPingsInDebugMode", "true")); + + /* + The following constants are all used to ensure the ping does not reach a length where it is unparsable by the Bedrock client + */ + private static final int MINECRAFT_VERSION_BYTES_LENGTH = GameProtocol.DEFAULT_BEDROCK_CODEC.getMinecraftVersion().getBytes(StandardCharsets.UTF_8).length; + private static final int BRAND_BYTES_LENGTH = GeyserImpl.NAME.getBytes(StandardCharsets.UTF_8).length; + /** + * The MOTD, sub-MOTD and Minecraft version ({@link #MINECRAFT_VERSION_BYTES_LENGTH}) combined cannot reach this length. + */ + private static final int MAGIC_RAKNET_LENGTH = 338; + + private static final Transport TRANSPORT = compatibleTransport(); + private final GeyserImpl geyser; private final EventLoopGroup group; private final ServerBootstrap bootstrap; private ChannelFuture future; + private Channel channel; public GeyserServer(GeyserImpl geyser, int threadCount) { this.geyser = geyser; - this.group = EventLoops.newEventLoopGroup(threadCount); + this.group = TRANSPORT.eventLoopGroupFactory().apply(threadCount); - this.bootstrap = this.createBootstrap(group); + this.bootstrap = this.createBootstrap(this.group); } - public ChannelFuture bind(InetSocketAddress address) { - return this.future = this.bootstrap.bind(address); + public void bind(InetSocketAddress address) { + this.future = this.bootstrap.bind(address).syncUninterruptibly(); + this.channel = this.future.channel(); + + // Add our ping handler + this.channel.pipeline().addAfter(RakServerOfflineHandler.NAME, RakPingHandler.NAME, new RakPingHandler(this)); } public void shutdown() { @@ -63,25 +103,122 @@ public final class GeyserServer { } private ServerBootstrap createBootstrap(EventLoopGroup group) { + // TODO + boolean enableProxyProtocol = this.geyser.getConfig().getBedrock().isEnableProxyProtocol(); + if (this.geyser.getConfig().isDebugMode()) { + this.geyser.getLogger().debug("EventLoop type: " + TRANSPORT.datagramChannel()); + if (TRANSPORT.datagramChannel() == NioDatagramChannel.class) { + if (System.getProperties().contains("disableNativeEventLoop")) { + this.geyser.getLogger().debug("EventLoop type is NIO because native event loops are disabled."); + } else { + this.geyser.getLogger().debug("Reason for no Epoll: " + Epoll.unavailabilityCause().toString()); + this.geyser.getLogger().debug("Reason for no KQueue: " + KQueue.unavailabilityCause().toString()); + } + } + } + return new ServerBootstrap() - .channelFactory(RakChannelFactory.server(EventLoops.getChannelType().getDatagramChannel())) - .option(RakChannelOption.RAK_ADVERTISEMENT, bedrockPong().toByteBuf()) + .channelFactory(RakChannelFactory.server(TRANSPORT.datagramChannel())) .group(group) + .option(RakChannelOption.RAK_HANDLE_PING, true) .childHandler(new GeyserServerInitializer(this.geyser)); } - // TODO: Temp - private BedrockPong bedrockPong() { - return new BedrockPong() + public BedrockPong onQuery(InetSocketAddress inetSocketAddress) { + if (geyser.getConfig().isDebugMode() && PRINT_DEBUG_PINGS) { + String ip = geyser.getConfig().isLogPlayerIpAddresses() ? inetSocketAddress.toString() : ""; + geyser.getLogger().debug(GeyserLocale.getLocaleStringLog("geyser.network.pinged", ip)); + } + + GeyserConfiguration config = geyser.getConfig(); + + GeyserPingInfo pingInfo = null; + if (config.isPassthroughMotd() || config.isPassthroughPlayerCounts()) { + IGeyserPingPassthrough pingPassthrough = geyser.getBootstrap().getGeyserPingPassthrough(); + pingInfo = pingPassthrough.getPingInformation(inetSocketAddress); + } + + BedrockPong pong = new BedrockPong() .edition("MCPE") .gameType("Survival") // Can only be Survival or Creative as of 1.16.210.59 .nintendoLimited(false) .protocolVersion(GameProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion()) .version(GameProtocol.DEFAULT_BEDROCK_CODEC.getMinecraftVersion()) // Required to not be empty as of 1.16.210.59. Can only contain . and numbers. - .ipv4Port(this.geyser.getConfig().getBedrock().port()) - .motd(this.geyser.getConfig().getBedrock().primaryMotd()) - .subMotd(this.geyser.getConfig().getBedrock().secondaryMotd()) - .playerCount(geyser.getSessionManager().getSessions().size()) - .maximumPlayerCount(this.geyser.getConfig().getMaxPlayers()); + .ipv4Port(this.geyser.getConfig().getBedrock().port()); + + if (config.isPassthroughMotd() && pingInfo != null && pingInfo.getDescription() != null) { + String[] motd = MessageTranslator.convertMessageLenient(pingInfo.getDescription()).split("\n"); + String mainMotd = motd[0]; // First line of the motd. + String subMotd = (motd.length != 1) ? motd[1] : GeyserImpl.NAME; // Second line of the motd if present, otherwise default. + + pong.motd(mainMotd.trim()); + pong.subMotd(subMotd.trim()); // Trimmed to shift it to the left, prevents the universe from collapsing on us just because we went 2 characters over the text box's limit. + } else { + pong.motd(config.getBedrock().primaryMotd()); + pong.subMotd(config.getBedrock().secondaryMotd()); + } + + if (config.isPassthroughPlayerCounts() && pingInfo != null) { + pong.playerCount(pingInfo.getPlayers().getOnline()); + pong.maximumPlayerCount(pingInfo.getPlayers().getMax()); + } else { + pong.playerCount(geyser.getSessionManager().getSessions().size()); + pong.maximumPlayerCount(config.getMaxPlayers()); + } + + // Fallbacks to prevent errors and allow Bedrock to see the server + if (pong.motd() == null || pong.motd().isBlank()) { + pong.motd(GeyserImpl.NAME); + } + if (pong.subMotd() == null || pong.subMotd().isBlank()) { + // Sub-MOTD cannot be empty as of 1.16.210.59 + pong.subMotd(GeyserImpl.NAME); + } + + // The ping will not appear if the MOTD + sub-MOTD is of a certain length. + // We don't know why, though + byte[] motdArray = pong.motd().getBytes(StandardCharsets.UTF_8); + int subMotdLength = pong.subMotd().getBytes(StandardCharsets.UTF_8).length; + if (motdArray.length + subMotdLength > (MAGIC_RAKNET_LENGTH - MINECRAFT_VERSION_BYTES_LENGTH)) { + // Shorten the sub-MOTD first since that only appears locally + if (subMotdLength > BRAND_BYTES_LENGTH) { + pong.subMotd(GeyserImpl.NAME); + subMotdLength = BRAND_BYTES_LENGTH; + } + if (motdArray.length > (MAGIC_RAKNET_LENGTH - MINECRAFT_VERSION_BYTES_LENGTH - subMotdLength)) { + // If the top MOTD is still too long, we chop it down + byte[] newMotdArray = new byte[MAGIC_RAKNET_LENGTH - MINECRAFT_VERSION_BYTES_LENGTH - subMotdLength]; + System.arraycopy(motdArray, 0, newMotdArray, 0, newMotdArray.length); + pong.motd(new String(newMotdArray, StandardCharsets.UTF_8)); + } + } + + //Bedrock will not even attempt a connection if the client thinks the server is full + //so we have to fake it not being full + if (pong.playerCount() >= pong.maximumPlayerCount()) { + pong.maximumPlayerCount(pong.playerCount() + 1); + } + + return pong; + } + + private static Transport compatibleTransport() { + TransportHelper.TransportMethod transportMethod = TransportHelper.determineTransportMethod(); + if (transportMethod == TransportHelper.TransportMethod.EPOLL) { + return new Transport(EpollDatagramChannel.class, EpollEventLoopGroup::new); + } + + if (transportMethod == TransportHelper.TransportMethod.KQUEUE) { + return new Transport(KQueueDatagramChannel.class, KQueueEventLoopGroup::new); + } + + // if (transportMethod == TransportHelper.TransportMethod.IO_URING) { + // return new Transport(IOUringDatagramChannel.class, IOUringEventLoopGroup::new); + // } + + return new Transport(NioDatagramChannel.class, NioEventLoopGroup::new); + } + + private record Transport(Class datagramChannel, Function eventLoopGroupFactory) { } } diff --git a/core/src/main/java/org/geysermc/geyser/network/netty/RakPingHandler.java b/core/src/main/java/org/geysermc/geyser/network/netty/RakPingHandler.java new file mode 100644 index 000000000..ecdd6585e --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/network/netty/RakPingHandler.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + */ + +package org.geysermc.geyser.network.netty; + +import io.netty.channel.ChannelHandler; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.SimpleChannelInboundHandler; +import lombok.RequiredArgsConstructor; +import org.cloudburstmc.netty.channel.raknet.RakPing; +import org.cloudburstmc.netty.channel.raknet.RakPong; +import org.cloudburstmc.netty.channel.raknet.config.RakChannelOption; + +@ChannelHandler.Sharable +@RequiredArgsConstructor +public class RakPingHandler extends SimpleChannelInboundHandler { + public static final String NAME = "rak-ping-handler"; + + private final GeyserServer server; + + @Override + protected void channelRead0(ChannelHandlerContext ctx, RakPing msg) throws Exception { + long guid = ctx.channel().config().getOption(RakChannelOption.RAK_GUID); + + RakPong pong = msg.reply(guid, this.server.onQuery(msg.getSender()).toByteBuf()); + ctx.writeAndFlush(pong); + } +} diff --git a/core/src/main/java/org/geysermc/geyser/registry/type/BlockMappings.java b/core/src/main/java/org/geysermc/geyser/registry/type/BlockMappings.java index 33f8670b3..5f32d7f73 100644 --- a/core/src/main/java/org/geysermc/geyser/registry/type/BlockMappings.java +++ b/core/src/main/java/org/geysermc/geyser/registry/type/BlockMappings.java @@ -60,14 +60,14 @@ public class BlockMappings { Set jigsawStates; public int getBedrockBlockId(int state) { - if (state >= this.javaToBedrockBlocks.length) { + if (state < 0 || state >= this.javaToBedrockBlocks.length) { return bedrockAir.getRuntimeId(); } return this.javaToBedrockBlocks[state].getRuntimeId(); } public BlockDefinition getBedrockBlock(int state) { - if (state >= this.javaToBedrockBlocks.length) { + if (state < 0 || state >= this.javaToBedrockBlocks.length) { return bedrockAir; } return this.javaToBedrockBlocks[state]; diff --git a/core/src/main/java/org/geysermc/geyser/util/LoginEncryptionUtils.java b/core/src/main/java/org/geysermc/geyser/util/LoginEncryptionUtils.java index bec5bd43c..b301d35a9 100644 --- a/core/src/main/java/org/geysermc/geyser/util/LoginEncryptionUtils.java +++ b/core/src/main/java/org/geysermc/geyser/util/LoginEncryptionUtils.java @@ -35,10 +35,10 @@ import com.nimbusds.jose.Payload; import com.nimbusds.jose.shaded.json.JSONObject; import com.nimbusds.jose.shaded.json.JSONValue; import com.nimbusds.jwt.SignedJWT; -import com.nukkitx.network.util.Preconditions; import org.cloudburstmc.protocol.bedrock.packet.LoginPacket; import org.cloudburstmc.protocol.bedrock.packet.ServerToClientHandshakePacket; import org.cloudburstmc.protocol.bedrock.util.EncryptionUtils; +import org.cloudburstmc.protocol.common.util.Preconditions; import org.geysermc.cumulus.form.CustomForm; import org.geysermc.cumulus.form.ModalForm; import org.geysermc.cumulus.form.SimpleForm; diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 2ddac533a..6264b0e55 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -6,7 +6,7 @@ guava = "29.0-jre" gson = "2.3.1" # Provided by Spigot 1.8.8 websocket = "1.5.1" protocol = "3.0.0.Beta2-SNAPSHOT" -raknet = "1.6.28-20220125.214016-6" +raknet = "0.0.1.Final-SNAPSHOT" mcauthlib = "d9d773e" mcprotocollib = "1.19.2-SNAPSHOT" packetlib = "3.0" @@ -82,7 +82,7 @@ junit = { group = "junit", name = "junit", version.ref = "junit" } mcauthlib = { group = "com.github.GeyserMC", name = "MCAuthLib", version.ref = "mcauthlib" } mcprotocollib = { group = "com.github.steveice10", name = "mcprotocollib", version.ref = "mcprotocollib" } packetlib = { group = "com.github.steveice10", name = "packetlib", version.ref = "packetlib" } -raknet = { group = "com.nukkitx.network", name = "raknet", version.ref = "raknet" } +raknet = { group = "org.cloudburstmc.netty", name = "netty-transport-raknet", version.ref = "raknet" } sponge-api = { group = "org.spongepowered", name = "spongeapi", version.ref = "sponge" } terminalconsoleappender = { group = "net.minecrell", name = "terminalconsoleappender", version.ref = "terminalconsoleappender" } velocity-api = { group = "com.velocitypowered", name = "velocity-api", version.ref = "velocity" }