diff --git a/module/placeholderapi-integration/build.gradle.kts b/module/placeholderapi-integration/build.gradle.kts new file mode 100644 index 00000000..6a3e6d9d --- /dev/null +++ b/module/placeholderapi-integration/build.gradle.kts @@ -0,0 +1,7 @@ +repositories { + maven("https://repo.extendedclip.com/content/repositories/placeholderapi/") +} + +dependencies { + compileOnly("me.clip:placeholderapi:2.11.6") +} diff --git a/module/placeholderapi-integration/src/main/java/org/battleplugins/arena/module/placeholderapi/BattleArenaExpansion.java b/module/placeholderapi-integration/src/main/java/org/battleplugins/arena/module/placeholderapi/BattleArenaExpansion.java new file mode 100644 index 00000000..e2a89ae4 --- /dev/null +++ b/module/placeholderapi-integration/src/main/java/org/battleplugins/arena/module/placeholderapi/BattleArenaExpansion.java @@ -0,0 +1,123 @@ +package org.battleplugins.arena.module.placeholderapi; + +import me.clip.placeholderapi.expansion.PlaceholderExpansion; +import org.battleplugins.arena.Arena; +import org.battleplugins.arena.ArenaPlayer; +import org.battleplugins.arena.BattleArena; +import org.battleplugins.arena.competition.Competition; +import org.battleplugins.arena.competition.phase.CompetitionPhaseType; +import org.battleplugins.arena.resolver.Resolver; +import org.battleplugins.arena.resolver.ResolverKey; +import org.battleplugins.arena.resolver.ResolverKeys; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class BattleArenaExpansion extends PlaceholderExpansion { + private final BattleArena plugin; + + public BattleArenaExpansion(BattleArena plugin) { + this.plugin = plugin; + } + + @Override + public @NotNull String getIdentifier() { + return "ba"; + } + + @Override + public @NotNull String getAuthor() { + return "BattlePlugins"; + } + + @Override + public @NotNull String getVersion() { + return this.plugin.getPluginMeta().getVersion(); + } + + @Override + public @Nullable String onPlaceholderRequest(Player player, @NotNull String params) { + String[] split = params.split("_"); + + // No data for us to parse + if (split.length < 2) { + return null; + } + + ArenaPlayer arenaPlayer = ArenaPlayer.getArenaPlayer(player); + if (arenaPlayer != null && params.startsWith("competition")) { + String placeholder = String.join("_", split).substring("competition_".length()); + + Resolver resolver = arenaPlayer.resolve(); + ResolverKey resolverKey = ResolverKeys.get(placeholder.replace("_", "-")); + if (resolverKey != null && resolver.has(resolverKey)) { + return resolver.resolveToString(resolverKey); + } + } + + // If player is null or no other placeholder resolvers have made it to this point, + // handle more general placeholders + + String arenaName = split[0]; + Arena arena = this.plugin.getArena(arenaName); + + // No arena, so not much we can do here + if (arena == null) { + return null; + } + + // Remaining text in split array + String placeholder = String.join("_", split).substring(arenaName.length() + 1); + switch (placeholder) { + case "active_competitions": { + return String.valueOf(this.plugin.getCompetitions(arena).size()); + } + case "online_players": { + int players = 0; + for (Competition competition : this.plugin.getCompetitions(arena)) { + players += competition.getAlivePlayerCount() + competition.getSpectatorCount(); + } + + return String.valueOf(players); + } + case "alive_players": { + int online = 0; + for (Competition competition : this.plugin.getCompetitions(arena)) { + online += competition.getAlivePlayerCount(); + } + + return String.valueOf(online); + } + case "spectators": { + int spectators = 0; + for (Competition competition : this.plugin.getCompetitions(arena)) { + spectators += competition.getSpectatorCount(); + } + + return String.valueOf(spectators); + } + case "waiting_competitions": { + int waitingCompetitions = 0; + for (Competition competition : this.plugin.getCompetitions(arena)) { + CompetitionPhaseType phase = competition.getPhase(); + if (CompetitionPhaseType.WAITING.equals(phase)) { + waitingCompetitions++; + } + } + return String.valueOf(waitingCompetitions); + } + case "ingame_competitions": { + int ingameCompetitions = 0; + for (Competition competition : this.plugin.getCompetitions(arena)) { + CompetitionPhaseType phase = competition.getPhase(); + if (CompetitionPhaseType.INGAME.equals(phase)) { + ingameCompetitions++; + } + } + return String.valueOf(ingameCompetitions); + } + } + + return null; + } +} diff --git a/module/placeholderapi-integration/src/main/java/org/battleplugins/arena/module/placeholderapi/PlaceholderApiContainer.java b/module/placeholderapi-integration/src/main/java/org/battleplugins/arena/module/placeholderapi/PlaceholderApiContainer.java new file mode 100644 index 00000000..b0ac022b --- /dev/null +++ b/module/placeholderapi-integration/src/main/java/org/battleplugins/arena/module/placeholderapi/PlaceholderApiContainer.java @@ -0,0 +1,16 @@ +package org.battleplugins.arena.module.placeholderapi; + +import org.battleplugins.arena.BattleArena; + +public class PlaceholderApiContainer { + private final BattleArenaExpansion expansion; + + public PlaceholderApiContainer(BattleArena plugin) { + this.expansion = new BattleArenaExpansion(plugin); + this.expansion.register(); + } + + public void disable() { + this.expansion.unregister(); + } +} diff --git a/module/placeholderapi-integration/src/main/java/org/battleplugins/arena/module/placeholderapi/PlaceholderApiIntegration.java b/module/placeholderapi-integration/src/main/java/org/battleplugins/arena/module/placeholderapi/PlaceholderApiIntegration.java new file mode 100644 index 00000000..2da51926 --- /dev/null +++ b/module/placeholderapi-integration/src/main/java/org/battleplugins/arena/module/placeholderapi/PlaceholderApiIntegration.java @@ -0,0 +1,39 @@ +package org.battleplugins.arena.module.placeholderapi; + +import org.battleplugins.arena.event.BattleArenaPostInitializeEvent; +import org.battleplugins.arena.event.BattleArenaShutdownEvent; +import org.battleplugins.arena.module.ArenaModule; +import org.battleplugins.arena.module.ArenaModuleInitializer; +import org.bukkit.Bukkit; +import org.bukkit.event.EventHandler; + +/** + * A module that allows for hooking into the PlaceholderAPI plugin. + */ +@ArenaModule(id = PlaceholderApiIntegration.ID, name = "PlaceholderAPI", description = "Adds support for hooking into the PlaceholderAPI plugin.", authors = "BattlePlugins") +public class PlaceholderApiIntegration implements ArenaModuleInitializer { + public static final String ID = "placeholderapi"; + + private PlaceholderApiContainer container; + + @EventHandler + public void onPostInitialize(BattleArenaPostInitializeEvent event) { + // Check that we have Vault installed + if (!Bukkit.getServer().getPluginManager().isPluginEnabled("PlaceholderAPI")) { + event.getBattleArena().module(PlaceholderApiIntegration.ID).ifPresent(container -> + container.disable("PlaceholderAPI is required for the PlaceholderAPI integration module to work!") + ); + + return; + } + + this.container = new PlaceholderApiContainer(event.getBattleArena()); + } + + @EventHandler + public void onShutdown(BattleArenaShutdownEvent event) { + if (this.container != null) { + this.container.disable(); + } + } +} diff --git a/plugin/src/main/java/org/battleplugins/arena/Arena.java b/plugin/src/main/java/org/battleplugins/arena/Arena.java index 27de1a81..71557613 100644 --- a/plugin/src/main/java/org/battleplugins/arena/Arena.java +++ b/plugin/src/main/java/org/battleplugins/arena/Arena.java @@ -27,6 +27,7 @@ import org.battleplugins.arena.resolver.Resolver; import org.battleplugins.arena.resolver.ResolverKeys; import org.battleplugins.arena.resolver.ResolverProvider; +import org.battleplugins.arena.util.Describable; import org.bukkit.configuration.ConfigurationSection; import org.jetbrains.annotations.Nullable; @@ -49,7 +50,7 @@ * active game actions will occur (i.e. game timer, score, etc.) */ @DocumentationSource("https://docs.battleplugins.org/books/user-guide/chapter/configuration") -public class Arena implements ArenaLike, ArenaListener, Resolvable, ConfigHolder { +public class Arena implements ArenaLike, ArenaListener, ConfigHolder, Resolvable, Describable { @Scoped private BattleArena plugin; @@ -330,6 +331,11 @@ public final Arena getArena() { return this; } + @Override + public final String describe() { + return this.getName(); + } + @Override public Resolver resolve() { return Resolver.builder() diff --git a/plugin/src/main/java/org/battleplugins/arena/ArenaLoader.java b/plugin/src/main/java/org/battleplugins/arena/ArenaLoader.java index 7b3b0757..f898dce0 100644 --- a/plugin/src/main/java/org/battleplugins/arena/ArenaLoader.java +++ b/plugin/src/main/java/org/battleplugins/arena/ArenaLoader.java @@ -35,7 +35,7 @@ public void load() { Bukkit.getPluginManager().registerEvents(arena, this.battleArena); - this.battleArena.arenas.put(arena.getName(), arena); + this.battleArena.arenas.put(arena.getName().toLowerCase(Locale.ROOT), arena); // Register command PluginCommand command = this.battleArena.getCommand(arena.getName().toLowerCase(Locale.ROOT)); diff --git a/plugin/src/main/java/org/battleplugins/arena/BattleArena.java b/plugin/src/main/java/org/battleplugins/arena/BattleArena.java index fd319d38..d474b857 100644 --- a/plugin/src/main/java/org/battleplugins/arena/BattleArena.java +++ b/plugin/src/main/java/org/battleplugins/arena/BattleArena.java @@ -224,7 +224,7 @@ void postInitialize() { // Initialize events for (Map.Entry> entry : this.config.getEvents().entrySet()) { - Arena arena = this.arenas.get(entry.getKey()); + Arena arena = this.getArena(entry.getKey()); if (arena == null) { this.warn("Event {} does not have a valid arena!", entry.getKey()); continue; @@ -283,7 +283,7 @@ public boolean isInArena(Player player) { * @return the arena from the given name */ public Optional arena(String name) { - return Optional.ofNullable(this.arenas.get(name)); + return Optional.ofNullable(this.getArena(name)); } /** @@ -294,7 +294,7 @@ public Optional arena(String name) { */ @Nullable public Arena getArena(String name) { - return this.arenas.get(name); + return this.arenas.get(name.toLowerCase(Locale.ROOT)); } /** @@ -714,7 +714,7 @@ private void loadArenaMaps() { } }); } catch (IOException e) { - throw new RuntimeException("Error loading maps for arena " + arenaName, e); + throw new RuntimeException("Error loading maps for arena " + arena.getName(), e); } } } diff --git a/plugin/src/main/java/org/battleplugins/arena/competition/Competition.java b/plugin/src/main/java/org/battleplugins/arena/competition/Competition.java index 7ce82013..137b24a5 100644 --- a/plugin/src/main/java/org/battleplugins/arena/competition/Competition.java +++ b/plugin/src/main/java/org/battleplugins/arena/competition/Competition.java @@ -65,6 +65,20 @@ public interface Competition> extends CompetitionLike getPlayers() { public final Set getSpectators() { return Collections.unmodifiableSet(this.playersByRole.getOrDefault(PlayerRole.SPECTATING, Set.of())); } + + @Override + public final int getAlivePlayerCount() { + return this.getPlayers().size(); + } + + @Override + public final int getSpectatorCount() { + return this.getSpectators().size(); + } @Override public final int getMaxPlayers() { @@ -385,11 +395,13 @@ private int calculateMaxPlayers() { @Override public Resolver resolve() { Resolver.Builder builder = this.arena.resolve().toBuilder() + .define(ResolverKeys.ALIVE_PLAYERS, ResolverProvider.simple(this.getAlivePlayerCount(), String::valueOf)) .define(ResolverKeys.COMPETITION, ResolverProvider.simple(this.getCompetition(), this.getMap()::getName)) - .define(ResolverKeys.ONLINE_PLAYERS, ResolverProvider.simple(this.getPlayers().size(), String::valueOf)) + .define(ResolverKeys.ONLINE_PLAYERS, ResolverProvider.simple(this.getAlivePlayerCount() + this.getSpectatorCount(), String::valueOf)) .define(ResolverKeys.MAP, ResolverProvider.simple(this.getMap(), CompetitionMap::getName)) .define(ResolverKeys.MAX_PLAYERS, ResolverProvider.simple(this.getMaxPlayers(), String::valueOf)) - .define(ResolverKeys.PHASE, ResolverProvider.simple(this.getPhaseManager().getCurrentPhase(), p -> p.getType().getName())); + .define(ResolverKeys.PHASE, ResolverProvider.simple(this.getPhaseManager().getCurrentPhase(), p -> p.getType().getName())) + .define(ResolverKeys.SPECTATORS, ResolverProvider.simple(this.getSpectatorCount(), String::valueOf)); this.getVictoryManager().resolve().mergeInto(builder); if (this.getPhaseManager().getCurrentPhase() instanceof LiveCompetitionPhase phase) { diff --git a/plugin/src/main/java/org/battleplugins/arena/competition/map/CompetitionMap.java b/plugin/src/main/java/org/battleplugins/arena/competition/map/CompetitionMap.java index 491e46cf..d05291be 100644 --- a/plugin/src/main/java/org/battleplugins/arena/competition/map/CompetitionMap.java +++ b/plugin/src/main/java/org/battleplugins/arena/competition/map/CompetitionMap.java @@ -1,9 +1,11 @@ package org.battleplugins.arena.competition.map; +import org.battleplugins.arena.util.Describable; + /** * A map for a competition. */ -public interface CompetitionMap { +public interface CompetitionMap extends Describable { /** * Gets the name of the map. @@ -18,4 +20,9 @@ public interface CompetitionMap { * @return the type of map */ MapType getType(); + + @Override + default String describe() { + return this.getName(); + } } diff --git a/plugin/src/main/java/org/battleplugins/arena/competition/phase/CompetitionPhase.java b/plugin/src/main/java/org/battleplugins/arena/competition/phase/CompetitionPhase.java index 20ea2683..141a5243 100644 --- a/plugin/src/main/java/org/battleplugins/arena/competition/phase/CompetitionPhase.java +++ b/plugin/src/main/java/org/battleplugins/arena/competition/phase/CompetitionPhase.java @@ -11,6 +11,7 @@ import org.battleplugins.arena.event.ArenaListener; import org.battleplugins.arena.event.action.EventAction; import org.battleplugins.arena.options.ArenaOptionType; +import org.battleplugins.arena.util.Describable; import org.jetbrains.annotations.Nullable; import java.util.List; @@ -20,7 +21,7 @@ /** * Represents the phase of a competition. */ -public abstract class CompetitionPhase> implements CompetitionLike, ArenaListener { +public abstract class CompetitionPhase> implements CompetitionLike, ArenaListener, Describable { @Id private CompetitionPhaseType> type; @@ -162,4 +163,9 @@ public final CompetitionPhase getPreviousPhase() { protected final void setPreviousPhase(CompetitionPhase previousPhase) { this.previousPhase = previousPhase; } + + @Override + public final String describe() { + return this.type.describe(); + } } diff --git a/plugin/src/main/java/org/battleplugins/arena/competition/phase/CompetitionPhaseType.java b/plugin/src/main/java/org/battleplugins/arena/competition/phase/CompetitionPhaseType.java index ebb98f9c..ffbbec78 100644 --- a/plugin/src/main/java/org/battleplugins/arena/competition/phase/CompetitionPhaseType.java +++ b/plugin/src/main/java/org/battleplugins/arena/competition/phase/CompetitionPhaseType.java @@ -5,6 +5,7 @@ import org.battleplugins.arena.competition.phase.phases.IngamePhase; import org.battleplugins.arena.competition.phase.phases.VictoryPhase; import org.battleplugins.arena.competition.phase.phases.WaitingPhase; +import org.battleplugins.arena.util.Describable; import org.jetbrains.annotations.Nullable; import java.util.HashMap; @@ -18,7 +19,7 @@ * @param the type of competition * @param the type of competition phase */ -public final class CompetitionPhaseType, T extends CompetitionPhase> { +public final class CompetitionPhaseType, T extends CompetitionPhase> implements Describable { private static final Map> PHASE_TYPES = new HashMap<>(); public static final CompetitionPhaseType> WAITING = new CompetitionPhaseType("waiting", WaitingPhase.class); @@ -53,6 +54,11 @@ public static , T extends CompetitionPhase> Competit return new CompetitionPhaseType<>(name, clazz); } + @Override + public String describe() { + return this.getName(); + } + @Override public boolean equals(Object o) { if (this == o) return true; diff --git a/plugin/src/main/java/org/battleplugins/arena/competition/victory/VictoryConditionType.java b/plugin/src/main/java/org/battleplugins/arena/competition/victory/VictoryConditionType.java index 9384b8dd..ea01953a 100644 --- a/plugin/src/main/java/org/battleplugins/arena/competition/victory/VictoryConditionType.java +++ b/plugin/src/main/java/org/battleplugins/arena/competition/victory/VictoryConditionType.java @@ -5,6 +5,7 @@ import org.battleplugins.arena.competition.victory.types.TeamsAliveCondition; import org.battleplugins.arena.competition.victory.types.TimeLimitCondition; import org.battleplugins.arena.config.DocumentationSource; +import org.battleplugins.arena.util.Describable; import org.jetbrains.annotations.Nullable; import java.util.HashMap; @@ -18,7 +19,7 @@ * @param the type of victory condition */ @DocumentationSource("https://docs.battleplugins.org/books/user-guide/page/victory-conditions-reference") -public final class VictoryConditionType, T extends VictoryCondition> { +public final class VictoryConditionType, T extends VictoryCondition> implements Describable { private static final Map> VICTORY_TYPES = new HashMap<>(); public static final VictoryConditionType HIGHEST_STAT = new VictoryConditionType<>("highest-stat", HighestStatCondition.class); @@ -52,6 +53,11 @@ public static , T extends VictoryCondition> Vict return new VictoryConditionType<>(name, clazz); } + @Override + public String describe() { + return this.getName(); + } + @Override public boolean equals(Object o) { if (this == o) return true; diff --git a/plugin/src/main/java/org/battleplugins/arena/module/ArenaModuleLoader.java b/plugin/src/main/java/org/battleplugins/arena/module/ArenaModuleLoader.java index 1a081105..fe171ab9 100644 --- a/plugin/src/main/java/org/battleplugins/arena/module/ArenaModuleLoader.java +++ b/plugin/src/main/java/org/battleplugins/arena/module/ArenaModuleLoader.java @@ -53,8 +53,7 @@ public void loadModules() throws IOException { AtomicReference> arenaModuleClassRef = new AtomicReference<>(); zipFile.stream().forEach((file -> { if (file.getName().endsWith(".class")) { - try { - InputStream inputStream = zipFile.getInputStream(file); + try (InputStream inputStream = zipFile.getInputStream(file)) { byte[] buffer = new byte[inputStream.available()]; inputStream.read(buffer); @@ -68,9 +67,16 @@ public void loadModules() throws IOException { // Set the annotation arenaModuleRef.set(clazz.getAnnotation(ArenaModule.class)); } + } catch (Throwable e) { + // Ignore NoClassDefFoundError - this is typically thrown when + // a module extends a class from a third party plugin which may not be + // found when loading the module if the plugin is not present. The + // above logic just looks for a class annotated with @ArenaModule + // which will fail after this code runs anyway. + if (e instanceof NoClassDefFoundError) { + return; + } - inputStream.close(); - } catch (Exception e) { this.plugin.error("Error when setting up module {}! Please contact the module author!", path.getFileName().toString(), e); // Add the exception to the failed modules set diff --git a/plugin/src/main/java/org/battleplugins/arena/module/ModuleLoadException.java b/plugin/src/main/java/org/battleplugins/arena/module/ModuleLoadException.java index f05b49a2..ce93cbd5 100644 --- a/plugin/src/main/java/org/battleplugins/arena/module/ModuleLoadException.java +++ b/plugin/src/main/java/org/battleplugins/arena/module/ModuleLoadException.java @@ -9,7 +9,7 @@ public ModuleLoadException(ArenaModule module, String message) { this.module = module; } - public ModuleLoadException(ArenaModule module, Exception exception) { + public ModuleLoadException(ArenaModule module, Throwable exception) { super(exception); this.module = module; diff --git a/plugin/src/main/java/org/battleplugins/arena/resolver/Resolver.java b/plugin/src/main/java/org/battleplugins/arena/resolver/Resolver.java index 165d5839..a79023d3 100644 --- a/plugin/src/main/java/org/battleplugins/arena/resolver/Resolver.java +++ b/plugin/src/main/java/org/battleplugins/arena/resolver/Resolver.java @@ -10,6 +10,8 @@ public interface Resolver extends Resolvable { T resolve(ResolverKey key); + String resolveToString(ResolverKey key); + boolean has(ResolverKey key); void mergeInto(Builder builder); diff --git a/plugin/src/main/java/org/battleplugins/arena/resolver/ResolverImpl.java b/plugin/src/main/java/org/battleplugins/arena/resolver/ResolverImpl.java index 21af567f..e32cc1c9 100644 --- a/plugin/src/main/java/org/battleplugins/arena/resolver/ResolverImpl.java +++ b/plugin/src/main/java/org/battleplugins/arena/resolver/ResolverImpl.java @@ -43,6 +43,16 @@ public T resolve(ResolverKey key) { return (T) provider.resolve(this); } + @Override + public String resolveToString(ResolverKey key) { + ResolverProvider provider = this.results.get(key); + if (provider == null) { + throw new IllegalArgumentException("No provider defined for key " + key); + } + + return provider.toString(this); + } + @Override public boolean has(ResolverKey key) { return this.results.containsKey(key); diff --git a/plugin/src/main/java/org/battleplugins/arena/resolver/ResolverKeys.java b/plugin/src/main/java/org/battleplugins/arena/resolver/ResolverKeys.java index edba4499..454052d3 100644 --- a/plugin/src/main/java/org/battleplugins/arena/resolver/ResolverKeys.java +++ b/plugin/src/main/java/org/battleplugins/arena/resolver/ResolverKeys.java @@ -5,7 +5,6 @@ import org.battleplugins.arena.competition.Competition; import org.battleplugins.arena.competition.map.CompetitionMap; import org.battleplugins.arena.competition.phase.CompetitionPhase; -import org.battleplugins.arena.competition.victory.VictoryCondition; import org.battleplugins.arena.competition.victory.VictoryConditionType; import org.battleplugins.arena.stat.ArenaStat; import org.battleplugins.arena.stat.StatHolder; @@ -13,27 +12,50 @@ import org.battleplugins.arena.util.TypeToken; import java.time.Duration; +import java.util.HashMap; +import java.util.Map; import java.util.Set; public final class ResolverKeys { - public static final ResolverKey ARENA = ResolverKey.create("arena", Arena.class); - public static final ResolverKey COMPETITION = ResolverKey.create("competition", Competition.class); - public static final ResolverKey KILLED = ResolverKey.create("killed", ArenaPlayer.class); - public static final ResolverKey KILLER = ResolverKey.create("killer", ArenaPlayer.class); - public static final ResolverKey PLAYER = ResolverKey.create("player", ArenaPlayer.class); - public static final ResolverKey LIVES_LEFT = ResolverKey.create("lives-left", Integer.class); - public static final ResolverKey ONLINE_PLAYERS = ResolverKey.create("online-players", Integer.class); - public static final ResolverKey MAP = ResolverKey.create("map", CompetitionMap.class); - public static final ResolverKey MAX_PLAYERS = ResolverKey.create("max-players", Integer.class); - public static final ResolverKey> PLAYERS = ResolverKey.create("players", TypeToken.of(Set.class)); - public static final ResolverKey STAT = ResolverKey.create("stat", ArenaStat.class); - public static final ResolverKey STAT_HOLDER = ResolverKey.create("stat-holder", StatHolder.class); - public static final ResolverKey OLD_STAT_VALUE = ResolverKey.create("old-stat-value", Object.class); - public static final ResolverKey NEW_STAT_VALUE = ResolverKey.create("new-stat-value", Object.class); - public static final ResolverKey PHASE = ResolverKey.create("phase", CompetitionPhase.class); - public static final ResolverKey TEAM = ResolverKey.create("team", ArenaTeam.class); - public static final ResolverKey TIME_REMAINING = ResolverKey.create("time-remaining", Duration.class); - public static final ResolverKey TIME_REMAINING_SHORT = ResolverKey.create("time-remaining-short", Duration.class); - public static final ResolverKey REMAINING_START_TIME = ResolverKey.create("remaining-start-time", Duration.class); - public static final ResolverKey VICTORY_CONDITION_TYPE = ResolverKey.create("victory-condition-type", VictoryConditionType.class); + private static final Map> RESOLVER_KEYS = new HashMap<>(); + + public static final ResolverKey ARENA = register("arena", Arena.class); + public static final ResolverKey ALIVE_PLAYERS = register("alive-players", Integer.class); + public static final ResolverKey COMPETITION = register("competition", Competition.class); + public static final ResolverKey KILLED = register("killed", ArenaPlayer.class); + public static final ResolverKey KILLER = register("killer", ArenaPlayer.class); + public static final ResolverKey PLAYER = register("player", ArenaPlayer.class); + public static final ResolverKey LIVES_LEFT = register("lives-left", Integer.class); + public static final ResolverKey ONLINE_PLAYERS = register("online-players", Integer.class); + public static final ResolverKey MAP = register("map", CompetitionMap.class); + public static final ResolverKey MAX_PLAYERS = register("max-players", Integer.class); + public static final ResolverKey> PLAYERS = register("players", TypeToken.of(Set.class)); + public static final ResolverKey SPECTATORS = register("spectators", Integer.class); + public static final ResolverKey STAT = register("stat", ArenaStat.class); + public static final ResolverKey STAT_HOLDER = register("stat-holder", StatHolder.class); + public static final ResolverKey OLD_STAT_VALUE = register("old-stat-value", Object.class); + public static final ResolverKey NEW_STAT_VALUE = register("new-stat-value", Object.class); + public static final ResolverKey PHASE = register("phase", CompetitionPhase.class); + public static final ResolverKey TEAM = register("team", ArenaTeam.class); + public static final ResolverKey TIME_REMAINING = register("time-remaining", Duration.class); + public static final ResolverKey TIME_REMAINING_SHORT = register("time-remaining-short", Duration.class); + public static final ResolverKey REMAINING_START_TIME = register("remaining-start-time", Duration.class); + public static final ResolverKey VICTORY_CONDITION_TYPE = register("victory-condition-type", VictoryConditionType.class); + + private static ResolverKey register(String name, Class type) { + ResolverKey key = ResolverKey.create(name, type); + RESOLVER_KEYS.put(name, key); + return key; + } + + private static ResolverKey register(String name, TypeToken type) { + ResolverKey key = ResolverKey.create(name, type); + RESOLVER_KEYS.put(name, key); + return key; + } + + @SuppressWarnings("unchecked") + public static ResolverKey get(String name) { + return (ResolverKey) RESOLVER_KEYS.get(name); + } } diff --git a/plugin/src/main/java/org/battleplugins/arena/stat/ArenaStat.java b/plugin/src/main/java/org/battleplugins/arena/stat/ArenaStat.java index 15db6ba8..5240640f 100644 --- a/plugin/src/main/java/org/battleplugins/arena/stat/ArenaStat.java +++ b/plugin/src/main/java/org/battleplugins/arena/stat/ArenaStat.java @@ -1,11 +1,13 @@ package org.battleplugins.arena.stat; +import org.battleplugins.arena.util.Describable; + /** * Represents a stat that can be tracked. * * @param the type of the stat */ -public interface ArenaStat { +public interface ArenaStat extends Describable { /** * Gets the key of the stat. @@ -34,4 +36,9 @@ public interface ArenaStat { * @return the type of the stat */ Class getType(); + + @Override + default String describe() { + return this.getName(); + } } diff --git a/plugin/src/main/java/org/battleplugins/arena/stat/StatHolder.java b/plugin/src/main/java/org/battleplugins/arena/stat/StatHolder.java index b0111339..668a3085 100644 --- a/plugin/src/main/java/org/battleplugins/arena/stat/StatHolder.java +++ b/plugin/src/main/java/org/battleplugins/arena/stat/StatHolder.java @@ -1,11 +1,12 @@ package org.battleplugins.arena.stat; +import org.battleplugins.arena.util.Describable; import org.jetbrains.annotations.Nullable; import java.util.Optional; import java.util.function.Function; -public interface StatHolder { +public interface StatHolder extends Describable { Optional stat(ArenaStat stat); @@ -15,6 +16,4 @@ public interface StatHolder { void setStat(ArenaStat stat, T value); void computeStat(ArenaStat stat, Function computeFunction); - - String describe(); } diff --git a/plugin/src/main/java/org/battleplugins/arena/team/ArenaTeam.java b/plugin/src/main/java/org/battleplugins/arena/team/ArenaTeam.java index 8f96f830..9bde14c3 100644 --- a/plugin/src/main/java/org/battleplugins/arena/team/ArenaTeam.java +++ b/plugin/src/main/java/org/battleplugins/arena/team/ArenaTeam.java @@ -4,6 +4,7 @@ import net.kyori.adventure.text.format.TextColor; import org.battleplugins.arena.config.ArenaOption; import org.battleplugins.arena.config.DocumentationSource; +import org.battleplugins.arena.util.Describable; import org.bukkit.inventory.ItemStack; import java.awt.Color; @@ -12,7 +13,7 @@ * Represents a team in an arena. */ @DocumentationSource("https://docs.battleplugins.org/books/user-guide/page/teams") -public class ArenaTeam { +public class ArenaTeam implements Describable { @ArenaOption(name = "name", description = "The name of the team.", required = true) private String name; @@ -92,6 +93,11 @@ public boolean isHostileTo(ArenaTeam team) { return !team.equals(this); } + @Override + public final String describe() { + return this.getName(); + } + @Override public String toString() { return "ArenaTeam{" + diff --git a/plugin/src/main/java/org/battleplugins/arena/util/Describable.java b/plugin/src/main/java/org/battleplugins/arena/util/Describable.java new file mode 100644 index 00000000..5347471f --- /dev/null +++ b/plugin/src/main/java/org/battleplugins/arena/util/Describable.java @@ -0,0 +1,15 @@ +package org.battleplugins.arena.util; + +/** + * A simple utility class to describe an object into a user-friendly + * string. + */ +public interface Describable { + + /** + * Describes the object into a user-friendly string. + * + * @return the user-friendly string + */ + String describe(); +} diff --git a/settings.gradle.kts b/settings.gradle.kts index b6dcb464..81b57d12 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -23,8 +23,9 @@ include("module:boundary-enforcer") include("module:classes") include("module:duels") include("module:one-in-the-chamber") +include("module:placeholderapi-integration") include("module:scoreboards") include("module:team-colors") include("module:team-heads") include("module:tournaments") -include("module:vault-integration") \ No newline at end of file +include("module:vault-integration")