feat(identity): display Leaf protection prefixes
This commit is contained in:
@@ -0,0 +1,111 @@
|
||||
package games.dmg.leaf;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.logging.Logger;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.Server;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.scoreboard.Scoreboard;
|
||||
import org.bukkit.scoreboard.ScoreboardManager;
|
||||
import org.bukkit.scoreboard.Team;
|
||||
|
||||
/** Conservatively decorates standard Spigot player identity surfaces. */
|
||||
public final class LeafIdentity {
|
||||
private static final String TEAM_NAME = "leaf_protected";
|
||||
|
||||
private record Names(String display, String playerList, String decoratedDisplay, String decoratedList) { }
|
||||
|
||||
private final Server server;
|
||||
private final Logger logger;
|
||||
private final Map<UUID, Names> names = new HashMap<>();
|
||||
private final Map<UUID, String> teamEntries = new HashMap<>();
|
||||
|
||||
public LeafIdentity(Server server, Logger logger) {
|
||||
this.server = server;
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
public synchronized void apply(Player player, String configuredPrefix) {
|
||||
String prefix = ChatColor.translateAlternateColorCodes('&', configuredPrefix);
|
||||
UUID playerId = player.getUniqueId();
|
||||
Names previous = names.get(playerId);
|
||||
String baseDisplay = previous == null ? player.getDisplayName() : previous.display();
|
||||
String baseList = previous == null ? player.getPlayerListName() : previous.playerList();
|
||||
String decoratedDisplay = prefix + baseDisplay;
|
||||
String decoratedList = prefix + baseList;
|
||||
|
||||
if (previous == null || player.getDisplayName().equals(previous.decoratedDisplay())) {
|
||||
player.setDisplayName(decoratedDisplay);
|
||||
}
|
||||
if (previous == null || player.getPlayerListName().equals(previous.decoratedList())) {
|
||||
player.setPlayerListName(decoratedList);
|
||||
}
|
||||
names.put(playerId, new Names(baseDisplay, baseList, decoratedDisplay, decoratedList));
|
||||
applyOverhead(player, prefix);
|
||||
}
|
||||
|
||||
public synchronized void remove(Player player) {
|
||||
UUID playerId = player.getUniqueId();
|
||||
Names installed = names.remove(playerId);
|
||||
if (installed != null) {
|
||||
if (player.getDisplayName().equals(installed.decoratedDisplay())) {
|
||||
player.setDisplayName(installed.display());
|
||||
}
|
||||
if (player.getPlayerListName().equals(installed.decoratedList())) {
|
||||
player.setPlayerListName(installed.playerList());
|
||||
}
|
||||
}
|
||||
|
||||
String entry = teamEntries.remove(playerId);
|
||||
Team team = leafTeam(false);
|
||||
if (entry != null && team != null) {
|
||||
team.removeEntry(entry);
|
||||
}
|
||||
}
|
||||
|
||||
private void applyOverhead(Player player, String prefix) {
|
||||
Scoreboard scoreboard = mainScoreboard();
|
||||
if (scoreboard == null) {
|
||||
return;
|
||||
}
|
||||
String entry = player.getName();
|
||||
Team existing = scoreboard.getEntryTeam(entry);
|
||||
Team leafTeam = leafTeam(true);
|
||||
if (leafTeam == null) {
|
||||
return;
|
||||
}
|
||||
if (existing != null && existing != leafTeam) {
|
||||
logger.fine("Leaf overhead prefix skipped for " + entry + ": player uses another team");
|
||||
return;
|
||||
}
|
||||
String oldEntry = teamEntries.put(player.getUniqueId(), entry);
|
||||
if (oldEntry != null && !oldEntry.equals(entry)) {
|
||||
leafTeam.removeEntry(oldEntry);
|
||||
}
|
||||
leafTeam.setPrefix(prefix);
|
||||
leafTeam.addEntry(entry);
|
||||
}
|
||||
|
||||
private Team leafTeam(boolean create) {
|
||||
Scoreboard scoreboard = mainScoreboard();
|
||||
if (scoreboard == null) {
|
||||
return null;
|
||||
}
|
||||
Team team = scoreboard.getTeam(TEAM_NAME);
|
||||
if (team == null && create) {
|
||||
try {
|
||||
team = scoreboard.registerNewTeam(TEAM_NAME);
|
||||
} catch (IllegalArgumentException exception) {
|
||||
logger.warning("Could not create Leaf scoreboard team: " + exception.getMessage());
|
||||
}
|
||||
}
|
||||
return team;
|
||||
}
|
||||
|
||||
private Scoreboard mainScoreboard() {
|
||||
ScoreboardManager manager = server.getScoreboardManager();
|
||||
return manager == null ? null : manager.getMainScoreboard();
|
||||
}
|
||||
}
|
||||
@@ -26,7 +26,7 @@ public final class LeafListener implements Listener {
|
||||
runtime.observe(event.getPlayer(), clock.instant());
|
||||
} catch (IOException exception) {
|
||||
logger.log(Level.SEVERE, "Could not persist Leaf player state on join", exception);
|
||||
runtime.removeProtection(event.getPlayer());
|
||||
runtime.removePresentation(event.getPlayer());
|
||||
event.getPlayer().sendMessage("Leaf protection is unavailable because state could not be saved.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,8 @@ public final class LeafPlugin extends JavaPlugin {
|
||||
getServer(),
|
||||
settingsProvider,
|
||||
stateManager,
|
||||
new LeafProtection()
|
||||
new LeafProtection(),
|
||||
new LeafIdentity(getServer(), getLogger())
|
||||
);
|
||||
registerRuntime();
|
||||
} catch (IllegalArgumentException | IOException exception) {
|
||||
@@ -43,7 +44,7 @@ public final class LeafPlugin extends JavaPlugin {
|
||||
public void onDisable() {
|
||||
if (runtime != null) {
|
||||
for (Player player : getServer().getOnlinePlayers()) {
|
||||
runtime.removeProtection(player);
|
||||
runtime.removePresentation(player);
|
||||
}
|
||||
}
|
||||
saveState();
|
||||
|
||||
@@ -27,17 +27,20 @@ public final class LeafRuntime {
|
||||
private final LeafSettingsProvider settingsProvider;
|
||||
private final LeafStateManager stateManager;
|
||||
private final LeafProtection protection;
|
||||
private final LeafIdentity identity;
|
||||
|
||||
public LeafRuntime(
|
||||
Server server,
|
||||
LeafSettingsProvider settingsProvider,
|
||||
LeafStateManager stateManager,
|
||||
LeafProtection protection
|
||||
LeafProtection protection,
|
||||
LeafIdentity identity
|
||||
) {
|
||||
this.server = Objects.requireNonNull(server, "server");
|
||||
this.settingsProvider = Objects.requireNonNull(settingsProvider, "settingsProvider");
|
||||
this.stateManager = Objects.requireNonNull(stateManager, "stateManager");
|
||||
this.protection = Objects.requireNonNull(protection, "protection");
|
||||
this.identity = Objects.requireNonNull(identity, "identity");
|
||||
}
|
||||
|
||||
public PlayerLeafState observe(Player player, Instant observedAt) throws IOException {
|
||||
@@ -105,13 +108,15 @@ public final class LeafRuntime {
|
||||
LeafSettings settings = settingsProvider.current();
|
||||
if (state != null && state.optedIn() && settings.enabled()) {
|
||||
protection.apply(player, settings.resistanceLevel());
|
||||
identity.apply(player, settings.prefix());
|
||||
} else {
|
||||
protection.remove(player);
|
||||
removePresentation(player);
|
||||
}
|
||||
}
|
||||
|
||||
public void removeProtection(Player player) {
|
||||
public void removePresentation(Player player) {
|
||||
protection.remove(player);
|
||||
identity.remove(player);
|
||||
}
|
||||
|
||||
public LeafStateManager stateManager() {
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
package games.dmg.leaf;
|
||||
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.util.UUID;
|
||||
import java.util.logging.Logger;
|
||||
import org.bukkit.Server;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.scoreboard.Scoreboard;
|
||||
import org.bukkit.scoreboard.ScoreboardManager;
|
||||
import org.bukkit.scoreboard.Team;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
final class LeafIdentityTest {
|
||||
@Test
|
||||
void decoratesTabChatAndAnAvailableOverheadTeamThenRestoresNames() {
|
||||
Fixtures fixtures = fixtures();
|
||||
LeafIdentity identity = new LeafIdentity(fixtures.server(), Logger.getAnonymousLogger());
|
||||
|
||||
identity.apply(fixtures.player(), "&a🍃 ");
|
||||
|
||||
String decorated = "§a🍃 Alex";
|
||||
verify(fixtures.player()).setDisplayName(decorated);
|
||||
verify(fixtures.player()).setPlayerListName(decorated);
|
||||
verify(fixtures.team()).setPrefix("§a🍃 ");
|
||||
verify(fixtures.team()).addEntry("Alex");
|
||||
|
||||
when(fixtures.player().getDisplayName()).thenReturn(decorated);
|
||||
when(fixtures.player().getPlayerListName()).thenReturn(decorated);
|
||||
identity.remove(fixtures.player());
|
||||
|
||||
verify(fixtures.player()).setDisplayName("Alex");
|
||||
verify(fixtures.player()).setPlayerListName("Alex");
|
||||
verify(fixtures.team()).removeEntry("Alex");
|
||||
}
|
||||
|
||||
@Test
|
||||
void leavesThirdPartyChangesAndTeamsUntouched() {
|
||||
Fixtures fixtures = fixtures();
|
||||
Team otherTeam = mock(Team.class);
|
||||
when(fixtures.scoreboard().getEntryTeam("Alex")).thenReturn(otherTeam);
|
||||
LeafIdentity identity = new LeafIdentity(fixtures.server(), Logger.getAnonymousLogger());
|
||||
identity.apply(fixtures.player(), "&a🍃 ");
|
||||
when(fixtures.player().getDisplayName()).thenReturn("ThirdPartyAlex");
|
||||
when(fixtures.player().getPlayerListName()).thenReturn("ThirdPartyAlex");
|
||||
|
||||
identity.remove(fixtures.player());
|
||||
|
||||
verify(otherTeam, never()).removeEntry("Alex");
|
||||
verify(fixtures.player(), never()).setDisplayName("Alex");
|
||||
verify(fixtures.player(), never()).setPlayerListName("Alex");
|
||||
}
|
||||
|
||||
private static Fixtures fixtures() {
|
||||
Server server = mock(Server.class);
|
||||
ScoreboardManager manager = mock(ScoreboardManager.class);
|
||||
Scoreboard scoreboard = mock(Scoreboard.class);
|
||||
Team team = mock(Team.class);
|
||||
Player player = mock(Player.class);
|
||||
when(server.getScoreboardManager()).thenReturn(manager);
|
||||
when(manager.getMainScoreboard()).thenReturn(scoreboard);
|
||||
when(scoreboard.getTeam("leaf_protected")).thenReturn(team);
|
||||
when(player.getUniqueId()).thenReturn(UUID.randomUUID());
|
||||
when(player.getName()).thenReturn("Alex");
|
||||
when(player.getDisplayName()).thenReturn("Alex");
|
||||
when(player.getPlayerListName()).thenReturn("Alex");
|
||||
return new Fixtures(server, scoreboard, team, player);
|
||||
}
|
||||
|
||||
private record Fixtures(Server server, Scoreboard scoreboard, Team team, Player player) { }
|
||||
}
|
||||
@@ -91,7 +91,8 @@ final class LeafRuntimeTest {
|
||||
server,
|
||||
new LeafSettingsProvider(LeafSettings.from(Map.of())),
|
||||
new LeafStateManager(new YamlLeafStateRepository(stateFile)),
|
||||
protection
|
||||
protection,
|
||||
mock(LeafIdentity.class)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user