From 9b264e6374229843f22a49533c70f93ef70b412f Mon Sep 17 00:00:00 2001 From: 7man7LMYT <67489949+7man7LMYT@users.noreply.github.com> Date: Tue, 9 May 2023 20:50:38 -0700 Subject: [PATCH] Add GeyserBedrockPingEvent (#3715) --- .../connection/GeyserBedrockPingEvent.java | 110 ++++++++++++++++++ .../type/GeyserBedrockPingEventImpl.java | 93 +++++++++++++++ .../geyser/network/netty/GeyserServer.java | 20 ++-- 3 files changed, 215 insertions(+), 8 deletions(-) create mode 100644 api/src/main/java/org/geysermc/geyser/api/event/connection/GeyserBedrockPingEvent.java create mode 100644 core/src/main/java/org/geysermc/geyser/event/type/GeyserBedrockPingEventImpl.java diff --git a/api/src/main/java/org/geysermc/geyser/api/event/connection/GeyserBedrockPingEvent.java b/api/src/main/java/org/geysermc/geyser/api/event/connection/GeyserBedrockPingEvent.java new file mode 100644 index 000000000..67a81ac58 --- /dev/null +++ b/api/src/main/java/org/geysermc/geyser/api/event/connection/GeyserBedrockPingEvent.java @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2019-2023 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.api.event.connection; + +import org.checkerframework.checker.index.qual.NonNegative; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.geysermc.event.Event; + +import java.net.InetSocketAddress; + +/** + * Called whenever Geyser gets pinged + * + * This event allows you to modify/obtain the MOTD, maximum player count, and current number of players online, + * Geyser will reply to the client with what was given. + */ +public interface GeyserBedrockPingEvent extends Event { + + /** + * Sets the given string as the primary motd, the given string cannot be null. + * + * @param primary the string to set as the primary motd + */ + void primaryMotd(@NonNull String primary); + + /** + * Sets the given string as the secondary motd, the given string cannot be null. + * Note: the secondary motd is only used for the LAN game entry. + * + * @param secondary the string to set as the secondary motd + */ + void secondaryMotd(@NonNull String secondary); + + /** + * Sets how many players are currently online, the given number cannot be below 0. + * + * @param count the number to set + */ + void playerCount(int count); + + /** + * Sets the maximum number of players that can join this server, the given number cannot be below 1. + * + * @param max the number to set + */ + void maxPlayerCount(int max); + + /** + * Gets the primary motd. + * + * @return the primary motd string + */ + @Nullable + String primaryMotd(); + + /** + * Gets the secondary motd. + * + * @return the secondary motd string + */ + @Nullable + String secondaryMotd(); + + /** + * Gets the current number of players. + * + * @return number of players online + */ + @NonNegative + int playerCount(); + + /** + * Gets the maximum number of players that can join this server + * + * @return maximum number of players that can join + */ + int maxPlayerCount(); + + /** + * Gets the {@link InetSocketAddress} of the client pinging us. + * + * @return a {@link InetSocketAddress} + */ + @NonNull + InetSocketAddress address(); +} diff --git a/core/src/main/java/org/geysermc/geyser/event/type/GeyserBedrockPingEventImpl.java b/core/src/main/java/org/geysermc/geyser/event/type/GeyserBedrockPingEventImpl.java new file mode 100644 index 000000000..579b4c68c --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/event/type/GeyserBedrockPingEventImpl.java @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2019-2023 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.event.type; + +import org.checkerframework.checker.index.qual.NonNegative; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.cloudburstmc.protocol.bedrock.BedrockPong; +import org.geysermc.geyser.api.event.connection.GeyserBedrockPingEvent; +import org.jetbrains.annotations.NotNull; + +import javax.annotation.Nonnull; +import java.net.InetSocketAddress; +import java.util.Objects; + +public class GeyserBedrockPingEventImpl implements GeyserBedrockPingEvent { + private final InetSocketAddress address; + private final BedrockPong pong; + + public GeyserBedrockPingEventImpl(BedrockPong pong, InetSocketAddress address) { + this.address = address; + this.pong = pong; + } + + @Override + public void primaryMotd(@Nonnull String primary) { + pong.motd(Objects.requireNonNull(primary, "Primary MOTD cannot be null")); + } + + @Override + public void secondaryMotd(@Nonnull String secondary) { + pong.subMotd(Objects.requireNonNull(secondary, "Secondary MOTD cannot be null")); + } + + @Override + public void playerCount(int count) { + if (count < 0) throw new IllegalArgumentException("Player count cannot be below 0"); + pong.playerCount(count); + } + + @Override + public void maxPlayerCount(int max) { + if (max < 1) throw new IllegalArgumentException("Max player count cannot be below 1"); + pong.maximumPlayerCount(max); + } + + @Override + public @Nullable String primaryMotd() { + return pong.motd(); + } + + @Override + public @Nullable String secondaryMotd() { + return pong.subMotd(); + } + + @Override + public @NonNegative int playerCount() { + return pong.playerCount(); + } + + @Override + public int maxPlayerCount() { + return pong.maximumPlayerCount(); + } + + @Override + public @NotNull InetSocketAddress address() { + return address; + } +} 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 097ea0056..31a355011 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 @@ -48,6 +48,7 @@ import org.cloudburstmc.netty.handler.codec.raknet.server.RakServerOfflineHandle import org.cloudburstmc.protocol.bedrock.BedrockPong; import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.configuration.GeyserConfiguration; +import org.geysermc.geyser.event.type.GeyserBedrockPingEventImpl; import org.geysermc.geyser.network.CIDRMatcher; import org.geysermc.geyser.network.GameProtocol; import org.geysermc.geyser.network.GeyserServerInitializer; @@ -220,6 +221,17 @@ public final class GeyserServer { pong.subMotd(config.getBedrock().secondaryMotd()); } + // Placed here to prevent overriding values set in the ping event. + 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()); + } + + this.geyser.eventBus().fire(new GeyserBedrockPingEventImpl(pong, inetSocketAddress)); + // https://github.com/GeyserMC/Geyser/issues/3388 pong.motd(pong.motd().replace(';', ':')); pong.subMotd(pong.subMotd().replace(';', ':')); @@ -251,14 +263,6 @@ public final class GeyserServer { } } - 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()); - } - //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()) {