added the scoreboard system

This commit is contained in:
Logan Magnan 2024-09-28 11:05:25 -04:00
parent ccfe0c6098
commit f250b18783
8 changed files with 529 additions and 0 deletions

201
src/main/aether/Aether.java Normal file
View File

@ -0,0 +1,201 @@
package aether;
import aether.event.BoardCreateEvent;
import aether.scoreboard.Board;
import aether.scoreboard.BoardAdapter;
import aether.scoreboard.BoardEntry;
import lombok.Getter;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
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.PlayerJoinEvent;
import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.plugin.java.JavaPlugin;
import org.bukkit.scheduler.BukkitRunnable;
import org.bukkit.scoreboard.DisplaySlot;
import org.bukkit.scoreboard.Objective;
import org.bukkit.scoreboard.Score;
import org.bukkit.scoreboard.Scoreboard;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static aether.AetherOptions.defaultOptions;
public class Aether implements Listener {
@Getter BoardAdapter adapter;
@Getter private JavaPlugin plugin;
@Getter private AetherOptions options;
private Pattern HEX_PATTERN = Pattern.compile("&#([A-Fa-f0-9]{6})");
public Aether(JavaPlugin plugin, BoardAdapter adapter, AetherOptions options) {
this.options = options;
this.plugin = plugin;
Bukkit.getPluginManager().registerEvents(this, plugin);
setAdapter(adapter);
run();
}
public Aether(JavaPlugin plugin, BoardAdapter adapter) {
this(plugin, adapter, defaultOptions());
}
public Aether(JavaPlugin plugin) {
this(plugin, null, defaultOptions());
}
private void run() {
new BukkitRunnable() {
@Override
public void run() {
if (adapter == null) return;
for (Player player : Bukkit.getOnlinePlayers()) {
Board board = Board.getByPlayer(player);
if (board != null) {
List<String> scores = adapter.getScoreboard(player, board, board.getCooldowns());
List<String> translatedScores = new ArrayList<>();
if (scores == null) {
if (!board.getEntries().isEmpty()) {
for (BoardEntry boardEntry : board.getEntries()) {
boardEntry.remove();
}
board.getEntries().clear();
}
continue;
}
for (String line : scores) {
translatedScores.add(ChatColor.translateAlternateColorCodes('&', line));
}
if (!options.scoreDirectionDown()) {
Collections.reverse(scores);
}
Scoreboard scoreboard = board.getScoreboard();
Objective objective = board.getObjective();
if (!(objective.getDisplayName().equals(adapter.getTitle(player)))) {
objective.setDisplayName(ChatColor.translateAlternateColorCodes('&', adapter.getTitle(player)));
}
outer:
for (int i = 0; i < scores.size(); i++) {
String text = scores.get(i);
int position;
if (options.scoreDirectionDown()) {
position = 15 - i;
} else {
position = i + 1;
}
Iterator<BoardEntry> iterator = new ArrayList<>(board.getEntries()).iterator();
while (iterator.hasNext()) {
BoardEntry boardEntry = iterator.next();
Score score = objective.getScore(boardEntry.getKey());
if (score != null && boardEntry.getText().equals(ChatColor.translateAlternateColorCodes('&', text))) {
if (score.getScore() == position) {
continue outer;
}
}
}
int positionToSearch = options.scoreDirectionDown() ? 15 - position : position - 1;
iterator = board.getEntries().iterator();
while (iterator.hasNext()) {
BoardEntry boardEntry = iterator.next();
int entryPosition = scoreboard.getObjective(DisplaySlot.SIDEBAR).getScore(boardEntry.getKey()).getScore();
if (!options.scoreDirectionDown()) {
if (entryPosition > scores.size()) {
iterator.remove();
boardEntry.remove();
}
}
}
BoardEntry entry = board.getByPosition(positionToSearch);
if (entry == null) {
new BoardEntry(board, text).send(position);
} else {
entry.setText(text).setup().send(position);
}
if (board.getEntries().size() > scores.size()) {
iterator = board.getEntries().iterator();
while (iterator.hasNext()) {
BoardEntry boardEntry = iterator.next();
if ((!translatedScores.contains(boardEntry.getText())) || Collections.frequency(board.getBoardEntriesFormatted(), boardEntry.getText()) > 1) {
iterator.remove();
boardEntry.remove();
}
}
}
}
adapter.onScoreboardCreate(player, scoreboard);
player.setScoreboard(scoreboard);
}
}
}
}.runTaskTimerAsynchronously(plugin, 20L, 2L);
}
public void setAdapter(BoardAdapter adapter) {
this.adapter = adapter;
for (Player player : Bukkit.getOnlinePlayers()) {
Board board = Board.getByPlayer(player);
if (board != null) {
Board.getBoards().remove(board);
}
Bukkit.getPluginManager().callEvent(new BoardCreateEvent(new Board(player, this, options), player));
}
}
@EventHandler
public void onPlayerJoinEvent(PlayerJoinEvent event) {
if (Board.getByPlayer(event.getPlayer()) == null) {
Bukkit.getPluginManager().callEvent(new BoardCreateEvent(new Board(event.getPlayer(), this, options), event.getPlayer()));
}
}
@EventHandler(priority = EventPriority.HIGHEST)
public void onPlayerQuitEvent(PlayerQuitEvent event) {
Board board = Board.getByPlayer(event.getPlayer());
if (board != null) {
Board.getBoards().remove(board);
}
}
public String colorize(String message) {
return ChatColor.translateAlternateColorCodes('&', message);
}
public String translateHexColorCodes(String message) {
char colorChar = '§';
Matcher matcher = HEX_PATTERN.matcher(message);
StringBuffer buffer = new StringBuffer(message.length() + 32);
while (matcher.find()) {
String group = matcher.group(1);
matcher.appendReplacement(buffer, "§x§" + group
.charAt(0) + '§' + group.charAt(1) + '§' + group
.charAt(2) + '§' + group.charAt(3) + '§' + group
.charAt(4) + '§' + group.charAt(5));
}
return matcher.appendTail(buffer).toString();
}
}

View File

@ -0,0 +1,18 @@
package aether;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.Accessors;
@Getter
@Setter
@Accessors(chain = true, fluent = true)
public class AetherOptions {
private boolean hook;
private boolean scoreDirectionDown;
static AetherOptions defaultOptions() {
return new AetherOptions().hook(false).scoreDirectionDown(false);
}
}

View File

@ -0,0 +1,27 @@
package aether.event;
import aether.scoreboard.Board;
import lombok.Getter;
import org.bukkit.entity.Player;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
public class BoardCreateEvent extends Event {
private static final HandlerList HANDLERS = new HandlerList();
@Getter private final Board board;
@Getter private final Player player;
public BoardCreateEvent(Board board, Player player) {
this.board = board;
this.player = player;
}
public static HandlerList getHandlerList() {
return HANDLERS;
}
public HandlerList getHandlers() {
return HANDLERS;
}
}

View File

@ -0,0 +1,129 @@
package aether.scoreboard;
import aether.Aether;
import aether.AetherOptions;
import aether.scoreboard.cooldown.BoardCooldown;
import lombok.Getter;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.entity.Player;
import org.bukkit.scoreboard.DisplaySlot;
import org.bukkit.scoreboard.Objective;
import org.bukkit.scoreboard.Scoreboard;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class Board {
@Getter private static Set<Board> boards = new HashSet<>();
private final Aether aether;
private final AetherOptions options;
@Getter private Scoreboard scoreboard;
@Getter private Player player;
@Getter private Objective objective;
@Getter private Set<String> keys;
@Getter private List<BoardEntry> entries;
private Set<BoardCooldown> cooldowns;
public Board(Player player, Aether aether, AetherOptions options) {
this.player = player;
this.aether = aether;
this.options = options;
this.keys = new HashSet<>();
this.entries = new ArrayList<>();
this.cooldowns = new HashSet<>();
setup();
}
public static Board getByPlayer(Player player) {
for (Board board : boards) {
if (board.getPlayer().getName().equals(player.getName())) {
return board;
}
}
return null;
}
private void setup() {
if (options.hook() && !player.getScoreboard().equals(Bukkit.getScoreboardManager().getMainScoreboard())) {
scoreboard = player.getScoreboard();
} else {
scoreboard = Bukkit.getScoreboardManager().getNewScoreboard();
}
objective = scoreboard.registerNewObjective("glaedr_is_shit", "dummy");
objective.setDisplaySlot(DisplaySlot.SIDEBAR);
if (aether.getAdapter() != null) {
objective.setDisplayName(ChatColor.translateAlternateColorCodes('&', aether.getAdapter().getTitle(player)));
} else {
objective.setDisplayName("Default Title");
}
boards.add(this);
}
public String getNewKey(BoardEntry entry) {
for (ChatColor color : ChatColor.values()) {
String colorText = color + "" + ChatColor.WHITE;
if (entry.getText().length() > 16) {
String sub = entry.getText().substring(0, 16);
colorText = colorText + ChatColor.getLastColors(sub);
}
if (!keys.contains(colorText)) {
keys.add(colorText);
return colorText;
}
}
throw new IndexOutOfBoundsException("No more keys available!");
}
public List<String> getBoardEntriesFormatted() {
List<String> toReturn = new ArrayList<>();
for (BoardEntry entry : new ArrayList<>(entries)) {
toReturn.add(entry.getText());
}
return toReturn;
}
public BoardEntry getByPosition(int position) {
int i = 0;
for (BoardEntry board : entries) {
if (i == position) {
return board;
}
i++;
}
return null;
}
public BoardCooldown getCooldown(String id) {
for (BoardCooldown cooldown : getCooldowns()) {
if (cooldown.getId().equals(id)) {
return cooldown;
}
}
return null;
}
public Set<BoardCooldown> getCooldowns() {
cooldowns.removeIf(cooldown -> System.currentTimeMillis() >= cooldown.getEnd());
return cooldowns;
}
}

View File

@ -0,0 +1,17 @@
package aether.scoreboard;
import aether.scoreboard.cooldown.BoardCooldown;
import org.bukkit.entity.Player;
import org.bukkit.scoreboard.Scoreboard;
import java.util.List;
import java.util.Set;
public interface BoardAdapter {
String getTitle(Player player);
List<String> getScoreboard(Player player, Board board, Set<BoardCooldown> cooldowns);
void onScoreboardCreate(Player player, Scoreboard board);
}

View File

@ -0,0 +1,90 @@
package aether.scoreboard;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.Accessors;
import org.bukkit.ChatColor;
import org.bukkit.scoreboard.Objective;
import org.bukkit.scoreboard.Score;
import org.bukkit.scoreboard.Scoreboard;
import org.bukkit.scoreboard.Team;
@Accessors(chain = true)
public class BoardEntry {
@Getter private Board board;
@Getter @Setter private String text;
@Getter private String originalText;
@Getter private String key;
@Getter private Team team;
public BoardEntry(Board board, String text) {
this.board = board;
this.text = text;
this.originalText = text;
this.key = board.getNewKey(this);
setup();
}
public BoardEntry setup() {
Scoreboard scoreboard = board.getScoreboard();
text = ChatColor.translateAlternateColorCodes('&', text);
String teamName = key;
if (teamName.length() > 16) {
teamName = teamName.substring(0, 16);
}
if (scoreboard.getTeam(teamName) != null) {
team = scoreboard.getTeam(teamName);
} else {
team = scoreboard.registerNewTeam(teamName);
}
if (!(team.getEntries().contains(key))) {
team.addEntry(key);
}
if (!(board.getEntries().contains(this))) {
board.getEntries().add(this);
}
return this;
}
public BoardEntry send(int position) {
Objective objective = board.getObjective();
if (text.length() > 16) {
boolean fix = text.toCharArray()[15] == ChatColor.COLOR_CHAR;
String prefix = fix ? text.substring(0, 15) : text.substring(0, 16);
String suffix = fix ? text.substring(15) : ChatColor.getLastColors(prefix) + text.substring(16);
team.setPrefix(prefix);
if (suffix.length() > 16) {
team.setSuffix(suffix.substring(0, 16));
} else {
team.setSuffix(suffix);
}
} else {
team.setPrefix(text);
team.setSuffix("");
}
Score score = objective.getScore(key);
score.setScore(position);
return this;
}
public void remove() {
board.getKeys().remove(key);
board.getScoreboard().resetScores(key);
}
}

View File

@ -0,0 +1,42 @@
package aether.scoreboard.cooldown;
import aether.scoreboard.Board;
import lombok.Getter;
import org.apache.commons.lang3.time.DurationFormatUtils;
import java.text.DecimalFormat;
public class BoardCooldown {
private static final DecimalFormat SECONDS_FORMATTER = new DecimalFormat("#0.0");
@Getter private final Board board;
@Getter private final String id;
@Getter private final double duration;
@Getter private final long end;
public BoardCooldown(Board board, String id, double duration) {
this.board = board;
this.id = id;
this.duration = duration;
this.end = (long) (System.currentTimeMillis() + (duration * 1000));
board.getCooldowns().add(this);
}
public String getFormattedString(BoardFormat format) {
if (format == null) {
throw new NullPointerException();
}
if (format == BoardFormat.SECONDS) {
return SECONDS_FORMATTER.format(((end - System.currentTimeMillis()) / 1000.0f));
} else {
return DurationFormatUtils.formatDuration(end - System.currentTimeMillis(), "mm:ss");
}
}
public void cancel() {
board.getCooldowns().remove(this);
}
}

View File

@ -0,0 +1,5 @@
package aether.scoreboard.cooldown;
public enum BoardFormat {
SECONDS, MINUTES, HOURS
}