diff --git a/README.md b/README.md index f56c00d..012bc01 100644 --- a/README.md +++ b/README.md @@ -52,8 +52,8 @@ players: | `` | Uses the result from a round with a matching id - useful to stop repeating veto deciders | `id="map-name"` | N/A | | `` | Represents a veto round | `id="veto"` | **Required:** ``, ``, `` | | `` | Which team vetoes first | N/A | A round | -| `` | What vetos should there be | `name` on the child element - what name should be displayed in the veto | Round(s) | -| `` | The veto order (ban, pick, etc.). The last element is enacted by the system (not by a team and so should not have the who attribute). | `ban-until="3"` (ban until 3 maps are left), `starting-team="2"` (the team who lost the decider starts), `time="30"` - the time in seconds that each team has to veto | If no attributes are present, `` and `` | +| `` | What vetos should there be | `name` on the child element - what name should be displayed in the veto | At least 2 rounds | +| `` | The veto order (ban, pick, etc.), with one element per option and at least one pick. The last element is enacted by the system (not by a team and so should not have a `team` attribute). | `ban-until="3"` (ban until 3 maps are left), `starting-team="2"` (the team who lost the decider starts), `time="30"` - the time in seconds that each team has to veto | If no attributes are present, `` and `` | | `` | A pick in the veto | `team="0"` - the team that gets to pick a map (zero is random map picked), `insert="back"` - whether the map should be added to the front or back of the maps to be played | N/A | | `` | A ban in the veto | `team="0"` - the team that gets to ban a map (zero is random map banned), `insert="back"` - whether the map should be added to the front or back of the maps to be played | N/A | diff --git a/build.gradle.kts b/build.gradle.kts index 64612df..4d210a9 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -51,7 +51,7 @@ tasks { expand( mapOf( "description" to description, - "apiVersion" to "1.21.11", + "apiVersion" to "26.2", "mainClass" to "dev.pgm.events.EventsPlugin", "version" to version, "commitHash" to commitHash.get(), diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index bf1de44..7d98320 100644 --- a/buildSrc/build.gradle.kts +++ b/buildSrc/build.gradle.kts @@ -9,7 +9,7 @@ repositories { } dependencies { - implementation("com.gradleup.shadow:com.gradleup.shadow.gradle.plugin:9.4.2") - implementation("com.diffplug.gradle.spotless:com.diffplug.gradle.spotless.gradle.plugin:8.6.0") - implementation("de.skuzzle.restrictimports:de.skuzzle.restrictimports.gradle.plugin:3.0.0") + implementation("com.gradleup.shadow:com.gradleup.shadow.gradle.plugin:9.6.1") + implementation("com.diffplug.spotless:com.diffplug.spotless.gradle.plugin:8.10.4") + implementation("de.skuzzle.restrictimports:de.skuzzle.restrictimports.gradle.plugin:3.0.2") } \ No newline at end of file diff --git a/buildSrc/src/main/kotlin/buildlogic.java-conventions.gradle.kts b/buildSrc/src/main/kotlin/buildlogic.java-conventions.gradle.kts index 5d41cc5..1fa574d 100644 --- a/buildSrc/src/main/kotlin/buildlogic.java-conventions.gradle.kts +++ b/buildSrc/src/main/kotlin/buildlogic.java-conventions.gradle.kts @@ -1,3 +1,5 @@ +import de.skuzzle.restrictimports.gradle.RestrictImports + plugins { `java-library` id("com.diffplug.spotless") @@ -6,7 +8,7 @@ plugins { java { toolchain { - languageVersion = JavaLanguageVersion.of(21) + languageVersion = JavaLanguageVersion.of(25) } } @@ -21,14 +23,14 @@ repositories { dependencies { // Annotations - api("org.jspecify:jspecify:1.0.0") + api("org.jspecify:jspecify:1.0.1") compileOnly("org.jetbrains:annotations:26.1.0") compileOnly("org.incendo:cloud-annotations:2.0.0") // Provided dependencies - compileOnly("dev.pgm.paper:paper-api:1.8_1.21.11-SNAPSHOT") + compileOnly("dev.pgm.paper:paper-api:1.8_26.2-SNAPSHOT") compileOnly("tc.oc.pgm:core:0.16-SNAPSHOT") { isTransitive = false } - compileOnly("net.kyori:adventure-api:4.26.1") + compileOnly("net.kyori:adventure-api:5.2.0") compileOnly("com.google.guava:guava:17.0") } @@ -50,17 +52,24 @@ spotless { removeUnusedImports() trimTrailingWhitespace() formatAnnotations() - palantirJavaFormat("2.93.0").style("GOOGLE").formatJavadoc(true) + palantirJavaFormat("2.102.0").style("GOOGLE").formatJavadoc(true) } } -restrictImports { - group { +tasks { + // Bypass inability to have two groups with same base packages inside the import restriction plugin + val restrictJavaxAnnotations = register("restrictJavaxAnnotations") { + group = "verification" reason = "Use org.jspecify.annotations to add annotations, or org.jetbrains.annotations if needed" bannedImports = listOf("javax.annotation.**") } - group { + val restrictAsserts = register("restrictAsserts") { + group = "verification" reason = "Use tc.oc.pgm.util.Assert to add assertions" bannedImports = listOf("com.google.common.base.Preconditions.**", "java.util.Objects.requireNonNull") } + // Enforce the import restrictions + check { + dependsOn(restrictJavaxAnnotations, restrictAsserts) + } } \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index c978528..68f8053 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1 +1,2 @@ -//org.gradle.configuration-cache=true +# https://github.com/diffplug/spotless/issues/2850 +org.gradle.configuration-cache=false diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index b1b8ef5..9fa7f28 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index df6a6ad..2e7bb9f 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-9.5.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.8.1-bin.zip networkTimeout=10000 retries=0 retryBackOffMs=500 diff --git a/gradlew b/gradlew index b9bb139..249efbb 100755 --- a/gradlew +++ b/gradlew @@ -20,7 +20,7 @@ ############################################################################## # -# Gradle start up script for POSIX generated by Gradle. +# gradlew start up script for POSIX generated by Gradle. # # Important for running: # @@ -29,7 +29,7 @@ # bash, then to run this script, type that shell name before the whole # command line, like: # -# ksh Gradle +# ksh gradlew # # Busybox and similar reduced shells will NOT work, because this script # requires all of these POSIX shell features: diff --git a/gradlew.bat b/gradlew.bat index 24c62d5..3185a43 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -19,13 +19,40 @@ @if "%DEBUG%"=="" @echo off @rem ########################################################################## @rem -@rem Gradle startup script for Windows +@rem gradlew startup script for Windows @rem @rem ########################################################################## @rem Set local scope for the variables, and ensure extensions are enabled setlocal EnableExtensions +@rem Catch executions from older scripts and ensure they exit cleanly. +@rem This can be removed once we can be reasonably confident that few people +@rem will be migrating directly to this new wrapper. +goto afterSafetyNet +:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: +:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: +:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: +:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: +:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: +:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: +:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: +:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: +:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: +:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: +:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: +:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: +:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: +:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: +:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: +:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: +:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: +:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: +:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: +:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: +goto exitWithErrorLevel +:afterSafetyNet + set DIRNAME=%~dp0 if "%DIRNAME%"=="" set DIRNAME=. @rem This is normally unused @@ -45,13 +72,14 @@ set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 if %ERRORLEVEL% equ 0 goto execute -echo. 1>&2 -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 -echo. 1>&2 -echo Please set the JAVA_HOME variable in your environment to match the 1>&2 -echo location of your Java installation. 1>&2 +1>&2 echo. +1>&2 echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +1>&2 echo. +1>&2 echo Please set the JAVA_HOME variable in your environment to match the +1>&2 echo location of your Java installation. "%COMSPEC%" /c exit 1 +goto exitWithErrorLevel :findJavaFromJavaHome set JAVA_HOME=%JAVA_HOME:"=% @@ -59,24 +87,26 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe if exist "%JAVA_EXE%" goto execute -echo. 1>&2 -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 -echo. 1>&2 -echo Please set the JAVA_HOME variable in your environment to match the 1>&2 -echo location of your Java installation. 1>&2 +1>&2 echo. +1>&2 echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +1>&2 echo. +1>&2 echo Please set the JAVA_HOME variable in your environment to match the +1>&2 echo location of your Java installation. "%COMSPEC%" /c exit 1 +goto exitWithErrorLevel :execute @rem Setup the command line -@rem Execute Gradle +@rem Execute gradlew @rem endlocal doesn't take effect until after the line is parsed and variables are expanded @rem which allows us to clear the local environment before executing the java command -endlocal & "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* & call :exitWithErrorLevel +endlocal & "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* & call :exitWithErrorLevel & goto exitWithErrorLevel +@rem This label must not be changed. We rely on old scripts being able to jump to this point. :exitWithErrorLevel @rem Use "%COMSPEC%" /c exit to allow operators to work properly in scripts "%COMSPEC%" /c exit %ERRORLEVEL% diff --git a/samples/formats/bo3.xml b/samples/formats/bo3.xml index b6a85a1..7c07de9 100644 --- a/samples/formats/bo3.xml +++ b/samples/formats/bo3.xml @@ -10,6 +10,7 @@ Limbo II Golden Drought II + Boom diff --git a/samples/formats/group.xml b/samples/formats/group.xml index e606abb..2589f64 100644 --- a/samples/formats/group.xml +++ b/samples/formats/group.xml @@ -34,6 +34,7 @@ Facility TE Dwyer Hill + Boom diff --git a/src/main/java/dev/pgm/events/api/events/TournamentEvent.java b/src/main/java/dev/pgm/events/api/events/TournamentEvent.java index c130eeb..328e217 100644 --- a/src/main/java/dev/pgm/events/api/events/TournamentEvent.java +++ b/src/main/java/dev/pgm/events/api/events/TournamentEvent.java @@ -2,11 +2,9 @@ import dev.pgm.events.format.TournamentFormat; import org.bukkit.event.Event; -import org.bukkit.event.HandlerList; -public class TournamentEvent extends Event { +public abstract class TournamentEvent extends Event { - private static final HandlerList handlers = new HandlerList(); private final TournamentFormat tournamentFormat; public TournamentEvent(TournamentFormat tournamentFormat) { @@ -16,13 +14,4 @@ public TournamentEvent(TournamentFormat tournamentFormat) { public TournamentFormat format() { return tournamentFormat; } - - public static HandlerList getHandlerList() { - return handlers; - } - - @Override - public HandlerList getHandlers() { - return handlers; - } } diff --git a/src/main/java/dev/pgm/events/api/events/TournamentFinishedEvent.java b/src/main/java/dev/pgm/events/api/events/TournamentFinishedEvent.java index 904016a..d574061 100644 --- a/src/main/java/dev/pgm/events/api/events/TournamentFinishedEvent.java +++ b/src/main/java/dev/pgm/events/api/events/TournamentFinishedEvent.java @@ -3,18 +3,33 @@ import dev.pgm.events.format.TournamentFormat; import dev.pgm.events.team.TournamentTeam; import java.util.Optional; +import org.bukkit.event.HandlerList; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; +@NullMarked public class TournamentFinishedEvent extends TournamentEvent { - private final Optional winningTeam; + private static final HandlerList HANDLER_LIST = new HandlerList(); + + private final @Nullable TournamentTeam winningTeam; public TournamentFinishedEvent( - TournamentFormat tournamentFormat, Optional winningTeam) { + TournamentFormat tournamentFormat, @Nullable TournamentTeam winningTeam) { super(tournamentFormat); this.winningTeam = winningTeam; } public Optional winningTeam() { - return winningTeam; + return Optional.ofNullable(winningTeam); + } + + @Override + public HandlerList getHandlers() { + return HANDLER_LIST; + } + + public static HandlerList getHandlerList() { + return HANDLER_LIST; } } diff --git a/src/main/java/dev/pgm/events/api/events/TournamentStartEvent.java b/src/main/java/dev/pgm/events/api/events/TournamentStartEvent.java index 37eeac5..533a29b 100644 --- a/src/main/java/dev/pgm/events/api/events/TournamentStartEvent.java +++ b/src/main/java/dev/pgm/events/api/events/TournamentStartEvent.java @@ -1,10 +1,24 @@ package dev.pgm.events.api.events; import dev.pgm.events.format.TournamentFormat; +import org.bukkit.event.HandlerList; +import org.jspecify.annotations.NullMarked; +@NullMarked public class TournamentStartEvent extends TournamentEvent { + private static final HandlerList HANDLER_LIST = new HandlerList(); + public TournamentStartEvent(TournamentFormat tournamentFormat) { super(tournamentFormat); } + + @Override + public HandlerList getHandlers() { + return HANDLER_LIST; + } + + public static HandlerList getHandlerList() { + return HANDLER_LIST; + } } diff --git a/src/main/java/dev/pgm/events/api/teams/DefaultTeamRegistry.java b/src/main/java/dev/pgm/events/api/teams/DefaultTeamRegistry.java index 0052ec0..52936e8 100644 --- a/src/main/java/dev/pgm/events/api/teams/DefaultTeamRegistry.java +++ b/src/main/java/dev/pgm/events/api/teams/DefaultTeamRegistry.java @@ -1,12 +1,13 @@ package dev.pgm.events.api.teams; +import static net.kyori.adventure.text.Component.text; + import com.google.common.collect.Maps; import dev.pgm.events.EventsPlugin; import dev.pgm.events.team.TournamentTeam; import java.util.ArrayList; import java.util.List; import java.util.Map; -import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; import org.bukkit.Bukkit; import tc.oc.pgm.util.Audience; @@ -55,8 +56,8 @@ public void reload() { teams.forEach(x -> teamMap.put(x.getName(), x)); Bukkit.getOnlinePlayers().stream() .filter(x -> x.hasPermission("events.staff")) - .forEach(x -> Audience.get(x) - .sendMessage(Component.text("Teams reloaded!", NamedTextColor.AQUA))); + .forEach( + x -> Audience.get(x).sendMessage(text("Teams reloaded!", NamedTextColor.AQUA))); }); }); } diff --git a/src/main/java/dev/pgm/events/commands/TournamentAdminCommands.java b/src/main/java/dev/pgm/events/commands/TournamentAdminCommands.java index 4b52df5..98b9ded 100644 --- a/src/main/java/dev/pgm/events/commands/TournamentAdminCommands.java +++ b/src/main/java/dev/pgm/events/commands/TournamentAdminCommands.java @@ -1,12 +1,14 @@ package dev.pgm.events.commands; +import static net.kyori.adventure.text.Component.text; + import dev.pgm.events.TournamentManager; import dev.pgm.events.api.teams.TournamentTeamRegistry; +import dev.pgm.events.format.TournamentFormat; import dev.pgm.events.team.TournamentPlayer; import dev.pgm.events.team.TournamentTeam; import dev.pgm.events.team.TournamentTeamManager; import dev.pgm.events.xml.MapFormatXMLParser; -import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; import org.bukkit.Bukkit; import org.bukkit.OfflinePlayer; @@ -35,8 +37,14 @@ public void tourney( TournamentManager manager, Match match, @Argument("format") @Greedy String pool) { - manager.createTournament(match, MapFormatXMLParser.parse(pool)); - Audience.get(sender).sendMessage(Component.text("Starting tournament.", NamedTextColor.GOLD)); + TournamentFormat format; + try { + format = MapFormatXMLParser.parse(pool); + } catch (IllegalArgumentException e) { + throw new CommandException(e.getMessage()); + } + manager.createTournament(match, format); + Audience.get(sender).sendMessage(text("Starting tournament.", NamedTextColor.GOLD)); } @Command("register ") @@ -62,7 +70,7 @@ public void register( teamManager.addTeam(team); Audience.get(sender) - .sendMessage(Component.text("Added team " + team.getName() + "!", NamedTextColor.YELLOW)); + .sendMessage(text("Added team " + team.getName() + "!", NamedTextColor.YELLOW)); } @Command("list") @@ -70,14 +78,13 @@ public void register( @Permission("events.staff") public void list(CommandSender sender, TournamentTeamRegistry registry) { Audience audience = Audience.get(sender); - audience.sendMessage(Component.text("------- ", NamedTextColor.GOLD) - .append(Component.text("Registered Teams", NamedTextColor.AQUA)) - .append(Component.text(" -------", NamedTextColor.GOLD))); + audience.sendMessage(text("------- ", NamedTextColor.GOLD) + .append(text("Registered Teams", NamedTextColor.AQUA)) + .append(text(" -------", NamedTextColor.GOLD))); for (TournamentTeam team : registry.getTeams()) - audience.sendMessage( - Component.text("- ", NamedTextColor.AQUA).append(Component.text(team.getName()))); + audience.sendMessage(text("- ", NamedTextColor.AQUA).append(text(team.getName()))); audience.sendMessage( - Component.text("Run /tourney info to see player roster!", NamedTextColor.YELLOW)); + text("Run /tourney info to see player roster!", NamedTextColor.YELLOW)); } @Command("info ") @@ -91,16 +98,15 @@ public void info( if (team == null) throw new CommandException("Team not found!"); Audience audience = Audience.get(sender); - audience.sendMessage(Component.text("------- ", NamedTextColor.GOLD) - .append(Component.text(team.getName(), NamedTextColor.AQUA)) - .append(Component.text(" -------", NamedTextColor.GOLD))); + audience.sendMessage(text("------- ", NamedTextColor.GOLD) + .append(text(team.getName(), NamedTextColor.AQUA)) + .append(text(" -------", NamedTextColor.GOLD))); for (TournamentPlayer player : team.getPlayers()) { String playerName = player.getUUID().toString() + " (player hasn't logged on)"; OfflinePlayer offline = Bukkit.getOfflinePlayer(player.getUUID()); if (offline.getName() != null) playerName = offline.getName(); - audience.sendMessage( - Component.text("- ", NamedTextColor.AQUA).append(Component.text(playerName))); + audience.sendMessage(text("- ", NamedTextColor.AQUA).append(text(playerName))); } } @@ -109,7 +115,6 @@ public void info( @Permission("events.staff") public void clear(CommandSender sender, TournamentTeamManager teamManager) { teamManager.clear(); - Audience.get(sender) - .sendMessage(Component.text("Unregistered all teams!", NamedTextColor.YELLOW)); + Audience.get(sender).sendMessage(text("Unregistered all teams!", NamedTextColor.YELLOW)); } } diff --git a/src/main/java/dev/pgm/events/commands/TournamentUserCommands.java b/src/main/java/dev/pgm/events/commands/TournamentUserCommands.java index 7d2064e..72f0dc6 100644 --- a/src/main/java/dev/pgm/events/commands/TournamentUserCommands.java +++ b/src/main/java/dev/pgm/events/commands/TournamentUserCommands.java @@ -1,11 +1,12 @@ package dev.pgm.events.commands; +import static net.kyori.adventure.text.Component.text; + import dev.pgm.events.EventsPlugin; import dev.pgm.events.format.TournamentFormat; import dev.pgm.events.format.rounds.RoundDescription; import dev.pgm.events.format.rounds.format.FormatTournamentImpl; import java.util.Optional; -import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; import org.bukkit.command.CommandSender; import tc.oc.pgm.lib.org.incendo.cloud.annotations.Command; @@ -22,14 +23,14 @@ public void currentScore(CommandSender sender, TournamentFormat format) { if (format instanceof FormatTournamentImpl) { String formatName = ((FormatTournamentImpl) format).getFormatRound().settings().name(); - audience.sendMessage(Component.text("For " + formatName + ":", NamedTextColor.YELLOW)); + audience.sendMessage(text("For " + formatName + ":", NamedTextColor.YELLOW)); audience.sendMessage(format.currentScore().condensed()); Optional parentOptional = EventsPlugin.get().getTournamentManager().currentTournament(); if (parentOptional.isPresent()) { audience.sendMessage( - Component.text("Overall score (excluding " + formatName + "):", NamedTextColor.YELLOW)); + text("Overall score (excluding " + formatName + "):", NamedTextColor.YELLOW)); audience.sendMessage(parentOptional.get().currentScore().condensed()); } } else { @@ -46,14 +47,14 @@ public void rounds(CommandSender sender, TournamentFormat format) { " (" + ((FormatTournamentImpl) format).getFormatRound().settings().name() + ")"; Audience audience = Audience.get(sender); - audience.sendMessage(Component.text("------- ", NamedTextColor.GOLD) - .append(Component.text(header, NamedTextColor.AQUA)) - .append(Component.text(" -------", NamedTextColor.GOLD))); + audience.sendMessage(text("------- ", NamedTextColor.GOLD) + .append(text(header, NamedTextColor.AQUA)) + .append(text(" -------", NamedTextColor.GOLD))); int round = 1; for (RoundDescription roundDescription : format.roundsInformation()) { audience.sendMessage( - Component.text(round + ". ", NamedTextColor.GOLD).append(roundDescription.roundInfo())); + text(round + ". ", NamedTextColor.GOLD).append(roundDescription.roundInfo())); round++; } } diff --git a/src/main/java/dev/pgm/events/commands/providers/TournamentProvider.java b/src/main/java/dev/pgm/events/commands/providers/TournamentProvider.java index c694141..a188f24 100644 --- a/src/main/java/dev/pgm/events/commands/providers/TournamentProvider.java +++ b/src/main/java/dev/pgm/events/commands/providers/TournamentProvider.java @@ -6,7 +6,7 @@ import dev.pgm.events.format.rounds.format.FormatRound; import java.util.Optional; import org.bukkit.command.CommandSender; -import org.jetbrains.annotations.NotNull; +import org.jspecify.annotations.NonNull; import org.jspecify.annotations.Nullable; import tc.oc.pgm.lib.org.incendo.cloud.context.CommandContext; import tc.oc.pgm.lib.org.incendo.cloud.exception.CommandExecutionException; @@ -23,8 +23,8 @@ public TournamentProvider(TournamentManager tournamentManager) { @Override public @Nullable TournamentFormat create( - @NotNull CommandContext context, - @NotNull AnnotationAccessor annotationAccessor) { + @NonNull CommandContext context, + @NonNull AnnotationAccessor annotationAccessor) { Optional tournamentFormat = tournamentManager.currentTournament(); if (tournamentFormat.isPresent()) { TournamentFormat format = tournamentFormat.get(); diff --git a/src/main/java/dev/pgm/events/format/RoundHolder.java b/src/main/java/dev/pgm/events/format/RoundHolder.java index 07f46b5..7841ccf 100644 --- a/src/main/java/dev/pgm/events/format/RoundHolder.java +++ b/src/main/java/dev/pgm/events/format/RoundHolder.java @@ -12,7 +12,6 @@ import java.util.List; import java.util.Map; import java.util.Optional; -import java.util.stream.Collectors; import tc.oc.pgm.api.match.Match; class RoundHolder { @@ -55,7 +54,7 @@ public List roundDescriptions(Context context) { return rounds.stream() .filter(x -> x.shouldShowInHistory() || context == Context.DEBUG) .map(TournamentRound::describe) - .collect(Collectors.toList()); + .toList(); } public Scores scores(TournamentTeamManager teamManager) { diff --git a/src/main/java/dev/pgm/events/format/TournamentFormatImpl.java b/src/main/java/dev/pgm/events/format/TournamentFormatImpl.java index fc5bd31..ee73dfa 100644 --- a/src/main/java/dev/pgm/events/format/TournamentFormatImpl.java +++ b/src/main/java/dev/pgm/events/format/TournamentFormatImpl.java @@ -1,5 +1,7 @@ package dev.pgm.events.format; +import static net.kyori.adventure.text.Component.text; + import dev.pgm.events.EventsPlugin; import dev.pgm.events.api.events.TournamentFinishedEvent; import dev.pgm.events.config.Context; @@ -12,13 +14,12 @@ import dev.pgm.events.team.TournamentTeamManager; import java.util.ArrayList; import java.util.List; -import java.util.Optional; -import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; import org.bukkit.Bukkit; import org.bukkit.event.EventPriority; import org.bukkit.event.HandlerList; import org.bukkit.event.Listener; +import org.jspecify.annotations.Nullable; import tc.oc.pgm.api.PGM; import tc.oc.pgm.api.match.Match; import tc.oc.pgm.util.Audience; @@ -87,14 +88,14 @@ else if (state == TournamentState.FINISHED) // if finished we outta here broadcastScore(); // todo: Check phase of previous round? - Optional winner = roundHolder.calculateWinner(teamManager); - if (winner.isPresent() || !hasNextRound()) { + TournamentTeam winner = roundHolder.calculateWinner(teamManager).orElse(null); + if (winner != null || !hasNextRound()) { unregisterAll(); // there's a winner, return and announce them state = TournamentState.FINISHED; - // if this doesn't call it just call it all normal like - onEnd(match, winner); + // fire before onEnd, which may synchronously advance a parent tournament Events.callEvent(new TournamentFinishedEvent(this, winner), EventPriority.NORMAL); + onEnd(match, winner); return; } @@ -160,14 +161,14 @@ private void broadcastScore() { 3 * 20); } - public void onEnd(Match match, Optional winner) { + public void onEnd(Match match, @Nullable TournamentTeam winner) { if (tournamentRoundOptions.shouldAnnounceWinner()) { - if (winner.isPresent()) { + if (winner != null) { match.sendMessage(teamManager - .formattedName(winner.get()) - .append(Component.text(" wins this round!", NamedTextColor.GOLD))); + .formattedName(winner) + .append(text(" wins this round!", NamedTextColor.GOLD))); } else { - match.sendMessage(Component.text("This round has ended in a draw!", NamedTextColor.GRAY)); + match.sendMessage(text("This round has ended in a draw!", NamedTextColor.GRAY)); } } diff --git a/src/main/java/dev/pgm/events/format/rounds/format/FormatRound.java b/src/main/java/dev/pgm/events/format/rounds/format/FormatRound.java index 9275a09..93108f8 100644 --- a/src/main/java/dev/pgm/events/format/rounds/format/FormatRound.java +++ b/src/main/java/dev/pgm/events/format/rounds/format/FormatRound.java @@ -11,12 +11,13 @@ import dev.pgm.events.team.TournamentTeam; import java.util.HashMap; import java.util.Map; +import org.jspecify.annotations.Nullable; import tc.oc.pgm.api.match.Match; public class FormatRound extends AbstractRound { private FormatTournamentImpl formatTournament; - private TournamentTeam winner; + private @Nullable TournamentTeam winner; private TournamentTeam selectingTeam; @@ -63,7 +64,7 @@ public Map scores() { return scores; } - public void setWinner(Match match, TournamentTeam winner) { + public void setWinner(Match match, @Nullable TournamentTeam winner) { this.winner = winner; tournament().nextRound(match); } diff --git a/src/main/java/dev/pgm/events/format/rounds/format/FormatRoundDescription.java b/src/main/java/dev/pgm/events/format/rounds/format/FormatRoundDescription.java index 548cba2..2e4a9ea 100644 --- a/src/main/java/dev/pgm/events/format/rounds/format/FormatRoundDescription.java +++ b/src/main/java/dev/pgm/events/format/rounds/format/FormatRoundDescription.java @@ -1,8 +1,10 @@ package dev.pgm.events.format.rounds.format; +import static net.kyori.adventure.text.Component.text; +import static net.kyori.adventure.text.event.HoverEvent.showText; + import dev.pgm.events.format.rounds.RoundDescription; import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.event.HoverEvent; import net.kyori.adventure.text.format.NamedTextColor; public class FormatRoundDescription implements RoundDescription { @@ -15,13 +17,12 @@ public FormatRoundDescription(FormatRound formatRound) { @Override public Component roundInfo() { - Component base = Component.text( + Component base = text( formatRound.settings().name() + " - Best of " + formatRound.settings().bestOf()); if (formatRound.formatTournament() != null) { - return base.hoverEvent(HoverEvent.showText(formatRound.formattedScore().condensed())); + return base.hoverEvent(showText(formatRound.formattedScore().condensed())); } else { - return base.hoverEvent( - HoverEvent.showText(Component.text("Loading...", NamedTextColor.YELLOW))); + return base.hoverEvent(showText(text("Loading...", NamedTextColor.YELLOW))); } } diff --git a/src/main/java/dev/pgm/events/format/rounds/format/FormatTournamentImpl.java b/src/main/java/dev/pgm/events/format/rounds/format/FormatTournamentImpl.java index 504a4d0..db987db 100644 --- a/src/main/java/dev/pgm/events/format/rounds/format/FormatTournamentImpl.java +++ b/src/main/java/dev/pgm/events/format/rounds/format/FormatTournamentImpl.java @@ -7,7 +7,7 @@ import dev.pgm.events.team.TournamentTeam; import dev.pgm.events.team.TournamentTeamManager; import java.time.Duration; -import java.util.Optional; +import org.jspecify.annotations.Nullable; import tc.oc.pgm.api.match.Match; public class FormatTournamentImpl extends TournamentFormatImpl { @@ -31,8 +31,8 @@ public FormatTournamentImpl( } @Override - public void onEnd(Match match, Optional winner) { - formatRound.setWinner(match, winner.orElse(null)); + public void onEnd(Match match, @Nullable TournamentTeam winner) { + formatRound.setWinner(match, winner); } public FormatRound getFormatRound() { diff --git a/src/main/java/dev/pgm/events/format/rounds/reference/ReferenceDescription.java b/src/main/java/dev/pgm/events/format/rounds/reference/ReferenceDescription.java index 3777960..c594cbf 100644 --- a/src/main/java/dev/pgm/events/format/rounds/reference/ReferenceDescription.java +++ b/src/main/java/dev/pgm/events/format/rounds/reference/ReferenceDescription.java @@ -1,5 +1,7 @@ package dev.pgm.events.format.rounds.reference; +import static net.kyori.adventure.text.Component.text; + import dev.pgm.events.format.rounds.RoundDescription; import net.kyori.adventure.text.Component; @@ -13,8 +15,7 @@ public ReferenceDescription(ReferenceRound referenceRound) { @Override public Component roundInfo() { - return Component.text( - "Reference round -> referencing: " + referenceRound.settings().targetID()); + return text("Reference round -> referencing: " + referenceRound.settings().targetID()); } @Override diff --git a/src/main/java/dev/pgm/events/format/rounds/replay/ReplayDescription.java b/src/main/java/dev/pgm/events/format/rounds/replay/ReplayDescription.java index 3d265a4..ef5cfa2 100644 --- a/src/main/java/dev/pgm/events/format/rounds/replay/ReplayDescription.java +++ b/src/main/java/dev/pgm/events/format/rounds/replay/ReplayDescription.java @@ -1,5 +1,7 @@ package dev.pgm.events.format.rounds.replay; +import static net.kyori.adventure.text.Component.text; + import dev.pgm.events.format.rounds.RoundDescription; import dev.pgm.events.format.rounds.RoundPhase; import net.kyori.adventure.text.Component; @@ -17,15 +19,14 @@ public ReplayDescription(ReplayRound round) { @Override public Component roundInfo() { // haven't yet decided if we're gonna play if here - if (round.phase() != RoundPhase.FINISHED) - return Component.text("Replay tied round", NamedTextColor.GRAY); + if (round.phase() != RoundPhase.FINISHED) return text("Replay tied round", NamedTextColor.GRAY); // not gonna replay if (round.shouldShowInHistory()) - return Component.text("Not replaying a round", NamedTextColor.GRAY) + return text("Not replaying a round", NamedTextColor.GRAY) .decorate(TextDecoration.STRIKETHROUGH); - return Component.text("Error replaying a round"); + return text("Error replaying a round"); } @Override diff --git a/src/main/java/dev/pgm/events/format/rounds/resultfrom/ResultFromDescription.java b/src/main/java/dev/pgm/events/format/rounds/resultfrom/ResultFromDescription.java index 6efb467..4acf571 100644 --- a/src/main/java/dev/pgm/events/format/rounds/resultfrom/ResultFromDescription.java +++ b/src/main/java/dev/pgm/events/format/rounds/resultfrom/ResultFromDescription.java @@ -1,5 +1,7 @@ package dev.pgm.events.format.rounds.resultfrom; +import static net.kyori.adventure.text.Component.text; + import dev.pgm.events.format.rounds.RoundDescription; import net.kyori.adventure.text.Component; @@ -13,7 +15,7 @@ public ResultFromDescription(ResultFromRound resultFromRound) { @Override public Component roundInfo() { - return Component.text( + return text( "ResultFrom round -> using result from: " + resultFromRound.settings().targetID()); } diff --git a/src/main/java/dev/pgm/events/format/rounds/single/SingleRoundDescription.java b/src/main/java/dev/pgm/events/format/rounds/single/SingleRoundDescription.java index 43410f6..add7823 100644 --- a/src/main/java/dev/pgm/events/format/rounds/single/SingleRoundDescription.java +++ b/src/main/java/dev/pgm/events/format/rounds/single/SingleRoundDescription.java @@ -1,5 +1,7 @@ package dev.pgm.events.format.rounds.single; +import static net.kyori.adventure.text.Component.text; + import dev.pgm.events.format.TournamentFormat; import dev.pgm.events.format.rounds.RoundDescription; import dev.pgm.events.team.TournamentTeam; @@ -22,23 +24,21 @@ public SingleRoundDescription(String mapName, SingleRound singleRound, Tournamen @Override public Component roundInfo() { - Component base = Component.text("Match on ", NamedTextColor.GRAY) - .append(Component.text(mapName, NamedTextColor.GOLD)); + Component base = + text("Match on ", NamedTextColor.GRAY).append(text(mapName, NamedTextColor.GOLD)); return switch (singleRound.phase()) { case UNLOADED -> base; - case WAITING -> base.append(Component.text(" - Waiting", NamedTextColor.GRAY)); + case WAITING -> base.append(text(" - Waiting", NamedTextColor.GRAY)); case RUNNING -> - base.append(Component.text(" - ", NamedTextColor.GRAY)) - .append(Component.text("Running", NamedTextColor.GREEN)); - case FINISHED -> - base.append(Component.text(" - ", NamedTextColor.GRAY)).append(winnersComponent()); + base.append(text(" - ", NamedTextColor.GRAY)).append(text("Running", NamedTextColor.GREEN)); + case FINISHED -> base.append(text(" - ", NamedTextColor.GRAY)).append(winnersComponent()); }; } private Component winnersComponent() { Collection teams = singleRound.scores().keySet(); if (teams.isEmpty()) { - return Component.text("Draw", NamedTextColor.GRAY); + return text("Draw", NamedTextColor.GRAY); } List teamNames = @@ -46,10 +46,10 @@ private Component winnersComponent() { Component result = Component.empty(); for (int i = 0; i < teamNames.size(); i++) { - if (i > 0) result = result.append(Component.text(", ", NamedTextColor.GRAY)); + if (i > 0) result = result.append(text(", ", NamedTextColor.GRAY)); result = result.append(teamNames.get(i)); } - return result.append(Component.text(" won")); + return result.append(text(" won")); } @Override diff --git a/src/main/java/dev/pgm/events/format/rounds/veto/VetoController.java b/src/main/java/dev/pgm/events/format/rounds/veto/VetoController.java index 03af214..5f1ff51 100644 --- a/src/main/java/dev/pgm/events/format/rounds/veto/VetoController.java +++ b/src/main/java/dev/pgm/events/format/rounds/veto/VetoController.java @@ -36,25 +36,16 @@ public VetoController( } public boolean hasMoreVetoing() { - // only one map left - if (options.size() == 1) return false; - - for (int i = vetoIndex; i < settings.vetoList().size(); i++) { - // teams still get to vote - if (settings.vetoList().get(i).team > 0) return true; - } - - return true; + return options.size() > 1; } public VetoSettings.VetoType currentType() { - return settings.vetoList().get(vetoIndex).vetoType; + return settings.vetoList().get(vetoIndex).vetoType(); } public Duration currentVetoDuration() { - if (settings.vetoList().get(vetoIndex).vetoDuration != null) - return settings.vetoList().get(vetoIndex).vetoDuration; - return settings.vetoTime(); + Duration duration = settings.vetoList().get(vetoIndex).vetoDuration(); + return duration != null ? duration : settings.vetoTime(); } public void completeVetos() { @@ -63,22 +54,21 @@ public void completeVetos() { for (int i = vetoIndex; i < settings.vetoList().size(); i++) { VetoSettings.Veto veto = settings.vetoList().get(i); VetoOption option = options.remove(random.nextInt(options.size())); - TournamentTeam team = veto.team == 0 ? null : rankedTeams.get(veto.team - 1); + TournamentTeam team = veto.team() == 0 ? null : rankedTeams.get(veto.team() - 1); - doVeto(option, veto.vetoType, team); + doVeto(option, veto.vetoType(), team); } } public VetoHistory veto(@Nullable TournamentTeam team, int choice) { - // maybe throw an exception? - if (!hasMoreVetoing()) return null; + if (!hasMoreVetoing()) throw new IllegalStateException("No vetoing left"); VetoSettings.Veto veto = settings.vetoList().get(vetoIndex); VetoOption vetoOption = options.remove(choice); - doVeto(vetoOption, veto.vetoType, team); + doVeto(vetoOption, veto.vetoType(), team); - VetoHistory history = new VetoHistory(team, veto.vetoType, vetoOption, veto.shouldAnnounce); + VetoHistory history = new VetoHistory(team, veto.vetoType(), vetoOption, veto.shouldAnnounce()); if (history.shouldAnnounce()) this.history.add(history); vetoIndex++; @@ -145,7 +135,7 @@ private void chooseLast(VetoOption option, TournamentTeam selectingTeam) { * @return the current picking team or NULL if no team is picking */ public Optional picking() { - int picker = settings.vetoList().get(vetoIndex).team; + int picker = settings.vetoList().get(vetoIndex).team(); if (picker == 0) return Optional.empty(); return Optional.of(rankedTeams.get(picker - 1)); diff --git a/src/main/java/dev/pgm/events/format/rounds/veto/VetoDescription.java b/src/main/java/dev/pgm/events/format/rounds/veto/VetoDescription.java index 5dd19aa..c92b656 100644 --- a/src/main/java/dev/pgm/events/format/rounds/veto/VetoDescription.java +++ b/src/main/java/dev/pgm/events/format/rounds/veto/VetoDescription.java @@ -1,5 +1,8 @@ package dev.pgm.events.format.rounds.veto; +import static net.kyori.adventure.text.Component.text; +import static net.kyori.adventure.text.event.HoverEvent.showText; + import dev.pgm.events.format.TournamentFormat; import dev.pgm.events.format.rounds.RoundDescription; import dev.pgm.events.format.rounds.veto.settings.VetoOption; @@ -8,7 +11,6 @@ import java.util.ArrayList; import java.util.List; import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.event.HoverEvent; import net.kyori.adventure.text.format.NamedTextColor; public class VetoDescription implements RoundDescription { @@ -24,17 +26,16 @@ public VetoDescription(TournamentFormat format, VetoRound vetoRound) { @Override public Component roundInfo() { return switch (vetoRound.phase()) { - case UNLOADED -> Component.text("Waiting to begin veto process", NamedTextColor.GRAY); + case UNLOADED -> text("Waiting to begin veto process", NamedTextColor.GRAY); case WAITING -> - Component.text("Deciding veto order with veto decider", NamedTextColor.GREEN) - .hoverEvent( - HoverEvent.showText(tournamentFormat.currentRound().describe().roundInfo())); + text("Deciding veto order with veto decider", NamedTextColor.GREEN) + .hoverEvent(showText(tournamentFormat.currentRound().describe().roundInfo())); case RUNNING -> - Component.text("Veto process is running now", NamedTextColor.GREEN) - .hoverEvent(HoverEvent.showText(formattedVetosSoFar())); + text("Veto process is running now", NamedTextColor.GREEN) + .hoverEvent(showText(formattedVetosSoFar())); case FINISHED -> - Component.text("Veto process has concluded", NamedTextColor.GRAY) - .hoverEvent(HoverEvent.showText(formattedVetosSoFar())); + text("Veto process has concluded", NamedTextColor.GRAY) + .hoverEvent(showText(formattedVetosSoFar())); }; } @@ -49,29 +50,26 @@ private Component formattedVetosSoFar() { } private Component historyLine(int index, VetoHistory history) { - return Component.text(index + 1 + ". ", NamedTextColor.GOLD) - .append(formatHistoryComponent(history)); + return text(index + 1 + ". ", NamedTextColor.GOLD).append(formatHistoryComponent(history)); } public Component formatHistoryComponent(VetoHistory history) { Component prefix; if (history.team() == null) { - prefix = Component.text("RANDOM", NamedTextColor.GOLD); + prefix = text("RANDOM", NamedTextColor.GOLD); } else { prefix = tournamentFormat.teamManager().formattedName(history.team()); } - prefix = prefix.append(Component.text(" has ", NamedTextColor.GRAY)); + prefix = prefix.append(text(" has ", NamedTextColor.GRAY)); Component action; if (history.vetoType() == VetoSettings.VetoType.BAN) { - action = Component.text("BANNED ", NamedTextColor.RED); + action = text("BANNED ", NamedTextColor.RED); } else { - action = Component.text("SELECTED ", NamedTextColor.GREEN); + action = text("SELECTED ", NamedTextColor.GREEN); } - return prefix - .append(action) - .append(Component.text(history.optionChosen().name(), NamedTextColor.GOLD)); + return prefix.append(action).append(text(history.optionChosen().name(), NamedTextColor.GOLD)); } public String countdown(TournamentTeam picking, VetoSettings.VetoType type) { @@ -86,29 +84,28 @@ private String actionWord(VetoSettings.VetoType type) { } public Component optionsHeader(VetoSettings.VetoType type) { - return Component.text("Choose an option to ", NamedTextColor.GRAY) - .append(actionComponent(type)); + return text("Choose an option to ", NamedTextColor.GRAY).append(actionComponent(type)); } public Component commandPrompt(VetoSettings.VetoType type) { return actionComponent(type) - .append(Component.text(" with: ", NamedTextColor.GRAY)) - .append(Component.text("/veto ", NamedTextColor.GOLD)); + .append(text(" with: ", NamedTextColor.GRAY)) + .append(text("/veto ", NamedTextColor.GOLD)); } public List formatOptions(List options, VetoSettings.VetoType type) { List comps = new ArrayList<>(options.size()); for (int i = 0; i < options.size(); i++) { - comps.add(Component.text((i + 1) + ". ", NamedTextColor.GOLD) - .append(Component.text(options.get(i).name(), NamedTextColor.AQUA))); + comps.add(text((i + 1) + ". ", NamedTextColor.GOLD) + .append(text(options.get(i).name(), NamedTextColor.AQUA))); } return comps; } private Component actionComponent(VetoSettings.VetoType type) { return switch (type) { - case BAN -> Component.text("BAN", NamedTextColor.RED); - case CHOOSE_FIRST, CHOOSE_LAST -> Component.text("PLAY", NamedTextColor.GREEN); + case BAN -> text("BAN", NamedTextColor.RED); + case CHOOSE_FIRST, CHOOSE_LAST -> text("PLAY", NamedTextColor.GREEN); }; } diff --git a/src/main/java/dev/pgm/events/format/rounds/veto/VetoHistory.java b/src/main/java/dev/pgm/events/format/rounds/veto/VetoHistory.java index 2fbd6d4..8cab7d0 100644 --- a/src/main/java/dev/pgm/events/format/rounds/veto/VetoHistory.java +++ b/src/main/java/dev/pgm/events/format/rounds/veto/VetoHistory.java @@ -5,38 +5,8 @@ import dev.pgm.events.team.TournamentTeam; import org.jspecify.annotations.Nullable; -public class VetoHistory { - - private final TournamentTeam team; - private final VetoSettings.VetoType vetoType; - private final VetoOption chosen; - - private final boolean shouldAnnounce; - - public VetoHistory( - @Nullable TournamentTeam team, - VetoSettings.VetoType vetoType, - VetoOption chosen, - boolean shouldAnnounce) { - this.team = team; - this.vetoType = vetoType; - this.chosen = chosen; - this.shouldAnnounce = shouldAnnounce; - } - - public @Nullable TournamentTeam team() { - return team; - } - - public VetoSettings.VetoType vetoType() { - return vetoType; - } - - public VetoOption optionChosen() { - return chosen; - } - - public boolean shouldAnnounce() { - return shouldAnnounce; - } -} +public record VetoHistory( + @Nullable TournamentTeam team, + VetoSettings.VetoType vetoType, + VetoOption optionChosen, + boolean shouldAnnounce) {} diff --git a/src/main/java/dev/pgm/events/format/rounds/veto/VetoRound.java b/src/main/java/dev/pgm/events/format/rounds/veto/VetoRound.java index fbd91aa..65d5d67 100644 --- a/src/main/java/dev/pgm/events/format/rounds/veto/VetoRound.java +++ b/src/main/java/dev/pgm/events/format/rounds/veto/VetoRound.java @@ -1,5 +1,7 @@ package dev.pgm.events.format.rounds.veto; +import static net.kyori.adventure.text.Component.text; + import dev.pgm.events.format.TournamentFormat; import dev.pgm.events.format.TournamentRoundOptions; import dev.pgm.events.format.rounds.AbstractRound; @@ -13,7 +15,6 @@ import java.util.List; import java.util.Optional; import java.util.Random; -import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; import tc.oc.pgm.api.match.Match; @@ -75,8 +76,8 @@ public void cleanup(Match match) { } public void startVeto(Match match, List vetoOrder) { - setPhase(RoundPhase.RUNNING); this.controller = new VetoController(tournament(), settings(), vetoOrder); + setPhase(RoundPhase.RUNNING); cycleVeto(match); } @@ -86,12 +87,12 @@ private void endVeto(Match match) { tournament().addRoundAfterCurrent(controller.toPlay()); // move the below formatting to the description class - match.sendMessage(Component.text("------ ", NamedTextColor.GOLD) - .append(Component.text("Veto Finished", NamedTextColor.AQUA)) - .append(Component.text(" ------", NamedTextColor.GOLD))); + match.sendMessage(text("------ ", NamedTextColor.GOLD) + .append(text("Veto Finished", NamedTextColor.AQUA)) + .append(text(" ------", NamedTextColor.GOLD))); for (int i = 0; i < controller.toPlay().size(); i++) { - match.sendMessage(Component.text((i + 1) + ". ", NamedTextColor.GOLD) + match.sendMessage(text((i + 1) + ". ", NamedTextColor.GOLD) .append(controller.toPlay().get(i).describe().roundInfo())); } @@ -134,6 +135,8 @@ public boolean validVetoNumber(int number) { } public void randomVeto(Match match) { + if (phase() != RoundPhase.RUNNING) return; + Optional picking = picking(); if (picking.isEmpty()) { tryAutoVeto(match); @@ -171,7 +174,7 @@ public void veto(Match match, TournamentTeam team, int pick) { // some quick checks before doing the veto if (phase() != RoundPhase.RUNNING) return; - if (!controller.isPicking(team) && canVeto()) return; + if (!controller.isPicking(team)) return; doVeto(match, team, pick); } diff --git a/src/main/java/dev/pgm/events/format/rounds/veto/VetoTournamentImpl.java b/src/main/java/dev/pgm/events/format/rounds/veto/VetoTournamentImpl.java index cbcce3a..f46c71b 100644 --- a/src/main/java/dev/pgm/events/format/rounds/veto/VetoTournamentImpl.java +++ b/src/main/java/dev/pgm/events/format/rounds/veto/VetoTournamentImpl.java @@ -8,8 +8,7 @@ import dev.pgm.events.team.TournamentTeamManager; import java.util.Comparator; import java.util.List; -import java.util.Optional; -import java.util.stream.Collectors; +import org.jspecify.annotations.Nullable; import tc.oc.pgm.api.match.Match; public class VetoTournamentImpl extends TournamentFormatImpl { @@ -26,15 +25,12 @@ public VetoTournamentImpl( } @Override - public void onEnd(Match match, Optional winner) { + public void onEnd(Match match, @Nullable TournamentTeam winner) { // list in descending order of score List vetoOrder = this.scores().scores().stream() .sorted(Comparator.comparingInt(Score::score).reversed()) .map(Score::team) - .collect(Collectors.toList()); - - // unregister everything with this fake event - unregisterAll(); + .toList(); // call callback to start the veto process vetoRound.startVeto(match, vetoOrder); diff --git a/src/main/java/dev/pgm/events/format/rounds/veto/settings/VetoOption.java b/src/main/java/dev/pgm/events/format/rounds/veto/settings/VetoOption.java index c3984a2..442b2cb 100644 --- a/src/main/java/dev/pgm/events/format/rounds/veto/settings/VetoOption.java +++ b/src/main/java/dev/pgm/events/format/rounds/veto/settings/VetoOption.java @@ -3,21 +3,4 @@ import dev.pgm.events.format.rounds.RoundSettings; import java.util.List; -public class VetoOption { - - private final List rounds; - private final String name; - - public VetoOption(List rounds, String name) { - this.rounds = rounds; - this.name = name; - } - - public List rounds() { - return rounds; - } - - public String name() { - return name; - } -} +public record VetoOption(List rounds, String name) {} diff --git a/src/main/java/dev/pgm/events/format/rounds/veto/settings/VetoSettings.java b/src/main/java/dev/pgm/events/format/rounds/veto/settings/VetoSettings.java index b2b9993..ab0a19f 100644 --- a/src/main/java/dev/pgm/events/format/rounds/veto/settings/VetoSettings.java +++ b/src/main/java/dev/pgm/events/format/rounds/veto/settings/VetoSettings.java @@ -28,6 +28,7 @@ public VetoSettings( List vetoList, List options) { super(id, scoring, showInHistory); + validate(vetoList, options); this.score = score; this.vetoTime = vetoTime; this.decider = decider; @@ -35,6 +36,40 @@ public VetoSettings( this.options = options; } + private VetoSettings(VetoSettings source, String id) { + super(id, source.scoring(), source.showInHistory()); + this.score = source.score; + this.vetoTime = source.vetoTime; + this.decider = source.decider; + this.vetoList = source.vetoList; + this.options = source.options; + } + + // the veto controller relies on these: each step consumes exactly one option, and the system + // enacts the last step once no choice is left + private static void validate(List vetoList, List options) { + if (options.size() < 2) + throw new IllegalArgumentException( + "Veto needs at least 2 options, but has " + options.size() + "."); + if (vetoList.size() != options.size()) + throw new IllegalArgumentException("Veto order has " + + vetoList.size() + + " steps but there are " + + options.size() + + " options. The order must contain exactly one step per option."); + for (Veto veto : vetoList) { + if (veto.team() < 0) + throw new IllegalArgumentException( + "Invalid team " + veto.team() + ". It must not be negative."); + } + if (vetoList.stream().allMatch(veto -> veto.vetoType() == VetoType.BAN)) + throw new IllegalArgumentException( + "Veto order has no pick steps, so no rounds would be played."); + if (vetoList.getLast().team() != 0) + throw new IllegalArgumentException( + "The last step of a veto order is enacted by the system and must not have a team."); + } + @Override public TournamentRound newRound(TournamentFormat format) { return new VetoRound(format, this); @@ -42,9 +77,7 @@ public TournamentRound newRound(TournamentFormat format) { @Override public TournamentRound newRound(TournamentFormat format, String id) { - return new VetoSettings( - id, vetoTime, decider, score, scoring(), showInHistory(), vetoList, options) - .newRound(format); + return new VetoSettings(this, id).newRound(format); } public int score() { @@ -73,17 +106,5 @@ public enum VetoType { CHOOSE_LAST } - public static class Veto { - public final VetoType vetoType; - public final int team; - public final Duration vetoDuration; - public final boolean shouldAnnounce; - - public Veto(VetoType vetoType, int team, Duration vetoDuration, boolean shouldAnnounce) { - this.vetoType = vetoType; - this.team = team; - this.vetoDuration = vetoDuration; - this.shouldAnnounce = shouldAnnounce; - } - } + public record Veto(VetoType vetoType, int team, Duration vetoDuration, boolean shouldAnnounce) {} } diff --git a/src/main/java/dev/pgm/events/format/rounds/vetoselector/VetoSelectorDescription.java b/src/main/java/dev/pgm/events/format/rounds/vetoselector/VetoSelectorDescription.java index 2dba2b4..942bcbc 100644 --- a/src/main/java/dev/pgm/events/format/rounds/vetoselector/VetoSelectorDescription.java +++ b/src/main/java/dev/pgm/events/format/rounds/vetoselector/VetoSelectorDescription.java @@ -1,5 +1,7 @@ package dev.pgm.events.format.rounds.vetoselector; +import static net.kyori.adventure.text.Component.text; + import dev.pgm.events.format.rounds.RoundDescription; import net.kyori.adventure.text.Component; @@ -13,7 +15,7 @@ public VetoSelectorDescription(VetoSelectorRound vetoSelectorRound) { @Override public Component roundInfo() { - return Component.text(vetoSelectorRound.getSelectingTeam().getName()); + return text(vetoSelectorRound.getSelectingTeam().getName()); } @Override diff --git a/src/main/java/dev/pgm/events/format/score/Scores.java b/src/main/java/dev/pgm/events/format/score/Scores.java index 18c917d..ba8ac92 100644 --- a/src/main/java/dev/pgm/events/format/score/Scores.java +++ b/src/main/java/dev/pgm/events/format/score/Scores.java @@ -69,7 +69,7 @@ public Pair topTwoOrdered() { } } - return Pair.create(new Score(highest, highestScore), new Score(second, secondHighestScore)); + return new Pair<>(new Score(highest, highestScore), new Score(second, secondHighestScore)); } public Score highestScore() { diff --git a/src/main/java/dev/pgm/events/format/score/TwoTeamFormattedScore.java b/src/main/java/dev/pgm/events/format/score/TwoTeamFormattedScore.java index b0bd9b0..ab0cea0 100644 --- a/src/main/java/dev/pgm/events/format/score/TwoTeamFormattedScore.java +++ b/src/main/java/dev/pgm/events/format/score/TwoTeamFormattedScore.java @@ -1,5 +1,7 @@ package dev.pgm.events.format.score; +import static net.kyori.adventure.text.Component.text; + import dev.pgm.events.team.TournamentTeam; import dev.pgm.events.team.TournamentTeamManager; import dev.pgm.events.utils.Pair; @@ -27,37 +29,37 @@ public TwoTeamFormattedScore( @Override public Component topLine() { return teamManager - .formattedName(topTwo.first.team()) - .append(Component.text(" - ", NamedTextColor.GRAY)) - .append(teamManager.formattedName(topTwo.second.team())); + .formattedName(topTwo.first().team()) + .append(text(" - ", NamedTextColor.GRAY)) + .append(teamManager.formattedName(topTwo.second().team())); } @Override public Component bottomLine() { Component first = - Component.text(topTwo.first.score(), teamManager.teamColour(topTwo.first.team())); - if (justWon.contains(topTwo.first.team())) { + text(topTwo.first().score(), teamManager.teamColour(topTwo.first().team())); + if (justWon.contains(topTwo.first().team())) { first = first.decorate(TextDecoration.BOLD).decorate(TextDecoration.UNDERLINED); } Component second = - Component.text(topTwo.second.score(), teamManager.teamColour(topTwo.second.team())); - if (justWon.contains(topTwo.second.team())) { + text(topTwo.second().score(), teamManager.teamColour(topTwo.second().team())); + if (justWon.contains(topTwo.second().team())) { second = second.decorate(TextDecoration.BOLD).decorate(TextDecoration.UNDERLINED); } - return first.append(Component.text(" - ", NamedTextColor.GRAY)).append(second); + return first.append(text(" - ", NamedTextColor.GRAY)).append(second); } @Override public Component condensed() { return teamManager - .formattedName(topTwo.first.team()) + .formattedName(topTwo.first().team()) .append(Component.space()) - .append(Component.text(topTwo.first.score(), NamedTextColor.WHITE)) - .append(Component.text(" - ", NamedTextColor.GRAY)) - .append(Component.text(topTwo.second.score(), NamedTextColor.WHITE)) + .append(text(topTwo.first().score(), NamedTextColor.WHITE)) + .append(text(" - ", NamedTextColor.GRAY)) + .append(text(topTwo.second().score(), NamedTextColor.WHITE)) .append(Component.space()) - .append(teamManager.formattedName(topTwo.second.team())); + .append(teamManager.formattedName(topTwo.second().team())); } } diff --git a/src/main/java/dev/pgm/events/team/DefaultTeamManager.java b/src/main/java/dev/pgm/events/team/DefaultTeamManager.java index a70333d..a84efd8 100644 --- a/src/main/java/dev/pgm/events/team/DefaultTeamManager.java +++ b/src/main/java/dev/pgm/events/team/DefaultTeamManager.java @@ -1,5 +1,7 @@ package dev.pgm.events.team; +import static net.kyori.adventure.text.Component.text; + import dev.pgm.events.utils.JoinUtils; import java.util.ArrayList; import java.util.Collection; @@ -104,7 +106,7 @@ public NamedTextColor teamColour(TournamentTeam tournamentTeam) { @Override public Component formattedName(TournamentTeam tournamentTeam) { - return Component.text(tournamentTeam.getName(), teamColour(tournamentTeam)); + return text(tournamentTeam.getName(), teamColour(tournamentTeam)); } @Override diff --git a/src/main/java/dev/pgm/events/team/TournamentTeam.java b/src/main/java/dev/pgm/events/team/TournamentTeam.java index 50c5d87..b2d0762 100644 --- a/src/main/java/dev/pgm/events/team/TournamentTeam.java +++ b/src/main/java/dev/pgm/events/team/TournamentTeam.java @@ -1,5 +1,7 @@ package dev.pgm.events.team; +import static net.kyori.adventure.text.Component.text; + import java.util.List; import java.util.Objects; import java.util.UUID; @@ -20,7 +22,7 @@ default boolean containsPlayer(UUID player) { } default void sendMessage(String message) { - sendMessage(Component.text(message)); + sendMessage(text(message)); } default void sendMessage(Component component) { diff --git a/src/main/java/dev/pgm/events/team/TournamentTeamManager.java b/src/main/java/dev/pgm/events/team/TournamentTeamManager.java index 5b622d5..5700c78 100644 --- a/src/main/java/dev/pgm/events/team/TournamentTeamManager.java +++ b/src/main/java/dev/pgm/events/team/TournamentTeamManager.java @@ -1,5 +1,7 @@ package dev.pgm.events.team; +import static net.kyori.adventure.text.Component.text; + import java.util.Collection; import java.util.Optional; import java.util.UUID; @@ -37,7 +39,7 @@ public interface TournamentTeamManager { NamedTextColor teamColour(TournamentTeam tournamentTeam); default Component formattedName(Competitor team) { - return tournamentTeam(team).map(this::formattedName).orElse(Component.text("NULL")); + return tournamentTeam(team).map(this::formattedName).orElse(text("NULL")); } Component formattedName(TournamentTeam tournamentTeam); diff --git a/src/main/java/dev/pgm/events/utils/Components.java b/src/main/java/dev/pgm/events/utils/Components.java index bf0be83..11986f3 100644 --- a/src/main/java/dev/pgm/events/utils/Components.java +++ b/src/main/java/dev/pgm/events/utils/Components.java @@ -1,5 +1,7 @@ package dev.pgm.events.utils; +import static net.kyori.adventure.text.Component.text; + import net.kyori.adventure.text.Component; import net.kyori.adventure.text.event.ClickEvent; import net.kyori.adventure.text.format.NamedTextColor; @@ -23,11 +25,9 @@ public static Component command(Style style, String command, String... cmdArgs) for (String arg : cmdArgs) builder.append(" ").append(Components.toArgument(arg)); command = builder.toString(); - var payload = ClickEvent.Payload.string(command); - return Component.text(command, style) + return text(command, style) .clickEvent(ClickEvent.runCommand(command)) - .hoverEvent(Component.text("Click to run ", NamedTextColor.GREEN) - .append(Component.text(command, style))); + .hoverEvent(text("Click to run ", NamedTextColor.GREEN).append(text(command, style))); } private static String toArgument(String input) { diff --git a/src/main/java/dev/pgm/events/utils/Pair.java b/src/main/java/dev/pgm/events/utils/Pair.java index 33a1785..f8aee49 100644 --- a/src/main/java/dev/pgm/events/utils/Pair.java +++ b/src/main/java/dev/pgm/events/utils/Pair.java @@ -1,16 +1,3 @@ package dev.pgm.events.utils; -public class Pair { - - public final T first; - public final U second; - - public Pair(T first, U second) { - this.first = first; - this.second = second; - } - - public static Pair create(T first, U second) { - return new Pair<>(first, second); - } -} +public record Pair(T first, U second) {} diff --git a/src/main/java/dev/pgm/events/xml/MapFormatXMLParser.java b/src/main/java/dev/pgm/events/xml/MapFormatXMLParser.java index 4d6b34c..76149b3 100644 --- a/src/main/java/dev/pgm/events/xml/MapFormatXMLParser.java +++ b/src/main/java/dev/pgm/events/xml/MapFormatXMLParser.java @@ -6,34 +6,41 @@ import dev.pgm.events.format.TournamentFormatImpl; import dev.pgm.events.format.TournamentRoundOptions; import dev.pgm.events.format.winner.BestOfCalculation; -import java.io.File; import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; import java.time.Duration; import org.jspecify.annotations.NonNull; import tc.oc.pgm.lib.org.jdom2.Document; import tc.oc.pgm.lib.org.jdom2.Element; import tc.oc.pgm.lib.org.jdom2.JDOMException; +import tc.oc.pgm.lib.org.jdom2.input.JDOMParseException; import tc.oc.pgm.lib.org.jdom2.input.SAXBuilder; public class MapFormatXMLParser { public static TournamentFormat parse(String name) { - File poolsFolder = new File(EventsPlugin.get().getDataFolder(), "formats"); - File xmlFile = new File(poolsFolder, name + ".xml"); + // messages are shown to the sender, so they must not include server paths + Path formats = + EventsPlugin.get().getDataFolder().toPath().resolve("formats").normalize(); + Path file = formats.resolve(name + ".xml").normalize(); + if (!file.startsWith(formats) || !Files.isRegularFile(file)) + throw new IllegalArgumentException("Format " + name + " was not found!"); + Document document; try { - document = new SAXBuilder().build(xmlFile); - Element root = document.getRootElement(); - return parse(root); + document = new SAXBuilder().build(file.toFile()); + } catch (JDOMParseException e) { + throw new IllegalArgumentException( + "Format " + name + " has invalid XML on line " + e.getLineNumber() + "!", e); } catch (JDOMException | IOException e) { - e.printStackTrace(); + throw new IllegalArgumentException("Format " + name + " could not be loaded!", e); } - return null; + return parse(document.getRootElement()); } public static TournamentFormat parse(Element root) { - String bestOfArgs = root.getAttributeValue("best-of"); - TournamentFormat format = getTournamentFormat(bestOfArgs); + TournamentFormat format = getTournamentFormat(RoundParser.parseInt(root, "best-of", null)); if (!root.getName().equalsIgnoreCase("format")) System.out.println( @@ -44,10 +51,7 @@ public static TournamentFormat parse(Element root) { return format; } - private static @NonNull TournamentFormat getTournamentFormat(String bestOfArgs) { - if (bestOfArgs == null) throw new IllegalArgumentException("No best-of specified on format!"); - int bestOf = Integer.parseInt(bestOfArgs); - + private static @NonNull TournamentFormat getTournamentFormat(int bestOf) { TournamentRoundOptions options = new TournamentRoundOptions( false, true, diff --git a/src/main/java/dev/pgm/events/xml/RoundParser.java b/src/main/java/dev/pgm/events/xml/RoundParser.java index d0bbc2b..dda11db 100644 --- a/src/main/java/dev/pgm/events/xml/RoundParser.java +++ b/src/main/java/dev/pgm/events/xml/RoundParser.java @@ -18,6 +18,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; +import org.jspecify.annotations.Nullable; import tc.oc.pgm.lib.org.jdom2.Element; public class RoundParser { @@ -34,6 +35,19 @@ public static TournamentRound parse(TournamentFormat format, Element round) { }; } + static int parseInt(Element element, String attribute, @Nullable String def) { + String value = element.getAttributeValue(attribute, def); + if (value == null) + throw new IllegalArgumentException( + "No " + attribute + " specified on " + element.getName() + "!"); + try { + return Integer.parseInt(value); + } catch (NumberFormatException e) { + throw new IllegalArgumentException( + "Invalid " + attribute + " " + value + ". It must be a whole number."); + } + } + public static class SingleParser { public static SingleRound parse(TournamentFormat format, Element element) { @@ -56,31 +70,59 @@ public static VetoRound parse(TournamentFormat format, Element element) { String id = element.getAttributeValue("id", "veto"); Element order = element.getChild("order"); if (order == null) throw new IllegalArgumentException("Order element is missing from veto!"); - Duration vetoTime; - try { - vetoTime = Duration.ofSeconds(Long.parseLong(order.getAttributeValue("time", "30"))); - } catch (NumberFormatException e) { + int seconds = parseInt(order, "time", "30"); + if (seconds < 1) throw new IllegalArgumentException( - "Invalid time! Times are provided in seconds with no units."); - } + "Invalid time " + seconds + ". It must be a positive number of seconds."); + Duration vetoTime = Duration.ofSeconds(seconds); + Element deciderElement = element.getChild("decider"); + if (deciderElement == null || deciderElement.getChildren().isEmpty()) + throw new IllegalArgumentException("Decider element is missing from veto!"); TournamentRound decider = - RoundParser.parse(format, element.getChild("decider").getChildren().getFirst()); - - List options = constructVetoOptions(format, element.getChild("options")); - List vetoList = constructVetoList(order, options.size()); + RoundParser.parse(format, deciderElement.getChildren().getFirst()); + + Element optionsElement = element.getChild("options"); + if (optionsElement == null) + throw new IllegalArgumentException("Options element is missing from veto!"); + List options = constructVetoOptions(format, optionsElement); + + int teams = format.teamManager().teams().size(); + List vetoList = constructVetoList(order, options.size(), teams); + + for (VetoSettings.Veto veto : vetoList) { + if (veto.team() > teams) + throw new IllegalArgumentException("Veto order refers to team " + + veto.team() + + " but only " + + teams + + " teams are registered."); + } VetoSettings settings = new VetoSettings(id, vetoTime, decider.settings(), 0, false, true, vetoList, options); return new VetoRound(format, settings); } - private static List constructVetoList(Element element, int numOptions) { + private static List constructVetoList( + Element element, int numOptions, int numTeams) { ArrayList vetos = new ArrayList<>(); if (element.getChildren().isEmpty()) { - int until = Integer.parseInt(element.getAttributeValue("ban-until", "1")); - int startingTeam = Integer.parseInt(element.getAttributeValue("starting-team", "2")); - int numTeams = Integer.parseInt(element.getAttributeValue("teams", "2")); + int until = parseInt(element, "ban-until", "1"); + int startingTeam = parseInt(element, "starting-team", "2"); + if (until < 1 || until > numOptions) + throw new IllegalArgumentException("Invalid ban-until " + + until + + ". It must be between 1 and the number of options (" + + numOptions + + ")."); + if (numTeams == 0) throw new IllegalArgumentException("No teams are registered!"); + if (startingTeam < 1 || startingTeam > numTeams) + throw new IllegalArgumentException("Invalid starting-team " + + startingTeam + + ". It must be between 1 and the number of registered teams (" + + numTeams + + ")."); int currentTeam = startingTeam; for (int i = numOptions; i > until; i--) { @@ -121,7 +163,7 @@ private static VetoSettings.Veto parseVeto(Element element) { if (type == null) throw new IllegalArgumentException("Invalid veto order " + element.getName()); - int team = Integer.parseInt(element.getAttributeValue("team", "0")); + int team = parseInt(element, "team", "0"); return new VetoSettings.Veto(type, team, null, true); } @@ -157,9 +199,7 @@ public static FormatRound parse(TournamentFormat format, Element element) { String name = element.getAttributeValue("name", "Format"); String id = element.getAttributeValue("id", name.toLowerCase()); - String bestOfArgs = element.getAttributeValue("best-of"); - if (bestOfArgs == null) throw new IllegalArgumentException("No best-of specified on format!"); - int bestOf = Integer.parseInt(bestOfArgs); + int bestOf = parseInt(element, "best-of", null); List rounds = new ArrayList<>(); for (Element child : element.getChildren()) { diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 011cf5c..fb02931 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -2,5 +2,6 @@ name: Events description: ${description} main: ${mainClass} version: ${version} (git-${commitHash}) +api-version: ${apiVersion} website: ${url} depend: [PGM] \ No newline at end of file