From b679130fb4920b2490abccef16c8cbe20035ca55 Mon Sep 17 00:00:00 2001 From: takagi Date: Thu, 22 Sep 2022 12:19:46 +0900 Subject: [PATCH] init --- .github/workflows/ci.yml | 21 +++ .gitignore | 3 + LICENSE | 21 +++ README.md | 34 +++++ pom.xml | 108 +++++++++++++++ .../java/net/iamtakagi/sudachi/Board.java | 100 ++++++++++++++ .../net/iamtakagi/sudachi/BoardAdapter.java | 19 +++ .../net/iamtakagi/sudachi/BoardEntry.java | 102 ++++++++++++++ .../net/iamtakagi/sudachi/BoardManager.java | 129 ++++++++++++++++++ .../net/iamtakagi/sudachi/BoardTimer.java | 49 +++++++ 10 files changed, 586 insertions(+) create mode 100644 .github/workflows/ci.yml create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 README.md create mode 100644 pom.xml create mode 100644 src/main/java/net/iamtakagi/sudachi/Board.java create mode 100644 src/main/java/net/iamtakagi/sudachi/BoardAdapter.java create mode 100644 src/main/java/net/iamtakagi/sudachi/BoardEntry.java create mode 100644 src/main/java/net/iamtakagi/sudachi/BoardManager.java create mode 100644 src/main/java/net/iamtakagi/sudachi/BoardTimer.java diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..fd84325 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,21 @@ +name: CI +on: + release: + types: [published] + workflow_dispatch: +jobs: + publish: + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-java@v3 + with: + java-version: '18' + distribution: 'adopt' + - name: Publish package + run: mvn --batch-mode deploy + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7298e6f --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.idea/ +target/ +dependency-reduced-pom.xml \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..d0659c0 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 iamtakagi + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..dfbd5f3 --- /dev/null +++ b/README.md @@ -0,0 +1,34 @@ +# sudachi + +## Getting Started + +### Installation + +`pom.xml` +```xml + + github + GitHub Packages + https://maven.pkg.github.com/2mug1/sudachi + + + + net.iamtakagi + sudachi + 1.0.0 + compile + +``` + +`build.gradle` +```gradle +repositories { + maven (url = "https://maven.pkg.github.com/2mug1/sudachi") +} +dependencies { + implementation("net.iamtakagi:sudachi:1.0.0") +} +``` + +## LICENSE +[MIT License](./LICENSE) (© 2022 iamtakagi) \ No newline at end of file diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..ccdfc62 --- /dev/null +++ b/pom.xml @@ -0,0 +1,108 @@ + + + 4.0.0 + + + + github + GitHub Packages + https://maven.pkg.github.com/2mug1/sudachi + + + + net.iamtakagi + sudachi + 1.0.0 + + + 18 + 18 + UTF-8 + + + + + papermc + https://papermc.io/repo/repository/maven-public/ + + + + + + org.projectlombok + lombok + 1.18.24 + provided + + + commons-io + commons-io + 2.11.0 + compile + + + org.apache.commons + commons-text + 1.9 + compile + + + com.google.code.gson + gson + 2.9.0 + compile + + + io.papermc.paper + paper-api + 1.19.2-R0.1-SNAPSHOT + provided + + + + + + + org.apache.maven.plugins + maven-shade-plugin + 2.4.3 + + + package + + shade + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + compile + compile + + compile + + + + testCompile + test-compile + + testCompile + + + + + 18 + 18 + -parameters + + + + + + \ No newline at end of file diff --git a/src/main/java/net/iamtakagi/sudachi/Board.java b/src/main/java/net/iamtakagi/sudachi/Board.java new file mode 100644 index 0000000..c845200 --- /dev/null +++ b/src/main/java/net/iamtakagi/sudachi/Board.java @@ -0,0 +1,100 @@ +package net.iamtakagi.sudachi; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import lombok.Getter; +import org.bukkit.ChatColor; +import org.bukkit.entity.Player; +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.java.JavaPlugin; +import org.bukkit.scoreboard.DisplaySlot; +import org.bukkit.scoreboard.Objective; +import org.bukkit.scoreboard.Scoreboard; + +@Getter +public class Board { + + private final BoardAdapter adapter; + private final Player player; + private List entries = new ArrayList<>(); + private Set timers = new HashSet<>(); + private Set keys = new HashSet<>(); + private Scoreboard scoreboard; + private Objective objective; + + public Board(Plugin plugin, Player player, BoardAdapter adapter) { + this.adapter = adapter; + this.player = player; + + this.init(plugin); + } + + private void init(Plugin plugin) { + if (!this.player.getScoreboard().equals(plugin.getServer().getScoreboardManager().getMainScoreboard())) { + this.scoreboard = this.player.getScoreboard(); + } else { + this.scoreboard = plugin.getServer().getScoreboardManager().getNewScoreboard(); + } + + this.objective = this.scoreboard.registerNewObjective("Default", "dummy"); + + this.objective.setDisplaySlot(DisplaySlot.SIDEBAR); + this.objective.setDisplayName(this.adapter.getTitle(player)); + } + + 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 getBoardEntriesFormatted() { + List toReturn = new ArrayList<>(); + + for (BoardEntry entry : new ArrayList<>(entries)) { + toReturn.add(entry.getText()); + } + + return toReturn; + } + + public BoardEntry getByPosition(int position) { + for (int i = 0; i < this.entries.size(); i++) { + if (i == position) { + return this.entries.get(i); + } + } + + return null; + } + + public BoardTimer getCooldown(String id) { + for (BoardTimer cooldown : getTimers()) { + if (cooldown.getId().equals(id)) { + return cooldown; + } + } + + return null; + } + + public Set getTimers() { + this.timers.removeIf(cooldown -> System.currentTimeMillis() >= cooldown.getEnd()); + return this.timers; + } + +} \ No newline at end of file diff --git a/src/main/java/net/iamtakagi/sudachi/BoardAdapter.java b/src/main/java/net/iamtakagi/sudachi/BoardAdapter.java new file mode 100644 index 0000000..23210e4 --- /dev/null +++ b/src/main/java/net/iamtakagi/sudachi/BoardAdapter.java @@ -0,0 +1,19 @@ +package net.iamtakagi.sudachi; + +import java.util.List; +import org.bukkit.entity.Player; +import org.bukkit.scoreboard.Scoreboard; + +public interface BoardAdapter { + + String getTitle(Player player); + + List getScoreboard(Player player, Board board); + + long getInterval(); + + void onScoreboardCreate(Player player, Scoreboard board); + + void preLoop(); + +} \ No newline at end of file diff --git a/src/main/java/net/iamtakagi/sudachi/BoardEntry.java b/src/main/java/net/iamtakagi/sudachi/BoardEntry.java new file mode 100644 index 0000000..fa9240e --- /dev/null +++ b/src/main/java/net/iamtakagi/sudachi/BoardEntry.java @@ -0,0 +1,102 @@ +package net.iamtakagi.sudachi; + +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; + +@Getter +@Accessors(chain = true) +public class BoardEntry { + + private final Board board; + + private final String originalText; + + private Team team; + + @Setter + private String text; + private String key; + + public BoardEntry(Board board, String text) { + this.board = board; + this.text = text; + this.originalText = text; + this.key = board.getNewKey(this); + + this.setup(); + } + + public BoardEntry setup() { + Scoreboard scoreboard = this.board.getScoreboard(); + + String teamName = this.key; + if (teamName.length() > 16) { + teamName = teamName.substring(0, 16); + } + + if (scoreboard.getTeam(teamName) != null) { + this.team = scoreboard.getTeam(teamName); + } else { + this.team = scoreboard.registerNewTeam(teamName); + } + + if (!(this.team.getEntries().contains(this.key))) { + this.team.addEntry(this.key); + } + + if (!(this.board.getEntries().contains(this))) { + this.board.getEntries().add(this); + } + + return this; + } + + public BoardEntry send(int position) { + Objective objective = board.getObjective(); + + if (this.text.length() > 16) { + this.team.setPrefix(this.text.substring(0, 16)); + + boolean addOne = this.team.getPrefix().endsWith(ChatColor.COLOR_CHAR + ""); + + if (addOne) { + this.team.setPrefix(this.text.substring(0, 15)); + } + + String suffix = ChatColor.getLastColors(this.team.getPrefix()) + + this.text.substring(addOne ? 15 : 16, this.text.length()); + + if (suffix.length() > 16) { + if (suffix.length() - 2 <= 16) { + suffix = this.text.substring(addOne ? 15 : 16, this.text.length()); + this.team.setSuffix(suffix.substring(0, suffix.length())); + } else { + this.team.setSuffix(suffix.substring(0, 16)); + } + + } else { + this.team.setSuffix(suffix); + } + } else { + this.team.setSuffix(""); + this.team.setPrefix(this.text); + } + + Score score = objective.getScore(this.key); + score.setScore(position); + + return this; + } + + public void remove() { + this.board.getKeys().remove(this.key); + this.board.getScoreboard().resetScores(this.key); + } + +} \ No newline at end of file diff --git a/src/main/java/net/iamtakagi/sudachi/BoardManager.java b/src/main/java/net/iamtakagi/sudachi/BoardManager.java new file mode 100644 index 0000000..afeb4ae --- /dev/null +++ b/src/main/java/net/iamtakagi/sudachi/BoardManager.java @@ -0,0 +1,129 @@ +package net.iamtakagi.sudachi; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import org.bukkit.entity.Player; +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; + +@Getter +@RequiredArgsConstructor +public class BoardManager extends BukkitRunnable { + + private final JavaPlugin plugin; + private final Map playerBoards = new HashMap<>(); + private final BoardAdapter adapter; + + @Override + public void run() { + this.adapter.preLoop(); + + for (Player player : plugin.getServer().getOnlinePlayers()) { + Board board = this.playerBoards.get(player.getUniqueId()); + + if (board == null) { + continue; + } + + try { + Scoreboard scoreboard = board.getScoreboard(); + + List scores = this.adapter.getScoreboard(player, board); + + if (scores != null) { + Collections.reverse(scores); + + Objective objective = board.getObjective(); + + if (!objective.getDisplayName().equals(this.adapter.getTitle(player))) { + objective.setDisplayName(this.adapter.getTitle(player)); + } + + if (scores.isEmpty()) { + Iterator iter = board.getEntries().iterator(); + while (iter.hasNext()) { + BoardEntry boardEntry = iter.next(); + boardEntry.remove(); + iter.remove(); + } + continue; + } + + forILoop: + for (int i = 0; i < scores.size(); i++) { + String text = scores.get(i); + int position = i + 1; + + for (BoardEntry boardEntry : new LinkedList<>(board.getEntries())) { + Score score = objective.getScore(boardEntry.getKey()); + + if (score != null && boardEntry.getText().equals(text)) { + if (score.getScore() == position) { + continue forILoop; + } + } + } + + Iterator iter = board.getEntries().iterator(); + while (iter.hasNext()) { + BoardEntry boardEntry = iter.next(); + int entryPosition = scoreboard.getObjective(DisplaySlot.SIDEBAR).getScore(boardEntry + .getKey()).getScore(); + if (entryPosition > scores.size()) { + boardEntry.remove(); + iter.remove(); + } + } + + int positionToSearch = position - 1; + + 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()) { + iter = board.getEntries().iterator(); + while (iter.hasNext()) { + BoardEntry boardEntry = iter.next(); + if (!scores.contains(boardEntry.getText()) || Collections.frequency(board + .getBoardEntriesFormatted(), boardEntry.getText()) > 1) { + boardEntry.remove(); + iter.remove(); + } + } + } + } + } else { + if (!board.getEntries().isEmpty()) { + board.getEntries().forEach(BoardEntry::remove); + board.getEntries().clear(); + } + } + + this.adapter.onScoreboardCreate(player, scoreboard); + + player.setScoreboard(scoreboard); + } catch (Exception e) { + e.printStackTrace(); + + plugin.getLogger().severe("Something went wrong while updating " + player.getName() + "'s scoreboard " + + "" + board + " - " + board.getAdapter() + ")"); + } + } + } + +} diff --git a/src/main/java/net/iamtakagi/sudachi/BoardTimer.java b/src/main/java/net/iamtakagi/sudachi/BoardTimer.java new file mode 100644 index 0000000..6e43698 --- /dev/null +++ b/src/main/java/net/iamtakagi/sudachi/BoardTimer.java @@ -0,0 +1,49 @@ +package net.iamtakagi.sudachi; + +import java.text.DecimalFormat; +import lombok.Getter; +import org.apache.commons.lang3.time.DurationFormatUtils; + +@Getter +public class BoardTimer { + + private static final DecimalFormat SECONDS_FORMATTER = new DecimalFormat("#0.0"); + private final Board board; + private final String id; + private final double duration; + private final long end; + + public BoardTimer(Board board, String id, double duration) { + this.board = board; + + this.id = id; + this.duration = duration; + + this.end = (long) (System.currentTimeMillis() + (duration * 1000)); + + if (board != null) { + board.getTimers().add(this); + } + } + + public String getFormattedString(TimerType format) { + if (format == TimerType.SECONDS) { + return SECONDS_FORMATTER.format(((this.end - System.currentTimeMillis()) / 1000.0f)); + } else { + return DurationFormatUtils.formatDuration(this.end - System.currentTimeMillis(), "mm:ss"); + } + } + + public void cancel() { + if (this.board != null) { + this.board.getTimers().remove(this); + } + } + + public enum TimerType { + SECONDS, + MINUTES, + HOURS + } + +} \ No newline at end of file