diff --git a/.editorconfig b/.editorconfig index 0882495e..5a0107fd 100644 --- a/.editorconfig +++ b/.editorconfig @@ -12,3 +12,6 @@ trim_trailing_whitespace = true ij_continuation_indent_size = 4 [*.yml] indent_size = 2 +[*.java] +ij_java_class_count_to_use_import_on_demand = 999999 +ij_java_names_count_to_use_import_on_demand = 999999 diff --git a/build.gradle.kts b/build.gradle.kts index 7669c288..2ab52c83 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -5,9 +5,9 @@ plugins { // Version of HungerGames val projectVersion = "5.0.0-beta1" // Where this builds on the server -val serverLocation = "1-21-4" +val serverLocation = "1-21-5" // Minecraft version to build against -val minecraftVersion = "1.21.4" +val minecraftVersion = "1.21.5" java.sourceCompatibility = JavaVersion.VERSION_21 @@ -41,7 +41,7 @@ dependencies { compileOnly("io.papermc.paper:paper-api:${minecraftVersion}-R0.1-SNAPSHOT") // Command Api - implementation("dev.jorel:commandapi-bukkit-shade:9.7.0") + implementation("dev.jorel:commandapi-bukkit-shade-mojang-mapped:10.0.1") // bStats implementation("org.bstats:bstats-bukkit:3.1.0") @@ -53,7 +53,7 @@ dependencies { compileOnly("me.clip:placeholderapi:2.11.6") // NBT-API - implementation("de.tr7zw:item-nbt-api:2.14.2-SNAPSHOT") { + implementation("de.tr7zw:item-nbt-api:2.15.0") { isTransitive = false } @@ -77,7 +77,7 @@ tasks { } processResources { val prop = ("version" to projectVersion) - filesMatching("plugin.yml") { + filesMatching("paper-plugin.yml") { expand(prop) } } @@ -91,16 +91,16 @@ tasks { exclude("com/shanebeestudios/hg/plugin/commands") exclude("com/shanebeestudios/hg/plugin/listeners") (options as StandardJavadocDocletOptions).links( - "https://jd.papermc.io/paper/1.21.1/", + "https://jd.papermc.io/paper/1.21.5/", "https://jd.advntr.dev/api/4.17.0/", "https://tr7zw.github.io/Item-NBT-API/v2-api/" ) } shadowJar { - relocate("fr.mrmicky.fastboard", "com.shanebeestudios.hg.api.fastboard") - relocate("dev.jorel.commandapi", "com.shanebeestudios.hg.api.commandapi") - relocate("de.tr7zw.changeme.nbtapi", "com.shanebeestudios.hg.api.nbt") + relocate("fr.mrmicky.fastboard", "com.shanebeestudios.hg.shaded-api.fastboard") + relocate("dev.jorel.commandapi", "com.shanebeestudios.hg.shaded-api.commandapi") + relocate("de.tr7zw.changeme.nbtapi", "com.shanebeestudios.hg.shaded-api.nbt") relocate("org.bstats", "com.shanebeestudios.hg.api.metrics") archiveFileName = "HungerGames-${projectVersion}.jar" } diff --git a/src/main/java/com/shanebeestudios/hg/api/command/CustomArg.java b/src/main/java/com/shanebeestudios/hg/api/command/CustomArg.java index cb4af4bc..75a9fc3a 100644 --- a/src/main/java/com/shanebeestudios/hg/api/command/CustomArg.java +++ b/src/main/java/com/shanebeestudios/hg/api/command/CustomArg.java @@ -1,22 +1,30 @@ package com.shanebeestudios.hg.api.command; +import com.shanebeestudios.hg.api.data.PlayerData; import com.shanebeestudios.hg.api.game.Game; +import com.shanebeestudios.hg.api.game.GameTeam; +import com.shanebeestudios.hg.api.util.Util; +import com.shanebeestudios.hg.plugin.HungerGames; +import com.shanebeestudios.hg.plugin.configs.Language; import com.shanebeestudios.hg.plugin.managers.GameManager; -import dev.jorel.commandapi.CommandAPI; +import com.shanebeestudios.hg.plugin.managers.PlayerManager; import dev.jorel.commandapi.arguments.Argument; import dev.jorel.commandapi.arguments.ArgumentSuggestions; import dev.jorel.commandapi.arguments.CustomArgument; +import dev.jorel.commandapi.arguments.EntitySelectorArgument; import dev.jorel.commandapi.arguments.StringArgument; -import dev.jorel.commandapi.exceptions.WrapperCommandSyntaxException; -import dev.jorel.commandapi.executors.ExecutionInfo; +import org.bukkit.entity.Player; import org.jetbrains.annotations.ApiStatus; +import java.util.List; import java.util.Locale; import java.util.concurrent.CompletableFuture; public abstract class CustomArg { + private static Language LANG; private static GameManager GAME_MANAGER; + private static PlayerManager PLAYER_MANAGER; /** * Initialize GameManager constant @@ -25,20 +33,10 @@ public abstract class CustomArg { * @param gameManager GameManager instance */ @ApiStatus.Internal - public static void init(GameManager gameManager) { + public static void init(HungerGames plugin, GameManager gameManager) { + LANG = plugin.getLang(); GAME_MANAGER = gameManager; - } - - /** - * @hidden - */ - public static Game getGame(ExecutionInfo info) throws WrapperCommandSyntaxException { - Game game = info.args().getByClass("game", Game.class); - if (game == null) { - String raw = info.args().getRaw("game"); - throw CommandAPI.failWithString("invalid game '" + raw + "'"); - } - return game; + PLAYER_MANAGER = plugin.getPlayerManager(); } /** @@ -47,10 +45,87 @@ public static Game getGame(ExecutionInfo info) throws WrapperCommandSyntax public static final CustomArg GAME = new CustomArg() { @Override public Argument get(String name) { - return new CustomArgument<>(new StringArgument(name), info -> - GAME_MANAGER.getGame(info.input().toLowerCase(Locale.ROOT))) - .includeSuggestions(ArgumentSuggestions.stringCollectionAsync(info -> - CompletableFuture.supplyAsync(GAME_MANAGER::getGameNames))); + return new CustomArgument<>(new StringArgument(name), info -> { + String gameName = info.input().toLowerCase(Locale.ROOT); + Game game = GAME_MANAGER.getGame(gameName); + if (game == null) { + String msg = LANG.command_base_invalid_game.replace("", gameName); + Util.throwCustomArgException(msg); + } + return game; + }).includeSuggestions(ArgumentSuggestions.stringCollectionAsync(info -> + CompletableFuture.supplyAsync(GAME_MANAGER::getGameNames))); + } + }; + + public static final CustomArg GAME_PLAYER_FOR_TEAM = new CustomArg() { + @Override + public Argument get(String name) { + return new CustomArgument<>(new EntitySelectorArgument.OnePlayer(name), info -> { + Player player = info.currentInput(); + if (PLAYER_MANAGER.getGame(player) == null) { + String msg = LANG.command_team_player_not_available.replace("", player.getName()); + Util.throwCustomArgException(msg); + } + return player; + }).replaceSuggestions(ArgumentSuggestions.stringCollectionAsync(info -> { + if (!(info.sender() instanceof Player player)) return null; + + // No player data = no game + PlayerData playerData = PLAYER_MANAGER.getPlayerData(player); + if (playerData == null) return null; + + // Not in game = no team + Game game = playerData.getGame(); + if (game == null) return null; + + // No team = no invite + GameTeam gameTeam = game.getGameScoreboard().getGameTeam(player); + if (gameTeam == null) return null; + + // Only leader can invite players + if (gameTeam.getLeader() != player) return null; + + List names = game.getGamePlayerData().getPlayers().stream() + .filter(p -> !gameTeam.isOnTeam(p) && !gameTeam.isPending(p)) + .map(Player::getName) + .toList(); + return CompletableFuture.supplyAsync(() -> names); + })); + } + }; + + public static final CustomArg GAME_PLAYER_ON_TEAM = new CustomArg() { + @Override + public Argument get(String name) { + return new CustomArgument<>(new EntitySelectorArgument.OnePlayer(name), info -> { + Player player = info.currentInput(); + if (PLAYER_MANAGER.getGame(player) == null) { + String msg = LANG.command_team_player_not_available.replace("", player.getName()); + Util.throwCustomArgException(msg); + } + return player; + }).replaceSuggestions(ArgumentSuggestions.stringCollectionAsync(info -> { + if (!(info.sender() instanceof Player player)) return null; + + // No player data = no game + PlayerData playerData = PLAYER_MANAGER.getPlayerData(player); + if (playerData == null) return null; + + // Not in game = no team + Game game = playerData.getGame(); + if (game == null) return null; + + // No team = no team members + GameTeam gameTeam = game.getGameScoreboard().getGameTeam(player); + if (gameTeam == null) return null; + + List names = gameTeam.getPlayers().stream() + .filter(teamMember -> teamMember != player) + .map(Player::getName) + .toList(); + return CompletableFuture.supplyAsync(() -> names); + })); } }; diff --git a/src/main/java/com/shanebeestudios/hg/api/data/KitData.java b/src/main/java/com/shanebeestudios/hg/api/data/KitData.java index 0d8b21a4..e7befa9e 100644 --- a/src/main/java/com/shanebeestudios/hg/api/data/KitData.java +++ b/src/main/java/com/shanebeestudios/hg/api/data/KitData.java @@ -4,12 +4,14 @@ import com.shanebeestudios.hg.api.util.Util; import com.shanebeestudios.hg.plugin.HungerGames; import com.shanebeestudios.hg.plugin.configs.Language; +import com.shanebeestudios.hg.plugin.managers.PlayerManager; import org.bukkit.entity.Player; import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.StringJoiner; /** @@ -18,8 +20,10 @@ @SuppressWarnings("unused") public class KitData { + private final PlayerManager playerManager = HungerGames.getPlugin().getPlayerManager(); private final Language lang = HungerGames.getPlugin().getLang(); - private final HashMap kitEntries = new HashMap<>(); + private final Map kitEntries = new HashMap<>(); + private final Map preselectedKits = new HashMap<>(); public boolean hasKitPermission(Player player, String kitName) { KitEntry kitEntry = this.kitEntries.get(kitName); @@ -27,13 +31,20 @@ public boolean hasKitPermission(Player player, String kitName) { } /** - * Give a player the default kit + * Give a player a preselected or default kit + *

If the player selected a kit in the KitGui before the game + * started, they will get this. + * Otherwise, if there is a default kit set up they will get that.

* * @param player Player to give kit to */ - public void giveDefaultKit(Player player) { - if (this.kitEntries.containsKey("default")) { - setKit(player, "default"); + public void givePreselectedOrDefaultKit(Player player) { + if (this.preselectedKits.containsKey(player)) { + KitEntry kitEntry = this.preselectedKits.get(player); + this.preselectedKits.remove(player); + kitEntry.setInventoryContent(player); + } else if (this.kitEntries.containsKey("default")) { + setKit(player, this.kitEntries.get("default")); Util.sendPrefixedMessage(player, this.lang.kits_give_default); } } @@ -41,15 +52,21 @@ public void giveDefaultKit(Player player) { /** * Set a kit for a player * - * @param player The player to set the kit for - * @param kitName The name of the kit to set + * @param player The player to set the kit for + * @param kitEntry The kit to set */ - public void setKit(Player player, String kitName) { - if (!this.kitEntries.containsKey(kitName)) { - Util.sendMessage(player, "" + kitName + this.lang.kits_doesnt_exist); - Util.sendMessage(player, "Available Kits:" + getKitListString(player)); + public void setKit(Player player, KitEntry kitEntry) { + PlayerData playerData = this.playerManager.getPlayerData(player); + // This shouldn't happen, but just in case + if (playerData == null) return; + + if (playerData.hasGameStared()) { + kitEntry.setInventoryContent(player); + this.preselectedKits.remove(player); } else { - this.kitEntries.get(kitName).setInventoryContent(player); + this.preselectedKits.put(player, kitEntry); + Util.sendPrefixedMessage(player, this.lang.kits_kit_gui_pre_select + .replace("", kitEntry.getName())); } } @@ -132,4 +149,8 @@ public void clearKits() { this.kitEntries.clear(); } + public void clearPreselectedKits() { + this.preselectedKits.clear(); + } + } diff --git a/src/main/java/com/shanebeestudios/hg/api/data/KitEntry.java b/src/main/java/com/shanebeestudios/hg/api/data/KitEntry.java index e084c23f..886b187c 100755 --- a/src/main/java/com/shanebeestudios/hg/api/data/KitEntry.java +++ b/src/main/java/com/shanebeestudios/hg/api/data/KitEntry.java @@ -1,7 +1,7 @@ package com.shanebeestudios.hg.api.data; -import com.shanebeestudios.hg.api.util.Util; import com.shanebeestudios.hg.api.game.Game; +import com.shanebeestudios.hg.api.util.Util; import com.shanebeestudios.hg.plugin.permission.Permissions; import com.shanebeestudios.hg.plugin.permission.Permissions.Permission; import org.bukkit.entity.Player; diff --git a/src/main/java/com/shanebeestudios/hg/api/data/Leaderboard.java b/src/main/java/com/shanebeestudios/hg/api/data/Leaderboard.java index 57b7ce20..199529bb 100644 --- a/src/main/java/com/shanebeestudios/hg/api/data/Leaderboard.java +++ b/src/main/java/com/shanebeestudios/hg/api/data/Leaderboard.java @@ -1,15 +1,23 @@ package com.shanebeestudios.hg.api.data; -import com.shanebeestudios.hg.plugin.configs.Language; +import com.shanebeestudios.hg.api.util.Util; +import com.shanebeestudios.hg.plugin.HungerGames; import org.bukkit.Bukkit; +import org.bukkit.configuration.ConfigurationSection; import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.entity.Player; -import com.shanebeestudios.hg.plugin.HungerGames; +import org.jetbrains.annotations.NotNull; import java.io.File; import java.io.IOException; -import java.util.*; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.stream.Collectors; /** * HungerGames leader boards @@ -18,265 +26,168 @@ @SuppressWarnings({"WeakerAccess", "unused"}) public class Leaderboard { + private static class Data implements Comparable { + private final String playerName; + private int score; + + public Data(UUID uuid) { + this.playerName = Bukkit.getOfflinePlayer(uuid).getName(); + } + + @Override + public int compareTo(@NotNull Data data) { + return data.score - this.score; + } + } + private final HungerGames plugin; - private final Language lang; private FileConfiguration leaderboardConfig; private File config_file; - private final Map wins; - private final Map kills; - private final Map deaths; - private final Map gamesPlayed; - - private final List sorted_players_wins; - private final List sorted_scores_wins; - private final List sorted_players_kills; - private final List sorted_scores_kills; - private final List sorted_players_deaths; - private final List sorted_scores_deaths; - private final List sorted_players_gamesPlayed; - private final List sorted_scores_gamesPlayed; + private final Map> stats = new HashMap<>(); public Leaderboard(HungerGames plugin) { this.plugin = plugin; - this.lang = plugin.getLang(); - wins = new TreeMap<>(); - kills = new TreeMap<>(); - deaths = new TreeMap<>(); - gamesPlayed = new TreeMap<>(); - sorted_players_wins = new ArrayList<>(); - sorted_scores_wins = new ArrayList<>(); - sorted_players_kills = new ArrayList<>(); - sorted_scores_kills = new ArrayList<>(); - sorted_players_deaths = new ArrayList<>(); - sorted_scores_deaths = new ArrayList<>(); - sorted_players_gamesPlayed = new ArrayList<>(); - sorted_scores_gamesPlayed = new ArrayList<>(); - loadLeaderboard(); - } - - /** Add a win to the leaderboard - * @param uuid UUID of player to add - * @deprecated Use {@link #addStat(UUID, Stats)} instead - */ - @Deprecated - public void addWin(UUID uuid) { - if (wins.containsKey(uuid.toString())) { - wins.replace(uuid.toString(), wins.get(uuid.toString()) + 1); - } else { - wins.put(uuid.toString(), 1); + for (Stats value : Stats.values()) { + this.stats.put(value, new LinkedHashMap<>()); } - saveLeaderboard(); - } - - /** Add a win to the leaderboard - * @param player Player to add - * @deprecated Use {@link #addStat(Player, Stats)} instead - */ - @Deprecated - public void addWin(Player player) { - addWin(player.getUniqueId()); - } - - /** Get the wins for a player from the leaderboard - * @param uuid UUID of player to get wins for - * @return Number of wins for the player - * @deprecated Use {@link #getStat(UUID, Stats)} instead - */ - @Deprecated - public int getWins(UUID uuid) { - return wins.get(uuid.toString()); - } - - /** Get the wins for a player from the leaderboard - * @param player Player to get wins for - * @return Number of wins for the player - * @deprecated Use {@link #getStat(Player, Stats)} instead - */ - @Deprecated - public int getWins(Player player) { - return getWins(player.getUniqueId()); - } - - /** Add a stat to the leaderboard (Will default to 1) - * @param uuid Uuid of player to add - * @param stat Stat to add - */ - public void addStat(UUID uuid, Stats stat) { - addStat(uuid, stat, 1); + loadLeaderboard(); } - /** Add a stat to the leaderboard - * @param uuid Uuid of player to add - * @param stat Stat to add + /** + * Add a stat to the leaderboard + * + * @param uuid Uuid of player to add + * @param stat Stat to add * @param amount Amount to add */ public void addStat(UUID uuid, Stats stat, int amount) { - Map map; - switch (stat) { - case KILLS: - map = this.kills; - break; - case DEATHS: - map = this.deaths; - break; - case GAMES: - map = this.gamesPlayed; - break; - default: - map = this.wins; - } - if (map.containsKey(uuid.toString())) { - map.replace(uuid.toString(), map.get(uuid.toString()) + amount); - } else { - map.put(uuid.toString(), amount); - } - saveLeaderboard(); + Map map = this.stats.get(stat); + Data data = map.getOrDefault(uuid.toString(), new Data(uuid)); + data.score += amount; + map.put(uuid.toString(), data); + sort(stat); } - /** Add a stat to the leaderboard (Will default to 1) + /** + * Add a stat to the leaderboard (Will default to 1) + * * @param player Player to add - * @param stat Stat to add + * @param stat Stat to add */ public void addStat(Player player, Stats stat) { addStat(player, stat, 1); } - /** Add a stat to the leaderboard + /** + * Add a stat to the leaderboard + * * @param player Player to add - * @param stat Stat to add + * @param stat Stat to add * @param amount Amount to add */ public void addStat(Player player, Stats stat, int amount) { addStat(player.getUniqueId(), stat, amount); } - /** Get a stat from the leaderboard + /** + * Get a stat from the leaderboard + * * @param player Player to get - * @param stat Stat to get + * @param stat Stat to get * @return Amount of the relative stat */ public int getStat(Player player, Stats stat) { return getStat(player.getUniqueId(), stat); } - /** Get a stat from the leaderboard + /** + * Get a stat from the leaderboard + * * @param uuid Uuid of player to get * @param stat Stat to get * @return Amount of the relative stat */ public int getStat(UUID uuid, Stats stat) { - Map map; - switch (stat) { - case KILLS: - map = this.kills; - break; - case DEATHS: - map = this.deaths; - break; - case GAMES: - map = this.gamesPlayed; - break; - default: - map = this.wins; + Map map = this.stats.get(stat); + if (map.containsKey(uuid.toString())) { + return map.get(uuid.toString()).score; } - return map.getOrDefault(uuid.toString(), 0); + return 0; } - /** Gets a list of players from a stat + /** + * Gets a list of players from a stat *

Will match up with scores from {@link #getStatsScores(Stats)}

+ * * @param stat Stat to get players from * @return Sorted list of players from a stat */ public List getStatsPlayers(Stats stat) { - switch (stat) { - case KILLS: - return sorted_players_kills; - case DEATHS: - return sorted_players_deaths; - case GAMES: - return sorted_players_gamesPlayed; - default: - return sorted_players_wins; + Map map = this.stats.get(stat); + List playerNames = new ArrayList<>(); + for (Map.Entry entry : map.entrySet()) { + playerNames.add(entry.getValue().playerName); } + return playerNames; } - /** Gets a list of scores from a stat + /** + * Gets a list of scores from a stat *

Will match up with players from {@link #getStatsPlayers(Stats)}

+ * * @param stat Stat to get scores from * @return Sorted list of scores from a stat */ - public List getStatsScores(Stats stat) { - switch (stat) { - case KILLS: - return sorted_scores_kills; - case DEATHS: - return sorted_scores_deaths; - case GAMES: - return sorted_scores_gamesPlayed; - default: - return sorted_scores_wins; + public List getStatsScores(Stats stat) { + Map map = this.stats.get(stat); + List playerScores = new ArrayList<>(); + for (Map.Entry entry : map.entrySet()) { + playerScores.add(entry.getValue().score); } + return playerScores; } - private void saveLeaderboard() { - leaderboardConfig.set("Total-Wins", wins); - leaderboardConfig.set("Total-Deaths", deaths); - leaderboardConfig.set("Total-Kills", kills); - leaderboardConfig.set("Games-Played", gamesPlayed); + public void saveLeaderboard() { + for (Stats stat : Stats.values()) { + ConfigurationSection statSection = this.leaderboardConfig.createSection(stat.getName()); + this.stats.get(stat).forEach((key, value) -> + statSection.set(key, value.score)); + } try { - leaderboardConfig.save(config_file); + this.leaderboardConfig.save(this.config_file); } catch (IOException e) { - e.printStackTrace(); + Util.warning("Could not save leaderboard file: %s", e.getMessage()); } - sortScores(wins, sorted_scores_wins, sorted_players_wins); - sortScores(kills, sorted_scores_kills, sorted_players_kills); - sortScores(deaths, sorted_scores_deaths, sorted_players_deaths); - sortScores(gamesPlayed, sorted_scores_gamesPlayed, sorted_players_gamesPlayed); } private void loadLeaderboard() { - config_file = new File(plugin.getDataFolder(), "leaderboard.yml"); - if (!config_file.exists()) { - plugin.saveResource("leaderboard.yml", true); + this.config_file = new File(this.plugin.getDataFolder(), "leaderboard.yml"); + if (!this.config_file.exists()) { + this.plugin.saveResource("leaderboard.yml", true); } - leaderboardConfig = YamlConfiguration.loadConfiguration(config_file); - getLeaderboard("Total-Wins", wins, sorted_scores_wins, sorted_players_wins); - getLeaderboard("Total-Kills", kills, sorted_scores_kills, sorted_players_kills); - getLeaderboard("Total-Deaths", deaths, sorted_scores_deaths, sorted_players_deaths); - getLeaderboard("Games-Played", gamesPlayed, sorted_scores_gamesPlayed, sorted_players_gamesPlayed); - } - - private void getLeaderboard(String path, Map map, List scores, List players) { - if (leaderboardConfig.getConfigurationSection(path) != null) { - //noinspection ConstantConditions - for (String key : leaderboardConfig.getConfigurationSection(path).getKeys(false)) { - map.put(key, leaderboardConfig.getInt(path + "." + key)); + this.leaderboardConfig = YamlConfiguration.loadConfiguration(this.config_file); + for (Stats stat : Stats.values()) { + ConfigurationSection statSection = this.leaderboardConfig.getConfigurationSection(stat.getName()); + if (statSection == null) continue; + + Map map = new HashMap<>(); + for (String key : statSection.getKeys(false)) { + UUID uuid = UUID.fromString(key); + Data data = new Data(uuid); + data.score = statSection.getInt(key); + map.put(key, data); } - sortScores(map, scores, players); + this.stats.put(stat, map); + sort(stat); } } - private void sortScores(Map map, List scores, List players) { - scores.clear(); - players.clear(); - for (Map.Entry sortingMap : entriesSortedByValues(map)) { - String player = Bukkit.getOfflinePlayer(UUID.fromString(sortingMap.getKey())).getName(); - int score = sortingMap.getValue(); - scores.add(String.valueOf(score)); - players.add(player != null ? player : lang.leaderboard_missing_player); - } - } - - private static > SortedSet> entriesSortedByValues(Map map) { - SortedSet> sortedEntries = new TreeSet<>( - (Map.Entry e2, Map.Entry e1) -> { - int res = e1.getValue().compareTo(e2.getValue()); - if (res == 0) return 1; - else return res; - } - ); - sortedEntries.addAll(map.entrySet()); - return sortedEntries; + private void sort(Stats stat) { + LinkedHashMap collect = this.stats.get(stat).entrySet().stream() + .sorted(Map.Entry.comparingByValue()) + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, + (e1, e2) -> e1, LinkedHashMap::new)); + this.stats.put(stat, collect); } /** @@ -288,7 +199,7 @@ public enum Stats { */ WINS("wins"), /** - * Amount of players a player has killed in a game + * Amount of players a player has killed in all games */ KILLS("kills"), /** @@ -297,7 +208,8 @@ public enum Stats { DEATHS("deaths"), /** * Amount of games a player has played - *

Only counted for a game a player has either won or died in. Leaving a game does not count

+ *

Only counted for a game a player has either won or died in. + * Leaving a game does not count

*/ GAMES("games"); @@ -310,7 +222,6 @@ public enum Stats { public String getName() { return this.stat; } - } } diff --git a/src/main/java/com/shanebeestudios/hg/api/data/PlayerData.java b/src/main/java/com/shanebeestudios/hg/api/data/PlayerData.java index ed736009..fe7ac45d 100755 --- a/src/main/java/com/shanebeestudios/hg/api/data/PlayerData.java +++ b/src/main/java/com/shanebeestudios/hg/api/data/PlayerData.java @@ -1,5 +1,9 @@ package com.shanebeestudios.hg.api.data; +import com.shanebeestudios.hg.api.game.Game; +import com.shanebeestudios.hg.api.game.GameTeam; +import com.shanebeestudios.hg.api.util.Util; +import com.shanebeestudios.hg.plugin.HungerGames; import org.bukkit.Bukkit; import org.bukkit.GameMode; import org.bukkit.Location; @@ -8,10 +12,6 @@ import org.bukkit.inventory.ItemStack; import org.bukkit.scoreboard.Scoreboard; import org.jetbrains.annotations.Nullable; -import com.shanebeestudios.hg.plugin.HungerGames; -import com.shanebeestudios.hg.api.game.Game; -import com.shanebeestudios.hg.api.game.GameTeam; -import com.shanebeestudios.hg.api.util.Util; import java.util.Arrays; import java.util.UUID; @@ -24,24 +24,27 @@ public class PlayerData implements Cloneable { private static final Scoreboard DUMMY = Bukkit.getScoreboardManager().getNewScoreboard(); - //Pregame data - private final ItemStack[] inv; - private final ItemStack[] equip; - private final int expL; - private final float expP; - private final double health; - private final int food; - private final float saturation; - private final GameMode mode; + private final Game game; + private final Player player; private final UUID uuid; - private final Scoreboard scoreboard; + + //Pregame data + private ItemStack[] inv; + private ItemStack[] equip; + private int expL; + private float expP; + private double health; + private int food; + private float saturation; + private GameMode mode; + private Scoreboard scoreboard; private Location previousLocation = null; private boolean online; + private boolean hasGameStarted; //InGame data private GameTeam gameTeam; private GameTeam pendingGameTeam; - private final Game game; /** * New player pre-game data file @@ -51,20 +54,25 @@ public class PlayerData implements Cloneable { */ public PlayerData(Player player, Game game) { this.game = game; + this.player = player; this.uuid = player.getUniqueId(); - inv = player.getInventory().getStorageContents(); - equip = player.getInventory().getArmorContents(); - expL = player.getLevel(); - expP = player.getExp(); - mode = player.getGameMode(); - food = player.getFoodLevel(); - saturation = player.getSaturation(); - health = player.getHealth(); - Util.clearInv(player); - player.setLevel(0); - player.setExp(0); - scoreboard = player.getScoreboard(); - online = true; + this.online = true; + } + + public void backup() { + this.hasGameStarted = true; + this.inv = this.player.getInventory().getStorageContents(); + this.equip = this.player.getInventory().getArmorContents(); + this.expL = this.player.getLevel(); + this.expP = this.player.getExp(); + this.mode = this.player.getGameMode(); + this.food = this.player.getFoodLevel(); + this.saturation = this.player.getSaturation(); + this.health = this.player.getHealth(); + Util.clearInv(this.player); + this.player.setLevel(0); + this.player.setExp(0); + this.scoreboard = this.player.getScoreboard(); } /** @@ -73,16 +81,15 @@ public PlayerData(Player player, Game game) { * @param player Player to restore data to */ public void restore(Player player) { - if (player == null) return; + if (player == null || !this.hasGameStarted) return; Util.clearInv(player); - player.setWalkSpeed(0.2f); - player.setLevel(expL); - player.setExp(expP); - player.setFoodLevel(food); - player.setSaturation(saturation); - player.getInventory().setStorageContents(inv); - player.getInventory().setArmorContents(equip); - player.setGameMode(mode); + player.setLevel(this.expL); + player.setExp(this.expP); + player.setFoodLevel(this.food); + player.setSaturation(this.saturation); + player.getInventory().setStorageContents(this.inv); + player.getInventory().setArmorContents(this.equip); + player.setGameMode(this.mode); player.updateInventory(); player.setInvulnerable(false); restoreHealth(player); @@ -95,11 +102,11 @@ public void restore(Player player) { // Restores later if player has an item in their inventory which changes their max health value @SuppressWarnings("ConstantConditions") private void restoreHealth(Player player) { - double att = player.getAttribute(Attribute.MAX_HEALTH).getValue(); - if (health > att) { - Bukkit.getScheduler().runTaskLater(HungerGames.getPlugin(), () -> player.setHealth(health), 10); + double maxHealth = player.getAttribute(Attribute.MAX_HEALTH).getValue(); + if (this.health > maxHealth) { + Bukkit.getScheduler().runTaskLater(HungerGames.getPlugin(), () -> player.setHealth(this.health), 10); } else { - player.setHealth(health); + player.setHealth(this.health); } } @@ -203,35 +210,30 @@ public boolean isOnline() { return online; } - public void setOnline(boolean online) { - this.online = online; + public boolean hasGameStared() { + return this.hasGameStarted; } - /** - * Get the {@link Player Bukkit Player} belonging to this player data - * - * @return Player belonging to this player data - */ - public Player getBukkitPlayer() { - return Bukkit.getPlayer(this.uuid); + public void setOnline(boolean online) { + this.online = online; } @Override public String toString() { return "PlayerData{" + - "inv=" + Arrays.toString(inv) + - ", equip=" + Arrays.toString(equip) + - ", expLevel=" + expL + - ", expPoints=" + expP + - ", health=" + health + - ", food=" + food + - ", saturation=" + saturation + - ", mode=" + mode + - ", uuid=" + uuid + - ", team=" + gameTeam + - ", pending=" + pendingGameTeam + - ", game=" + game + - '}'; + "inv=" + Arrays.toString(inv) + + ", equip=" + Arrays.toString(equip) + + ", expLevel=" + expL + + ", expPoints=" + expP + + ", health=" + health + + ", food=" + food + + ", saturation=" + saturation + + ", mode=" + mode + + ", uuid=" + uuid + + ", team=" + gameTeam + + ", pending=" + pendingGameTeam + + ", game=" + game + + '}'; } @Override diff --git a/src/main/java/com/shanebeestudios/hg/api/data/PlayerSession.java b/src/main/java/com/shanebeestudios/hg/api/data/PlayerSession.java index d0f81a53..0491c046 100755 --- a/src/main/java/com/shanebeestudios/hg/api/data/PlayerSession.java +++ b/src/main/java/com/shanebeestudios/hg/api/data/PlayerSession.java @@ -1,7 +1,8 @@ package com.shanebeestudios.hg.api.data; -import com.shanebeestudios.hg.plugin.HungerGames; +import com.shanebeestudios.hg.api.game.Game; import com.shanebeestudios.hg.api.util.Util; +import com.shanebeestudios.hg.plugin.HungerGames; import com.shanebeestudios.hg.plugin.configs.Language; import io.papermc.paper.datacomponent.DataComponentTypes; import org.bukkit.Location; @@ -60,11 +61,13 @@ public void click(Player player, Block block) { Util.sendPrefixedMessage(player, this.lang.command_create_session_next_corner); } else if (this.stage == Stage.CORNER_2) { this.corner2 = block; - if (isBigEnough()) { + if (isTooSmall()) { + Util.sendPrefixedMessage(player, this.lang.command_create_session_error_too_small); + } else if (isOverlapping()) { + Util.sendPrefixedMessage(player, this.lang.command_create_session_error_overlap); + } else { this.stage = Stage.SPAWN_LOCATIONS; Util.sendPrefixedMessage(player, this.lang.command_create_session_select_spawns); - } else { - Util.sendPrefixedMessage(player, this.lang.command_create_session_error_too_small); } } else if (this.stage == Stage.SPAWN_LOCATIONS) { if (this.spawnLocations.size() >= this.maxPlayers) { @@ -96,7 +99,7 @@ public void click(Player player, Block block) { } } - public void finalizeGame(Player player) { + private void finalizeGame(Player player) { HungerGames plugin = HungerGames.getPlugin(); plugin.getGameManager().createGame(this.name, this.corner1, this.corner2, this.spawnLocations, @@ -104,10 +107,20 @@ public void finalizeGame(Player player) { plugin.getSessionManager().endPlayerSession(player); } - public boolean isBigEnough() { - if (this.corner1 == null || this.corner2 == null) return false; + private boolean isTooSmall() { + if (this.corner1 == null || this.corner2 == null) return true; BoundingBox boundingBox = BoundingBox.of(this.corner1, this.corner2); - return boundingBox.getWidthX() > 5 && boundingBox.getWidthZ() > 5 && boundingBox.getHeight() > 5; + return boundingBox.getWidthX() <= 5 || boundingBox.getWidthZ() <= 5 || boundingBox.getHeight() <= 5; + } + + private boolean isOverlapping() { + if (this.corner1 == null || this.corner2 == null) return true; + BoundingBox boundingBox = BoundingBox.of(this.corner1, this.corner2); + for (Game game : HungerGames.getPlugin().getGameManager().getGames()) { + BoundingBox gameBox = game.getGameArenaData().getGameRegion().getBoundingBox(); + if (boundingBox.overlaps(gameBox)) return true; + } + return false; } } diff --git a/src/main/java/com/shanebeestudios/hg/api/events/ChestOpenEvent.java b/src/main/java/com/shanebeestudios/hg/api/events/ChestOpenEvent.java index 5c98ceff..ce7a7427 100755 --- a/src/main/java/com/shanebeestudios/hg/api/events/ChestOpenEvent.java +++ b/src/main/java/com/shanebeestudios/hg/api/events/ChestOpenEvent.java @@ -1,7 +1,7 @@ package com.shanebeestudios.hg.api.events; -import com.shanebeestudios.hg.api.game.Game; import com.shanebeestudios.hg.api.data.ItemData.ChestType; +import com.shanebeestudios.hg.api.game.Game; import org.bukkit.block.Block; import org.bukkit.event.Event; import org.bukkit.event.HandlerList; diff --git a/src/main/java/com/shanebeestudios/hg/api/events/GameStartEvent.java b/src/main/java/com/shanebeestudios/hg/api/events/GameStartEvent.java index d8774fb6..ed703f34 100644 --- a/src/main/java/com/shanebeestudios/hg/api/events/GameStartEvent.java +++ b/src/main/java/com/shanebeestudios/hg/api/events/GameStartEvent.java @@ -1,8 +1,8 @@ package com.shanebeestudios.hg.api.events; +import com.shanebeestudios.hg.api.game.Game; import org.bukkit.event.Event; import org.bukkit.event.HandlerList; -import com.shanebeestudios.hg.api.game.Game; /** * Called when a game starts the pregame stage of a game @@ -10,29 +10,31 @@ @SuppressWarnings("unused") public class GameStartEvent extends Event { - private static final HandlerList handlers = new HandlerList(); - private Game game; - - public GameStartEvent(Game game) { - this.game = game; - } - - /** Get the game involved in this event - * @return The game - */ - public Game getGame() { - return this.game; - } - - @SuppressWarnings("NullableProblems") - @Override - public HandlerList getHandlers() { - return handlers; - } - - @SuppressWarnings("unused") - public static HandlerList getHandlerList() { - return handlers; - } + private static final HandlerList handlers = new HandlerList(); + private Game game; + + public GameStartEvent(Game game) { + this.game = game; + } + + /** + * Get the game involved in this event + * + * @return The game + */ + public Game getGame() { + return this.game; + } + + @SuppressWarnings("NullableProblems") + @Override + public HandlerList getHandlers() { + return handlers; + } + + @SuppressWarnings("unused") + public static HandlerList getHandlerList() { + return handlers; + } } diff --git a/src/main/java/com/shanebeestudios/hg/api/events/PlayerDeathGameEvent.java b/src/main/java/com/shanebeestudios/hg/api/events/PlayerDeathGameEvent.java index c6528131..44148cdc 100644 --- a/src/main/java/com/shanebeestudios/hg/api/events/PlayerDeathGameEvent.java +++ b/src/main/java/com/shanebeestudios/hg/api/events/PlayerDeathGameEvent.java @@ -1,7 +1,7 @@ package com.shanebeestudios.hg.api.events; -import com.shanebeestudios.hg.api.util.Util; import com.shanebeestudios.hg.api.game.Game; +import com.shanebeestudios.hg.api.util.Util; import org.bukkit.damage.DamageSource; import org.bukkit.entity.Player; import org.bukkit.event.HandlerList; @@ -23,7 +23,7 @@ public class PlayerDeathGameEvent extends PlayerDeathEvent { public PlayerDeathGameEvent(@NotNull Player player, DamageSource damageSource, @NotNull List drops, @Nullable String deathMessage, @NotNull Game game) { - super(player, damageSource, drops, 0, Util.getMini(deathMessage)); + super(player, damageSource, drops, 0, Util.getMini(deathMessage), true); this.game = game; } diff --git a/src/main/java/com/shanebeestudios/hg/api/events/PlayerJoinGameEvent.java b/src/main/java/com/shanebeestudios/hg/api/events/PlayerJoinGameEvent.java index a317fb65..dbd08e80 100644 --- a/src/main/java/com/shanebeestudios/hg/api/events/PlayerJoinGameEvent.java +++ b/src/main/java/com/shanebeestudios/hg/api/events/PlayerJoinGameEvent.java @@ -1,60 +1,64 @@ package com.shanebeestudios.hg.api.events; +import com.shanebeestudios.hg.api.game.Game; import org.bukkit.entity.Player; import org.bukkit.event.Cancellable; import org.bukkit.event.Event; import org.bukkit.event.HandlerList; -import com.shanebeestudios.hg.api.game.Game; /** * Called when a player joins a game */ public class PlayerJoinGameEvent extends Event implements Cancellable { - private static final HandlerList handlers = new HandlerList(); - private Game game; - private Player player; - private boolean isCancelled; - - public PlayerJoinGameEvent(Game game, Player player) { - this.game = game; - this.player = player; - this.isCancelled = false; - } - - /** Get the player that joined a game - * @return The player that joined the game - */ - public Player getPlayer() { - return this.player; - } - - /** Get the game the player joined - * @return The game the player joined - */ - public Game getGame() { - return this.game; - } - - @SuppressWarnings("NullableProblems") - @Override - public HandlerList getHandlers() { - return handlers; - } - - @SuppressWarnings("unused") - public static HandlerList getHandlerList() { - return handlers; - } - - @Override - public boolean isCancelled() { - return this.isCancelled; - } - - @Override - public void setCancelled(boolean isCancelled) { - this.isCancelled = isCancelled; - } + private static final HandlerList handlers = new HandlerList(); + private Game game; + private Player player; + private boolean isCancelled; + + public PlayerJoinGameEvent(Game game, Player player) { + this.game = game; + this.player = player; + this.isCancelled = false; + } + + /** + * Get the player that joined a game + * + * @return The player that joined the game + */ + public Player getPlayer() { + return this.player; + } + + /** + * Get the game the player joined + * + * @return The game the player joined + */ + public Game getGame() { + return this.game; + } + + @SuppressWarnings("NullableProblems") + @Override + public HandlerList getHandlers() { + return handlers; + } + + @SuppressWarnings("unused") + public static HandlerList getHandlerList() { + return handlers; + } + + @Override + public boolean isCancelled() { + return this.isCancelled; + } + + @Override + public void setCancelled(boolean isCancelled) { + this.isCancelled = isCancelled; + } } diff --git a/src/main/java/com/shanebeestudios/hg/api/events/PlayerLeaveGameEvent.java b/src/main/java/com/shanebeestudios/hg/api/events/PlayerLeaveGameEvent.java index 1c46263f..d5a539b5 100644 --- a/src/main/java/com/shanebeestudios/hg/api/events/PlayerLeaveGameEvent.java +++ b/src/main/java/com/shanebeestudios/hg/api/events/PlayerLeaveGameEvent.java @@ -1,56 +1,62 @@ package com.shanebeestudios.hg.api.events; +import com.shanebeestudios.hg.api.game.Game; import org.bukkit.entity.Player; import org.bukkit.event.Event; import org.bukkit.event.HandlerList; -import com.shanebeestudios.hg.api.game.Game; /** * Called when a player leaves a game */ public class PlayerLeaveGameEvent extends Event { - private static final HandlerList handlers = new HandlerList(); - private Game game; - private Player player; - private boolean death; - - public PlayerLeaveGameEvent(Game game, Player player, boolean death) { - this.game = game; - this.player = player; - this.death = death; - } - - /** Get the game the player left - * @return The game the player left - */ - public Game getGame() { - return this.game; - } - - /** Get the player that left the game - * @return The player that left the game - */ - public Player getPlayer() { - return this.player; - } - - /** Check if the player died when they left the game - * @return If the player died when they left the game - */ - @SuppressWarnings("unused") - public boolean getDied() { - return death; - } - - @Override - public HandlerList getHandlers() { - return handlers; - } - - @SuppressWarnings("unused") - public static HandlerList getHandlerList() { - return handlers; - } + private static final HandlerList handlers = new HandlerList(); + private Game game; + private Player player; + private boolean death; + + public PlayerLeaveGameEvent(Game game, Player player, boolean death) { + this.game = game; + this.player = player; + this.death = death; + } + + /** + * Get the game the player left + * + * @return The game the player left + */ + public Game getGame() { + return this.game; + } + + /** + * Get the player that left the game + * + * @return The player that left the game + */ + public Player getPlayer() { + return this.player; + } + + /** + * Check if the player died when they left the game + * + * @return If the player died when they left the game + */ + @SuppressWarnings("unused") + public boolean getDied() { + return death; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + @SuppressWarnings("unused") + public static HandlerList getHandlerList() { + return handlers; + } } diff --git a/src/main/java/com/shanebeestudios/hg/api/game/Game.java b/src/main/java/com/shanebeestudios/hg/api/game/Game.java index 53d75015..55e531c5 100755 --- a/src/main/java/com/shanebeestudios/hg/api/game/Game.java +++ b/src/main/java/com/shanebeestudios/hg/api/game/Game.java @@ -28,9 +28,8 @@ import org.bukkit.entity.Player; import java.util.ArrayList; -import java.util.Collection; import java.util.List; -import java.util.UUID; +import java.util.StringJoiner; /** * General game object @@ -54,15 +53,15 @@ public class Game { private NearestPlayerCompassTask nearestPlayerCompassTask; // Data Objects - final GameArenaData gameArenaData; + private final GameArenaData gameArenaData; private final GameEntityData gameEntityData; - final GameScoreboard gameScoreboard; - final GameBarData bar; - final GamePlayerData gamePlayerData; - final GameBlockData gameBlockData; - final GameItemData gameItemData; - final GameCommandData gameCommandData; - final GameBorderData gameBorderData; + private final GameScoreboard gameScoreboard; + private final GameBarData bar; + private final GamePlayerData gamePlayerData; + private final GameBlockData gameBlockData; + private final GameItemData gameItemData; + private final GameCommandData gameCommandData; + private final GameBorderData gameBorderData; /** * Create a new game @@ -92,7 +91,7 @@ public Game(String name, GameRegion gameRegion, List spawns, Location this.gameItemData = new GameItemData(this); this.gameCommandData = new GameCommandData(this); this.gameBorderData = new GameBorderData(this); - this.gameArenaData.spawns.addAll(spawns); + this.gameArenaData.getSpawns().addAll(spawns); // If lobby signs are not properly setup, game is not ready if (!this.gameBlockData.setLobbyBlock(lobbySign)) { @@ -229,10 +228,12 @@ public int getRemainingTime() { public void startWaitingPeriod() { this.gameArenaData.setStatus(Status.WAITING); long start = System.currentTimeMillis(); - this.gameBlockData.logBlocksForRollback(); + int count = this.gameBlockData.logBlocksForRollback(); this.gameBlockData.setupRandomizedBonusChests(); long fin = System.currentTimeMillis() - start; - Util.log("Logged blocks in %s ms", fin); + if (Config.SETTINGS_DEBUG) { + Util.log("Logged %,d blocks in %sms for arena %s", count, fin, getGameArenaData().getName()); + } } /** @@ -244,6 +245,7 @@ public void startPreGameCountdown() { Bukkit.getPluginManager().callEvent(event); this.gameArenaData.setStatus(Status.COUNTDOWN); + this.gamePlayerData.putAllPlayersIntoArena(); this.startingTask = new StartingTask(this); } @@ -267,8 +269,8 @@ public void startRunningGame() { if (Config.MOBS_SPAWN_ENABLED) this.mobSpawnerTask = new MobSpawnerTask(this); if (Config.CHESTS_CHEST_DROP_ENABLED) this.chestDropTask = new ChestDropTask(this); this.gameBlockData.updateLobbyBlock(); - if (Config.bossbar) { - this.bar.createBossBar(gameArenaData.timer); + if (Config.SETTINGS_BOSSBAR_COUNTDOWN) { + this.bar.createBossBar(this.gameArenaData.getTimer()); } if (Config.WORLD_BORDER_ENABLED) { this.gameBorderData.initialize(); @@ -301,7 +303,7 @@ public void cancelTasks() { * @param player Player who joined */ private void broadcastJoin(Player player) { - if (!Config.BROADCAST_JOIN_MESSAGES) return; + if (!Config.SETTINGS_BROADCAST_JOIN_MESSAGES) return; String name = this.getGameArenaData().getName(); Util.broadcast(this.lang.game_waiting_join .replace("", name) @@ -366,26 +368,28 @@ public boolean joinGame(Player player, boolean savePreviousLocation) { } case READY -> { if (!canJoin(player)) return false; - this.gamePlayerData.addPlayerData(player); + this.gamePlayerData.addPlayerData(player, savePreviousLocation); startWaitingPeriod(); broadcastJoin(player); + Util.sendPrefixedMessage(player, this.lang.game_joined_waiting_to_teleport.replace("", arenaName)); } case WAITING -> { if (!canJoin(player)) return false; - this.gamePlayerData.addPlayerData(player); + this.gamePlayerData.addPlayerData(player, savePreviousLocation); if (this.gamePlayerData.getPlayers().size() >= this.gameArenaData.getMinPlayers()) { startPreGameCountdown(); } else { broadcastJoin(player); + Util.sendPrefixedMessage(player, this.lang.game_joined_waiting_to_teleport.replace("", arenaName)); } } case COUNTDOWN -> { if (!canJoin(player)) return false; - this.gamePlayerData.addPlayerData(player); + this.gamePlayerData.addPlayerData(player, savePreviousLocation); + this.gamePlayerData.putPlayerIntoArena(player); } } - this.gamePlayerData.putPlayerIntoArena(player, savePreviousLocation); return true; } @@ -399,78 +403,91 @@ public void stop() { /** * Stop the game * - * @param death Whether the game stopped after the result of a death (false = no winnings payed out) + * @param death Whether the game stopped after the result of a death (false = no winnings paid out) */ - public void stop(Boolean death) { + public void stop(boolean death) { if (Config.WORLD_BORDER_ENABLED) { this.gameBorderData.resetBorder(); } this.gameEntityData.removeEntities(); this.gameScoreboard.resetSidebars(); - // TODO win list should be players - List win = new ArrayList<>(); + List winners = new ArrayList<>(); cancelTasks(); for (Player player : this.gamePlayerData.getPlayers()) { - UUID uuid = player.getUniqueId(); - PlayerData playerData = this.playerManager.getPlayerData(uuid); + PlayerData playerData = this.playerManager.getPlayerData(player); assert playerData != null; - Location previousLocation = playerData.getPreviousLocation(); - this.gamePlayerData.heal(player); - playerData.restore(player); - win.add(uuid); - this.gamePlayerData.exit(player, previousLocation); - this.playerManager.removePlayerData(uuid); + if (playerData.hasGameStared()) { + Location previousLocation = playerData.getPreviousLocation(); + + this.gamePlayerData.heal(player); + playerData.restore(player); + winners.add(player); + this.gamePlayerData.exit(player, previousLocation); + } + this.playerManager.removePlayerData(player); } for (Player spectator : this.gamePlayerData.getSpectators()) { this.gamePlayerData.leaveSpectate(spectator); } - if (gameArenaData.getStatus() == Status.RUNNING) { - bar.clearBar(); + if (this.gameArenaData.getStatus() == Status.RUNNING) { + this.bar.clearBar(); } - if (!win.isEmpty() && death) { - double db = (double) Config.cash / win.size(); - for (UUID u : win) { - if (Config.giveReward) { - Player p = Bukkit.getPlayer(u); - assert p != null; - if (!Config.rewardCommands.isEmpty()) { - for (String cmd : Config.rewardCommands) { - if (!cmd.equalsIgnoreCase("none")) - Bukkit.dispatchCommand(Bukkit.getConsoleSender(), cmd.replace("", p.getName())); + // Handle rewards and stats + if (!winners.isEmpty() && death) { + double winningReward = (double) Config.REWARD_CASH / winners.size(); + for (Player winner : winners) { + if (Config.REWARD_ENABLED) { + // Run reward commands + if (!Config.REWARD_COMMANDS.isEmpty()) { + for (String cmd : Config.REWARD_COMMANDS) { + if (cmd.equalsIgnoreCase("none")) continue; + Bukkit.dispatchCommand(Bukkit.getConsoleSender(), cmd.replace("", winner.getName())); } } - if (!Config.rewardMessages.isEmpty()) { - for (String msg : Config.rewardMessages) { - if (!msg.equalsIgnoreCase("none")) - Util.sendMessage(p, msg.replace("", p.getName())); + // Send reward messages + if (!Config.REWARD_MESSAGES.isEmpty()) { + for (String msg : Config.REWARD_MESSAGES) { + if (msg.equalsIgnoreCase("none")) continue; + Util.sendMessage(winner, msg.replace("", winner.getName())); } } - if (Config.cash != 0) { - Vault.economy.depositPlayer(Bukkit.getServer().getOfflinePlayer(u), db); - Util.sendMessage(p, lang.winning_amount.replace("", String.valueOf(db))); + // Deposit reward winnings + if (winningReward > 0) { + Vault.ECONOMY.depositPlayer(winner, winningReward); + Util.sendMessage(winner, this.lang.game_winning_amount + // Format money with commas and 2 decimal spaces + .replace("", "%,.2f"), winningReward); } } - this.plugin.getLeaderboard().addStat(u, Leaderboard.Stats.WINS); - this.plugin.getLeaderboard().addStat(u, Leaderboard.Stats.GAMES); + this.plugin.getLeaderboard().addStat(winner, Leaderboard.Stats.WINS); + this.plugin.getLeaderboard().addStat(winner, Leaderboard.Stats.GAMES); } } this.gameBlockData.clearChests(); - String winner = Util.translateStop(Util.convertUUIDListToStringList(win)); // Broadcast wins if (death) { - String broadcast = this.lang.game_player_won.replace("", this.gameArenaData.name).replace("", winner); - if (Config.broadcastWinMessages) { + // String together winners names + StringJoiner joiner = new StringJoiner(", "); + winners.forEach(player -> joiner.add(player.getName())); + String joinedWinners = joiner.toString(); + + String broadcast = this.lang.game_player_won + .replace("", this.gameArenaData.getName()) + .replace("", joinedWinners); + if (Config.SETTINGS_BROADCAST_WIN_MESSAGES) { Util.broadcast(broadcast); } else { this.gamePlayerData.messageAllPlayers(broadcast); } } + + // Run rollback if (this.gameBlockData.requiresRollback()) { if (this.plugin.isEnabled()) { new RollbackTask(this); @@ -481,18 +498,17 @@ public void stop(Boolean death) { } else { this.gameArenaData.setStatus(Status.READY); } + + // Run stop commands this.gameCommandData.runCommands(CommandType.STOP, null); - // Call GameEndEvent - Collection winners = new ArrayList<>(); - for (UUID uuid : win) { - winners.add(Bukkit.getPlayer(uuid)); - } + // Game has ended, we can clear some data + this.gamePlayerData.postGameReset(); + this.gameScoreboard.postGameReset(); + this.gameItemData.postGameReset(); + this.plugin.getLeaderboard().saveLeaderboard(); - // Game has ended, we can clear all players now - this.gamePlayerData.clearPlayers(); - this.gamePlayerData.clearSpectators(); - this.gameScoreboard.clearGameTeams(); + // Call GameEndEvent new GameEndEvent(this, winners, death).callEvent(); } @@ -502,7 +518,7 @@ void updateAfterDeath(Player player, boolean death) { if (isGameOver()) { if (!death) { for (Player player1 : this.gamePlayerData.getPlayers()) { - if (this.gamePlayerData.kills.get(player1) >= 1) { + if (this.gamePlayerData.getKills().get(player1) >= 1) { death = true; } } @@ -536,7 +552,7 @@ boolean isGameOver() { assert playerData != null; GameTeam gameTeam = playerData.getTeam(); - if (gameTeam != null && (gameTeam.getPlayers().size() >= gamePlayerData.players.size())) { + if (gameTeam != null && (gameTeam.getPlayers().size() >= this.gamePlayerData.getPlayers().size())) { for (Player player1 : this.gamePlayerData.getPlayers()) { if (!gameTeam.getPlayers().contains(player1)) { return false; @@ -554,14 +570,10 @@ boolean canJoin(Player player) { Util.sendPrefixedMessage(player, this.lang.game_full.replace("", name)); return false; } - return vaultCheck(player); - } - - boolean vaultCheck(Player player) { - if (Config.economy) { - int cost = this.getGameArenaData().getCost(); - if (Vault.economy.getBalance(player) >= cost) { - Vault.economy.withdrawPlayer(player, cost); + int cost = this.getGameArenaData().getCost(); + if (Config.HAS_ECONOMY && cost > 0) { + if (Vault.ECONOMY.getBalance(player) >= cost) { + Vault.ECONOMY.withdrawPlayer(player, cost); return true; } else { Util.sendPrefixedMessage(player, this.lang.command_join_no_money.replace("", String.valueOf(cost))); diff --git a/src/main/java/com/shanebeestudios/hg/api/game/GameArenaData.java b/src/main/java/com/shanebeestudios/hg/api/game/GameArenaData.java index 88689139..c8c00cd5 100644 --- a/src/main/java/com/shanebeestudios/hg/api/game/GameArenaData.java +++ b/src/main/java/com/shanebeestudios/hg/api/game/GameArenaData.java @@ -2,6 +2,7 @@ import com.shanebeestudios.hg.api.status.Status; import com.shanebeestudios.hg.api.util.Util; +import com.shanebeestudios.hg.plugin.HungerGames; import net.kyori.adventure.text.Component; import org.bukkit.Location; @@ -13,18 +14,18 @@ */ public class GameArenaData extends Data { - final String name; - final GameRegion gameRegion; - int timer; - int minPlayers; - int maxPlayers; + private final String name; + private final GameRegion gameRegion; + private int timer; + private int minPlayers; + private int maxPlayers; private int freeRoamTime; - int cost; - final List spawns; - Location exit; + private int cost; + private final List spawns; + private Location exit; private Status status = Status.NOT_READY; - int chestRefillTime = 0; - int chestRefillRepeat = 0; + private int chestRefillTime = 0; + private int chestRefillRepeat = 0; GameArenaData(Game game, String name, GameRegion gameRegion, int timer, int minPlayers, int maxPlayers, int freeRoamTime, int cost) { super(game); @@ -75,6 +76,15 @@ public boolean isInRegion(Location location) { return gameRegion.isInRegion(location); } + public Game checkOverlap() { + for (Game toCheck : HungerGames.getPlugin().getGameManager().getGames()) { + if (this.game.equals(toCheck)) continue; + + if (toCheck.getGameArenaData().getGameRegion().getBoundingBox().overlaps(this.gameRegion.getBoundingBox())) return toCheck; + } + return null; + } + /** * Get the free roam time of the game * diff --git a/src/main/java/com/shanebeestudios/hg/api/game/GameBarData.java b/src/main/java/com/shanebeestudios/hg/api/game/GameBarData.java index a9a7aef6..b5ba5e8e 100644 --- a/src/main/java/com/shanebeestudios/hg/api/game/GameBarData.java +++ b/src/main/java/com/shanebeestudios/hg/api/game/GameBarData.java @@ -1,6 +1,5 @@ package com.shanebeestudios.hg.api.game; -import com.shanebeestudios.hg.plugin.HungerGames; import com.shanebeestudios.hg.api.util.Util; import net.kyori.adventure.audience.Audience; import net.kyori.adventure.bossbar.BossBar; @@ -47,7 +46,7 @@ public void createBossBar(int time) { */ public void bossBarUpdate(int remaining) { if (this.bar == null) return; - float remain = ((float) remaining) / ((float) getGame().gameArenaData.timer); + float remain = ((float) remaining) / ((float) getGame().getGameArenaData().getTimer()); int min = (remaining / 60); int sec = (remaining % 60); String title = formatTitle(min, sec); diff --git a/src/main/java/com/shanebeestudios/hg/api/game/GameBlockData.java b/src/main/java/com/shanebeestudios/hg/api/game/GameBlockData.java index 48427361..2c1de92b 100644 --- a/src/main/java/com/shanebeestudios/hg/api/game/GameBlockData.java +++ b/src/main/java/com/shanebeestudios/hg/api/game/GameBlockData.java @@ -1,8 +1,8 @@ package com.shanebeestudios.hg.api.game; -import com.shanebeestudios.hg.api.util.BlockUtils; import com.shanebeestudios.hg.api.data.ItemData; import com.shanebeestudios.hg.api.data.ItemFrameData; +import com.shanebeestudios.hg.api.util.BlockUtils; import com.shanebeestudios.hg.plugin.configs.Config; import org.bukkit.Location; import org.bukkit.Material; @@ -167,29 +167,37 @@ public boolean canBeFilled(Location location) { /** * Log all blocks in an arena for rollback */ - public void logBlocksForRollback() { + public int logBlocksForRollback() { + int count = 0; for (Location location : this.getGame().getGameArenaData().getGameRegion().getBlocks(null)) { Block block = location.getBlock(); this.blocks.add(block.getState()); + count++; if (Config.CHESTS_BONUS_RANDOMIZE_ENABLED && BlockUtils.isBonusBlockReplacement(block)) { this.randomBonusChests.add(block); block.setType(Material.AIR); } } + return count; } @SuppressWarnings("UnstableApiUsage") public void setupRandomizedBonusChests() { + if (!Config.CHESTS_BONUS_RANDOMIZE_ENABLED) return; + Optional first = BlockUtils.getBonusBlockTypes().stream().findFirst(); if (first.isEmpty()) return; + BlockData blockData = first.get().createBlockData(); + int bonusChestAmount = this.random.nextInt(Config.CHESTS_BONUS_RANDOMIZE_MIN, Config.CHESTS_BONUS_RANDOMIZE_MAX + 1); + List randoms = new ArrayList<>(this.randomBonusChests); + for (int i = 0; i < bonusChestAmount; i++) { + if (randoms.isEmpty()) return; - if (Config.CHESTS_BONUS_RANDOMIZE_ENABLED) { - this.randomBonusChests.forEach(bonusChest -> { - if (this.random.nextInt(100) < Config.CHESTS_BONUS_RANDOMIZE_CHANCE) { - bonusChest.setBlockData(blockData); - } - }); + Collections.shuffle(randoms); + Block bonusChest = randoms.getFirst(); + randoms.remove(bonusChest); + bonusChest.setBlockData(blockData); } } diff --git a/src/main/java/com/shanebeestudios/hg/api/game/GameCommandData.java b/src/main/java/com/shanebeestudios/hg/api/game/GameCommandData.java index f53cdf4a..0b7e7413 100644 --- a/src/main/java/com/shanebeestudios/hg/api/game/GameCommandData.java +++ b/src/main/java/com/shanebeestudios/hg/api/game/GameCommandData.java @@ -62,8 +62,8 @@ public void runCommands(CommandType commandType, @Nullable Player player) { if (!type.equals(commandType.getName())) continue; if (command.equalsIgnoreCase("none")) continue; command = command.split(":")[1] - .replace("", this.game.gameArenaData.gameRegion.getWorld().getName()) - .replace("", this.game.gameArenaData.getName()); + .replace("", this.game.getGameArenaData().getGameRegion().getWorld().getName()) + .replace("", this.game.getGameArenaData().getName()); if (player != null) { command = command.replace("", player.getName()); } diff --git a/src/main/java/com/shanebeestudios/hg/api/game/GameItemData.java b/src/main/java/com/shanebeestudios/hg/api/game/GameItemData.java index 6a692c7c..cf526d29 100644 --- a/src/main/java/com/shanebeestudios/hg/api/game/GameItemData.java +++ b/src/main/java/com/shanebeestudios/hg/api/game/GameItemData.java @@ -36,4 +36,11 @@ public KitData getKitData() { return this.kitData; } + /** + * Clear preselected kits after game stops + */ + public void postGameReset() { + this.kitData.clearPreselectedKits(); + } + } diff --git a/src/main/java/com/shanebeestudios/hg/api/game/GameLobbyWall.java b/src/main/java/com/shanebeestudios/hg/api/game/GameLobbyWall.java index 726b6afb..69feb39d 100644 --- a/src/main/java/com/shanebeestudios/hg/api/game/GameLobbyWall.java +++ b/src/main/java/com/shanebeestudios/hg/api/game/GameLobbyWall.java @@ -1,31 +1,40 @@ package com.shanebeestudios.hg.api.game; -import com.shanebeestudios.hg.plugin.HungerGames; import com.shanebeestudios.hg.api.util.Constants; import com.shanebeestudios.hg.api.util.Util; +import com.shanebeestudios.hg.plugin.configs.Config; import org.bukkit.Location; -import org.bukkit.block.Block; import org.bukkit.block.BlockFace; import org.bukkit.block.Sign; +import org.bukkit.block.data.BlockData; import org.bukkit.block.data.Directional; +import org.bukkit.block.data.Rotatable; import org.bukkit.block.sign.Side; +import org.bukkit.block.sign.SignSide; import org.bukkit.persistence.PersistentDataContainer; import org.bukkit.persistence.PersistentDataType; +import org.jetbrains.annotations.Nullable; + +import java.util.List; /** * Data holder for a {@link Game Game's} lobby signs */ public class GameLobbyWall extends Data { + private final GameArenaData gameArenaData; private Location signLoc1; private Location signLoc2; private Location signLoc3; protected GameLobbyWall(Game game) { super(game); + this.gameArenaData = game.getGameArenaData(); } - /** Get location of far left sign of lobby wall + /** + * Get location of far left sign of lobby wall + * * @return Location of sign */ public Location getSignLocation() { @@ -38,6 +47,7 @@ public Location getSignLocation() { * @param location The location of the sign to which the lobby will be set at * @return True if lobby is set */ + @SuppressWarnings("CallToPrintStackTrace") protected boolean setLobbyBlock(Location location) { GameArenaData gameArenaData = this.game.getGameArenaData(); try { @@ -45,22 +55,24 @@ protected boolean setLobbyBlock(Location location) { Sign sign1 = (Sign) location.getBlock().getState(); PersistentDataContainer pdc = sign1.getPersistentDataContainer(); pdc.set(Constants.LOBBY_SIGN_KEY, PersistentDataType.STRING, gameArenaData.getName()); - Block c = sign1.getBlock(); - BlockFace face = Util.getSignFace(((Directional) sign1.getBlockData()).getFacing()); - Sign sign2 = (Sign) c.getRelative(face).getState(); + BlockFace face; + BlockData sign1Data = sign1.getBlockData(); + if (sign1Data instanceof Directional directional) { + // Wall Sign + face = Util.getSignFace(directional.getFacing()); + } else if (sign1Data instanceof Rotatable rotatable) { + // Floor/Hanging Sign + face = Util.getSignFace(rotatable.getRotation()); + } else { + return false; + } + Sign sign2 = (Sign) sign1.getBlock().getRelative(face).getState(); Sign sign3 = (Sign) sign2.getBlock().getRelative(face).getState(); this.signLoc2 = sign2.getLocation(); this.signLoc3 = sign3.getLocation(); - sign1.getSide(Side.FRONT).line(0, Util.getMini(this.lang.lobby_sign_1_1)); - sign1.getSide(Side.FRONT).line(1, Util.getMini("" + gameArenaData.getName())); - sign1.getSide(Side.FRONT).line(2, Util.getMini(this.lang.lobby_sign_1_3)); - if (gameArenaData.cost > 0) - sign1.getSide(Side.FRONT).line(3, Util.getMini(HungerGames.getPlugin().getLang().lobby_sign_cost.replace("", String.valueOf(gameArenaData.cost)))); - sign2.getSide(Side.FRONT).line(0, Util.getMini(this.lang.lobby_sign_2_1)); - sign2.getSide(Side.FRONT).line(1, gameArenaData.getStatus().getName()); - sign3.getSide(Side.FRONT).line(0, Util.getMini(this.lang.lobby_sign_3_1)); - sign3.getSide(Side.FRONT).line(1, Util.getMini("0/%s", gameArenaData.getMaxPlayers())); + updateSignLines(List.of(sign1, sign2, sign3)); + sign1.setWaxed(true); sign2.setWaxed(true); sign3.setWaxed(true); @@ -69,30 +81,52 @@ protected boolean setLobbyBlock(Location location) { sign3.update(true); } catch (Exception e) { Util.warning("Failed to setup lobby wall for arena '%s', msg: %s", gameArenaData.getName(), e.getMessage()); + if (Config.SETTINGS_DEBUG) { + e.printStackTrace(); + } return false; } return true; } void updateLobbyBlock() { - if (this.signLoc2 == null || this.signLoc3 == null) { + if (this.signLoc1 == null || this.signLoc2 == null || this.signLoc3 == null) { Util.warning("The lobby wall seems to be missing for '%s'", this.game.getGameArenaData().getName()); return; } - if (this.signLoc2.getBlock().getState() instanceof Sign sign2 && this.signLoc3.getBlock().getState() instanceof Sign sign3) { - GameArenaData gameArenaData = this.game.getGameArenaData(); - sign2.getSide(Side.FRONT).line(1, gameArenaData.getStatus().getName()); - sign3.getSide(Side.FRONT).line(1, Util.getMini("%s/%s", - this.game.getGamePlayerData().getPlayers().size(), - gameArenaData.getMaxPlayers())); - sign2.update(true); - sign3.update(true); + if (this.signLoc1.getBlock().getState() instanceof Sign sign1 && this.signLoc2.getBlock().getState() instanceof Sign sign2 && this.signLoc3.getBlock().getState() instanceof Sign sign3) { + updateSignLines(List.of(sign1, sign2, sign3)); } } + void updateSignLines(List signs) { + for (int signCount = 0; signCount < 3; signCount++) { + List lines = this.lang.lobby_signs_lines.get(signCount); + Sign sign = signs.get(signCount); + SignSide side = sign.getSide(Side.FRONT); + for (int lineCount = 0; lineCount < 4; lineCount++) { + String line = replacements(lines.get(lineCount)); + if (line == null || line.isEmpty()) continue; + side.line(lineCount, Util.getMini(line)); + } + sign.update(true); + } + } + + private @Nullable String replacements(String line) { + if (line.contains("") && this.gameArenaData.getCost() <= 0) return null; + return line + .replace("", this.gameArenaData.getName()) + .replace("", this.gameArenaData.getStatus().getStringName()) + .replace("", "" + this.gameArenaData.getCost()) + .replace("", "" + this.game.getGamePlayerData().getPlayers().size()) + .replace("", "" + this.gameArenaData.getMaxPlayers()); + } + boolean isLobbyValid() { - return this.signLoc1.getBlock().getState() instanceof Sign && this.signLoc2.getBlock().getState() instanceof Sign - && this.signLoc3.getBlock().getState() instanceof Sign; + return this.signLoc1.getBlock().getState() instanceof Sign + && this.signLoc2 != null && this.signLoc2.getBlock().getState() instanceof Sign + && this.signLoc3 != null && this.signLoc3.getBlock().getState() instanceof Sign; } } diff --git a/src/main/java/com/shanebeestudios/hg/api/game/GamePlayerData.java b/src/main/java/com/shanebeestudios/hg/api/game/GamePlayerData.java index 5ee1f0f5..defe6b1f 100644 --- a/src/main/java/com/shanebeestudios/hg/api/game/GamePlayerData.java +++ b/src/main/java/com/shanebeestudios/hg/api/game/GamePlayerData.java @@ -31,7 +31,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.UUID; /** * Data class for holding a {@link Game Game's} players @@ -47,14 +46,14 @@ public class GamePlayerData extends Data { private final GameManager gameManager; // Player Lists - final List players = new ArrayList<>(); - final List spectators = new ArrayList<>(); + private final Map players = new HashMap<>(); + private final Map spectators = new HashMap<>(); // This list contains all players who have joined the arena // Will be used to broadcast messages even if a player is no longer in the game - final List allPlayers = new ArrayList<>(); + private final List allPlayers = new ArrayList<>(); // Data lists - final Map kills = new HashMap<>(); + private final Map kills = new HashMap<>(); private final List randomizedSpawns = new ArrayList<>(); protected GamePlayerData(Game game) { @@ -63,33 +62,32 @@ protected GamePlayerData(Game game) { this.gameManager = this.plugin.getGameManager(); } - // TODO Data methods - /** * Get a list of all players in the game * * @return UUID list of all players in game */ public List getPlayers() { - return this.players; + return new ArrayList<>(this.players.keySet()); } - void clearPlayers() { + void postGameReset() { this.players.clear(); + this.spectators.clear(); this.allPlayers.clear(); } + public Map getKills() { + return this.kills; + } + /** * Get a list of all players currently spectating the game * * @return List of spectators */ public List getSpectators() { - return new ArrayList<>(this.spectators); - } - - void clearSpectators() { - spectators.clear(); + return new ArrayList<>(this.spectators.keySet()); } // Utility methods @@ -120,7 +118,7 @@ private void startMessage(Player player) { */ public void respawnAll() { this.randomizedSpawns.clear(); - for (Player player : this.players) { + for (Player player : this.players.keySet()) { player.teleport(pickRandomSpawn()); } } @@ -176,8 +174,8 @@ public void unFreeze(Player player) { */ public void messageAllActivePlayers(String message) { List allPlayers = new ArrayList<>(); - allPlayers.addAll(this.players); - allPlayers.addAll(this.spectators); + allPlayers.addAll(this.players.keySet()); + allPlayers.addAll(this.spectators.keySet()); for (Player player : allPlayers) { Util.sendMessage(player, message); } @@ -192,7 +190,7 @@ public void messageAllActivePlayers(String message) { */ public void messageAllPlayers(String message) { List allPlayers = new ArrayList<>(this.allPlayers); - allPlayers.addAll(this.spectators); + allPlayers.addAll(this.spectators.keySet()); for (Player player : allPlayers) { Util.sendPrefixedMessage(player, message); } @@ -208,13 +206,22 @@ private Location pickRandomSpawn() { return spawn; } - void addPlayerData(Player player) { - this.players.add(player); + void addPlayerData(Player player, boolean savePreviousLocation) { + this.players.put(player, savePreviousLocation); this.allPlayers.add(player); this.game.getGameBlockData().updateLobbyBlock(); + this.playerManager.createPlayerData(player, this.game); + } + + /** + * Teleport all waiting players into the arena + */ + public void putAllPlayersIntoArena() { + this.players.keySet().forEach(this::putPlayerIntoArena); } - void putPlayerIntoArena(Player player, boolean savePreviousLocation) { + void putPlayerIntoArena(Player player) { + boolean savePreviousLocation = this.players.get(player); Location loc = pickRandomSpawn(); if (loc.getBlock().getRelative(BlockFace.DOWN).getType() == Material.AIR) { while (loc.getBlock().getRelative(BlockFace.DOWN).getType() == Material.AIR) { @@ -226,15 +233,16 @@ void putPlayerIntoArena(Player player, boolean savePreviousLocation) { player.leaveVehicle(); } - Location previousLocation = player.getLocation(); + Location previousLocation = player.getLocation().clone(); // Teleport async into the arena so it loads a little more smoothly player.teleportAsync(loc).thenAccept(a -> { - PlayerData playerData = new PlayerData(player, this.game); - if (savePreviousLocation && Config.savePreviousLocation) { + PlayerData playerData = this.playerManager.getPlayerData(player); + assert playerData != null; + playerData.backup(); + if (savePreviousLocation && Config.SETTINGS_SAVE_PREVIOUS_LOCATION) { playerData.setPreviousLocation(previousLocation); } - this.playerManager.addPlayerData(playerData); this.game.getGameScoreboard().setupBoard(player); heal(player); @@ -244,7 +252,7 @@ void putPlayerIntoArena(Player player, boolean savePreviousLocation) { this.game.getGameScoreboard().updateBoards(); this.game.getGameCommandData().runCommands(GameCommandData.CommandType.JOIN, player); - this.game.getGameItemData().getKitData().giveDefaultKit(player); + this.game.getGameItemData().getKitData().givePreselectedOrDefaultKit(player); }); } @@ -265,12 +273,11 @@ public void addKill(Player player) { */ public void leaveGame(Player player, boolean death) { new PlayerLeaveGameEvent(this.game, player, death).callEvent(); - UUID uuid = player.getUniqueId(); this.players.remove(player); if (!death) this.allPlayers.remove(player); // Only remove the player if they voluntarily left the game unFreeze(player); if (death) { - if (Config.SPECTATE_ENABLED && Config.spectateOnDeath && !game.isGameOver()) { + if (Config.SPECTATE_ENABLED && Config.SPECTATE_DEATH_TO_SPECTATE && !game.isGameOver()) { spectate(player); player.playSound(player.getLocation(), Config.SOUNDS_DEATH, 5, 1); player.showTitle(createTitle()); @@ -281,7 +288,7 @@ public void leaveGame(Player player, boolean death) { } } heal(player); - PlayerData playerData = this.playerManager.getPlayerData(uuid); + PlayerData playerData = this.playerManager.getPlayerData(player); assert playerData != null; Location previousLocation = playerData.getPreviousLocation(); @@ -316,24 +323,23 @@ void exit(Player player, @Nullable Location exitLocation) { * @param spectator The player to spectate */ public void spectate(Player spectator) { - UUID uuid = spectator.getUniqueId(); spectator.teleport(this.game.getGameArenaData().getSpawns().getFirst()); - if (this.playerManager.hasPlayerData(uuid)) { - this.playerManager.transferPlayerDataToSpectator(uuid); + if (this.playerManager.hasPlayerData(spectator)) { + this.playerManager.transferPlayerDataToSpectator(spectator); } else { - this.playerManager.addSpectatorData(new PlayerData(spectator, game)); + this.playerManager.createSpectatorData(spectator, this.game); } - this.spectators.add(spectator); + this.spectators.put(spectator, true); // TODO should we handle location saving? spectator.setGameMode(GameMode.SURVIVAL); spectator.setCollidable(false); if (Config.SPECTATE_FLY) spectator.setAllowFlight(true); - if (Config.SPECTATE_HIDE) { - for (Player player : this.players) { + if (Config.SPECTATE_HIDE_HIDE_SPECTATORS) { + for (Player player : this.players.keySet()) { player.hidePlayer(this.plugin, spectator); } - for (Player player : this.spectators) { + for (Player player : this.spectators.keySet()) { player.hidePlayer(this.plugin, spectator); } } @@ -348,8 +354,7 @@ public void spectate(Player spectator) { * @param spectator The player to remove */ public void leaveSpectate(Player spectator) { - UUID uuid = spectator.getUniqueId(); - PlayerData playerData = this.playerManager.getSpectatorData(uuid); + PlayerData playerData = this.playerManager.getSpectatorData(spectator); if (playerData == null) return; Location previousLocation = playerData.getPreviousLocation(); @@ -362,10 +367,10 @@ public void leaveSpectate(Player spectator) { if (mode == GameMode.SURVIVAL || mode == GameMode.ADVENTURE) spectator.setAllowFlight(false); } - if (Config.SPECTATE_HIDE) + if (Config.SPECTATE_HIDE_HIDE_SPECTATORS) revealPlayer(spectator); exit(spectator, previousLocation); - this.playerManager.removeSpectatorData(uuid); + this.playerManager.removeSpectatorData(spectator); } void revealPlayer(Player hidden) { diff --git a/src/main/java/com/shanebeestudios/hg/api/game/GameScoreboard.java b/src/main/java/com/shanebeestudios/hg/api/game/GameScoreboard.java index 41361e6c..907a754b 100644 --- a/src/main/java/com/shanebeestudios/hg/api/game/GameScoreboard.java +++ b/src/main/java/com/shanebeestudios/hg/api/game/GameScoreboard.java @@ -24,7 +24,7 @@ public class GameScoreboard extends Data { private final GameSidebar gameSidebar; private final Scoreboard scoreboard; private final Team baseBukkitTeam; - final Map gameTeams = new HashMap<>(); + private final Map gameTeams = new HashMap<>(); protected GameScoreboard(Game game) { super(game); @@ -101,9 +101,9 @@ public void createGameTeam(Player player, String teamName) { } /** - * Clear all {@link GameTeam GameTeams} + * Clear all {@link GameTeam GameTeams} after game stops */ - public void clearGameTeams() { + public void postGameReset() { this.gameTeams.forEach((team, gameTeam) -> gameTeam.unregister()); this.gameTeams.clear(); } diff --git a/src/main/java/com/shanebeestudios/hg/api/game/GameTeam.java b/src/main/java/com/shanebeestudios/hg/api/game/GameTeam.java index 098f8097..525089b2 100755 --- a/src/main/java/com/shanebeestudios/hg/api/game/GameTeam.java +++ b/src/main/java/com/shanebeestudios/hg/api/game/GameTeam.java @@ -1,9 +1,9 @@ package com.shanebeestudios.hg.api.game; -import com.shanebeestudios.hg.api.util.Util; -import com.shanebeestudios.hg.plugin.configs.Language; import com.shanebeestudios.hg.api.data.PlayerData; +import com.shanebeestudios.hg.api.util.Util; import com.shanebeestudios.hg.plugin.configs.Config; +import com.shanebeestudios.hg.plugin.configs.Language; import com.shanebeestudios.hg.plugin.managers.PlayerManager; import org.bukkit.entity.Player; import org.bukkit.scoreboard.Team; diff --git a/src/main/java/com/shanebeestudios/hg/api/gui/KitGUI.java b/src/main/java/com/shanebeestudios/hg/api/gui/KitGUI.java index 3e096dd3..36a9d19e 100644 --- a/src/main/java/com/shanebeestudios/hg/api/gui/KitGUI.java +++ b/src/main/java/com/shanebeestudios/hg/api/gui/KitGUI.java @@ -1,11 +1,12 @@ package com.shanebeestudios.hg.api.gui; -import com.shanebeestudios.hg.api.util.Util; import com.shanebeestudios.hg.api.data.KitEntry; -import com.shanebeestudios.hg.plugin.configs.Language; +import com.shanebeestudios.hg.api.util.Util; import com.shanebeestudios.hg.plugin.HungerGames; +import com.shanebeestudios.hg.plugin.configs.Language; import io.papermc.paper.datacomponent.DataComponentTypes; import io.papermc.paper.datacomponent.item.ItemLore; +import io.papermc.paper.datacomponent.item.TooltipDisplay; import net.kyori.adventure.text.Component; import org.bukkit.Bukkit; import org.bukkit.entity.Player; @@ -38,11 +39,16 @@ public KitGUI(KitsGUI kitsGUI, Player player, KitEntry kitEntry) { Util.getMini(this.lang.kits_kit_gui_title.replace("", kitEntry.getName()))); // SETUP INVENTORY - // white line - ItemStack line = ItemType.WHITE_WOOL.createItemStack(); - // TODO hide tooltip (1.21.5 way) + // divider + ItemStack divider = ItemType.BLACK_STAINED_GLASS_PANE.createItemStack(); + if (Util.RUNNING_1_21_5) { + divider.setData(DataComponentTypes.TOOLTIP_DISPLAY, TooltipDisplay.tooltipDisplay() + .hideTooltip(true)); + } else { + divider.setData(DataComponentTypes.CUSTOM_NAME, Component.text(" ")); + } for (int i = 9; i < 18; i++) { - this.inventory.setItem(i, line); + this.inventory.setItem(i, divider); } // exit button @@ -71,7 +77,7 @@ public KitGUI(KitsGUI kitsGUI, Player player, KitEntry kitEntry) { } this.inventory.setItem(1, chestplate); - // Helmet + // Leggings ItemStack leggings = kitEntry.getLeggings(); if (leggings == null) { leggings = ItemType.BARRIER.createItemStack(); @@ -79,7 +85,7 @@ public KitGUI(KitsGUI kitsGUI, Player player, KitEntry kitEntry) { } this.inventory.setItem(2, leggings); - // Helmet + // Boots ItemStack boots = kitEntry.getBoots(); if (boots == null) { boots = ItemType.BARRIER.createItemStack(); @@ -90,8 +96,13 @@ public KitGUI(KitsGUI kitsGUI, Player player, KitEntry kitEntry) { // Potions ItemStack potion = ItemType.POTION.createItemStack(); potion.setData(DataComponentTypes.CUSTOM_NAME, Util.getMini(this.lang.kits_kit_gui_potion_effects)); - // TODO 1.21.5 hide potion contents - potion.addItemFlags(ItemFlag.HIDE_ADDITIONAL_TOOLTIP); + if (Util.RUNNING_1_21_5) { + potion.setData(DataComponentTypes.TOOLTIP_DISPLAY, TooltipDisplay.tooltipDisplay() + .hideTooltip(true)); + } else { + //noinspection deprecation + potion.addItemFlags(ItemFlag.HIDE_ADDITIONAL_TOOLTIP); + } List potionEffects = kitEntry.getPotionEffects(); List lore = new ArrayList<>(); @@ -119,7 +130,7 @@ public void open() { public void click(int slot) { // APPLY if (slot == 7) { - this.kitsGUI.getKitData().setKit(this.player, this.kitEntry.getName()); + this.kitsGUI.getKitData().setKit(this.player, this.kitEntry); this.player.closeInventory(); } // EXIT diff --git a/src/main/java/com/shanebeestudios/hg/api/gui/KitsGUI.java b/src/main/java/com/shanebeestudios/hg/api/gui/KitsGUI.java index efc5a678..7fb6e3c3 100644 --- a/src/main/java/com/shanebeestudios/hg/api/gui/KitsGUI.java +++ b/src/main/java/com/shanebeestudios/hg/api/gui/KitsGUI.java @@ -1,12 +1,12 @@ package com.shanebeestudios.hg.api.gui; -import com.shanebeestudios.hg.api.registry.Registries; -import com.shanebeestudios.hg.api.util.Util; import com.shanebeestudios.hg.api.data.KitData; import com.shanebeestudios.hg.api.data.KitEntry; -import com.shanebeestudios.hg.plugin.configs.Language; import com.shanebeestudios.hg.api.game.Game; +import com.shanebeestudios.hg.api.registry.Registries; +import com.shanebeestudios.hg.api.util.Util; import com.shanebeestudios.hg.plugin.HungerGames; +import com.shanebeestudios.hg.plugin.configs.Language; import io.papermc.paper.datacomponent.DataComponentTypes; import io.papermc.paper.registry.TypedKey; import io.papermc.paper.registry.keys.tags.ItemTypeTagKeys; diff --git a/src/main/java/com/shanebeestudios/hg/api/gui/SpectatorGUI.java b/src/main/java/com/shanebeestudios/hg/api/gui/SpectatorGUI.java index 3d1b26e7..98bbd00f 100644 --- a/src/main/java/com/shanebeestudios/hg/api/gui/SpectatorGUI.java +++ b/src/main/java/com/shanebeestudios/hg/api/gui/SpectatorGUI.java @@ -1,9 +1,9 @@ package com.shanebeestudios.hg.api.gui; -import com.shanebeestudios.hg.plugin.configs.Language; import com.shanebeestudios.hg.api.game.Game; import com.shanebeestudios.hg.api.util.Util; import com.shanebeestudios.hg.plugin.HungerGames; +import com.shanebeestudios.hg.plugin.configs.Language; import net.kyori.adventure.text.Component; import org.bukkit.Bukkit; import org.bukkit.Material; diff --git a/src/main/java/com/shanebeestudios/hg/api/parsers/ItemParser.java b/src/main/java/com/shanebeestudios/hg/api/parsers/ItemParser.java index 79cc240e..de088cb7 100644 --- a/src/main/java/com/shanebeestudios/hg/api/parsers/ItemParser.java +++ b/src/main/java/com/shanebeestudios/hg/api/parsers/ItemParser.java @@ -3,15 +3,18 @@ import com.shanebeestudios.hg.api.registry.Registries; import com.shanebeestudios.hg.api.util.NBTApi; import com.shanebeestudios.hg.api.util.Util; +import io.papermc.paper.datacomponent.DataComponentType; import io.papermc.paper.datacomponent.DataComponentTypes; import io.papermc.paper.datacomponent.item.DyedItemColor; import io.papermc.paper.datacomponent.item.ItemEnchantments; import io.papermc.paper.datacomponent.item.PotionContents; +import io.papermc.paper.datacomponent.item.TooltipDisplay; import net.kyori.adventure.text.Component; import org.bukkit.Color; import org.bukkit.NamespacedKey; import org.bukkit.configuration.ConfigurationSection; import org.bukkit.enchantments.Enchantment; +import org.bukkit.inventory.ItemFlag; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemType; import org.bukkit.potion.PotionEffect; @@ -22,12 +25,10 @@ import java.util.ArrayList; import java.util.List; import java.util.Locale; -import java.util.Map; -import java.util.concurrent.atomic.AtomicInteger; public class ItemParser { - @SuppressWarnings({"UnstableApiUsage", "unchecked"}) + @SuppressWarnings({"UnstableApiUsage"}) public static @Nullable ItemStack parseItem(@Nullable ConfigurationSection config) { if (config == null) return null; @@ -133,25 +134,6 @@ public class ItemParser { } } - // POTION_EFFECT -// if (config.contains("potion_effects")) { -// List> potionEffectsList = (List>) config.getList("potion_effects"); -// assert potionEffectsList != null; -// PotionContents.Builder builder = PotionContents.potionContents(); -// AtomicInteger color = new AtomicInteger(-1); -// potionEffectsList.forEach(entry -> { -// PotionEffect potionEffect = parsePotionEffect(entry); -// if (potionEffect != null) { -// builder.addCustomEffect(potionEffect); -// if (entry.containsKey("custom_color")) { -// color.set((int) entry.get("custom_color")); -// } -// } -// }); -// if (color.get() != -1) builder.customColor(Color.fromRGB(color.get())); -// itemStack.setData(DataComponentTypes.POTION_CONTENTS, builder.build()); -// } - // POTION_EFFECT if (config.isConfigurationSection("potion_effects")) { ConfigurationSection potionEffectsSection = config.getConfigurationSection("potion_effects"); @@ -170,11 +152,32 @@ public class ItemParser { itemStack.setData(DataComponentTypes.POTION_CONTENTS, builder.build()); } - // DYED_COLOR if (config.contains("dyed_color")) { int color = config.getInt("dyed_color"); - itemStack.setData(DataComponentTypes.DYED_COLOR, DyedItemColor.dyedItemColor(Color.fromRGB(color), false)); + itemStack.setData(DataComponentTypes.DYED_COLOR, DyedItemColor.dyedItemColor().color(Color.fromRGB(color))); + if (!Util.RUNNING_1_21_5) { + itemStack.addItemFlags(ItemFlag.HIDE_ADDITIONAL_TOOLTIP); + } + } + + // HIDDEN COMPONENTS + if (Util.RUNNING_1_21_5 && config.isList("hidden_components")) { + TooltipDisplay.Builder builder = TooltipDisplay.tooltipDisplay(); + for (String compKey : config.getStringList("hidden_components")) { + NamespacedKey namespacedKey = NamespacedKey.fromString(compKey); + if (namespacedKey == null) { + Util.warning("Invalid component key: " + compKey); + continue; + } + DataComponentType type = Registries.DATA_COMPONENT_TYPE_REGISTRY.get(namespacedKey); + if (type == null) { + Util.warning("Invalid component key: " + compKey); + continue; + } + builder.addHiddenComponents(type); + } + itemStack.setData(DataComponentTypes.TOOLTIP_DISPLAY, builder.build()); } // NBT diff --git a/src/main/java/com/shanebeestudios/hg/api/region/RegionUtils.java b/src/main/java/com/shanebeestudios/hg/api/region/RegionUtils.java new file mode 100644 index 00000000..5ff653a8 --- /dev/null +++ b/src/main/java/com/shanebeestudios/hg/api/region/RegionUtils.java @@ -0,0 +1,46 @@ +package com.shanebeestudios.hg.api.region; + +import com.shanebeestudios.hg.api.util.Util; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.block.Block; +import org.bukkit.entity.Entity; + +/** + * Utility methods for Folia's threaded regions + */ +public class RegionUtils { + + /** + * Check if an object is owned by the curent region + *

Currently accepts Block/Location/Entity. + * If not running Folia this will just return true

+ * + * @param object Object to check + * @return Whether object is running in the current region + */ + public static boolean isOwnedByCurrentRegion(Object object) { + if (!Util.IS_RUNNING_FOLIA) return true; + + if (object instanceof Block block) return Bukkit.isOwnedByCurrentRegion(block); + else if (object instanceof Entity entity) return Bukkit.isOwnedByCurrentRegion(entity); + else if (object instanceof Location location) return Bukkit.isOwnedByCurrentRegion(location); + return true; + } + + /** + * Check if a chunk location is owned by a current region + *

If not running Folia this will just return true

+ * + * @param world World of chunk + * @param chunkX Chunk X of chunk + * @param chunkZ Chunk Z of chunk + * @return Whether chunk is running in the current region + */ + public static boolean isOwnedByCurrentRegion(World world, int chunkX, int chunkZ) { + if (!Util.IS_RUNNING_FOLIA) return true; + return Bukkit.isOwnedByCurrentRegion(world, chunkX, chunkZ); + } + +} diff --git a/src/main/java/com/shanebeestudios/hg/api/region/TaskUtils.java b/src/main/java/com/shanebeestudios/hg/api/region/TaskUtils.java new file mode 100644 index 00000000..1a807db0 --- /dev/null +++ b/src/main/java/com/shanebeestudios/hg/api/region/TaskUtils.java @@ -0,0 +1,110 @@ +package com.shanebeestudios.hg.api.region; + +import com.shanebeestudios.hg.api.region.scheduler.FoliaScheduler; +import com.shanebeestudios.hg.api.region.scheduler.Scheduler; +import com.shanebeestudios.hg.api.region.scheduler.SpigotScheduler; +import com.shanebeestudios.hg.api.util.Util; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.entity.Entity; +import org.bukkit.plugin.Plugin; +import org.jetbrains.annotations.NotNull; + +/** + * Utility class for creating {@link Scheduler Schedulers} + *

Initialize before use, {@link #initialize(Plugin)}

+ *

If initialized with `useFoliaSchedulers=true`, will return a {@link FoliaScheduler} + * else will return a {@link SpigotScheduler}

+ */ +public class TaskUtils { + + private static Plugin plugin; + private static boolean useFoliaSchedulers; + + /** + * Initialize schedulers + * + * @param plugin Plugin to reference for tasks + */ + public static void initialize(@NotNull Plugin plugin) { + if (TaskUtils.plugin != null) { + throw new IllegalStateException("TaskUtils already initialized!"); + } + TaskUtils.plugin = plugin; + TaskUtils.useFoliaSchedulers = Util.IS_RUNNING_FOLIA; + } + + /** + * Get the plugin that these schedulers will use + * + * @return Plugin for schedulers + */ + public static Plugin getPlugin() { + pluginCheck(); + return plugin; + } + + /** + * Get a global scheduler + *

This is used for global World/Server tasks which don't require regions

+ *

If running Spigot or Paper (with Paper schedulers disabled) this will use a normal Bukkit Scheduler

+ * + * @return Global scheduler + */ + public static Scheduler getGlobalScheduler() { + pluginCheck(); + if (useFoliaSchedulers) return FoliaScheduler.getGlobalScheduler(); + return new SpigotScheduler(); + } + + /** + * Get a regional scheduler based on a location + *

This is used for scheduling tasks at a specific location

+ *

If running Spigot or Paper (with Paper schedulers disabled) this will use a normal Bukkit Scheduler

+ * + * @param location Location to grab region from + * @return Region scheduler + */ + public static Scheduler getRegionalScheduler(Location location) { + pluginCheck(); + if (useFoliaSchedulers) + return FoliaScheduler.getRegionalScheduler(location); + return new SpigotScheduler(); + } + + /** + * Get an entity scheduler + *

This is used for scheduling tasks linked to an entity + * The tasks will move with the entity to whatever region they're in

+ *

If running Spigot or Paper (with Paper schedulers disabled) this will use a normal Bukkit Scheduler

+ * + * @param entity Entity to attach scheduler to + * @return Entity scheduler + */ + public static Scheduler getEntityScheduler(Entity entity) { + pluginCheck(); + if (useFoliaSchedulers) + return FoliaScheduler.getEntityScheduler(entity); + return new SpigotScheduler(); + } + + /** + * Cancel all currently running tasks + */ + public static void cancelTasks() { + pluginCheck(); + if (useFoliaSchedulers) { + Bukkit.getGlobalRegionScheduler().cancelTasks(plugin); + Bukkit.getAsyncScheduler().cancelTasks(plugin); + } else { + Bukkit.getScheduler().cancelTasks(plugin); + } + } + + private static void pluginCheck() { + if (plugin == null) { + throw new IllegalStateException("TaskUtils has not been initialized!"); + } + } + +} diff --git a/src/main/java/com/shanebeestudios/hg/api/region/scheduler/FoliaScheduler.java b/src/main/java/com/shanebeestudios/hg/api/region/scheduler/FoliaScheduler.java new file mode 100644 index 00000000..eb5ac615 --- /dev/null +++ b/src/main/java/com/shanebeestudios/hg/api/region/scheduler/FoliaScheduler.java @@ -0,0 +1,108 @@ +package com.shanebeestudios.hg.api.region.scheduler; + +import com.shanebeestudios.hg.api.region.TaskUtils; +import com.shanebeestudios.hg.api.region.scheduler.task.FoliaTask; +import com.shanebeestudios.hg.api.region.scheduler.task.Task; +import io.papermc.paper.threadedregions.scheduler.AsyncScheduler; +import io.papermc.paper.threadedregions.scheduler.GlobalRegionScheduler; +import io.papermc.paper.threadedregions.scheduler.RegionScheduler; +import io.papermc.paper.threadedregions.scheduler.ScheduledTask; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.entity.Entity; +import org.jetbrains.annotations.Nullable; + +import java.util.concurrent.TimeUnit; + +/** + * A {@link Scheduler} based on Folia/Paper regionalized schedulers + */ +public class FoliaScheduler implements Scheduler { + + private static final GlobalRegionScheduler GLOBAL_SCHEDULER = Bukkit.getGlobalRegionScheduler(); + private static final RegionScheduler REGION_SCHEDULER = Bukkit.getRegionScheduler(); + private static final AsyncScheduler ASYNC_SCHEDULER = Bukkit.getAsyncScheduler(); + + public static FoliaScheduler getGlobalScheduler() { + return new FoliaScheduler(null, null); + } + + public static FoliaScheduler getRegionalScheduler(Location location) { + return new FoliaScheduler(null, location); + } + + public static FoliaScheduler getEntityScheduler(Entity entity) { + return new FoliaScheduler(entity, null); + } + + private final @Nullable Entity entity; + private final @Nullable Location location; + + public FoliaScheduler(@Nullable Entity entity, @Nullable Location location) { + this.entity = entity; + this.location = location; + } + + @Override + public FoliaTask runTask(Runnable task) { + ScheduledTask scheduledTask; + if (this.entity != null) { + scheduledTask = this.entity.getScheduler().run(TaskUtils.getPlugin(), t -> task.run(), null); + } else if (this.location != null) { + scheduledTask = REGION_SCHEDULER.run(TaskUtils.getPlugin(), this.location, t -> task.run()); + } else { + scheduledTask = GLOBAL_SCHEDULER.run(TaskUtils.getPlugin(), t -> task.run()); + } + return new FoliaTask(scheduledTask); + } + + @Override + public FoliaTask runTaskAsync(Runnable task) { + ScheduledTask scheduledTask = ASYNC_SCHEDULER.runNow(TaskUtils.getPlugin(), t -> task.run()); + return new FoliaTask(scheduledTask); + } + + @Override + public FoliaTask runTaskLater(Runnable task, long delay) { + if (delay <= 0) delay = 1; + ScheduledTask scheduledTask; + if (this.entity != null) { + scheduledTask = this.entity.getScheduler().runDelayed(TaskUtils.getPlugin(), t -> task.run(), null, delay); + } else if (this.location != null) { + scheduledTask = REGION_SCHEDULER.runDelayed(TaskUtils.getPlugin(), this.location, t -> task.run(), delay); + } else { + scheduledTask = GLOBAL_SCHEDULER.runDelayed(TaskUtils.getPlugin(), t -> task.run(), delay); + } + return new FoliaTask(scheduledTask); + } + + @Override + public Task runTaskLaterAsync(Runnable task, long delay) { + if (delay <= 0) delay = 1; + ScheduledTask scheduledTask = ASYNC_SCHEDULER.runDelayed(TaskUtils.getPlugin(), t -> task.run(), delay * 50, TimeUnit.MILLISECONDS); + return new FoliaTask(scheduledTask); + } + + @Override + public FoliaTask runTaskTimer(Runnable task, long delay, long period) { + if (delay <= 0) delay = 1; + ScheduledTask scheduledTask; + if (this.entity != null) { + scheduledTask = this.entity.getScheduler().runAtFixedRate(TaskUtils.getPlugin(), t -> task.run(), null, delay, period); + } else if (this.location != null) { + scheduledTask = REGION_SCHEDULER.runAtFixedRate(TaskUtils.getPlugin(), this.location, t -> task.run(), delay, period); + } else { + scheduledTask = GLOBAL_SCHEDULER.runAtFixedRate(TaskUtils.getPlugin(), t -> task.run(), delay, period); + } + return new FoliaTask(scheduledTask); + } + + @Override + public Task runTaskTimerAsync(Runnable task, long delay, long period) { + if (delay <= 0) delay = 1; + ScheduledTask scheduledTask = ASYNC_SCHEDULER.runAtFixedRate(TaskUtils.getPlugin(), t -> task.run(), + delay * 50, period * 50, TimeUnit.MILLISECONDS); + return new FoliaTask(scheduledTask); + } + +} diff --git a/src/main/java/com/shanebeestudios/hg/api/region/scheduler/Scheduler.java b/src/main/java/com/shanebeestudios/hg/api/region/scheduler/Scheduler.java new file mode 100644 index 00000000..138463ac --- /dev/null +++ b/src/main/java/com/shanebeestudios/hg/api/region/scheduler/Scheduler.java @@ -0,0 +1,68 @@ +package com.shanebeestudios.hg.api.region.scheduler; + +import com.shanebeestudios.hg.api.region.scheduler.task.Task; + +/** + * Scheduler for scheduling tasks + *

Changes based on Spigot/Paper vs. Folia usage

+ * + * @param Underlying task return type + */ +@SuppressWarnings({"unused", "UnusedReturnValue"}) +public interface Scheduler { + + /** + * Run a task on the next tick + * + * @param task Task to run + * @return Instance of scheduler + */ + Task runTask(Runnable task); + + /** + * Run an async task on the next tick + * + * @param task Task to run + * @return Instance of scheduler + */ + Task runTaskAsync(Runnable task); + + /** + * Run a task at a later time + * + * @param task Task to run + * @param delay Delay in ticks + * @return Instance of scheduler + */ + Task runTaskLater(Runnable task, long delay); + + /** + * Run an async task at a later time + * + * @param task Task to run + * @param delay Delay in ticks + * @return Instance of scheduler + */ + Task runTaskLaterAsync(Runnable task, long delay); + + /** + * Run a repeating task + * + * @param task Task to run + * @param delay Delay in ticks to start + * @param period Period in ticks to repeat + * @return Instance of scheduler + */ + Task runTaskTimer(Runnable task, long delay, long period); + + /** + * Run an async repeating task + * + * @param task Task to run + * @param delay Delay in ticks to start + * @param period Period in ticks to repeat + * @return Instance of scheduler + */ + Task runTaskTimerAsync(Runnable task, long delay, long period); + +} diff --git a/src/main/java/com/shanebeestudios/hg/api/region/scheduler/SpigotScheduler.java b/src/main/java/com/shanebeestudios/hg/api/region/scheduler/SpigotScheduler.java new file mode 100644 index 00000000..d645a708 --- /dev/null +++ b/src/main/java/com/shanebeestudios/hg/api/region/scheduler/SpigotScheduler.java @@ -0,0 +1,47 @@ +package com.shanebeestudios.hg.api.region.scheduler; + +import com.shanebeestudios.hg.api.region.TaskUtils; +import com.shanebeestudios.hg.api.region.scheduler.task.SpigotTask; +import com.shanebeestudios.hg.api.region.scheduler.task.Task; +import org.bukkit.Bukkit; +import org.bukkit.scheduler.BukkitScheduler; +import org.bukkit.scheduler.BukkitTask; + +/** + * A {@link Scheduler} using the {@link BukkitScheduler} + */ +public class SpigotScheduler implements Scheduler { + + private static final BukkitScheduler SCHEDULER = Bukkit.getScheduler(); + + @Override + public SpigotTask runTask(Runnable task) { + return new SpigotTask(SCHEDULER.runTask(TaskUtils.getPlugin(), task)); + } + + @Override + public SpigotTask runTaskAsync(Runnable task) { + return new SpigotTask(SCHEDULER.runTaskAsynchronously(TaskUtils.getPlugin(), task)); + } + + @Override + public SpigotTask runTaskLater(Runnable task, long delay) { + return new SpigotTask(SCHEDULER.runTaskLater(TaskUtils.getPlugin(), task, delay)); + } + + @Override + public Task runTaskLaterAsync(Runnable task, long delay) { + return new SpigotTask(SCHEDULER.runTaskLaterAsynchronously(TaskUtils.getPlugin(), task, delay)); + } + + @Override + public SpigotTask runTaskTimer(Runnable task, long delay, long period) { + return new SpigotTask(SCHEDULER.runTaskTimer(TaskUtils.getPlugin(), task, delay, period)); + } + + @Override + public Task runTaskTimerAsync(Runnable task, long delay, long period) { + return new SpigotTask(SCHEDULER.runTaskTimerAsynchronously(TaskUtils.getPlugin(), task, delay, period)); + } + +} diff --git a/src/main/java/com/shanebeestudios/hg/api/region/scheduler/package-info.java b/src/main/java/com/shanebeestudios/hg/api/region/scheduler/package-info.java new file mode 100644 index 00000000..233997b9 --- /dev/null +++ b/src/main/java/com/shanebeestudios/hg/api/region/scheduler/package-info.java @@ -0,0 +1,5 @@ +/** + * Utility classes for Schedulers + *

Things will differ based on Spigot/Paper vs. Folia servers

+ */ +package com.shanebeestudios.hg.api.region.scheduler; diff --git a/src/main/java/com/shanebeestudios/hg/api/region/scheduler/task/FoliaTask.java b/src/main/java/com/shanebeestudios/hg/api/region/scheduler/task/FoliaTask.java new file mode 100644 index 00000000..82512984 --- /dev/null +++ b/src/main/java/com/shanebeestudios/hg/api/region/scheduler/task/FoliaTask.java @@ -0,0 +1,32 @@ +package com.shanebeestudios.hg.api.region.scheduler.task; + +import io.papermc.paper.threadedregions.scheduler.ScheduledTask; + +/** + * Task wrapper for Folia's {@link ScheduledTask} + */ +public class FoliaTask implements Task { + + private final ScheduledTask scheduledTask; + + public FoliaTask(ScheduledTask scheduledTask) { + this.scheduledTask = scheduledTask; + } + + @Override + public void cancel() { + this.scheduledTask.cancel(); + } + + @Override + public boolean isCancelled() { + return this.scheduledTask.isCancelled(); + } + + @Override + public int getTaskId() { + // ScheduledTask doesn't have an ID?!?!? + return -1; + } + +} diff --git a/src/main/java/com/shanebeestudios/hg/api/region/scheduler/task/SpigotTask.java b/src/main/java/com/shanebeestudios/hg/api/region/scheduler/task/SpigotTask.java new file mode 100644 index 00000000..7f4d2c6d --- /dev/null +++ b/src/main/java/com/shanebeestudios/hg/api/region/scheduler/task/SpigotTask.java @@ -0,0 +1,31 @@ +package com.shanebeestudios.hg.api.region.scheduler.task; + +import org.bukkit.scheduler.BukkitTask; + +/** + * Task wrapper for Bukkit's {@link BukkitTask} + */ +public class SpigotTask implements Task { + + private final BukkitTask bukkitTask; + + public SpigotTask(BukkitTask bukkitTask) { + this.bukkitTask = bukkitTask; + } + + @Override + public void cancel() { + this.bukkitTask.cancel(); + } + + @Override + public boolean isCancelled() { + return this.bukkitTask.isCancelled(); + } + + @Override + public int getTaskId() { + return this.bukkitTask.getTaskId(); + } + +} diff --git a/src/main/java/com/shanebeestudios/hg/api/region/scheduler/task/Task.java b/src/main/java/com/shanebeestudios/hg/api/region/scheduler/task/Task.java new file mode 100644 index 00000000..2ac046a5 --- /dev/null +++ b/src/main/java/com/shanebeestudios/hg/api/region/scheduler/task/Task.java @@ -0,0 +1,29 @@ +package com.shanebeestudios.hg.api.region.scheduler.task; + +/** + * Base task to be implemented by server tasks + * + * @param Task type + */ +public interface Task { + + /** + * Cancel this task + */ + void cancel(); + + /** + * Check if this task has been cancelled + * + * @return Whether this task has been cancelled + */ + boolean isCancelled(); + + /** + * Get the ID of this task + * + * @return ID of this task + */ + int getTaskId(); + +} diff --git a/src/main/java/com/shanebeestudios/hg/api/registry/Registries.java b/src/main/java/com/shanebeestudios/hg/api/registry/Registries.java index b685bd23..384cee4f 100644 --- a/src/main/java/com/shanebeestudios/hg/api/registry/Registries.java +++ b/src/main/java/com/shanebeestudios/hg/api/registry/Registries.java @@ -1,5 +1,6 @@ package com.shanebeestudios.hg.api.registry; +import io.papermc.paper.datacomponent.DataComponentType; import io.papermc.paper.registry.RegistryAccess; import io.papermc.paper.registry.RegistryKey; import org.bukkit.Registry; @@ -21,6 +22,7 @@ public class Registries { public static final Registry ATTRIBUTE_REGISTRY = RegistryAccess.registryAccess().getRegistry(RegistryKey.ATTRIBUTE); public static final Registry BLOCK_TYPE_REGISTRY = RegistryAccess.registryAccess().getRegistry(RegistryKey.BLOCK); public static final Registry DAMAGE_TYPE_REGISTRY = RegistryAccess.registryAccess().getRegistry(RegistryKey.DAMAGE_TYPE); + public static final Registry DATA_COMPONENT_TYPE_REGISTRY = RegistryAccess.registryAccess().getRegistry(RegistryKey.DATA_COMPONENT_TYPE); public static final Registry ENCHANTMENT_REGISTRY = RegistryAccess.registryAccess().getRegistry(RegistryKey.ENCHANTMENT); public static final Registry ENTITY_TYPE_REGISTRY = RegistryAccess.registryAccess().getRegistry(RegistryKey.ENTITY_TYPE); public static final Registry ITEM_TYPE_REGISTRY = RegistryAccess.registryAccess().getRegistry(RegistryKey.ITEM); diff --git a/src/main/java/com/shanebeestudios/hg/api/status/PlayerStatus.java b/src/main/java/com/shanebeestudios/hg/api/status/PlayerStatus.java index b1699f6a..b7835e7c 100644 --- a/src/main/java/com/shanebeestudios/hg/api/status/PlayerStatus.java +++ b/src/main/java/com/shanebeestudios/hg/api/status/PlayerStatus.java @@ -1,7 +1,7 @@ package com.shanebeestudios.hg.api.status; -import com.shanebeestudios.hg.plugin.HungerGames; import com.shanebeestudios.hg.api.util.Util; +import com.shanebeestudios.hg.plugin.HungerGames; import com.shanebeestudios.hg.plugin.configs.Language; import net.kyori.adventure.text.Component; diff --git a/src/main/java/com/shanebeestudios/hg/api/status/Status.java b/src/main/java/com/shanebeestudios/hg/api/status/Status.java index 2b260bc1..dde0b858 100755 --- a/src/main/java/com/shanebeestudios/hg/api/status/Status.java +++ b/src/main/java/com/shanebeestudios/hg/api/status/Status.java @@ -1,8 +1,8 @@ package com.shanebeestudios.hg.api.status; +import com.shanebeestudios.hg.api.util.Util; import com.shanebeestudios.hg.plugin.HungerGames; import com.shanebeestudios.hg.plugin.configs.Language; -import com.shanebeestudios.hg.api.util.Util; import net.kyori.adventure.text.Component; /** @@ -26,29 +26,28 @@ public enum Status { * Game is starting to run in the free roam state */ FREE_ROAM, - /** - * Game is running - */ - RUNNING, - /** - * Game has stopped - */ - STOPPED, + /** + * Game is running + */ + RUNNING, + /** + * Game has stopped + */ + STOPPED, /** * Game is currently rolling back blocks */ ROLLBACK, - /** - * Game is broken - */ - BROKEN, - /** - * Game is not ready - */ - NOT_READY - ; + /** + * Game is broken + */ + BROKEN, + /** + * Game is not ready + */ + NOT_READY; - final Language lang = HungerGames.getPlugin().getLang(); + final Language lang = HungerGames.getPlugin().getLang(); public boolean isActive() { return switch (this) { @@ -57,19 +56,19 @@ public boolean isActive() { }; } - public Component getName() { + public Component getName() { return switch (this) { - case RUNNING -> Util.getMini(this.lang.game_status_running); - case STOPPED -> Util.getMini(this.lang.game_status_stopped); case READY -> Util.getMini(this.lang.game_status_ready); case WAITING -> Util.getMini(this.lang.game_status_waiting); - case BROKEN -> Util.getMini(this.lang.game_status_broken); + case COUNTDOWN -> Util.getMini(this.lang.game_status_countdown); + case FREE_ROAM -> Util.getMini(this.lang.game_status_free_roam); + case RUNNING -> Util.getMini(this.lang.game_status_running); + case STOPPED -> Util.getMini(this.lang.game_status_stopped); case ROLLBACK -> Util.getMini(this.lang.game_status_rollback); + case BROKEN -> Util.getMini(this.lang.game_status_broken); case NOT_READY -> Util.getMini(this.lang.game_status_not_ready); - case FREE_ROAM -> Util.getMini(this.lang.game_status_beginning); - case COUNTDOWN -> Util.getMini(this.lang.game_status_countdown); }; - } + } public String getStringName() { return Util.unMini(this.getName()); diff --git a/src/main/java/com/shanebeestudios/hg/api/util/HgLogger.java b/src/main/java/com/shanebeestudios/hg/api/util/HgLogger.java index c271bf13..dfbb5f0f 100644 --- a/src/main/java/com/shanebeestudios/hg/api/util/HgLogger.java +++ b/src/main/java/com/shanebeestudios/hg/api/util/HgLogger.java @@ -11,7 +11,9 @@ protected HgLogger(String name, String resourceBundleName) { super(name, resourceBundleName); } - /** Get an instance of HgLogger + /** + * Get an instance of HgLogger + * * @return new instance of HgLogger */ public static HgLogger getLogger() { diff --git a/src/main/java/com/shanebeestudios/hg/api/util/ItemUtils.java b/src/main/java/com/shanebeestudios/hg/api/util/ItemUtils.java index 04592627..d97f1cfa 100644 --- a/src/main/java/com/shanebeestudios/hg/api/util/ItemUtils.java +++ b/src/main/java/com/shanebeestudios/hg/api/util/ItemUtils.java @@ -1,8 +1,8 @@ package com.shanebeestudios.hg.api.util; import com.shanebeestudios.hg.plugin.HungerGames; -import com.shanebeestudios.hg.plugin.configs.Language; import com.shanebeestudios.hg.plugin.configs.Config; +import com.shanebeestudios.hg.plugin.configs.Language; import io.papermc.paper.datacomponent.DataComponentTypes; import io.papermc.paper.datacomponent.item.ItemLore; import io.papermc.paper.persistence.PersistentDataContainerView; @@ -38,12 +38,12 @@ public static ItemStack getTrackingStick() { pdc.set(Constants.TRACKING_STICK_KEY, PersistentDataType.BOOLEAN, true); itemStack.setItemMeta(itemMeta); - itemStack.setData(DataComponentTypes.ITEM_NAME, Util.getMini(LANG.tracking_stick_name)); + itemStack.setData(DataComponentTypes.ITEM_NAME, Util.getMini(LANG.item_tracking_stick_name)); itemStack.setData(DataComponentTypes.MAX_STACK_SIZE, 1); - itemStack.setData(DataComponentTypes.MAX_DAMAGE, Config.TRACKING_STICK_USES); + itemStack.setData(DataComponentTypes.MAX_DAMAGE, Config.SETTINGS_TRACKING_STICK_USES); itemStack.setData(DataComponentTypes.DAMAGE, 0); List lore = new ArrayList<>(); - LANG.tracking_stick_lore.forEach(line -> lore.add(Util.getMini(line))); + LANG.item_tracking_stick_lore.forEach(line -> lore.add(Util.getMini(line))); itemStack.setData(DataComponentTypes.LORE, ItemLore.lore(lore)); return itemStack; } diff --git a/src/main/java/com/shanebeestudios/hg/api/util/NBTApi.java b/src/main/java/com/shanebeestudios/hg/api/util/NBTApi.java index d451a49c..bd8dd674 100644 --- a/src/main/java/com/shanebeestudios/hg/api/util/NBTApi.java +++ b/src/main/java/com/shanebeestudios/hg/api/util/NBTApi.java @@ -1,7 +1,6 @@ package com.shanebeestudios.hg.api.util; import de.tr7zw.changeme.nbtapi.NBT; -import de.tr7zw.changeme.nbtapi.NBTCompound; import de.tr7zw.changeme.nbtapi.NBTContainer; import de.tr7zw.changeme.nbtapi.NbtApiException; import de.tr7zw.changeme.nbtapi.iface.ReadWriteNBT; @@ -130,10 +129,10 @@ public static void applyNBTToEntity(Entity entity, String nbtString) { * Get a pretty NBT string *

This is the same as what vanilla Minecraft outputs when using the '/data' command

* - * @param compound Compound to convert to pretty - * @param split When null NBT will print on one long line, if not null NBT compound will be - * split into lines with JSON style, and this string will start each line off - * (usually spaces) + * @param nbtString NBT string to convert to pretty + * @param split When null NBT will print on one long line, if not null NBT compound will be + * split into lines with JSON style, and this string will start each line off + * (usually spaces) * @return Pretty string of NBTCompound */ @SuppressWarnings("deprecation") diff --git a/src/main/java/com/shanebeestudios/hg/api/util/Util.java b/src/main/java/com/shanebeestudios/hg/api/util/Util.java index 9cb88877..a80f27bf 100755 --- a/src/main/java/com/shanebeestudios/hg/api/util/Util.java +++ b/src/main/java/com/shanebeestudios/hg/api/util/Util.java @@ -3,6 +3,7 @@ import com.shanebeestudios.hg.plugin.HungerGames; import com.shanebeestudios.hg.plugin.configs.Config; import com.shanebeestudios.hg.plugin.configs.Language; +import dev.jorel.commandapi.arguments.CustomArgument.CustomArgumentException; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.minimessage.MiniMessage; import net.md_5.bungee.api.ChatColor; @@ -14,10 +15,6 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; -import java.util.UUID; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -27,7 +24,13 @@ @SuppressWarnings("WeakerAccess") public class Util { - public static final BlockFace[] BLOCK_FACES = new BlockFace[]{BlockFace.EAST, BlockFace.WEST, BlockFace.NORTH, BlockFace.SOUTH}; + // PUBLIC + // Quick link to help for removing legacy stuff later + public static final boolean RUNNING_1_21_5 = isRunningMinecraft(1, 21, 5); + public static final boolean IS_RUNNING_FOLIA = classExists("io.papermc.paper.threadedregions.FoliaWatchdogThread"); + + + // PRIVATE private static final Pattern HEX_PATTERN = Pattern.compile("<#([A-Fa-f0-9]){6}>"); private static final CommandSender CONSOLE = Bukkit.getConsoleSender(); private static final MiniMessage MINI_MESSAGE = MiniMessage.miniMessage(); @@ -168,6 +171,10 @@ public static Component getMini(String format, Object... objects) { return MINI_MESSAGE.deserialize(String.format(format, objects)); } + public static void throwCustomArgException(String message) throws CustomArgumentException { + throw CustomArgumentException.fromAdventureComponent(getMini(getPrefix() + " " + message)); + } + /** * Convert a MiniMessage/Component to text * @@ -217,38 +224,6 @@ public static void clearInv(Player player) { player.updateInventory(); } - /** - * Convert a list of UUIDs to a string of player names - * - * @param uuid UUID list to convert - * @return List of player names - */ - public static List convertUUIDListToStringList(List uuid) { - List winners = new ArrayList<>(); - for (UUID id : uuid) { - winners.add(Objects.requireNonNull(Bukkit.getPlayer(id)).getName()); - } - return winners; - } - - public static String translateStop(List win) { - StringBuilder builder = null; - int count = 0; - for (String s : win) { - count++; - if (count == 1) builder = new StringBuilder(s); - else if (count == win.size()) { - builder.append(", and ").append(s); - } else { - builder.append(", ").append(s); - } - } - if (builder != null) - return builder.toString(); - else - return "No one"; - } - @SuppressWarnings("DataFlowIssue") public static @NotNull NamespacedKey getPluginKey(String key) { return NamespacedKey.fromString("hungergames:" + key); diff --git a/src/main/java/com/shanebeestudios/hg/api/util/Validate.java b/src/main/java/com/shanebeestudios/hg/api/util/Validate.java deleted file mode 100644 index e997fc0f..00000000 --- a/src/main/java/com/shanebeestudios/hg/api/util/Validate.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.shanebeestudios.hg.api.util; - -public abstract class Validate { - - /** - * Validate if a value is between a min and max value - * - * @param value Value to validate - * @param min Min value amount - * @param max Max value amount - */ - public static void isBetween(int value, int min, int max) { - //value >= min && value <= max, "Value must be between " + min + " and " + max); - // TODO this seems like a wasted class/method - } - -} diff --git a/src/main/java/com/shanebeestudios/hg/api/util/Vault.java b/src/main/java/com/shanebeestudios/hg/api/util/Vault.java index 7b548288..d2f009d2 100755 --- a/src/main/java/com/shanebeestudios/hg/api/util/Vault.java +++ b/src/main/java/com/shanebeestudios/hg/api/util/Vault.java @@ -1,23 +1,22 @@ package com.shanebeestudios.hg.api.util; +import net.milkbowl.vault.economy.Economy; import org.bukkit.Bukkit; import org.bukkit.plugin.RegisteredServiceProvider; -import net.milkbowl.vault.economy.Economy; - /** * General Vault class */ public class Vault { - public static Economy economy = null; + public static Economy ECONOMY = null; public static boolean setupEconomy() { RegisteredServiceProvider economyProvider = Bukkit.getServer().getServicesManager().getRegistration(net.milkbowl.vault.economy.Economy.class); if (economyProvider != null) { - economy = economyProvider.getProvider(); + ECONOMY = economyProvider.getProvider(); } - return (economy != null); + return (ECONOMY != null); } } diff --git a/src/main/java/com/shanebeestudios/hg/package-info.java b/src/main/java/com/shanebeestudios/hg/package-info.java deleted file mode 100644 index 67415b6c..00000000 --- a/src/main/java/com/shanebeestudios/hg/package-info.java +++ /dev/null @@ -1,4 +0,0 @@ -/** - * General HungerGames package - */ -package com.shanebeestudios.hg; diff --git a/src/main/java/com/shanebeestudios/hg/plugin/HungerGames.java b/src/main/java/com/shanebeestudios/hg/plugin/HungerGames.java index 8e4a6b63..8988f24e 100755 --- a/src/main/java/com/shanebeestudios/hg/plugin/HungerGames.java +++ b/src/main/java/com/shanebeestudios/hg/plugin/HungerGames.java @@ -1,6 +1,7 @@ package com.shanebeestudios.hg.plugin; import com.shanebeestudios.hg.api.data.Leaderboard; +import com.shanebeestudios.hg.api.region.TaskUtils; import com.shanebeestudios.hg.api.util.NBTApi; import com.shanebeestudios.hg.api.util.Util; import com.shanebeestudios.hg.plugin.commands.MainCommand; @@ -31,12 +32,16 @@ import dev.jorel.commandapi.exceptions.UnsupportedVersionException; import io.lumine.mythic.api.MythicProvider; import org.bstats.bukkit.Metrics; +import org.bstats.charts.DrilldownPie; import org.bstats.charts.SimplePie; import org.bukkit.Bukkit; import org.bukkit.event.HandlerList; import org.bukkit.plugin.PluginManager; import org.bukkit.plugin.java.JavaPlugin; +import java.util.HashMap; +import java.util.Map; + /** * Main class for HungerGames */ @@ -54,11 +59,11 @@ public class HungerGames extends JavaPlugin { private ArenaConfig arenaConfig; // Managers - private GameManager gameManager; - private PlayerManager playerManager; - private KillManager killManager; private ItemManager itemManager; + private PlayerManager playerManager; private KitManager kitManager; + private GameManager gameManager; + private KillManager killManager; private SessionManager sessionManager; private MobManager mobManager; private io.lumine.mythic.api.mobs.MobManager mythicMobManager; @@ -88,6 +93,7 @@ public void onEnable() { return; } NBTApi.initializeNBTApi(); + TaskUtils.initialize(this); loadPlugin(true); } @@ -107,11 +113,10 @@ public void loadPlugin(boolean load) { } this.lang = new Language(this); this.itemManager = new ItemManager(this); + this.playerManager = new PlayerManager(); this.kitManager = new KitManager(this); this.sessionManager = new SessionManager(); - this.mobManager = new MobManager(this); - this.playerManager = new PlayerManager(); this.gameManager = new GameManager(this); this.arenaConfig = new ArenaConfig(this); this.leaderboard = new Leaderboard(this); @@ -156,6 +161,7 @@ private void unloadPlugin(boolean reload) { this.arenaConfig = null; this.killManager = null; this.gameManager = null; + this.leaderboard.saveLeaderboard(); this.leaderboard = null; HandlerList.unregisterAll(this); if (reload) { @@ -174,15 +180,18 @@ public void onDisable() { private void setupMetrics() { this.metrics = new Metrics(this, 25144); // Config - this.metrics.addCustomChart(new SimplePie("config-worldborder-enabled", () -> - "" + Config.WORLD_BORDER_ENABLED)); - this.metrics.addCustomChart(new SimplePie("config-chestdrop-enabled", () -> - "" + Config.CHESTS_CHEST_DROP_ENABLED)); + this.metrics.addCustomChart(new DrilldownPie("config", () -> { + Map> map = new HashMap<>(); + map.put("worldborder-enabled", Map.of("" + Config.WORLD_BORDER_ENABLED, 1)); + map.put("chestdrop-enabled", Map.of("" + Config.CHESTS_CHEST_DROP_ENABLED, 1)); + map.put("reward-enabled", Map.of("" + Config.REWARD_ENABLED, 1)); + map.put("spectate-enabled", Map.of("" + Config.SPECTATE_ENABLED, 1)); + return map; + })); // Arenas this.metrics.addCustomChart(new SimplePie("arenas-count", () -> "" + this.gameManager.getGames().size())); - } private void loadListeners() { diff --git a/src/main/java/com/shanebeestudios/hg/plugin/commands/CreateCommand.java b/src/main/java/com/shanebeestudios/hg/plugin/commands/CreateCommand.java index 9beeb490..412e1465 100644 --- a/src/main/java/com/shanebeestudios/hg/plugin/commands/CreateCommand.java +++ b/src/main/java/com/shanebeestudios/hg/plugin/commands/CreateCommand.java @@ -1,9 +1,9 @@ package com.shanebeestudios.hg.plugin.commands; -import com.shanebeestudios.hg.plugin.HungerGames; -import com.shanebeestudios.hg.api.util.Util; import com.shanebeestudios.hg.api.data.PlayerSession; import com.shanebeestudios.hg.api.game.Game; +import com.shanebeestudios.hg.api.util.Util; +import com.shanebeestudios.hg.plugin.HungerGames; import com.shanebeestudios.hg.plugin.managers.SessionManager; import com.shanebeestudios.hg.plugin.permission.Permissions; import dev.jorel.commandapi.arguments.Argument; diff --git a/src/main/java/com/shanebeestudios/hg/plugin/commands/DebugCommand.java b/src/main/java/com/shanebeestudios/hg/plugin/commands/DebugCommand.java index 6afdff52..cfacefe2 100644 --- a/src/main/java/com/shanebeestudios/hg/plugin/commands/DebugCommand.java +++ b/src/main/java/com/shanebeestudios/hg/plugin/commands/DebugCommand.java @@ -1,8 +1,8 @@ package com.shanebeestudios.hg.plugin.commands; -import com.shanebeestudios.hg.plugin.HungerGames; import com.shanebeestudios.hg.api.command.CustomArg; import com.shanebeestudios.hg.api.game.Game; +import com.shanebeestudios.hg.plugin.HungerGames; import com.shanebeestudios.hg.plugin.permission.Permissions; import dev.jorel.commandapi.arguments.Argument; import dev.jorel.commandapi.arguments.LiteralArgument; @@ -19,7 +19,8 @@ protected Argument register() { .withPermission(Permissions.COMMAND_DEBUG.permission()) .then(CustomArg.GAME.get("game") .executes(info -> { - Game game = CustomArg.getGame(info); + Game game = info.args().getByClass("game", Game.class); + assert game != null; this.gameManager.checkGame(game, info.sender()); })); } diff --git a/src/main/java/com/shanebeestudios/hg/plugin/commands/DeleteArenaCommand.java b/src/main/java/com/shanebeestudios/hg/plugin/commands/DeleteArenaCommand.java index 851e8abb..0d40760d 100644 --- a/src/main/java/com/shanebeestudios/hg/plugin/commands/DeleteArenaCommand.java +++ b/src/main/java/com/shanebeestudios/hg/plugin/commands/DeleteArenaCommand.java @@ -1,11 +1,11 @@ package com.shanebeestudios.hg.plugin.commands; -import com.shanebeestudios.hg.plugin.HungerGames; import com.shanebeestudios.hg.api.command.CustomArg; -import com.shanebeestudios.hg.api.util.Util; import com.shanebeestudios.hg.api.game.Game; import com.shanebeestudios.hg.api.game.GameArenaData; import com.shanebeestudios.hg.api.game.GamePlayerData; +import com.shanebeestudios.hg.api.util.Util; +import com.shanebeestudios.hg.plugin.HungerGames; import com.shanebeestudios.hg.plugin.permission.Permissions; import dev.jorel.commandapi.arguments.Argument; import dev.jorel.commandapi.arguments.LiteralArgument; @@ -25,7 +25,8 @@ protected Argument register() { .then(CustomArg.GAME.get("game") .executes(info -> { CommandSender sender = info.sender(); - Game game = CustomArg.getGame(info); + Game game = info.args().getByClass("game", Game.class); + assert game != null; GamePlayerData gamePlayerData = game.getGamePlayerData(); GameArenaData gameArenaData = game.getGameArenaData(); String name = gameArenaData.getName(); diff --git a/src/main/java/com/shanebeestudios/hg/plugin/commands/EditCommand.java b/src/main/java/com/shanebeestudios/hg/plugin/commands/EditCommand.java index 89882dbf..f3de01f2 100644 --- a/src/main/java/com/shanebeestudios/hg/plugin/commands/EditCommand.java +++ b/src/main/java/com/shanebeestudios/hg/plugin/commands/EditCommand.java @@ -1,11 +1,11 @@ package com.shanebeestudios.hg.plugin.commands; -import com.shanebeestudios.hg.plugin.HungerGames; import com.shanebeestudios.hg.api.command.CustomArg; -import com.shanebeestudios.hg.api.util.Util; import com.shanebeestudios.hg.api.game.Game; import com.shanebeestudios.hg.api.game.GameArenaData; import com.shanebeestudios.hg.api.game.GameBorderData; +import com.shanebeestudios.hg.api.util.Util; +import com.shanebeestudios.hg.plugin.HungerGames; import com.shanebeestudios.hg.plugin.permission.Permissions; import dev.jorel.commandapi.CommandAPI; import dev.jorel.commandapi.arguments.Argument; @@ -32,9 +32,8 @@ protected Argument register() { return LiteralArgument.literal("edit") .withPermission(Permissions.COMMAND_EDIT.permission()) .then(CustomArg.GAME.get("game") - .then(chestRefillRepeat()) - .then(chestRefillTime()) .then(border()) + .then(chestRefill()) .then(info()) .then(locations()) ); @@ -46,7 +45,7 @@ private Argument border() { .then(LiteralArgument.literal("final_size") .then(new IntegerArgument("final_size", 5) .executes(info -> { - Game game = CustomArg.getGame(info); + Game game = info.args().getByClass("game", Game.class); int finalSize = info.args().getByClass("final_size", Integer.class); GameBorderData gameBorderData = game.getGameBorderData(); gameBorderData.setFinalBorderSize(finalSize); @@ -56,7 +55,7 @@ private Argument border() { .then(LiteralArgument.literal("countdown_start") .then(new IntegerArgument("countdown_start", 5) .executes(info -> { - Game game = CustomArg.getGame(info); + Game game = info.args().getByClass("game", Game.class); int countdownStart = info.args().getByClass("countdown_start", Integer.class); GameBorderData gameBorderData = game.getGameBorderData(); gameBorderData.setBorderCountdownStart(countdownStart); @@ -66,7 +65,7 @@ private Argument border() { .then(LiteralArgument.literal("countdown_end") .then(new IntegerArgument("countdown_end", 5) .executes(info -> { - Game game = CustomArg.getGame(info); + Game game = info.args().getByClass("game", Game.class); int countdownEnd = info.args().getByClass("countdown_end", Integer.class); GameBorderData gameBorderData = game.getGameBorderData(); gameBorderData.setBorderCountdownEnd(countdownEnd); @@ -77,7 +76,7 @@ private Argument border() { .then(LiteralArgument.literal("center_location") .then(new Location2DArgument("center_location", LocationType.BLOCK_POSITION) .executes(info -> { - Game game = CustomArg.getGame(info); + Game game = info.args().getByClass("game", Game.class); Location2D centerLocation = info.args().getByClass("center_location", Location2D.class); GameBorderData gameBorderData = game.getGameBorderData(); gameBorderData.setCenterLocation(convert(centerLocation)); @@ -87,46 +86,43 @@ private Argument border() { } @SuppressWarnings("DataFlowIssue") - private Argument chestRefillTime() { - return LiteralArgument.literal("chest-refill-time") - .then(new IntegerArgument("time", 30) - .executes(info -> { - Game game = CustomArg.getGame(info); - CommandSender sender = info.sender(); - String name = game.getGameArenaData().getName(); - int time = info.args().getByClass("time", Integer.class); - if (time % 30 != 0) { - Util.sendPrefixedMessage(sender, "