From 61b3ffd0deaa1d5183965aee774b8f14be1c6424 Mon Sep 17 00:00:00 2001 From: chris Date: Wed, 24 Jan 2024 22:28:03 +0100 Subject: [PATCH] Feature: Allow setting a different port in the motd (#4293) * Allow changing the broadcasted port using a system property. This may be needed if the port Geyser runs on & the port Bedrock players connect on do not match - e.g. due to port forwarding/different routing. * initial stab at making the broadcast port an (unsafe) config option * Automatically set broadcast port to be the bind port unless manually overridden * Warn about broadcast port mismatch * Use 0 instead of -1 as indicator to broadcast the port geyser is running on --- .../geyser/api/network/BedrockListener.java | 8 ++++ .../java/org/geysermc/geyser/GeyserImpl.java | 16 +++++++ .../defaults/ConnectionTestCommand.java | 46 ++++++++++++------- .../configuration/GeyserConfiguration.java | 2 + .../GeyserJacksonConfiguration.java | 9 ++++ .../geyser/network/netty/GeyserServer.java | 16 ++++++- core/src/main/resources/config.yml | 3 ++ 7 files changed, 81 insertions(+), 19 deletions(-) diff --git a/api/src/main/java/org/geysermc/geyser/api/network/BedrockListener.java b/api/src/main/java/org/geysermc/geyser/api/network/BedrockListener.java index 61fe286aa..af35d7ad1 100644 --- a/api/src/main/java/org/geysermc/geyser/api/network/BedrockListener.java +++ b/api/src/main/java/org/geysermc/geyser/api/network/BedrockListener.java @@ -50,6 +50,14 @@ public interface BedrockListener { */ int port(); + /** + * Gets the broadcast port that's sent to Bedrock clients with the motd. + * This is the port that Bedrock clients will connect with. It usually does not differ from the listening port. + * + * @return the broadcast port + */ + int broadcastPort(); + /** * Gets the primary MOTD shown to Bedrock players if a ping passthrough setting is not enabled. *

diff --git a/core/src/main/java/org/geysermc/geyser/GeyserImpl.java b/core/src/main/java/org/geysermc/geyser/GeyserImpl.java index 13212b161..e9ea08260 100644 --- a/core/src/main/java/org/geysermc/geyser/GeyserImpl.java +++ b/core/src/main/java/org/geysermc/geyser/GeyserImpl.java @@ -314,6 +314,22 @@ public class GeyserImpl implements GeyserApi { } } + String broadcastPort = System.getProperty("geyserBroadcastPort", ""); + if (!broadcastPort.isEmpty()) { + int parsedPort; + try { + parsedPort = Integer.parseInt(broadcastPort); + if (parsedPort < 1 || parsedPort > 65535) { + throw new NumberFormatException("The broadcast port must be between 1 and 65535 inclusive!"); + } + } catch (NumberFormatException e) { + logger.error(String.format("Invalid broadcast port: %s! Defaulting to configured port.", broadcastPort + " (" + e.getMessage() + ")")); + parsedPort = config.getBedrock().port(); + } + config.getBedrock().setBroadcastPort(parsedPort); + logger.info("Broadcast port set from system property: " + parsedPort); + } + boolean floodgatePresent = bootstrap.testFloodgatePluginPresent(); if (config.getRemote().authType() == AuthType.FLOODGATE && !floodgatePresent) { logger.severe(GeyserLocale.getLocaleStringLog("geyser.bootstrap.floodgate.not_installed") + " " diff --git a/core/src/main/java/org/geysermc/geyser/command/defaults/ConnectionTestCommand.java b/core/src/main/java/org/geysermc/geyser/command/defaults/ConnectionTestCommand.java index ad51826c3..981c97595 100644 --- a/core/src/main/java/org/geysermc/geyser/command/defaults/ConnectionTestCommand.java +++ b/core/src/main/java/org/geysermc/geyser/command/defaults/ConnectionTestCommand.java @@ -31,6 +31,7 @@ import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.api.util.PlatformType; import org.geysermc.geyser.command.GeyserCommand; import org.geysermc.geyser.command.GeyserCommandSource; +import org.geysermc.geyser.configuration.GeyserConfiguration; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.text.GeyserLocale; import org.geysermc.geyser.util.LoopbackUtil; @@ -84,7 +85,7 @@ public class ConnectionTestCommand extends GeyserCommand { return; } } else { - port = 19132; + port = geyser.getConfig().getBedrock().broadcastPort(); } String ip = fullAddress[0]; @@ -112,30 +113,41 @@ public class ConnectionTestCommand extends GeyserCommand { return; } - // Issue: do the ports not line up? - if (port != geyser.getConfig().getBedrock().port()) { - if (fullAddress.length == 2) { - sender.sendMessage("The port you are testing with (" + port + ") is not the same as you set in your Geyser configuration (" - + geyser.getConfig().getBedrock().port() + ")"); - sender.sendMessage("Re-run the command with the port in the config, or change the `bedrock` `port` in the config."); - if (geyser.getConfig().getBedrock().isCloneRemotePort()) { - sender.sendMessage("You have `clone-remote-port` enabled. This option ignores the `bedrock` `port` in the config, and uses the Java server port instead."); + GeyserConfiguration config = geyser.getConfig(); + + // Issue: do the ports not line up? We only check this if players don't override the broadcast port - if they do, they (hopefully) know what they're doing + if (config.getBedrock().broadcastPort() == config.getBedrock().port()) { + if (port != config.getBedrock().port()) { + if (fullAddress.length == 2) { + sender.sendMessage("The port you are testing with (" + port + ") is not the same as you set in your Geyser configuration (" + + config.getBedrock().port() + ")"); + sender.sendMessage("Re-run the command with the port in the config, or change the `bedrock` `port` in the config."); + if (config.getBedrock().isCloneRemotePort()) { + sender.sendMessage("You have `clone-remote-port` enabled. This option ignores the `bedrock` `port` in the config, and uses the Java server port instead."); + } + } else { + sender.sendMessage("You did not specify the port to check (add it with \":\"), " + + "and the default port 19132 does not match the port in your Geyser configuration (" + + config.getBedrock().port() + ")!"); + sender.sendMessage("Re-run the command with that port, or change the port in the config under `bedrock` `port`."); } - } else { - sender.sendMessage("You did not specify the port to check (add it with \":\"), " + - "and the default port 19132 does not match the port in your Geyser configuration (" - + geyser.getConfig().getBedrock().port() + ")!"); - sender.sendMessage("Re-run the command with that port, or change the port in the config under `bedrock` `port`."); + } + } else { + if (config.getBedrock().broadcastPort() != port) { + sender.sendMessage("The port you are testing with (" + port + ") is not the same as the broadcast port set in your Geyser configuration (" + + config.getBedrock().broadcastPort() + "). "); + sender.sendMessage("You ONLY need to change the broadcast port if clients connects with a port different from the port Geyser is running on."); + sender.sendMessage("Re-run the command with the port in the config, or change the `bedrock` `broadcast-port` in the config."); } } // Issue: is the `bedrock` `address` in the config different? - if (!geyser.getConfig().getBedrock().address().equals("0.0.0.0")) { + if (!config.getBedrock().address().equals("0.0.0.0")) { sender.sendMessage("The address specified in `bedrock` `address` is not \"0.0.0.0\" - this may cause issues unless this is deliberate and intentional."); } // Issue: did someone turn on enable-proxy-protocol, and they didn't mean it? - if (geyser.getConfig().getBedrock().isEnableProxyProtocol()) { + if (config.getBedrock().isEnableProxyProtocol()) { sender.sendMessage("You have the `enable-proxy-protocol` setting enabled. " + "Unless you're deliberately using additional software that REQUIRES this setting, you may not need it enabled."); } @@ -166,7 +178,7 @@ public class ConnectionTestCommand extends GeyserCommand { String connectionTestMotd = "Geyser Connection Test " + randomStr; CONNECTION_TEST_MOTD = connectionTestMotd; - sender.sendMessage("Testing server connection now. Please wait..."); + sender.sendMessage("Testing server connection to " + ip + " with port: " + port + " now. Please wait..."); JsonNode output; try { String hostname = URLEncoder.encode(ip, StandardCharsets.UTF_8); diff --git a/core/src/main/java/org/geysermc/geyser/configuration/GeyserConfiguration.java b/core/src/main/java/org/geysermc/geyser/configuration/GeyserConfiguration.java index f9bb15b32..d12ab79e9 100644 --- a/core/src/main/java/org/geysermc/geyser/configuration/GeyserConfiguration.java +++ b/core/src/main/java/org/geysermc/geyser/configuration/GeyserConfiguration.java @@ -122,6 +122,8 @@ public interface GeyserConfiguration { void setPort(int port); + void setBroadcastPort(int broadcastPort); + boolean isCloneRemotePort(); int getCompressionLevel(); diff --git a/core/src/main/java/org/geysermc/geyser/configuration/GeyserJacksonConfiguration.java b/core/src/main/java/org/geysermc/geyser/configuration/GeyserJacksonConfiguration.java index 0874daa07..b3b7e8cd4 100644 --- a/core/src/main/java/org/geysermc/geyser/configuration/GeyserJacksonConfiguration.java +++ b/core/src/main/java/org/geysermc/geyser/configuration/GeyserJacksonConfiguration.java @@ -172,6 +172,15 @@ public abstract class GeyserJacksonConfiguration implements GeyserConfiguration return port; } + @Setter + @JsonProperty("broadcast-port") + private int broadcastPort = 0; + + @Override + public int broadcastPort() { + return broadcastPort; + } + @Getter @JsonProperty("clone-remote-port") private boolean cloneRemotePort = false; 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 20340826f..401a7f2cf 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 @@ -102,6 +102,11 @@ public final class GeyserServer { private ChannelFuture bootstrapFuture; + /** + * The port to broadcast in the pong. This can be different from the port the server is bound to, e.g. due to port forwarding. + */ + private final int broadcastPort; + public GeyserServer(GeyserImpl geyser, int threadCount) { this.geyser = geyser; this.group = TRANSPORT.eventLoopGroupFactory().apply(threadCount); @@ -115,6 +120,13 @@ public final class GeyserServer { } else { this.proxiedAddresses = null; } + + // It's set to 0 only if no system property or manual config value was set + if (geyser.getConfig().getBedrock().broadcastPort() == 0) { + geyser.getConfig().getBedrock().setBroadcastPort(geyser.getConfig().getBedrock().port()); + } + + this.broadcastPort = geyser.getConfig().getBedrock().broadcastPort(); } public CompletableFuture bind(InetSocketAddress address) { @@ -243,8 +255,8 @@ public final class GeyserServer { .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()) - .ipv6Port(this.geyser.getConfig().getBedrock().port()) + .ipv4Port(this.broadcastPort) + .ipv6Port(this.broadcastPort) .serverId(bootstrapFuture.channel().config().getOption(RakChannelOption.RAK_GUID)); if (config.isPassthroughMotd() && pingInfo != null && pingInfo.getDescription() != null) { diff --git a/core/src/main/resources/config.yml b/core/src/main/resources/config.yml index b10c2788e..0617b316c 100644 --- a/core/src/main/resources/config.yml +++ b/core/src/main/resources/config.yml @@ -30,6 +30,9 @@ bedrock: # How much to compress network traffic to the Bedrock client. The higher the number, the more CPU usage used, but # the smaller the bandwidth used. Does not have any effect below -1 or above 9. Set to -1 to disable. compression-level: 6 + # The port to broadcast to Bedrock clients with the MOTD that they should use to connect to the server. + # DO NOT uncomment and change this unless Geyser runs on a different internal port than the one that is used to connect. + # broadcast-port: 19132 # Whether to enable PROXY protocol or not for clients. You DO NOT WANT this feature unless you run UDP reverse proxy # in front of your Geyser instance. enable-proxy-protocol: false