package tc.oc.commons.bukkit.nick; import javax.annotation.Nullable; import javax.inject.Inject; import javax.inject.Singleton; import org.bukkit.Skin; import org.bukkit.entity.Player; import org.bukkit.scoreboard.Scoreboard; import org.bukkit.scoreboard.Team; import tc.oc.commons.bukkit.chat.FlairRenderer; import tc.oc.commons.bukkit.chat.FullNameRenderer; import tc.oc.commons.bukkit.chat.NameStyle; import tc.oc.commons.bukkit.chat.NameType; import tc.oc.commons.core.scheduler.Scheduler; /** * Manages the Bukkit aspects of a player's name and appearance */ @Singleton public class PlayerAppearanceChanger { private static final NameType REAL_NAME_TYPE = new NameType(NameStyle.VERBOSE, true, true, false, false, false); private static final NameType NICKNAME_TYPE = new NameType(NameStyle.VERBOSE, true, false, false, false, false); private final IdentityProvider identityProvider; private final NicknameConfiguration config; private final Scheduler scheduler; private final FullNameRenderer nameRenderer; private final UsernameRenderer usernameRenderer; private final FlairRenderer flairRenderer; @Inject PlayerAppearanceChanger(IdentityProvider identityProvider, NicknameConfiguration config, Scheduler scheduler, FullNameRenderer nameRenderer, UsernameRenderer usernameRenderer, FlairRenderer flairRenderer) { this.identityProvider = identityProvider; this.config = config; this.scheduler = scheduler; this.nameRenderer = nameRenderer; this.usernameRenderer = usernameRenderer; this.flairRenderer = flairRenderer; } /** * Refresh the given player's appearance for all viewers. */ public void refreshPlayer(Player player) { refreshPlayer(player, identityProvider.currentIdentity(player)); } /** * Refresh the given player's appearance for all viewers, assuming the given identity is their current one. * * This is necessary if the player's identity changes, or if their current identity's name is invalidated. */ public void refreshPlayer(final Player player, final Identity identity) { player.setDisplayName(nameRenderer.getLegacyName(identity, REAL_NAME_TYPE)); final String legacyNickname = renderLegacyNickname(identity); for(Player viewer : player.getServer().getOnlinePlayers()) { refreshFakeNameAndSkin(player, identity, legacyNickname, viewer); } if(config.overheadFlair()) { String prefix = usernameRenderer.getColor(identity, REAL_NAME_TYPE).toString(); if(identity.getNickname() == null) { prefix = flairRenderer.getLegacyName(identity, REAL_NAME_TYPE) + prefix; } setOverheadNamePrefix(player, prefix); } } /** * Refresh the appearance of all players for the given viewer */ public void refreshViewer(final Player viewer) { for(Player player : viewer.getServer().getOnlinePlayers()) { final Identity identity = identityProvider.currentIdentity(player); refreshFakeNameAndSkin(player, identity, renderLegacyNickname(identity), viewer); } } /** * Release any resources being used to maintain the given player's appearance */ public void cleanupAfterPlayer(Player player) { if(config.overheadFlair()) { // Remove players from their "overhead flair team" on quit final Team team = player.getServer().getScoreboardManager().getMainScoreboard().getPlayerTeam(player); if(team != null) { scheduler.debounceTask(() -> team.removePlayer(player)); } } } /** * Refresh the given player's fake appearance for the given viewer, assuming the given identity */ private void refreshFakeNameAndSkin(Player player, Identity identity, @Nullable String fakeDisplayName, Player viewer) { if(identity.isRevealed(viewer)) { player.setFakeNameAndSkin(viewer, null, null); player.setFakeDisplayName(viewer, null); } else { player.setFakeNameAndSkin(viewer, identity.getNickname(), Skin.EMPTY); player.setFakeDisplayName(viewer, fakeDisplayName); } } /** * Sets a prefix for a player's overhead name by adding them to a scoreboard team. * Don't use this if scoreboard teams are being used for any other purpose. */ private static void setOverheadNamePrefix(Player player, String prefix) { final Scoreboard scoreboard = player.getServer().getScoreboardManager().getMainScoreboard(); prefix = prefix.substring(0, Math.min(prefix.length(), 14)); Team team = scoreboard.getTeam(prefix); if(team == null) { team = scoreboard.registerNewTeam(prefix); team.setPrefix(prefix); team.setOption(Team.Option.COLLISION_RULE, Team.OptionStatus.NEVER); } team.addPlayer(player); } private @Nullable String renderLegacyNickname(Identity identity) { return identity.getNickname() == null ? null : nameRenderer.getLegacyName(identity, NICKNAME_TYPE); } }