From ffed59b5a651e1ca15f6d8cb593200532d34e06b Mon Sep 17 00:00:00 2001 From: TTtie Date: Mon, 21 Jul 2025 21:58:08 +0000 Subject: [PATCH] feat: allow attaching extra ping data on modern This patch allows modern Minecraft servers to include additional match data in the ping response by splitting off extra ping data attachments into a ExtraPingDataRequestEvent that individual platforms can call with their own implementation that can be appropriately handled afterwards. On SP, this is handled as usual. On modern Minecraft, this is implemented using a ProtocolLib hook. Signed-off-by: TTtie --- .../pgm/listeners/ServerPingDataListener.java | 11 +++-- .../platform/modern/impl/ModernMiscUtil.java | 9 ---- .../modern/packets/PacketManipulations.java | 46 ++++++++++++++++++- .../sportpaper/SportPaperListener.java | 16 ++++++- .../platform/sportpaper/impl/SpMiscUtil.java | 8 ---- .../java/tc/oc/pgm/util/bukkit/MiscUtils.java | 7 --- .../util/event/ExtraPingDataRequestEvent.java | 25 ++++++++++ 7 files changed, 93 insertions(+), 29 deletions(-) create mode 100644 util/src/main/java/tc/oc/pgm/util/event/ExtraPingDataRequestEvent.java diff --git a/core/src/main/java/tc/oc/pgm/listeners/ServerPingDataListener.java b/core/src/main/java/tc/oc/pgm/listeners/ServerPingDataListener.java index 86e39cf529..3f101b22a8 100644 --- a/core/src/main/java/tc/oc/pgm/listeners/ServerPingDataListener.java +++ b/core/src/main/java/tc/oc/pgm/listeners/ServerPingDataListener.java @@ -1,7 +1,6 @@ package tc.oc.pgm.listeners; import static tc.oc.pgm.util.Assert.assertNotNull; -import static tc.oc.pgm.util.bukkit.MiscUtils.MISC_UTILS; import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; @@ -30,6 +29,7 @@ import tc.oc.pgm.api.match.event.MatchLoadEvent; import tc.oc.pgm.map.contrib.PlayerContributor; import tc.oc.pgm.util.ClassLogger; +import tc.oc.pgm.util.event.ExtraPingDataRequestEvent; public class ServerPingDataListener implements Listener { @@ -66,7 +66,7 @@ public void onMatchLoad(MatchLoadEvent event) { @EventHandler public void onServerListPing(ServerListPingEvent event) { - if (!ready.get() || legacySportPaper.get()) return; + if (!ready.get()) return; // Remove vanished players from player sample/ping count Iterator playerSample = event.iterator(); @@ -76,9 +76,14 @@ public void onServerListPing(ServerListPingEvent event) { playerSample.remove(); } } + } + + @EventHandler + public void onExtraDataRequest(ExtraPingDataRequestEvent event) { + if (legacySportPaper.get()) return; try { - JsonObject root = MISC_UTILS.getServerListExtra(event, PGM.get()); + JsonObject root = event.getServerListExtra(PGM.get()); this.matchManager.getMatches().forEachRemaining(match -> { String matchId = match.getId(); try { diff --git a/platform/platform-modern/src/main/java/tc/oc/pgm/platform/modern/impl/ModernMiscUtil.java b/platform/platform-modern/src/main/java/tc/oc/pgm/platform/modern/impl/ModernMiscUtil.java index 9779b697de..ad313fe8e8 100644 --- a/platform/platform-modern/src/main/java/tc/oc/pgm/platform/modern/impl/ModernMiscUtil.java +++ b/platform/platform-modern/src/main/java/tc/oc/pgm/platform/modern/impl/ModernMiscUtil.java @@ -2,7 +2,6 @@ import static tc.oc.pgm.util.platform.Supports.Variant.PAPER; -import com.google.gson.JsonObject; import java.nio.file.Path; import java.util.List; import net.kyori.adventure.key.Key; @@ -33,9 +32,7 @@ import org.bukkit.event.entity.EntityDamageEvent; import org.bukkit.event.entity.EntityExplodeEvent; import org.bukkit.event.entity.PlayerDeathEvent; -import org.bukkit.event.server.ServerListPingEvent; import org.bukkit.inventory.ItemStack; -import org.bukkit.plugin.Plugin; import org.bukkit.scoreboard.Team; import tc.oc.pgm.platform.modern.material.ModernBlockMaterialData; import tc.oc.pgm.util.DataVersions; @@ -45,12 +42,6 @@ @Supports(value = PAPER, minVersion = "1.21.5") public class ModernMiscUtil implements MiscUtils { - @Override - public JsonObject getServerListExtra(ServerListPingEvent event, Plugin plugin) { - // TODO: PLATFORM 1.20 no support for extra fields in server ping - return new JsonObject(); - } - @Override public EventException createEventException(Throwable cause, Event event) { return new EventException(cause); diff --git a/platform/platform-modern/src/main/java/tc/oc/pgm/platform/modern/packets/PacketManipulations.java b/platform/platform-modern/src/main/java/tc/oc/pgm/platform/modern/packets/PacketManipulations.java index b5f2cd2a21..491e4f6ee3 100644 --- a/platform/platform-modern/src/main/java/tc/oc/pgm/platform/modern/packets/PacketManipulations.java +++ b/platform/platform-modern/src/main/java/tc/oc/pgm/platform/modern/packets/PacketManipulations.java @@ -1,13 +1,22 @@ package tc.oc.pgm.platform.modern.packets; import com.comphenix.protocol.PacketType; +import com.comphenix.protocol.ProtocolLibrary; import com.comphenix.protocol.events.ListenerPriority; import com.comphenix.protocol.events.PacketEvent; +import com.comphenix.protocol.injector.netty.WirePacket; +import com.google.gson.JsonObject; +import com.mojang.serialization.Codec; +import com.mojang.serialization.Dynamic; +import com.mojang.serialization.JsonOps; +import io.netty.buffer.Unpooled; import java.util.ArrayList; import java.util.List; import java.util.Map; import net.minecraft.core.particles.ParticleOptions; +import net.minecraft.network.FriendlyByteBuf; import net.minecraft.network.protocol.game.ClientboundSetEntityDataPacket; +import net.minecraft.network.protocol.status.ServerStatus; import net.minecraft.network.syncher.EntityDataAccessor; import net.minecraft.network.syncher.SynchedEntityData; import net.minecraft.world.entity.Entity; @@ -16,6 +25,7 @@ import org.bukkit.plugin.Plugin; import tc.oc.pgm.platform.modern.listeners.PlayerTracker; import tc.oc.pgm.platform.modern.util.Packets; +import tc.oc.pgm.util.event.ExtraPingDataRequestEvent; import tc.oc.pgm.util.reflect.ReflectionUtils; @SuppressWarnings("unchecked") @@ -45,7 +55,8 @@ public PacketManipulations(Plugin plugin, PlayerTracker tracker) { Map.of( PacketType.Play.Server.ENTITY_STATUS, this::handleEntityStatus, PacketType.Play.Server.PLAYER_COMBAT_KILL, this::handleCombatKill, - PacketType.Play.Server.ENTITY_METADATA, this::handleEntityMetadata)); + PacketType.Play.Server.ENTITY_METADATA, this::handleEntityMetadata, + PacketType.Status.Server.SERVER_INFO, this::handleServerPing)); } private void handleEntityStatus(PacketEvent event) { @@ -127,4 +138,37 @@ private void handleEntityMetadata(PacketEvent event) { if (!checkHealth && !hideParticles) return; } } + + private void handleServerPing(PacketEvent event) { + JsonObject pingExtra = new JsonObject(); + new ExtraPingDataRequestEvent() { + @Override + public JsonObject getServerListExtra(Plugin plugin) { + return (JsonObject) + pingExtra.asMap().computeIfAbsent(plugin.namespace(), k -> new JsonObject()); + } + }.callEvent(); + + if (!pingExtra.isEmpty()) { + // Encode the response manually, otherwise the extra data will get lost + var serverPing = (ServerStatus) event.getPacket().getServerPings().read(0).getHandle(); + var jsonData = ServerStatus.CODEC + .encodeStart(JsonOps.INSTANCE, serverPing) + .getOrThrow() + .getAsJsonObject(); + + jsonData.add("bukkit_extra", pingExtra); + + var byteBuf = new FriendlyByteBuf(Unpooled.buffer()); + byteBuf.writeJsonWithCodec(Codec.PASSTHROUGH, new Dynamic<>(JsonOps.INSTANCE, jsonData)); + + ProtocolLibrary.getProtocolManager() + .sendWirePacket( + event.getPlayer(), + new WirePacket(PacketType.Status.Server.SERVER_INFO, byteBuf.array())); + + byteBuf.release(); + event.setCancelled(true); + } + } } diff --git a/platform/platform-sportpaper/src/main/java/tc/oc/pgm/platform/sportpaper/SportPaperListener.java b/platform/platform-sportpaper/src/main/java/tc/oc/pgm/platform/sportpaper/SportPaperListener.java index 2c520ccc1f..3b28676428 100644 --- a/platform/platform-sportpaper/src/main/java/tc/oc/pgm/platform/sportpaper/SportPaperListener.java +++ b/platform/platform-sportpaper/src/main/java/tc/oc/pgm/platform/sportpaper/SportPaperListener.java @@ -2,8 +2,11 @@ import static tc.oc.pgm.util.event.EventUtil.handleCall; +import com.google.gson.JsonObject; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; +import org.bukkit.plugin.Plugin; +import tc.oc.pgm.util.event.ExtraPingDataRequestEvent; import tc.oc.pgm.util.event.block.BlockDispenseEntityEvent; import tc.oc.pgm.util.event.block.BlockFallEvent; import tc.oc.pgm.util.event.entity.EntityDespawnInVoidEvent; @@ -19,7 +22,6 @@ import tc.oc.pgm.util.event.player.PlayerSpawnEntityEvent; public class SportPaperListener implements Listener { - @EventHandler(ignoreCancelled = true) public void onBlockFall(org.bukkit.event.block.BlockFallEvent sportEvent) { BlockFallEvent pgmEvent = new BlockFallEvent(sportEvent.getBlock(), sportEvent.getEntity()); @@ -108,4 +110,16 @@ public void onEntityDespawn(org.bukkit.event.entity.EntityDespawnInVoidEvent spo EntityDespawnInVoidEvent pgmEvent = new EntityDespawnInVoidEvent(sportEvent.getEntity()); handleCall(pgmEvent, sportEvent); } + + @EventHandler + public void onServerPing(final org.bukkit.event.server.ServerListPingEvent event) { + handleCall( + new ExtraPingDataRequestEvent() { + @Override + public JsonObject getServerListExtra(Plugin plugin) { + return event.getOrCreateExtra(plugin); + } + }, + event); + } } diff --git a/platform/platform-sportpaper/src/main/java/tc/oc/pgm/platform/sportpaper/impl/SpMiscUtil.java b/platform/platform-sportpaper/src/main/java/tc/oc/pgm/platform/sportpaper/impl/SpMiscUtil.java index 5caee5d6f8..f352633ff1 100644 --- a/platform/platform-sportpaper/src/main/java/tc/oc/pgm/platform/sportpaper/impl/SpMiscUtil.java +++ b/platform/platform-sportpaper/src/main/java/tc/oc/pgm/platform/sportpaper/impl/SpMiscUtil.java @@ -4,7 +4,6 @@ import static tc.oc.pgm.util.platform.Supports.Priority.HIGH; import static tc.oc.pgm.util.platform.Supports.Variant.SPORTPAPER; -import com.google.gson.JsonObject; import java.nio.file.Files; import java.nio.file.Path; import java.util.List; @@ -30,9 +29,7 @@ import org.bukkit.event.entity.EntityDamageEvent; import org.bukkit.event.entity.EntityExplodeEvent; import org.bukkit.event.entity.PlayerDeathEvent; -import org.bukkit.event.server.ServerListPingEvent; import org.bukkit.inventory.ItemStack; -import org.bukkit.plugin.Plugin; import tc.oc.pgm.platform.sportpaper.material.LegacyMaterialData; import tc.oc.pgm.util.DataVersions; import tc.oc.pgm.util.bukkit.MiscUtils; @@ -49,11 +46,6 @@ public boolean yield(Event event) { return true; } - @Override - public JsonObject getServerListExtra(ServerListPingEvent event, Plugin plugin) { - return event.getOrCreateExtra(plugin); - } - @Override public EventException createEventException(Throwable cause, Event event) { return new EventException(cause, event); diff --git a/util/src/main/java/tc/oc/pgm/util/bukkit/MiscUtils.java b/util/src/main/java/tc/oc/pgm/util/bukkit/MiscUtils.java index a201c77ebb..93eaf4587a 100644 --- a/util/src/main/java/tc/oc/pgm/util/bukkit/MiscUtils.java +++ b/util/src/main/java/tc/oc/pgm/util/bukkit/MiscUtils.java @@ -1,6 +1,5 @@ package tc.oc.pgm.util.bukkit; -import com.google.gson.JsonObject; import java.nio.file.Path; import java.util.List; import net.kyori.adventure.key.Key; @@ -19,9 +18,7 @@ import org.bukkit.event.entity.EntityDamageEvent; import org.bukkit.event.entity.EntityExplodeEvent; import org.bukkit.event.entity.PlayerDeathEvent; -import org.bukkit.event.server.ServerListPingEvent; import org.bukkit.inventory.ItemStack; -import org.bukkit.plugin.Plugin; import org.bukkit.scoreboard.Team; import tc.oc.pgm.util.material.BlockMaterialData; import tc.oc.pgm.util.platform.Platform; @@ -33,10 +30,6 @@ default boolean yield(Event event) { return false; } - default JsonObject getServerListExtra(ServerListPingEvent event, Plugin plugin) { - return new JsonObject(); - } - default EventException createEventException(Throwable cause, Event event) { return new EventException(cause); } diff --git a/util/src/main/java/tc/oc/pgm/util/event/ExtraPingDataRequestEvent.java b/util/src/main/java/tc/oc/pgm/util/event/ExtraPingDataRequestEvent.java new file mode 100644 index 0000000000..5a28561ada --- /dev/null +++ b/util/src/main/java/tc/oc/pgm/util/event/ExtraPingDataRequestEvent.java @@ -0,0 +1,25 @@ +package tc.oc.pgm.util.event; + +import com.google.gson.JsonObject; +import org.bukkit.event.HandlerList; +import org.bukkit.event.server.ServerEvent; +import org.bukkit.plugin.Plugin; + +/** + * Called by individual platform implementations to allow listeners to attach additional server ping + * data + */ +public abstract class ExtraPingDataRequestEvent extends ServerEvent { + public abstract JsonObject getServerListExtra(Plugin plugin); + + private static final HandlerList handlers = new HandlerList(); + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +}