From 024e00586f1146a96e95c7841b0b037144fea63e Mon Sep 17 00:00:00 2001 From: BT <43831917+calcastor@users.noreply.github.com> Date: Mon, 13 Apr 2026 11:37:50 -0700 Subject: [PATCH 1/5] Migrate to PacketEvents Signed-off-by: BT <43831917+calcastor@users.noreply.github.com> --- .../buildlogic.java-conventions.gradle.kts | 10 +-- .../java/dev/pgm/community/Community.java | 2 +- .../platform/modern/ModernPlatform.java | 5 +- .../platform/modern/ModernPlayerUtils.java | 2 +- .../platform/modern/PacketManipulations.java | 87 ++++++++----------- .../platform/sportpaper/SpPlatform.java | 3 +- .../java/dev/pgm/community/util/Platform.java | 9 +- 7 files changed, 48 insertions(+), 70 deletions(-) diff --git a/buildSrc/src/main/kotlin/buildlogic.java-conventions.gradle.kts b/buildSrc/src/main/kotlin/buildlogic.java-conventions.gradle.kts index bcea9954..bf8c1e43 100644 --- a/buildSrc/src/main/kotlin/buildlogic.java-conventions.gradle.kts +++ b/buildSrc/src/main/kotlin/buildlogic.java-conventions.gradle.kts @@ -12,10 +12,10 @@ java { repositories { mavenCentral() - maven("https://repo.pgm.fyi/snapshots") // Sportpaper & other pgm-specific stuff - maven("https://repo.papermc.io/repository/maven-public/") // PaperMC repo - maven("https://hub.spigotmc.org/nexus/content/repositories/snapshots/") // Spigot repo - maven("https://repo.aikar.co/content/groups/aikar/") // aikar repo + maven("https://repo.pgm.fyi/snapshots") // SportPaper & other PGM-specific stuff + maven("https://repo.papermc.io/repository/maven-public/") // Paper builds & paperweight plugin + maven("https://repo.aikar.co/content/groups/aikar/") // Aikar repo + maven("https://repo.codemc.io/repository/maven-releases/") // PacketEvents mavenLocal() // Local last } @@ -37,7 +37,7 @@ dependencies { compileOnly("tc.oc.pgm:util:0.16-SNAPSHOT") compileOnly("tc.oc.occ:Environment:1.0.0-SNAPSHOT") compileOnly("org.incendo:cloud-annotations:2.0.0") - compileOnly("net.dmulloy2:ProtocolLib:5.4.0") + compileOnly("com.github.retrooper:packetevents-spigot:2.12.0") // Paper and SportPaper include these (or equivalents) compileOnly("it.unimi.dsi:fastutil:8.5.15") diff --git a/core/src/main/java/dev/pgm/community/Community.java b/core/src/main/java/dev/pgm/community/Community.java index 61d056ba..a804d9db 100644 --- a/core/src/main/java/dev/pgm/community/Community.java +++ b/core/src/main/java/dev/pgm/community/Community.java @@ -97,7 +97,7 @@ private void setupTranslations() { } public void registerListener(Listener listener) { - Platform.MANIFEST.onEnable(this); + Platform.MANIFEST.onEnable(); getServer().getPluginManager().registerEvents(listener, this); } diff --git a/platform/platform-modern/src/main/java/dev/pgm/community/platform/modern/ModernPlatform.java b/platform/platform-modern/src/main/java/dev/pgm/community/platform/modern/ModernPlatform.java index 99572f99..ea80b27d 100644 --- a/platform/platform-modern/src/main/java/dev/pgm/community/platform/modern/ModernPlatform.java +++ b/platform/platform-modern/src/main/java/dev/pgm/community/platform/modern/ModernPlatform.java @@ -5,12 +5,11 @@ import dev.pgm.community.util.Platform; import dev.pgm.community.util.Supports; -import org.bukkit.plugin.Plugin; @Supports(value = PAPER, minVersion = "1.21.11", priority = HIGHEST) public class ModernPlatform implements Platform.Manifest { @Override - public void onEnable(Plugin plugin) { - new PacketManipulations(plugin); + public void onEnable() { + new PacketManipulations(); } } diff --git a/platform/platform-modern/src/main/java/dev/pgm/community/platform/modern/ModernPlayerUtils.java b/platform/platform-modern/src/main/java/dev/pgm/community/platform/modern/ModernPlayerUtils.java index 483014bc..e86efaa6 100644 --- a/platform/platform-modern/src/main/java/dev/pgm/community/platform/modern/ModernPlayerUtils.java +++ b/platform/platform-modern/src/main/java/dev/pgm/community/platform/modern/ModernPlayerUtils.java @@ -77,7 +77,7 @@ public Skin getPlayerSkin(Player player, Player viewer) { if (playerSkins.containsKey(player.getUniqueId())) { Map uuidSkinMap = playerSkins.get(player.getUniqueId()); Skin skin = uuidSkinMap.get(viewer.getUniqueId()); - if (skin != null) return skin; + if (skin != null && !skin.isEmpty()) return skin; } return getPlayerSkin(player); diff --git a/platform/platform-modern/src/main/java/dev/pgm/community/platform/modern/PacketManipulations.java b/platform/platform-modern/src/main/java/dev/pgm/community/platform/modern/PacketManipulations.java index ca26b38f..af3b4534 100644 --- a/platform/platform-modern/src/main/java/dev/pgm/community/platform/modern/PacketManipulations.java +++ b/platform/platform-modern/src/main/java/dev/pgm/community/platform/modern/PacketManipulations.java @@ -2,53 +2,38 @@ import static dev.pgm.community.util.PlayerUtils.PLAYER_UTILS; -import com.comphenix.protocol.PacketType; -import com.comphenix.protocol.events.ListenerPriority; -import com.comphenix.protocol.events.PacketEvent; -import com.comphenix.protocol.wrappers.EnumWrappers; -import com.comphenix.protocol.wrappers.PlayerInfoData; -import com.comphenix.protocol.wrappers.WrappedChatComponent; -import com.comphenix.protocol.wrappers.WrappedGameProfile; -import com.comphenix.protocol.wrappers.WrappedSignedProperty; +import com.github.retrooper.packetevents.event.PacketListenerPriority; +import com.github.retrooper.packetevents.event.PacketSendEvent; +import com.github.retrooper.packetevents.protocol.packettype.PacketType; +import com.github.retrooper.packetevents.protocol.player.TextureProperty; +import com.github.retrooper.packetevents.protocol.player.UserProfile; +import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerPlayerInfoUpdate; import java.util.List; import java.util.Map; -import java.util.Set; -import java.util.UUID; +import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; import org.apache.commons.lang3.StringUtils; import org.bukkit.Bukkit; import org.bukkit.entity.Player; -import org.bukkit.plugin.Plugin; -import tc.oc.pgm.platform.modern.packets.PacketSender; +import org.jspecify.annotations.NonNull; import tc.oc.pgm.platform.modern.util.Packets; import tc.oc.pgm.util.skin.Skin; -public class PacketManipulations implements PacketSender { +public class PacketManipulations { - public PacketManipulations(Plugin plugin) { - Packets.register( - plugin, - ListenerPriority.LOWEST, - Map.of(PacketType.Play.Server.PLAYER_INFO, this::handlePlayerInfo)); + public PacketManipulations() { + Packets.registerSend( + PacketListenerPriority.LOWEST, + Map.of(PacketType.Play.Server.PLAYER_INFO_UPDATE, this::handlePlayerInfo)); } - private void handlePlayerInfo(PacketEvent event) { + private void handlePlayerInfo(@NonNull PacketSendEvent event) { Player viewer = event.getPlayer(); + WrapperPlayServerPlayerInfoUpdate wrapper = new WrapperPlayServerPlayerInfoUpdate(event); - Set actions = - event.getPacket().getPlayerInfoActions().read(0); - boolean hasAddPlayer = actions.contains(EnumWrappers.PlayerInfoAction.ADD_PLAYER); - boolean hasUpdateDisplayName = - actions.contains(EnumWrappers.PlayerInfoAction.UPDATE_DISPLAY_NAME); - - if (!hasAddPlayer && !hasUpdateDisplayName) return; - - List infoList = event.getPacket().getPlayerInfoDataLists().read(0); - for (int i = 0; i < infoList.size(); i++) { - PlayerInfoData playerInfoData = infoList.get(i); - if (playerInfoData == null) continue; - - UUID playerId = playerInfoData.getProfileId(); - Player player = Bukkit.getPlayer(playerId); + boolean modified = false; + List entries = wrapper.getEntries(); + for (WrapperPlayServerPlayerInfoUpdate.PlayerInfo entry : entries) { + Player player = Bukkit.getPlayer(entry.getProfileId()); if (player == null || player.equals(viewer) || !player.isOnline()) continue; String playerDisplayName = PLAYER_UTILS.getPlayerDisplayName(player, viewer); @@ -56,26 +41,22 @@ private void handlePlayerInfo(PacketEvent event) { if (StringUtils.isBlank(playerName) || StringUtils.isBlank(playerDisplayName)) continue; - WrappedGameProfile wrappedGameProfile = playerInfoData.getProfile().withName(playerName); - if (hasAddPlayer) { - Skin playerSkin = PLAYER_UTILS.getPlayerSkin(player, viewer); - wrappedGameProfile - .getProperties() - .put( - "textures", - new WrappedSignedProperty( - "textures", playerSkin.getData(), playerSkin.getSignature())); - } + UserProfile profile = entry.getGameProfile(); + profile.setName(playerName); - infoList.set( - i, - new PlayerInfoData( - playerId, - playerInfoData.getLatency(), - playerInfoData.isListed(), - playerInfoData.getGameMode(), - wrappedGameProfile, - WrappedChatComponent.fromLegacyText(playerDisplayName))); + Skin skin = PLAYER_UTILS.getPlayerSkin(player, viewer); + profile.getTextureProperties().clear(); + profile + .getTextureProperties() + .add(new TextureProperty("textures", skin.getData(), skin.getSignature())); + + entry.setGameProfile(profile); + entry.setDisplayName( + LegacyComponentSerializer.legacySection().deserialize(playerDisplayName)); + + modified = true; } + + if (modified) event.markForReEncode(true); } } diff --git a/platform/platform-sportpaper/src/main/java/dev/pgm/community/platform/sportpaper/SpPlatform.java b/platform/platform-sportpaper/src/main/java/dev/pgm/community/platform/sportpaper/SpPlatform.java index ffe9a911..9849bc26 100644 --- a/platform/platform-sportpaper/src/main/java/dev/pgm/community/platform/sportpaper/SpPlatform.java +++ b/platform/platform-sportpaper/src/main/java/dev/pgm/community/platform/sportpaper/SpPlatform.java @@ -5,10 +5,9 @@ import dev.pgm.community.util.Platform; import dev.pgm.community.util.Supports; -import org.bukkit.plugin.Plugin; @Supports(value = SPORTPAPER, priority = HIGHEST) public class SpPlatform implements Platform.Manifest { @Override - public void onEnable(Plugin plugin) {} + public void onEnable() {} } diff --git a/util/src/main/java/dev/pgm/community/util/Platform.java b/util/src/main/java/dev/pgm/community/util/Platform.java index b922044f..0e47fe6d 100644 --- a/util/src/main/java/dev/pgm/community/util/Platform.java +++ b/util/src/main/java/dev/pgm/community/util/Platform.java @@ -5,8 +5,7 @@ import dev.pgm.community.util.Supports.Variant; import java.util.Arrays; import org.bukkit.Bukkit; -import org.bukkit.plugin.Plugin; -import org.jetbrains.annotations.NotNull; +import org.jspecify.annotations.NonNull; import org.reflections.Reflections; import org.reflections.util.ClasspathHelper; import org.reflections.util.ConfigurationBuilder; @@ -33,7 +32,7 @@ public abstract class Platform { .orElse(null); } - public static final @NotNull Manifest MANIFEST = get(Manifest.class); + public static final @NonNull Manifest MANIFEST = get(Manifest.class); /** * Do a minimum sanity-check of the platform's viability and early-load some codepaths @@ -44,7 +43,7 @@ public static void init() throws Throwable { Effects.EFFECTS.dummy(); } - public static @NotNull T get(Class clazz) { + public static @NonNull T get(Class clazz) { return (T) Platform.getBestSupported(clazz); } @@ -80,6 +79,6 @@ private static Object getBestSupported(Class parent) { } public interface Manifest { - void onEnable(Plugin plugin); + void onEnable(); } } From 8bacdde106f5a6bc704fc9517e6821d2477d973e Mon Sep 17 00:00:00 2001 From: BT <43831917+calcastor@users.noreply.github.com> Date: Tue, 14 Apr 2026 23:00:09 -0700 Subject: [PATCH 2/5] Implement getPlayerSkin in NickIntegration Signed-off-by: BT <43831917+calcastor@users.noreply.github.com> --- .../pgm/community/nick/feature/PGMNickIntegration.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/core/src/main/java/dev/pgm/community/nick/feature/PGMNickIntegration.java b/core/src/main/java/dev/pgm/community/nick/feature/PGMNickIntegration.java index 827353d7..4de68ba3 100644 --- a/core/src/main/java/dev/pgm/community/nick/feature/PGMNickIntegration.java +++ b/core/src/main/java/dev/pgm/community/nick/feature/PGMNickIntegration.java @@ -1,5 +1,6 @@ package dev.pgm.community.nick.feature; +import static dev.pgm.community.util.PlayerUtils.PLAYER_UTILS; import static net.kyori.adventure.text.Component.text; import dev.pgm.community.utils.PGMUtils; @@ -10,11 +11,13 @@ import net.kyori.adventure.text.format.NamedTextColor; import net.kyori.adventure.text.format.TextDecoration; import org.bukkit.entity.Player; +import org.jspecify.annotations.NonNull; import tc.oc.pgm.api.PGM; import tc.oc.pgm.api.integration.Integration; import tc.oc.pgm.api.integration.NickIntegration; import tc.oc.pgm.api.match.Match; import tc.oc.pgm.api.player.MatchPlayer; +import tc.oc.pgm.util.skin.Skin; public class PGMNickIntegration implements NickIntegration { @@ -36,6 +39,11 @@ public String getNick(Player player) { return nick.getOnlineNick(player.getUniqueId()); } + @Override + public Skin getPlayerSkin(@NonNull Player player, Player viewer) { + return PLAYER_UTILS.getPlayerSkin(player, viewer); + } + public void cancelTask() { hotbarTask.cancel(true); } From 035dd611a41465786122fc2adf3ab5000e2c06e5 Mon Sep 17 00:00:00 2001 From: BT <43831917+calcastor@users.noreply.github.com> Date: Mon, 13 Apr 2026 22:10:11 -0700 Subject: [PATCH 3/5] Fix nickname support Signed-off-by: BT <43831917+calcastor@users.noreply.github.com> --- .../java/dev/pgm/community/Community.java | 10 +- .../pgm/community/nick/PlayerIdentity.java | 214 ++++++++++++++++++ .../nick/feature/PGMNickIntegration.java | 4 +- .../nick/feature/types/NickFeatureCore.java | 16 +- .../pgm/community/nick/skin/SkinCache.java | 14 +- .../pgm/community/nick/skin/SkinManager.java | 20 ++ core/src/main/resources/plugin.yml | 2 +- .../platform/modern/ModernPlatform.java | 22 +- .../platform/modern/ModernPlayerUtils.java | 73 +++--- .../platform/modern/PacketManipulations.java | 145 ++++++++++-- .../platform/sportpaper/SpPlatform.java | 5 +- .../platform/sportpaper/SpPlayerUtils.java | 24 +- .../java/dev/pgm/community/util/Platform.java | 5 +- .../dev/pgm/community/util/PlayerUtils.java | 9 +- 14 files changed, 457 insertions(+), 106 deletions(-) create mode 100644 core/src/main/java/dev/pgm/community/nick/PlayerIdentity.java diff --git a/core/src/main/java/dev/pgm/community/Community.java b/core/src/main/java/dev/pgm/community/Community.java index a804d9db..be926c7f 100644 --- a/core/src/main/java/dev/pgm/community/Community.java +++ b/core/src/main/java/dev/pgm/community/Community.java @@ -1,5 +1,7 @@ package dev.pgm.community; +import static dev.pgm.community.nick.PlayerIdentity.PLAYER_IDENTITY; + import dev.pgm.community.commands.graph.CommunityCommandGraph; import dev.pgm.community.events.CommunityEvent; import dev.pgm.community.feature.FeatureManager; @@ -51,8 +53,11 @@ public void onEnable() { } catch (Throwable t) { getLogger().log(Level.SEVERE, "Failed to initialize Community platform", t); getServer().getPluginManager().disablePlugin(this); + return; } + Platform.MANIFEST.onEnable(this); + this.setupConfig(); getLogger().info(dev.pgm.community.database.DatabaseExecutor.describeBackend()); this.setupFeatures(); @@ -60,7 +65,9 @@ public void onEnable() { @Override public void onDisable() { - features.disable(); + Platform.MANIFEST.onDisable(); + if (features != null) features.disable(); + PLAYER_IDENTITY.clearAll(); dev.pgm.community.database.DatabaseExecutor.shutdown(); } @@ -97,7 +104,6 @@ private void setupTranslations() { } public void registerListener(Listener listener) { - Platform.MANIFEST.onEnable(); getServer().getPluginManager().registerEvents(listener, this); } diff --git a/core/src/main/java/dev/pgm/community/nick/PlayerIdentity.java b/core/src/main/java/dev/pgm/community/nick/PlayerIdentity.java new file mode 100644 index 00000000..6877d3d4 --- /dev/null +++ b/core/src/main/java/dev/pgm/community/nick/PlayerIdentity.java @@ -0,0 +1,214 @@ +package dev.pgm.community.nick; + +import static dev.pgm.community.util.PlayerUtils.PLAYER_UTILS; + +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.UUID; +import org.bukkit.entity.Player; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; +import tc.oc.pgm.util.skin.Skin; + +@NullMarked +public final class PlayerIdentity { + public static final int MAX_NICK_LENGTH = 16; + public static final PlayerIdentity PLAYER_IDENTITY = new PlayerIdentity(); + + private final Map> playerSkins = new HashMap<>(); + private final Map> playerNames = new HashMap<>(); + private final Map> playerDisplayNames = new HashMap<>(); + private final Map>> viewerTeamEntries = new HashMap<>(); + private final Map> viewerVisibleNames = new HashMap<>(); + + private PlayerIdentity() {} + + public void set( + Player player, + Player viewer, + @Nullable String displayName, + @Nullable String nick, + @Nullable Skin skin) { + validateNick(nick); + + UUID playerId = player.getUniqueId(); + UUID viewerId = viewer.getUniqueId(); + + set(playerSkins, playerId, viewerId, skin); + set(playerNames, playerId, viewerId, nick); + set(playerDisplayNames, playerId, viewerId, displayName); + setVisibleName(viewerId, player.getName(), getName(player, viewer)); + } + + private static void set( + Map> identities, UUID playerId, UUID viewerId, @Nullable T value) { + if (value == null) { + Map viewers = identities.get(playerId); + if (viewers == null) return; + + viewers.remove(viewerId); + if (viewers.isEmpty()) identities.remove(playerId); + return; + } + + identities.computeIfAbsent(playerId, k -> new HashMap<>()).put(viewerId, value); + } + + public static void validateNick(@Nullable String name) { + if (name != null && name.length() > MAX_NICK_LENGTH) { + throw new IllegalArgumentException( + "Player nick names are limited to " + MAX_NICK_LENGTH + " characters in length"); + } + } + + public void clearPlayer(UUID playerId, String realName) { + playerSkins.remove(playerId); + playerNames.remove(playerId); + playerDisplayNames.remove(playerId); + viewerVisibleNames.values().removeIf(visibleNames -> { + visibleNames.remove(realName); + return visibleNames.isEmpty(); + }); + } + + public void clearViewer(UUID viewerId) { + clearViewer(playerSkins, viewerId); + clearViewer(playerNames, viewerId); + clearViewer(playerDisplayNames, viewerId); + viewerTeamEntries.remove(viewerId); + viewerVisibleNames.remove(viewerId); + } + + private static void clearViewer(Map> identities, UUID viewerId) { + identities.values().removeIf(viewers -> { + viewers.remove(viewerId); + return viewers.isEmpty(); + }); + } + + public void clearAll() { + playerSkins.clear(); + playerNames.clear(); + playerDisplayNames.clear(); + viewerTeamEntries.clear(); + viewerVisibleNames.clear(); + } + + public @Nullable String getVisibleName(UUID viewerId, String realName) { + Map visibleNames = viewerVisibleNames.get(viewerId); + return visibleNames == null ? null : visibleNames.get(realName); + } + + private void setVisibleName(UUID viewerId, String realName, String visibleName) { + if (realName.equals(visibleName)) { + clearVisibleName(viewerId, realName); + return; + } + + viewerVisibleNames.computeIfAbsent(viewerId, k -> new HashMap<>()).put(realName, visibleName); + } + + private void clearVisibleName(UUID viewerId, String realName) { + Map visibleNames = viewerVisibleNames.get(viewerId); + if (visibleNames == null) return; + + visibleNames.remove(realName); + if (visibleNames.isEmpty()) viewerVisibleNames.remove(viewerId); + } + + public @Nullable String getTeamName(UUID viewerId, String entry) { + Map> teamEntries = viewerTeamEntries.get(viewerId); + if (teamEntries == null) return null; + + for (Map.Entry> team : teamEntries.entrySet()) { + if (team.getValue().contains(entry)) return team.getKey(); + } + + return null; + } + + public boolean hasTeamEntry(UUID viewerId, String teamName, String entry) { + Map> teamEntries = viewerTeamEntries.get(viewerId); + if (teamEntries == null) return false; + + HashSet entries = teamEntries.get(teamName); + return entries != null && entries.contains(entry); + } + + public void createTeam(UUID viewerId, String teamName, Collection entries) { + removeTeam(viewerId, teamName); + addTeamEntries(viewerId, teamName, entries); + } + + public void removeTeam(UUID viewerId, String teamName) { + Map> teamEntries = viewerTeamEntries.get(viewerId); + if (teamEntries == null) return; + + teamEntries.remove(teamName); + if (teamEntries.isEmpty()) viewerTeamEntries.remove(viewerId); + } + + public void addTeamEntries(UUID viewerId, String teamName, Collection entries) { + if (entries.isEmpty()) return; + + Map> teamEntries = + viewerTeamEntries.computeIfAbsent(viewerId, k -> new HashMap<>()); + teamEntries.values().forEach(team -> team.removeAll(entries)); + teamEntries.values().removeIf(Collection::isEmpty); + teamEntries.computeIfAbsent(teamName, k -> new HashSet<>()).addAll(entries); + } + + public void removeTeamEntries(UUID viewerId, String teamName, Collection entries) { + Map> teamEntries = viewerTeamEntries.get(viewerId); + if (teamEntries == null) return; + + HashSet team = teamEntries.get(teamName); + if (team == null) return; + + team.removeAll(entries); + if (team.isEmpty()) teamEntries.remove(teamName); + if (teamEntries.isEmpty()) viewerTeamEntries.remove(viewerId); + } + + public String getDisplayName(Player player, Player viewer) { + String displayName = get(playerDisplayNames, player.getUniqueId(), viewer.getUniqueId()); + if (displayName != null) return displayName; + + return player.getDisplayName(); + } + + public boolean hasDisplayName(Player player, Player viewer) { + return get(playerDisplayNames, player.getUniqueId(), viewer.getUniqueId()) != null; + } + + public String getName(Player player, Player viewer) { + String name = get(playerNames, player.getUniqueId(), viewer.getUniqueId()); + if (name != null) return name; + + return player.getName(); + } + + public boolean hasName(Player player, Player viewer) { + return get(playerNames, player.getUniqueId(), viewer.getUniqueId()) != null; + } + + public Skin getSkin(Player player, Player viewer) { + Skin skin = get(playerSkins, player.getUniqueId(), viewer.getUniqueId()); + if (skin != null && !skin.isEmpty()) return skin; + + return PLAYER_UTILS.getPlayerSkin(player); + } + + public boolean hasSkin(Player player, Player viewer) { + Skin skin = get(playerSkins, player.getUniqueId(), viewer.getUniqueId()); + return skin != null && !skin.isEmpty(); + } + + private static @Nullable T get( + Map> identities, UUID playerId, UUID viewerId) { + Map viewers = identities.get(playerId); + return viewers == null ? null : viewers.get(viewerId); + } +} diff --git a/core/src/main/java/dev/pgm/community/nick/feature/PGMNickIntegration.java b/core/src/main/java/dev/pgm/community/nick/feature/PGMNickIntegration.java index 4de68ba3..02f1a1d7 100644 --- a/core/src/main/java/dev/pgm/community/nick/feature/PGMNickIntegration.java +++ b/core/src/main/java/dev/pgm/community/nick/feature/PGMNickIntegration.java @@ -1,6 +1,6 @@ package dev.pgm.community.nick.feature; -import static dev.pgm.community.util.PlayerUtils.PLAYER_UTILS; +import static dev.pgm.community.nick.PlayerIdentity.PLAYER_IDENTITY; import static net.kyori.adventure.text.Component.text; import dev.pgm.community.utils.PGMUtils; @@ -41,7 +41,7 @@ public String getNick(Player player) { @Override public Skin getPlayerSkin(@NonNull Player player, Player viewer) { - return PLAYER_UTILS.getPlayerSkin(player, viewer); + return PLAYER_IDENTITY.getSkin(player, viewer); } public void cancelTask() { diff --git a/core/src/main/java/dev/pgm/community/nick/feature/types/NickFeatureCore.java b/core/src/main/java/dev/pgm/community/nick/feature/types/NickFeatureCore.java index 627859c3..b7ec4160 100644 --- a/core/src/main/java/dev/pgm/community/nick/feature/types/NickFeatureCore.java +++ b/core/src/main/java/dev/pgm/community/nick/feature/types/NickFeatureCore.java @@ -86,9 +86,21 @@ public SkinManager getSkinManager() { @Override public void enable() { super.enable(); + skins.enable(); integrate(); } + @Override + public void disable() { + if (pgmNicks != null) { + pgmNicks.cancelTask(); + pgmNicks = null; + } + + skins.disable(); + super.disable(); + } + private void integrate() { if (isPGMEnabled()) { pgmNicks = new PGMNickIntegration(this); @@ -183,12 +195,12 @@ public void onJoin(PlayerJoinEvent event) { .thenAcceptAsync(name -> this.setNick(player.getUniqueId(), name) .thenAcceptAsync(success -> { if (success) { - nickedPlayers.put(player.getUniqueId(), nick.getName()); + nickedPlayers.put(player.getUniqueId(), name); Audience.get(player) .sendWarning(text( "You had no nickname, so a random one has been assigned", NamedTextColor.GREEN)); - sendLoginNotification(player, nick.getName(), true); + sendLoginNotification(player, name, true); } })); } diff --git a/core/src/main/java/dev/pgm/community/nick/skin/SkinCache.java b/core/src/main/java/dev/pgm/community/nick/skin/SkinCache.java index 432884c8..618d8db0 100644 --- a/core/src/main/java/dev/pgm/community/nick/skin/SkinCache.java +++ b/core/src/main/java/dev/pgm/community/nick/skin/SkinCache.java @@ -1,5 +1,6 @@ package dev.pgm.community.nick.skin; +import static dev.pgm.community.nick.PlayerIdentity.PLAYER_IDENTITY; import static dev.pgm.community.util.PlayerUtils.PLAYER_UTILS; import com.google.common.cache.Cache; @@ -67,6 +68,15 @@ public void onPlayerQuit(PlayerQuitEvent event) { if (canUseSkin(player)) { offlineSkins.put(player.getUniqueId(), PLAYER_UTILS.getPlayerSkin(player)); } + + PLAYER_IDENTITY.clearViewer(player.getUniqueId()); + UUID playerId = player.getUniqueId(); + String playerName = player.getName(); + Bukkit.getScheduler().runTask(Community.get(), () -> { + if (Bukkit.getPlayer(playerId) == null) { + PLAYER_IDENTITY.clearPlayer(playerId, playerName); + } + }); } @EventHandler(priority = EventPriority.LOW) @@ -79,9 +89,6 @@ public void refreshNamesOnLogin(PlayerJoinEvent event) { refreshPlayer(event.getPlayer()); } - // SPORTPAPER STUFF - TODO: Add alternative method and check if server is running SportPaper to - // enable - private void refreshAllViewers(Player player) { Bukkit.getOnlinePlayers().forEach(viewer -> refreshFakeName(player, viewer)); } @@ -109,7 +116,6 @@ private void refreshSelfView(Player viewer) { Bukkit.getOnlinePlayers().forEach(other -> refreshFakeName(other, viewer)); } - // TODO: Figure out how to use without SPORTPAPER API private void refreshFakeName(Player player, Player viewer) { boolean nicked = Integration.getNick(player) != null; boolean areFriends = Integration.isFriend(player, viewer); diff --git a/core/src/main/java/dev/pgm/community/nick/skin/SkinManager.java b/core/src/main/java/dev/pgm/community/nick/skin/SkinManager.java index a2cbb56b..bf26d861 100644 --- a/core/src/main/java/dev/pgm/community/nick/skin/SkinManager.java +++ b/core/src/main/java/dev/pgm/community/nick/skin/SkinManager.java @@ -1,19 +1,39 @@ package dev.pgm.community.nick.skin; +import static dev.pgm.community.nick.PlayerIdentity.PLAYER_IDENTITY; + import dev.pgm.community.Community; import org.bukkit.entity.Player; +import org.bukkit.event.HandlerList; import tc.oc.pgm.util.skin.Skin; public class SkinManager { private final SkinCache cache; + private boolean registered; public SkinManager() { this.cache = new SkinCache(); + enable(); + } + + public void enable() { + if (registered) return; + Community.get().registerListener(cache); + registered = true; } public void setSkin(Player player, Skin skin) { cache.onSkinRefresh(player, skin); } + + public void disable() { + if (registered) { + HandlerList.unregisterAll(cache); + registered = false; + } + + PLAYER_IDENTITY.clearAll(); + } } diff --git a/core/src/main/resources/plugin.yml b/core/src/main/resources/plugin.yml index b15c74c2..a8f7008c 100644 --- a/core/src/main/resources/plugin.yml +++ b/core/src/main/resources/plugin.yml @@ -5,4 +5,4 @@ main: ${mainClass} version: ${version} (git-${commitHash}) website: ${url} author: ${author} -softdepend: [Database, PGM, Environment] +softdepend: [Database, PGM, Environment, packetevents] diff --git a/platform/platform-modern/src/main/java/dev/pgm/community/platform/modern/ModernPlatform.java b/platform/platform-modern/src/main/java/dev/pgm/community/platform/modern/ModernPlatform.java index ea80b27d..dde1d5fa 100644 --- a/platform/platform-modern/src/main/java/dev/pgm/community/platform/modern/ModernPlatform.java +++ b/platform/platform-modern/src/main/java/dev/pgm/community/platform/modern/ModernPlatform.java @@ -5,11 +5,29 @@ import dev.pgm.community.util.Platform; import dev.pgm.community.util.Supports; +import org.bukkit.Bukkit; +import org.bukkit.plugin.Plugin; @Supports(value = PAPER, minVersion = "1.21.11", priority = HIGHEST) public class ModernPlatform implements Platform.Manifest { + private PacketManipulations packetManipulations; + + @Override + public void onEnable(Plugin plugin) { + if (!plugin.getServer().getPluginManager().isPluginEnabled("packetevents")) { + Bukkit.getServer().getPluginManager().disablePlugin(plugin); + throw new IllegalStateException( + "PacketEvents is not installed, and is required for Community modern version support"); + } + + packetManipulations = new PacketManipulations(); + } + @Override - public void onEnable() { - new PacketManipulations(); + public void onDisable() { + if (packetManipulations != null) { + packetManipulations.unregister(); + packetManipulations = null; + } } } diff --git a/platform/platform-modern/src/main/java/dev/pgm/community/platform/modern/ModernPlayerUtils.java b/platform/platform-modern/src/main/java/dev/pgm/community/platform/modern/ModernPlayerUtils.java index e86efaa6..9695072a 100644 --- a/platform/platform-modern/src/main/java/dev/pgm/community/platform/modern/ModernPlayerUtils.java +++ b/platform/platform-modern/src/main/java/dev/pgm/community/platform/modern/ModernPlayerUtils.java @@ -1,17 +1,20 @@ package dev.pgm.community.platform.modern; +import static dev.pgm.community.nick.PlayerIdentity.PLAYER_IDENTITY; import static dev.pgm.community.util.Supports.Variant.PAPER; import com.destroystokyo.paper.profile.CraftPlayerProfile; import com.destroystokyo.paper.profile.PlayerProfile; +import com.github.retrooper.packetevents.PacketEvents; +import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerTeams; +import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerTeams.TeamMode; import dev.pgm.community.util.PlayerUtils; import dev.pgm.community.util.Supports; import java.net.MalformedURLException; import java.net.URI; import java.net.URISyntaxException; import java.util.Arrays; -import java.util.HashMap; -import java.util.Map; +import java.util.List; import java.util.UUID; import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; import org.bukkit.Material; @@ -21,6 +24,7 @@ import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.meta.SkullMeta; import org.bukkit.profile.PlayerTextures; +import org.jspecify.annotations.NonNull; import tc.oc.pgm.platform.modern.util.Skins; import tc.oc.pgm.util.skin.Skin; @@ -32,59 +36,44 @@ public Skin getPlayerSkin(Player player) { return Skins.fromProfile(craftPlayer.getProfile()); } - private final Map> playerSkins = new HashMap<>(); - private final Map> playerNames = new HashMap<>(); - private final Map> playerDisplayNames = new HashMap<>(); - @Override public void setFakeNameAndSkin( Player player, Player viewer, String displayName, String nick, Skin skin) { - playerSkins - .computeIfAbsent(player.getUniqueId(), k -> new HashMap<>()) - .put(viewer.getUniqueId(), skin); - playerNames - .computeIfAbsent(player.getUniqueId(), k -> new HashMap<>()) - .put(viewer.getUniqueId(), nick); - playerDisplayNames - .computeIfAbsent(player.getUniqueId(), k -> new HashMap<>()) - .put(viewer.getUniqueId(), displayName); - } + String oldName = PLAYER_IDENTITY.getName(player, viewer); - @Override - public String getPlayerDisplayName(Player player, Player viewer) { - if (playerDisplayNames.containsKey(player.getUniqueId())) { - Map uuidStringMap = playerDisplayNames.get(player.getUniqueId()); - String displayName = uuidStringMap.get(viewer.getUniqueId()); - if (displayName != null) return displayName; - } + PLAYER_IDENTITY.set(player, viewer, displayName, nick, skin); - return LegacyComponentSerializer.legacySection().serialize(player.displayName()); - } + String newName = PLAYER_IDENTITY.getName(player, viewer); - @Override - public String getPlayerName(Player player, Player viewer) { - if (playerNames.containsKey(player.getUniqueId())) { - Map uuidStringMap = playerNames.get(player.getUniqueId()); - String name = uuidStringMap.get(viewer.getUniqueId()); - if (name != null) return name; + if (!oldName.equals(newName)) { + updateTeamEntry(viewer, oldName, newName); } - - return player.getName(); } - @Override - public Skin getPlayerSkin(Player player, Player viewer) { - if (playerSkins.containsKey(player.getUniqueId())) { - Map uuidSkinMap = playerSkins.get(player.getUniqueId()); - Skin skin = uuidSkinMap.get(viewer.getUniqueId()); - if (skin != null && !skin.isEmpty()) return skin; - } + private void updateTeamEntry(@NonNull Player viewer, String oldName, String newName) { + UUID viewerId = viewer.getUniqueId(); + String teamName = PLAYER_IDENTITY.getTeamName(viewerId, oldName); + if (teamName == null) return; + + sendTeamPacketSilently(viewer, teamName, TeamMode.REMOVE_ENTITIES, List.of(oldName)); + PLAYER_IDENTITY.removeTeamEntries(viewerId, teamName, List.of(oldName)); + + sendTeamPacketSilently(viewer, teamName, TeamMode.ADD_ENTITIES, List.of(newName)); + PLAYER_IDENTITY.addTeamEntries(viewerId, teamName, List.of(newName)); + } - return getPlayerSkin(player); + private void sendTeamPacketSilently( + Player viewer, String teamName, TeamMode mode, java.util.Collection names) { + PacketEvents.getAPI() + .getPlayerManager() + .sendPacketSilently( + viewer, + new WrapperPlayServerTeams( + teamName, mode, (WrapperPlayServerTeams.ScoreBoardTeamInfo) null, names)); } @Override - public ItemStack customSkull(String url, String displayName, String... lore) { + public ItemStack customSkull(@NonNull String url, String displayName, String... lore) { ItemStack head = new ItemStack(Material.PLAYER_HEAD); if (url.isEmpty()) { return head; diff --git a/platform/platform-modern/src/main/java/dev/pgm/community/platform/modern/PacketManipulations.java b/platform/platform-modern/src/main/java/dev/pgm/community/platform/modern/PacketManipulations.java index af3b4534..7f80b7c8 100644 --- a/platform/platform-modern/src/main/java/dev/pgm/community/platform/modern/PacketManipulations.java +++ b/platform/platform-modern/src/main/java/dev/pgm/community/platform/modern/PacketManipulations.java @@ -1,62 +1,165 @@ package dev.pgm.community.platform.modern; -import static dev.pgm.community.util.PlayerUtils.PLAYER_UTILS; +import static dev.pgm.community.nick.PlayerIdentity.PLAYER_IDENTITY; +import com.github.retrooper.packetevents.event.PacketListenerCommon; import com.github.retrooper.packetevents.event.PacketListenerPriority; import com.github.retrooper.packetevents.event.PacketSendEvent; import com.github.retrooper.packetevents.protocol.packettype.PacketType; import com.github.retrooper.packetevents.protocol.player.TextureProperty; import com.github.retrooper.packetevents.protocol.player.UserProfile; import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerPlayerInfoUpdate; +import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerTeams; +import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerTeams.TeamMode; +import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.UUID; import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; import org.apache.commons.lang3.StringUtils; import org.bukkit.Bukkit; import org.bukkit.entity.Player; import org.jspecify.annotations.NonNull; -import tc.oc.pgm.platform.modern.util.Packets; +import tc.oc.pgm.util.nms.packets.PacketEventsUtil; import tc.oc.pgm.util.skin.Skin; public class PacketManipulations { + private final PacketListenerCommon listener; public PacketManipulations() { - Packets.registerSend( + this.listener = PacketEventsUtil.registerSend( PacketListenerPriority.LOWEST, - Map.of(PacketType.Play.Server.PLAYER_INFO_UPDATE, this::handlePlayerInfo)); + Map.of( + PacketType.Play.Server.PLAYER_INFO_UPDATE, + this::handlePlayerInfo, + PacketType.Play.Server.TEAMS, + this::handleScoreboardTeams)); + } + + public void unregister() { + PacketEventsUtil.unregister(listener); } private void handlePlayerInfo(@NonNull PacketSendEvent event) { Player viewer = event.getPlayer(); - WrapperPlayServerPlayerInfoUpdate wrapper = new WrapperPlayServerPlayerInfoUpdate(event); + var wrapper = new WrapperPlayServerPlayerInfoUpdate(event); + + var actions = wrapper.getActions(); + boolean hasAddPlayer = actions.contains(WrapperPlayServerPlayerInfoUpdate.Action.ADD_PLAYER); + boolean hasUpdateDisplayName = + actions.contains(WrapperPlayServerPlayerInfoUpdate.Action.UPDATE_DISPLAY_NAME); + + if (!hasAddPlayer && !hasUpdateDisplayName) return; boolean modified = false; - List entries = wrapper.getEntries(); - for (WrapperPlayServerPlayerInfoUpdate.PlayerInfo entry : entries) { + var entries = wrapper.getEntries(); + for (var entry : entries) { Player player = Bukkit.getPlayer(entry.getProfileId()); - if (player == null || player.equals(viewer) || !player.isOnline()) continue; + if (player == null || player.equals(viewer)) continue; + + boolean hasName = PLAYER_IDENTITY.hasName(player, viewer); + boolean hasSkin = PLAYER_IDENTITY.hasSkin(player, viewer); + boolean hasDisplayName = PLAYER_IDENTITY.hasDisplayName(player, viewer); + + if (!hasName && !hasSkin && !hasDisplayName) continue; + + if (hasAddPlayer && (hasName || hasSkin)) { + UserProfile profile = entry.getGameProfile(); + if (hasName) { + String playerName = PLAYER_IDENTITY.getName(player, viewer); + if (StringUtils.isBlank(playerName)) continue; - String playerDisplayName = PLAYER_UTILS.getPlayerDisplayName(player, viewer); - String playerName = PLAYER_UTILS.getPlayerName(player, viewer); + profile.setName(playerName); + } - if (StringUtils.isBlank(playerName) || StringUtils.isBlank(playerDisplayName)) continue; + if (hasSkin) { + Skin skin = PLAYER_IDENTITY.getSkin(player, viewer); + profile.getTextureProperties().clear(); + profile + .getTextureProperties() + .add(new TextureProperty("textures", skin.getData(), skin.getSignature())); + } - UserProfile profile = entry.getGameProfile(); - profile.setName(playerName); + entry.setGameProfile(profile); + } - Skin skin = PLAYER_UTILS.getPlayerSkin(player, viewer); - profile.getTextureProperties().clear(); - profile - .getTextureProperties() - .add(new TextureProperty("textures", skin.getData(), skin.getSignature())); + if (hasUpdateDisplayName && hasDisplayName) { + String playerDisplayName = PLAYER_IDENTITY.getDisplayName(player, viewer); + if (StringUtils.isBlank(playerDisplayName)) continue; - entry.setGameProfile(profile); - entry.setDisplayName( - LegacyComponentSerializer.legacySection().deserialize(playerDisplayName)); + entry.setDisplayName( + LegacyComponentSerializer.legacySection().deserialize(playerDisplayName)); + } modified = true; } if (modified) event.markForReEncode(true); } + + private void handleScoreboardTeams(@NonNull PacketSendEvent event) { + Player viewer = event.getPlayer(); + UUID viewerId = viewer.getUniqueId(); + var wrapper = new WrapperPlayServerTeams(event); + TeamMode mode = wrapper.getTeamMode(); + String teamName = wrapper.getTeamName(); + + List players = new ArrayList<>(wrapper.getPlayers()); + if (players.isEmpty()) { + trackTeamPacket(viewerId, teamName, mode, players); + return; + } + + boolean modified = false; + for (int i = 0; i < players.size(); i++) { + String entry = players.get(i); + String visibleName = PLAYER_IDENTITY.getVisibleName(viewerId, entry); + if (mode == TeamMode.REMOVE_ENTITIES) { + if (!StringUtils.isBlank(visibleName) + && PLAYER_IDENTITY.hasTeamEntry(viewerId, teamName, visibleName)) { + if (!visibleName.equals(entry)) { + players.set(i, visibleName); + modified = true; + } + continue; + } + + if (PLAYER_IDENTITY.hasTeamEntry(viewerId, teamName, entry)) continue; + + players.remove(i--); + modified = true; + continue; + } + + if (visibleName == null || visibleName.equals(entry)) continue; + + players.set(i, visibleName); + modified = true; + } + + if (mode == TeamMode.REMOVE_ENTITIES && players.isEmpty()) { + event.setCancelled(true); + return; + } + + if (modified) { + wrapper.setPlayers(players); + event.markForReEncode(true); + } + + trackTeamPacket(viewerId, teamName, mode, players); + } + + private void trackTeamPacket( + UUID viewerId, String teamName, TeamMode mode, List players) { + switch (mode) { + case CREATE -> PLAYER_IDENTITY.createTeam(viewerId, teamName, players); + case REMOVE -> PLAYER_IDENTITY.removeTeam(viewerId, teamName); + case ADD_ENTITIES -> PLAYER_IDENTITY.addTeamEntries(viewerId, teamName, players); + case REMOVE_ENTITIES -> PLAYER_IDENTITY.removeTeamEntries(viewerId, teamName, players); + case UPDATE -> { + // Team metadata update; membership is unchanged. + } + } + } } diff --git a/platform/platform-sportpaper/src/main/java/dev/pgm/community/platform/sportpaper/SpPlatform.java b/platform/platform-sportpaper/src/main/java/dev/pgm/community/platform/sportpaper/SpPlatform.java index 9849bc26..2dbe1010 100644 --- a/platform/platform-sportpaper/src/main/java/dev/pgm/community/platform/sportpaper/SpPlatform.java +++ b/platform/platform-sportpaper/src/main/java/dev/pgm/community/platform/sportpaper/SpPlatform.java @@ -7,7 +7,4 @@ import dev.pgm.community.util.Supports; @Supports(value = SPORTPAPER, priority = HIGHEST) -public class SpPlatform implements Platform.Manifest { - @Override - public void onEnable() {} -} +public class SpPlatform implements Platform.Manifest {} diff --git a/platform/platform-sportpaper/src/main/java/dev/pgm/community/platform/sportpaper/SpPlayerUtils.java b/platform/platform-sportpaper/src/main/java/dev/pgm/community/platform/sportpaper/SpPlayerUtils.java index 2e9bc6d4..3a2ec9e5 100644 --- a/platform/platform-sportpaper/src/main/java/dev/pgm/community/platform/sportpaper/SpPlayerUtils.java +++ b/platform/platform-sportpaper/src/main/java/dev/pgm/community/platform/sportpaper/SpPlayerUtils.java @@ -1,5 +1,6 @@ package dev.pgm.community.platform.sportpaper; +import static dev.pgm.community.nick.PlayerIdentity.PLAYER_IDENTITY; import static dev.pgm.community.util.Supports.Variant.SPORTPAPER; import com.mojang.authlib.GameProfile; @@ -19,6 +20,8 @@ import org.bukkit.inventory.ItemFlag; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.meta.SkullMeta; +import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.Nullable; import tc.oc.pgm.platform.sportpaper.utils.Skins; import tc.oc.pgm.util.bukkit.BukkitUtils; import tc.oc.pgm.util.skin.Skin; @@ -35,6 +38,7 @@ public Skin getPlayerSkin(Player player) { @Override public void setFakeNameAndSkin( Player player, Player viewer, String displayName, String nick, Skin skin) { + PLAYER_IDENTITY.set(player, viewer, displayName, nick, skin); var fakeSkin = skin == null ? null : new org.bukkit.Skin(skin.getData(), skin.getSignature()); player.setFakeDisplayName(viewer, displayName); @@ -42,23 +46,7 @@ public void setFakeNameAndSkin( } @Override - public String getPlayerDisplayName(Player player, Player viewer) { - return player.getDisplayName(viewer); - } - - @Override - public String getPlayerName(Player player, Player viewer) { - return player.getName(viewer); - } - - @Override - public Skin getPlayerSkin(Player player, Player viewer) { - org.bukkit.Skin skin = player.getSkin(viewer); - return new Skin(skin.getData(), skin.getSignature()); - } - - @Override - public ItemStack customSkull(String url, String displayName, String... lore) { + public ItemStack customSkull(@NonNull String url, String displayName, String... lore) { ItemStack head = new ItemStack(Material.SKULL_ITEM); head.setDurability((short) SkullType.PLAYER.ordinal()); if (url.isEmpty()) { @@ -82,7 +70,7 @@ public ItemStack customSkull(String url, String displayName, String... lore) { return head; } - private static GameProfile createGameProfile(String url) { + private static @Nullable GameProfile createGameProfile(String url) { GameProfile profile = new GameProfile(UUID.randomUUID(), null); PropertyMap propertyMap = profile.getProperties(); if (propertyMap == null) { diff --git a/util/src/main/java/dev/pgm/community/util/Platform.java b/util/src/main/java/dev/pgm/community/util/Platform.java index 0e47fe6d..e541e9e8 100644 --- a/util/src/main/java/dev/pgm/community/util/Platform.java +++ b/util/src/main/java/dev/pgm/community/util/Platform.java @@ -5,6 +5,7 @@ import dev.pgm.community.util.Supports.Variant; import java.util.Arrays; import org.bukkit.Bukkit; +import org.bukkit.plugin.Plugin; import org.jspecify.annotations.NonNull; import org.reflections.Reflections; import org.reflections.util.ClasspathHelper; @@ -79,6 +80,8 @@ private static Object getBestSupported(Class parent) { } public interface Manifest { - void onEnable(); + default void onEnable(Plugin plugin) {} + + default void onDisable() {} } } diff --git a/util/src/main/java/dev/pgm/community/util/PlayerUtils.java b/util/src/main/java/dev/pgm/community/util/PlayerUtils.java index 99c3679e..89951181 100644 --- a/util/src/main/java/dev/pgm/community/util/PlayerUtils.java +++ b/util/src/main/java/dev/pgm/community/util/PlayerUtils.java @@ -2,6 +2,7 @@ import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; +import org.jspecify.annotations.NonNull; import tc.oc.pgm.util.skin.Skin; public interface PlayerUtils { @@ -11,11 +12,5 @@ public interface PlayerUtils { void setFakeNameAndSkin(Player player, Player viewer, String displayName, String nick, Skin skin); - String getPlayerDisplayName(Player player, Player viewer); - - String getPlayerName(Player player, Player viewer); - - Skin getPlayerSkin(Player player, Player viewer); - - ItemStack customSkull(String url, String displayName, String... lore); + ItemStack customSkull(@NonNull String url, String displayName, String... lore); } From 232904369596a20510782039f637236c711f46ed Mon Sep 17 00:00:00 2001 From: BT <43831917+calcastor@users.noreply.github.com> Date: Mon, 13 Apr 2026 22:10:11 -0700 Subject: [PATCH 4/5] Dependency updates and adapt for new Minecraft versioning a Signed-off-by: BT <43831917+calcastor@users.noreply.github.com> --- .../buildlogic.java-conventions.gradle.kts | 16 +++++++++-- .../java/dev/pgm/community/util/Platform.java | 28 +++++++++++++++---- 2 files changed, 37 insertions(+), 7 deletions(-) diff --git a/buildSrc/src/main/kotlin/buildlogic.java-conventions.gradle.kts b/buildSrc/src/main/kotlin/buildlogic.java-conventions.gradle.kts index bf8c1e43..cca8484a 100644 --- a/buildSrc/src/main/kotlin/buildlogic.java-conventions.gradle.kts +++ b/buildSrc/src/main/kotlin/buildlogic.java-conventions.gradle.kts @@ -16,16 +16,28 @@ repositories { maven("https://repo.papermc.io/repository/maven-public/") // Paper builds & paperweight plugin maven("https://repo.aikar.co/content/groups/aikar/") // Aikar repo maven("https://repo.codemc.io/repository/maven-releases/") // PacketEvents + exclusiveContent { + forRepository { + maven("https://jitpack.io") + } + filter { + includeGroup("com.github.OvercastCommunity.adventure-platform") + includeGroup("com.github.MinusKube") + } + } mavenLocal() // Local last } dependencies { api("com.zaxxer:HikariCP:2.4.1") { isTransitive = false } - api("fr.minuskube.inv:smart-invs:1.2.7") { isTransitive = false } + // Latest SmartInvs commit + api("com.github.MinusKube:SmartInvs:9c9dbbee16") { isTransitive = false } api("redis.clients:jedis:3.5.1") api("net.kyori:adventure-api:4.26.1") api("net.kyori:adventure-text-serializer-plain:4.26.1") - api("net.kyori:adventure-platform-bukkit:4.4.1") + // adventure-platform fork with ViaVersion and 1.21.11+ fixes + // https://github.com/OvercastCommunity/adventure-platform + api("com.github.OvercastCommunity.adventure-platform:adventure-platform-bukkit:04de657e85") api("org.reflections:reflections:0.10.2") // Annotations diff --git a/util/src/main/java/dev/pgm/community/util/Platform.java b/util/src/main/java/dev/pgm/community/util/Platform.java index e541e9e8..28e886d3 100644 --- a/util/src/main/java/dev/pgm/community/util/Platform.java +++ b/util/src/main/java/dev/pgm/community/util/Platform.java @@ -4,6 +4,7 @@ import dev.pgm.community.util.Supports.Variant; import java.util.Arrays; +import java.util.regex.Pattern; import org.bukkit.Bukkit; import org.bukkit.plugin.Plugin; import org.jspecify.annotations.NonNull; @@ -20,13 +21,25 @@ public abstract class Platform { .addUrls(ClasspathHelper.forPackage("dev.pgm.community", Platform.class.getClassLoader())) .forPackage("dev.pgm.community.platform") .setScanners(TypesAnnotated)); + private static final Pattern VERSION_MATCHER = + Pattern.compile("(\\d{1,2})\\.(\\d{1,2})\\.(\\d{1,2})"); public static final Version MINECRAFT_VERSION; public static final Variant VARIANT; + private static Version parseServerVersion(final @NonNull String versionName) { + var matcher = VERSION_MATCHER.matcher(versionName); + return matcher.find() + ? new Version( + Integer.parseInt(matcher.group(1)), + Integer.parseInt(matcher.group(2)), + Integer.parseInt(matcher.group(3))) + : new Version(0, 0, 0); + } + static { var sv = Bukkit.getServer(); - MINECRAFT_VERSION = TextParser.parseVersion(sv.getBukkitVersion().split("-")[0]); + MINECRAFT_VERSION = parseServerVersion(sv.getBukkitVersion()); VARIANT = Arrays.stream(Supports.Variant.values()) .filter(v -> v.matcher.test(sv)) .findFirst() @@ -61,10 +74,15 @@ private static Object getBestSupported(Class parent) { Supports[] supportList = clazz.getDeclaredAnnotationsByType(Supports.class); for (Supports sup : supportList) { if (VARIANT != sup.value()) continue; - if (!sup.minVersion().isEmpty() - && MINECRAFT_VERSION.isOlderThan(TextParser.parseVersion(sup.minVersion()))) continue; - if (!sup.maxVersion().isEmpty() - && TextParser.parseVersion(sup.maxVersion()).isOlderThan(MINECRAFT_VERSION)) continue; + if (!sup.minVersion().isEmpty()) { + Version min = TextParser.parseVersion(sup.minVersion()); + if (MINECRAFT_VERSION.isOlderThan(min)) continue; + } + + if (!sup.maxVersion().isEmpty()) { + Version max = TextParser.parseVersion(sup.maxVersion()); + if (max.isOlderThan(MINECRAFT_VERSION)) continue; + } if (priority == null || priority.compareTo(sup.priority()) < 0) { priority = sup.priority(); From cae00d46035c0bff1d41b0961c16c80d81667c67 Mon Sep 17 00:00:00 2001 From: applenick Date: Sun, 10 May 2026 10:58:04 -0700 Subject: [PATCH 5/5] Relocate PlayerIdentity and synchronize access Signed-off-by: applenick --- .../java/dev/pgm/community/Community.java | 2 +- .../nick/feature/PGMNickIntegration.java | 2 +- .../nick/{ => identity}/PlayerIdentity.java | 38 ++++++++++--------- .../pgm/community/nick/skin/SkinCache.java | 4 +- .../pgm/community/nick/skin/SkinManager.java | 2 +- .../platform/modern/ModernPlayerUtils.java | 2 +- .../platform/modern/PacketManipulations.java | 2 +- .../platform/sportpaper/SpPlayerUtils.java | 2 +- 8 files changed, 29 insertions(+), 25 deletions(-) rename core/src/main/java/dev/pgm/community/nick/{ => identity}/PlayerIdentity.java (82%) diff --git a/core/src/main/java/dev/pgm/community/Community.java b/core/src/main/java/dev/pgm/community/Community.java index be926c7f..02a1f7a0 100644 --- a/core/src/main/java/dev/pgm/community/Community.java +++ b/core/src/main/java/dev/pgm/community/Community.java @@ -1,6 +1,6 @@ package dev.pgm.community; -import static dev.pgm.community.nick.PlayerIdentity.PLAYER_IDENTITY; +import static dev.pgm.community.nick.identity.PlayerIdentity.PLAYER_IDENTITY; import dev.pgm.community.commands.graph.CommunityCommandGraph; import dev.pgm.community.events.CommunityEvent; diff --git a/core/src/main/java/dev/pgm/community/nick/feature/PGMNickIntegration.java b/core/src/main/java/dev/pgm/community/nick/feature/PGMNickIntegration.java index 02f1a1d7..190c94dd 100644 --- a/core/src/main/java/dev/pgm/community/nick/feature/PGMNickIntegration.java +++ b/core/src/main/java/dev/pgm/community/nick/feature/PGMNickIntegration.java @@ -1,6 +1,6 @@ package dev.pgm.community.nick.feature; -import static dev.pgm.community.nick.PlayerIdentity.PLAYER_IDENTITY; +import static dev.pgm.community.nick.identity.PlayerIdentity.PLAYER_IDENTITY; import static net.kyori.adventure.text.Component.text; import dev.pgm.community.utils.PGMUtils; diff --git a/core/src/main/java/dev/pgm/community/nick/PlayerIdentity.java b/core/src/main/java/dev/pgm/community/nick/identity/PlayerIdentity.java similarity index 82% rename from core/src/main/java/dev/pgm/community/nick/PlayerIdentity.java rename to core/src/main/java/dev/pgm/community/nick/identity/PlayerIdentity.java index 6877d3d4..d1fbd5ed 100644 --- a/core/src/main/java/dev/pgm/community/nick/PlayerIdentity.java +++ b/core/src/main/java/dev/pgm/community/nick/identity/PlayerIdentity.java @@ -1,4 +1,4 @@ -package dev.pgm.community.nick; +package dev.pgm.community.nick.identity; import static dev.pgm.community.util.PlayerUtils.PLAYER_UTILS; @@ -25,7 +25,7 @@ public final class PlayerIdentity { private PlayerIdentity() {} - public void set( + public synchronized void set( Player player, Player viewer, @Nullable String displayName, @@ -63,7 +63,7 @@ public static void validateNick(@Nullable String name) { } } - public void clearPlayer(UUID playerId, String realName) { + public synchronized void clearPlayer(UUID playerId, String realName) { playerSkins.remove(playerId); playerNames.remove(playerId); playerDisplayNames.remove(playerId); @@ -73,7 +73,7 @@ public void clearPlayer(UUID playerId, String realName) { }); } - public void clearViewer(UUID viewerId) { + public synchronized void clearViewer(UUID viewerId) { clearViewer(playerSkins, viewerId); clearViewer(playerNames, viewerId); clearViewer(playerDisplayNames, viewerId); @@ -88,7 +88,7 @@ private static void clearViewer(Map> identities, UUID vie }); } - public void clearAll() { + public synchronized void clearAll() { playerSkins.clear(); playerNames.clear(); playerDisplayNames.clear(); @@ -96,7 +96,7 @@ public void clearAll() { viewerVisibleNames.clear(); } - public @Nullable String getVisibleName(UUID viewerId, String realName) { + public synchronized @Nullable String getVisibleName(UUID viewerId, String realName) { Map visibleNames = viewerVisibleNames.get(viewerId); return visibleNames == null ? null : visibleNames.get(realName); } @@ -118,7 +118,7 @@ private void clearVisibleName(UUID viewerId, String realName) { if (visibleNames.isEmpty()) viewerVisibleNames.remove(viewerId); } - public @Nullable String getTeamName(UUID viewerId, String entry) { + public synchronized @Nullable String getTeamName(UUID viewerId, String entry) { Map> teamEntries = viewerTeamEntries.get(viewerId); if (teamEntries == null) return null; @@ -129,7 +129,7 @@ private void clearVisibleName(UUID viewerId, String realName) { return null; } - public boolean hasTeamEntry(UUID viewerId, String teamName, String entry) { + public synchronized boolean hasTeamEntry(UUID viewerId, String teamName, String entry) { Map> teamEntries = viewerTeamEntries.get(viewerId); if (teamEntries == null) return false; @@ -137,12 +137,12 @@ public boolean hasTeamEntry(UUID viewerId, String teamName, String entry) { return entries != null && entries.contains(entry); } - public void createTeam(UUID viewerId, String teamName, Collection entries) { + public synchronized void createTeam(UUID viewerId, String teamName, Collection entries) { removeTeam(viewerId, teamName); addTeamEntries(viewerId, teamName, entries); } - public void removeTeam(UUID viewerId, String teamName) { + public synchronized void removeTeam(UUID viewerId, String teamName) { Map> teamEntries = viewerTeamEntries.get(viewerId); if (teamEntries == null) return; @@ -150,7 +150,8 @@ public void removeTeam(UUID viewerId, String teamName) { if (teamEntries.isEmpty()) viewerTeamEntries.remove(viewerId); } - public void addTeamEntries(UUID viewerId, String teamName, Collection entries) { + public synchronized void addTeamEntries( + UUID viewerId, String teamName, Collection entries) { if (entries.isEmpty()) return; Map> teamEntries = @@ -160,7 +161,8 @@ public void addTeamEntries(UUID viewerId, String teamName, Collection en teamEntries.computeIfAbsent(teamName, k -> new HashSet<>()).addAll(entries); } - public void removeTeamEntries(UUID viewerId, String teamName, Collection entries) { + public synchronized void removeTeamEntries( + UUID viewerId, String teamName, Collection entries) { Map> teamEntries = viewerTeamEntries.get(viewerId); if (teamEntries == null) return; @@ -172,36 +174,36 @@ public void removeTeamEntries(UUID viewerId, String teamName, Collection if (teamEntries.isEmpty()) viewerTeamEntries.remove(viewerId); } - public String getDisplayName(Player player, Player viewer) { + public synchronized String getDisplayName(Player player, Player viewer) { String displayName = get(playerDisplayNames, player.getUniqueId(), viewer.getUniqueId()); if (displayName != null) return displayName; return player.getDisplayName(); } - public boolean hasDisplayName(Player player, Player viewer) { + public synchronized boolean hasDisplayName(Player player, Player viewer) { return get(playerDisplayNames, player.getUniqueId(), viewer.getUniqueId()) != null; } - public String getName(Player player, Player viewer) { + public synchronized String getName(Player player, Player viewer) { String name = get(playerNames, player.getUniqueId(), viewer.getUniqueId()); if (name != null) return name; return player.getName(); } - public boolean hasName(Player player, Player viewer) { + public synchronized boolean hasName(Player player, Player viewer) { return get(playerNames, player.getUniqueId(), viewer.getUniqueId()) != null; } - public Skin getSkin(Player player, Player viewer) { + public synchronized Skin getSkin(Player player, Player viewer) { Skin skin = get(playerSkins, player.getUniqueId(), viewer.getUniqueId()); if (skin != null && !skin.isEmpty()) return skin; return PLAYER_UTILS.getPlayerSkin(player); } - public boolean hasSkin(Player player, Player viewer) { + public synchronized boolean hasSkin(Player player, Player viewer) { Skin skin = get(playerSkins, player.getUniqueId(), viewer.getUniqueId()); return skin != null && !skin.isEmpty(); } diff --git a/core/src/main/java/dev/pgm/community/nick/skin/SkinCache.java b/core/src/main/java/dev/pgm/community/nick/skin/SkinCache.java index 618d8db0..0c62492a 100644 --- a/core/src/main/java/dev/pgm/community/nick/skin/SkinCache.java +++ b/core/src/main/java/dev/pgm/community/nick/skin/SkinCache.java @@ -1,6 +1,6 @@ package dev.pgm.community.nick.skin; -import static dev.pgm.community.nick.PlayerIdentity.PLAYER_IDENTITY; +import static dev.pgm.community.nick.identity.PlayerIdentity.PLAYER_IDENTITY; import static dev.pgm.community.util.PlayerUtils.PLAYER_UTILS; import com.google.common.cache.Cache; @@ -127,6 +127,8 @@ private void refreshFakeName(Player player, Player viewer) { if (nicked && !canSeeRealName) { String nick = Integration.getNick(player); MatchPlayer matchPlayer = PGM.get().getMatchManager().getPlayer(player); + if (matchPlayer == null) return; + String displayName = PGM.get() .getNameDecorationRegistry() .getDecoratedName(player, matchPlayer.getParty().getColor()); diff --git a/core/src/main/java/dev/pgm/community/nick/skin/SkinManager.java b/core/src/main/java/dev/pgm/community/nick/skin/SkinManager.java index bf26d861..099cb29e 100644 --- a/core/src/main/java/dev/pgm/community/nick/skin/SkinManager.java +++ b/core/src/main/java/dev/pgm/community/nick/skin/SkinManager.java @@ -1,6 +1,6 @@ package dev.pgm.community.nick.skin; -import static dev.pgm.community.nick.PlayerIdentity.PLAYER_IDENTITY; +import static dev.pgm.community.nick.identity.PlayerIdentity.PLAYER_IDENTITY; import dev.pgm.community.Community; import org.bukkit.entity.Player; diff --git a/platform/platform-modern/src/main/java/dev/pgm/community/platform/modern/ModernPlayerUtils.java b/platform/platform-modern/src/main/java/dev/pgm/community/platform/modern/ModernPlayerUtils.java index 9695072a..470e75f1 100644 --- a/platform/platform-modern/src/main/java/dev/pgm/community/platform/modern/ModernPlayerUtils.java +++ b/platform/platform-modern/src/main/java/dev/pgm/community/platform/modern/ModernPlayerUtils.java @@ -1,6 +1,6 @@ package dev.pgm.community.platform.modern; -import static dev.pgm.community.nick.PlayerIdentity.PLAYER_IDENTITY; +import static dev.pgm.community.nick.identity.PlayerIdentity.PLAYER_IDENTITY; import static dev.pgm.community.util.Supports.Variant.PAPER; import com.destroystokyo.paper.profile.CraftPlayerProfile; diff --git a/platform/platform-modern/src/main/java/dev/pgm/community/platform/modern/PacketManipulations.java b/platform/platform-modern/src/main/java/dev/pgm/community/platform/modern/PacketManipulations.java index 7f80b7c8..0c3f04fb 100644 --- a/platform/platform-modern/src/main/java/dev/pgm/community/platform/modern/PacketManipulations.java +++ b/platform/platform-modern/src/main/java/dev/pgm/community/platform/modern/PacketManipulations.java @@ -1,6 +1,6 @@ package dev.pgm.community.platform.modern; -import static dev.pgm.community.nick.PlayerIdentity.PLAYER_IDENTITY; +import static dev.pgm.community.nick.identity.PlayerIdentity.PLAYER_IDENTITY; import com.github.retrooper.packetevents.event.PacketListenerCommon; import com.github.retrooper.packetevents.event.PacketListenerPriority; diff --git a/platform/platform-sportpaper/src/main/java/dev/pgm/community/platform/sportpaper/SpPlayerUtils.java b/platform/platform-sportpaper/src/main/java/dev/pgm/community/platform/sportpaper/SpPlayerUtils.java index 3a2ec9e5..7bd21b01 100644 --- a/platform/platform-sportpaper/src/main/java/dev/pgm/community/platform/sportpaper/SpPlayerUtils.java +++ b/platform/platform-sportpaper/src/main/java/dev/pgm/community/platform/sportpaper/SpPlayerUtils.java @@ -1,6 +1,6 @@ package dev.pgm.community.platform.sportpaper; -import static dev.pgm.community.nick.PlayerIdentity.PLAYER_IDENTITY; +import static dev.pgm.community.nick.identity.PlayerIdentity.PLAYER_IDENTITY; import static dev.pgm.community.util.Supports.Variant.SPORTPAPER; import com.mojang.authlib.GameProfile;