diff --git a/gradle.properties b/gradle.properties index 6321981..7c26eff 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1 +1 @@ -version = 1.6.10 \ No newline at end of file +version = 1.6.11 \ No newline at end of file diff --git a/src/main/java/org/alexdev/unlimitednametags/api/UNTAPI.java b/src/main/java/org/alexdev/unlimitednametags/api/UNTAPI.java index 5857adb..5b2833f 100644 --- a/src/main/java/org/alexdev/unlimitednametags/api/UNTAPI.java +++ b/src/main/java/org/alexdev/unlimitednametags/api/UNTAPI.java @@ -2,6 +2,7 @@ package org.alexdev.unlimitednametags.api; import me.tofaa.entitylib.meta.display.AbstractDisplayMeta; +import net.kyori.adventure.text.Component; import org.alexdev.unlimitednametags.UnlimitedNameTags; import org.alexdev.unlimitednametags.config.Settings; import org.alexdev.unlimitednametags.hook.hat.HatHook; @@ -359,6 +360,60 @@ public class UNTAPI { plugin.getNametagManager().setNametagOverride(player, modified); } + /** + * Forces a specific component as the nametag for a player. + * This overrides any existing nametag configuration or overrides. + * + * @param player the player whose nametag should be forced + * @param component the component to display as the nametag + */ + public void setForcedNametag(@NotNull Player player, @NotNull Component component) { + plugin.getNametagManager().getPacketDisplayText(player).ifPresent(packetNameTag -> { + packetNameTag.setForcedNameTag(component); + packetNameTag.refresh(); + }); + } + + /** + * Forces a specific component as the nametag for a player, visible only to a specific viewer. + * This overrides any existing nametag configuration or overrides for that specific viewer. + * + * @param player the player whose nametag should be forced + * @param viewer the player who will see the forced nametag + * @param component the component to display as the nametag + */ + public void setForcedNametag(@NotNull Player player, @NotNull Player viewer, @NotNull Component component) { + plugin.getNametagManager().getPacketDisplayText(player).ifPresent(packetNameTag -> { + packetNameTag.setForcedNameTag(viewer.getUniqueId(), component); + packetNameTag.refreshForPlayer(viewer); + }); + } + + /** + * Clears the forced nametag for a player. + * + * @param player the player whose forced nametag should be cleared + */ + public void clearForcedNametag(@NotNull Player player) { + plugin.getNametagManager().getPacketDisplayText(player).ifPresent(packetNameTag -> { + packetNameTag.clearForcedNameTag(); + packetNameTag.refresh(); + }); + } + + /** + * Clears the forced nametag for a player, for a specific viewer. + * + * @param player the player whose forced nametag should be cleared + * @param viewer the player who was seeing the forced nametag + */ + public void clearForcedNametag(@NotNull Player player, @NotNull Player viewer) { + plugin.getNametagManager().getPacketDisplayText(player).ifPresent(packetNameTag -> { + packetNameTag.clearForcedNameTag(viewer.getUniqueId()); + packetNameTag.refreshForPlayer(viewer); + }); + } + static final class NotRegisteredException extends IllegalStateException { private static final String MESSAGE = """ diff --git a/src/main/java/org/alexdev/unlimitednametags/packet/PacketNameTag.java b/src/main/java/org/alexdev/unlimitednametags/packet/PacketNameTag.java index 9366d20..4814093 100644 --- a/src/main/java/org/alexdev/unlimitednametags/packet/PacketNameTag.java +++ b/src/main/java/org/alexdev/unlimitednametags/packet/PacketNameTag.java @@ -27,6 +27,7 @@ import org.bukkit.Location; import org.bukkit.attribute.AttributeInstance; import org.bukkit.entity.Player; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.util.*; import java.util.function.Consumer; @@ -43,6 +44,9 @@ public class PacketNameTag { private final Set blocked; @NotNull private final Map relationalCache; + private final Map calculatedTextCache = Maps.newConcurrentMap(); + private final Map forcedRelationalText = Maps.newConcurrentMap(); + private volatile Component forcedGlobalText = null; private long lastUpdate; @Setter private boolean visible; @@ -51,7 +55,7 @@ public class PacketNameTag { private float scale; private float offset; private float increasedOffset; - private boolean removed; + private volatile boolean removed; @Setter private boolean sneaking; @@ -84,6 +88,16 @@ public class PacketNameTag { return false; } + calculatedTextCache.put(player.getUniqueId(), text); + + if (forcedRelationalText.containsKey(player.getUniqueId()) || forcedGlobalText != null) { + return false; + } + + return applyText(player, text); + } + + private boolean applyText(@NotNull Player player, @NotNull Component text) { if (text.equals(relationalCache.get(player.getUniqueId()))) { return false; } @@ -99,7 +113,7 @@ public class PacketNameTag { return true; } - public void modify(User user, Consumer consumer) { + public void modify(@Nullable User user, @NotNull Consumer consumer) { if (removed) { return; } @@ -114,7 +128,7 @@ public class PacketNameTag { }); } - public void modifyOwner(Consumer consumer) { + public void modifyOwner(@NotNull Consumer consumer) { if (removed) { return; } @@ -127,7 +141,7 @@ public class PacketNameTag { modify(owner, consumer); } - public void modifyOwnerEntity(Consumer consumer) { + public void modifyOwnerEntity(@NotNull Consumer consumer) { if (removed) { return; } @@ -140,7 +154,7 @@ public class PacketNameTag { modifyEntity(owner, consumer); } - public void modify(Consumer consumer) { + public void modify(@NotNull Consumer consumer) { if (removed) { return; } @@ -151,7 +165,7 @@ public class PacketNameTag { }); } - public void modifyEntity(User user, Consumer consumer) { + public void modifyEntity(@Nullable User user, @NotNull Consumer consumer) { if (removed) { return; } @@ -159,7 +173,7 @@ public class PacketNameTag { perPlayerEntity.modify(user, consumer); } - public void modifyEntity(Consumer consumer) { + public void modifyEntity(@NotNull Consumer consumer) { if (removed) { return; } @@ -354,7 +368,7 @@ public class PacketNameTag { } private boolean hasRequiredPermissions(@NotNull Player player) { - boolean isOwner = isOwner(player); + final boolean isOwner = isOwner(player); if (!isOwner && !player.hasPermission("unt.shownametags")) { return false; } @@ -439,6 +453,7 @@ public class PacketNameTag { } relationalCache.remove(player.getUniqueId()); + calculatedTextCache.remove(player.getUniqueId()); plugin.getPacketManager().removePassenger(player, entityId); } @@ -463,6 +478,7 @@ public class PacketNameTag { } perPlayerEntity.getEntities().remove(player.getUniqueId()); relationalCache.remove(player.getUniqueId()); + calculatedTextCache.remove(player.getUniqueId()); } public boolean canPlayerSee(@NotNull Player player) { @@ -520,6 +536,9 @@ public class PacketNameTag { plugin.getPacketManager().removePassenger(entityId); relationalCache.clear(); + calculatedTextCache.clear(); + forcedRelationalText.clear(); + forcedGlobalText = null; } public void handleQuit(@NotNull Player player) { @@ -571,6 +590,51 @@ public class PacketNameTag { return properties; } + public void setForcedNameTag(@NotNull Component component) { + this.forcedGlobalText = component; + updateAllViewers(); + } + + public void setForcedNameTag(@NotNull UUID viewer, @NotNull Component component) { + this.forcedRelationalText.put(viewer, component); + updateViewer(viewer); + } + + public void clearForcedNameTag() { + this.forcedGlobalText = null; + updateAllViewers(); + } + + public void clearForcedNameTag(@NotNull UUID viewer) { + this.forcedRelationalText.remove(viewer); + updateViewer(viewer); + } + + private void updateAllViewers() { + for (final UUID uuid : getViewers()) { + updateViewer(uuid); + } + } + + private void updateViewer(@NotNull UUID uuid) { + final Player player = Bukkit.getPlayer(uuid); + if (player == null) return; + + Component textToUse = calculatedTextCache.get(uuid); + + if (forcedRelationalText.containsKey(uuid)) { + textToUse = forcedRelationalText.get(uuid); + } else if (forcedGlobalText != null) { + textToUse = forcedGlobalText; + } + + if (textToUse != null) { + if (applyText(player, textToUse)) { + refreshForPlayer(player); + } + } + } + /** * Returns an unmodifiable set containing the viewers of the nametag. *