feat(participation): highlight opted-out players
Release / release (push) Successful in 2m40s
CI / build (push) Successful in 1m8s

This commit is contained in:
dmg
2026-08-15 09:13:34 -04:00
parent ea267ff867
commit 323d177573
7 changed files with 345 additions and 0 deletions
@@ -0,0 +1,127 @@
package games.dmg.spigottyrant;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
import org.bukkit.ChatColor;
import org.bukkit.Server;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.scoreboard.Scoreboard;
import org.bukkit.scoreboard.ScoreboardManager;
import org.bukkit.scoreboard.Team;
public final class OptOutGlowController implements Runnable, Listener {
private static final String TEAM_NAME = "tyrant-optout";
private final TyrantStateManager stateManager;
private final Server server;
private final PluginSettings settings;
private final Scoreboard scoreboard;
private final Team greenTeam;
private final Map<UUID, PreviousPresentation> managed = new HashMap<>();
public OptOutGlowController(
TyrantStateManager stateManager,
Server server,
PluginSettings settings
) {
this.stateManager = stateManager;
this.server = server;
this.settings = settings;
ScoreboardManager manager = Objects.requireNonNull(
server.getScoreboardManager(), "Scoreboard manager is unavailable"
);
scoreboard = manager.getMainScoreboard();
Team existing = scoreboard.getTeam(TEAM_NAME);
greenTeam = existing == null ? scoreboard.registerNewTeam(TEAM_NAME) : existing;
greenTeam.setColor(ChatColor.GREEN);
}
@Override
public void run() {
GameState game = stateManager.game();
Player tyrant = game.tyrantId().map(server::getPlayer).orElse(null);
boolean tyrantAvailable = tyrant != null && !tyrant.isDead();
Set<UUID> online = new HashSet<>();
for (Player player : server.getOnlinePlayers()) {
online.add(player.getUniqueId());
PlayerState state = stateManager.players().get(player.getUniqueId());
boolean sameWorld = tyrantAvailable && !player.isDead()
&& player.getWorld().equals(tyrant.getWorld());
double distanceSquared = sameWorld
? player.getLocation().distanceSquared(tyrant.getLocation())
: Double.POSITIVE_INFINITY;
boolean shouldGlow = state != null && OptOutGlowPolicy.shouldGlow(
game, state, tyrantAvailable, sameWorld, distanceSquared,
settings.tyrantRangeBlocks()
);
if (shouldGlow && !managed.containsKey(player.getUniqueId())) {
apply(player);
} else if (!shouldGlow && managed.containsKey(player.getUniqueId())) {
restore(player.getUniqueId(), player);
}
}
for (UUID playerId : Set.copyOf(managed.keySet())) {
if (!online.contains(playerId) && server.getPlayer(playerId) == null) {
restore(playerId, null);
}
}
}
@EventHandler(priority = EventPriority.MONITOR)
public void onQuit(PlayerQuitEvent event) {
if (managed.containsKey(event.getPlayer().getUniqueId())) {
restore(event.getPlayer().getUniqueId(), event.getPlayer());
}
}
public void clearAll() {
for (UUID playerId : Set.copyOf(managed.keySet())) {
restore(playerId, server.getPlayer(playerId));
}
}
private void apply(Player player) {
Team previousTeam = scoreboard.getEntryTeam(player.getName());
if (previousTeam == greenTeam) {
previousTeam = null;
}
managed.put(
player.getUniqueId(),
new PreviousPresentation(player.getName(), previousTeam, player.isGlowing())
);
greenTeam.addEntry(player.getName());
player.setGlowing(true);
}
private void restore(UUID playerId, Player player) {
PreviousPresentation previous = managed.remove(playerId);
if (previous == null) {
return;
}
greenTeam.removeEntry(previous.playerName());
if (previous.team() != null) {
try {
previous.team().addEntry(previous.playerName());
} catch (IllegalStateException ignored) {
// The prior team was removed while this plugin managed the entry.
}
}
if (player != null) {
player.setGlowing(previous.glowing());
}
}
private record PreviousPresentation(
String playerName,
Team team,
boolean glowing
) {
}
}
@@ -0,0 +1,22 @@
package games.dmg.spigottyrant;
public final class OptOutGlowPolicy {
private OptOutGlowPolicy() {
}
public static boolean shouldGlow(
GameState game,
PlayerState player,
boolean tyrantAvailable,
boolean playerAvailableInTyrantWorld,
double distanceSquared,
double rangeBlocks
) {
return game.lifecycle() == GameLifecycle.RUNNING
&& game.tyrantId().isPresent()
&& player.optedOutUntil().isPresent()
&& tyrantAvailable
&& playerAvailableInTyrantWorld
&& distanceSquared <= rangeBlocks * rangeBlocks;
}
}
@@ -15,6 +15,7 @@ public final class SpigotTyrantPlugin extends JavaPlugin {
private AssassinEffectController assassinEffects;
private FixerEffectController fixerEffects;
private TyrantEffectController tyrantEffects;
private OptOutGlowController optOutGlow;
@Override
public void onEnable() {
@@ -69,6 +70,7 @@ public final class SpigotTyrantPlugin extends JavaPlugin {
FixerAbilityService fixerAbilities = new FixerAbilityService(readiness, settings);
fixerEffects = new FixerEffectController(stateManager, getServer(), settings, clock);
tyrantEffects = new TyrantEffectController(stateManager, getServer(), settings);
optOutGlow = new OptOutGlowController(stateManager, getServer(), settings);
VigilanteCombatTracker combatTracker = new VigilanteCombatTracker();
vigilanteEffects = new VigilanteEffectController(
stateManager, getServer(), combatTracker, settings, clock
@@ -128,6 +130,7 @@ public final class SpigotTyrantPlugin extends JavaPlugin {
),
this
);
getServer().getPluginManager().registerEvents(optOutGlow, this);
getServer().getPluginManager().registerEvents(
new VigilanteCombatListener(
stateManager, combatTracker, clock, settings.vigilanteCombatDuration()
@@ -176,6 +179,7 @@ public final class SpigotTyrantPlugin extends JavaPlugin {
getServer().getScheduler().runTaskTimer(this, assassinEffects, 10L, 10L);
getServer().getScheduler().runTaskTimer(this, fixerEffects, 10L, 10L);
getServer().getScheduler().runTaskTimer(this, tyrantEffects, 10L, 10L);
getServer().getScheduler().runTaskTimer(this, optOutGlow, 10L, 10L);
getServer().getScheduler().runTaskTimer(
this,
new AbilityItemRefreshTask(
@@ -191,6 +195,9 @@ public final class SpigotTyrantPlugin extends JavaPlugin {
@Override
public void onDisable() {
if (optOutGlow != null) {
optOutGlow.clearAll();
}
if (tyrantEffects != null) {
tyrantEffects.clearAll();
}