diff --git a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserPaperPingPassthrough.java b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserPaperPingPassthrough.java index 15bc693fb..15bd6bde1 100644 --- a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserPaperPingPassthrough.java +++ b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserPaperPingPassthrough.java @@ -35,6 +35,7 @@ import org.geysermc.geyser.ping.IGeyserPingPassthrough; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.lang.reflect.Constructor; import java.net.InetSocketAddress; /** @@ -42,6 +43,8 @@ import java.net.InetSocketAddress; * applied. */ public final class GeyserPaperPingPassthrough implements IGeyserPingPassthrough { + private static final Constructor OLD_CONSTRUCTOR = ReflectedNames.getOldPaperPingConstructor(); + private final GeyserSpigotLogger logger; public GeyserPaperPingPassthrough(GeyserSpigotLogger logger) { @@ -54,9 +57,17 @@ public final class GeyserPaperPingPassthrough implements IGeyserPingPassthrough try { // We'd rather *not* use deprecations here, but unfortunately any Adventure class would be relocated at // runtime because we still have to shade in our own Adventure class. For now. - PaperServerListPingEvent event = new PaperServerListPingEvent(new GeyserStatusClient(inetSocketAddress), - Bukkit.getMotd(), Bukkit.shouldSendChatPreviews(), Bukkit.getOnlinePlayers().size(), - Bukkit.getMaxPlayers(), Bukkit.getVersion(), MinecraftProtocol.getJavaProtocolVersion(), null); + PaperServerListPingEvent event; + if (OLD_CONSTRUCTOR != null) { + // Approximately pre-1.19 + event = OLD_CONSTRUCTOR.newInstance(new GeyserStatusClient(inetSocketAddress), + Bukkit.getMotd(), Bukkit.getOnlinePlayers().size(), + Bukkit.getMaxPlayers(), Bukkit.getVersion(), MinecraftProtocol.getJavaProtocolVersion(), null); + } else { + event = new PaperServerListPingEvent(new GeyserStatusClient(inetSocketAddress), + Bukkit.getMotd(), Bukkit.shouldSendChatPreviews(), Bukkit.getOnlinePlayers().size(), + Bukkit.getMaxPlayers(), Bukkit.getVersion(), MinecraftProtocol.getJavaProtocolVersion(), null); + } Bukkit.getPluginManager().callEvent(event); if (event.isCancelled()) { // We have to send a ping, so not really sure what else to do here. diff --git a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotPlugin.java b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotPlugin.java index cee38228c..2ebce7acd 100644 --- a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotPlugin.java +++ b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/GeyserSpigotPlugin.java @@ -167,14 +167,16 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap { if (geyserConfig.isLegacyPingPassthrough()) { this.geyserSpigotPingPassthrough = GeyserLegacyPingPassthrough.init(geyser); } else { - try { - Class.forName("com.destroystokyo.paper.event.server.PaperServerListPingEvent"); + if (ReflectedNames.checkPaperPingEvent()) { this.geyserSpigotPingPassthrough = new GeyserPaperPingPassthrough(geyserLogger); - } catch (ClassNotFoundException e) { + } else if (ReflectedNames.newSpigotPingConstructorExists()) { this.geyserSpigotPingPassthrough = new GeyserSpigotPingPassthrough(geyserLogger); + } else { + // Can't enable one of the other options + this.geyserSpigotPingPassthrough = GeyserLegacyPingPassthrough.init(geyser); } } - geyserLogger.debug("Spigot ping passthrough type: " + (this.geyserSpigotPingPassthrough == null ? null : this.geyserSpigotPingPassthrough.getClass())); + geyserLogger.info("Spigot ping passthrough type: " + (this.geyserSpigotPingPassthrough == null ? null : this.geyserSpigotPingPassthrough.getClass())); this.geyserCommandManager = new GeyserSpigotCommandManager(geyser); diff --git a/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/ReflectedNames.java b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/ReflectedNames.java new file mode 100644 index 000000000..3185f2d30 --- /dev/null +++ b/bootstrap/spigot/src/main/java/org/geysermc/geyser/platform/spigot/ReflectedNames.java @@ -0,0 +1,90 @@ +/* + * 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.platform.spigot; + +import com.destroystokyo.paper.event.server.PaperServerListPingEvent; +import com.destroystokyo.paper.network.StatusClient; +import org.bukkit.event.server.ServerListPingEvent; +import org.bukkit.util.CachedServerIcon; + +import javax.annotation.Nullable; +import java.lang.reflect.Constructor; +import java.net.InetAddress; + +/** + * A utility class for checking on the existence of classes, constructors, fields, methods + */ +public final class ReflectedNames { + + static boolean checkPaperPingEvent() { + return classExists("com.destroystokyo.paper.event.server.PaperServerListPingEvent"); + } + + /** + * @return if this class name exists + */ + private static boolean classExists(String clazz) { + try { + Class.forName(clazz); + return true; + } catch (ClassNotFoundException e) { + return false; + } + } + + static boolean newSpigotPingConstructorExists() { + return getConstructor(ServerListPingEvent.class, InetAddress.class, String.class, boolean.class, int.class, int.class) != null; + } + + static Constructor getOldPaperPingConstructor() { + if (getConstructor(PaperServerListPingEvent.class, StatusClient.class, String.class, boolean.class, int.class, + int.class, String.class, int.class, CachedServerIcon.class) != null) { + // @NotNull StatusClient client, @NotNull String motd, boolean shouldSendChatPreviews, int numPlayers, int maxPlayers, + // @NotNull String version, int protocolVersion, @Nullable CachedServerIcon favicon + // New constructor is present + return null; + } + // @NotNull StatusClient client, @NotNull String motd, int numPlayers, int maxPlayers, + // @NotNull String version, int protocolVersion, @Nullable CachedServerIcon favicon + return getConstructor(PaperServerListPingEvent.class, StatusClient.class, String.class, int.class, int.class, + String.class, int.class, CachedServerIcon.class); + } + + /** + * @return if this class has a constructor with the specified arguments + */ + @Nullable + private static Constructor getConstructor(Class clazz, Class... args) { + try { + return clazz.getConstructor(args); + } catch (NoSuchMethodException e) { + return null; + } + } + + private ReflectedNames() { + } +}