diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b10fc519..d1bb0f0a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -16,7 +16,7 @@ jobs: distribution: 'adopt' cache: gradle - name: Build with Gradle - run: ./gradlew build + run: ./gradlew bundledJar - name: Publish to Maven Repository if: ${{ success() && github.repository == 'BattlePlugins/BattleArena' && github.ref_name == 'rewrite' }} run: ./gradlew publish diff --git a/module/arena-restoration/build.gradle.kts b/module/arena-restoration/build.gradle.kts index 0ff38073..45d08f5f 100644 --- a/module/arena-restoration/build.gradle.kts +++ b/module/arena-restoration/build.gradle.kts @@ -3,6 +3,5 @@ repositories { } dependencies { - compileOnlyApi(project(":plugin")) compileOnly(libs.worldedit) } \ No newline at end of file diff --git a/module/boundary-enforcer/build.gradle.kts b/module/boundary-enforcer/build.gradle.kts index 0d7ff71b..e69de29b 100644 --- a/module/boundary-enforcer/build.gradle.kts +++ b/module/boundary-enforcer/build.gradle.kts @@ -1,3 +0,0 @@ -dependencies { - compileOnlyApi(project(":plugin")) -} \ No newline at end of file diff --git a/module/build.gradle.kts b/module/build.gradle.kts new file mode 100644 index 00000000..9b27c392 --- /dev/null +++ b/module/build.gradle.kts @@ -0,0 +1,14 @@ +subprojects { + dependencies { + compileOnlyApi(project(":plugin")) + } + + tasks.jar { + from("src/main/java/resources") { + include("*") + } + + archiveFileName.set("${project.name}.jar") + archiveClassifier.set("") + } +} \ No newline at end of file diff --git a/module/classes/build.gradle.kts b/module/classes/build.gradle.kts index 0d7ff71b..e69de29b 100644 --- a/module/classes/build.gradle.kts +++ b/module/classes/build.gradle.kts @@ -1,3 +0,0 @@ -dependencies { - compileOnlyApi(project(":plugin")) -} \ No newline at end of file diff --git a/module/team-heads/build.gradle.kts b/module/team-heads/build.gradle.kts index 0d7ff71b..e69de29b 100644 --- a/module/team-heads/build.gradle.kts +++ b/module/team-heads/build.gradle.kts @@ -1,3 +0,0 @@ -dependencies { - compileOnlyApi(project(":plugin")) -} \ No newline at end of file diff --git a/module/tournaments/build.gradle.kts b/module/tournaments/build.gradle.kts index 0d7ff71b..e69de29b 100644 --- a/module/tournaments/build.gradle.kts +++ b/module/tournaments/build.gradle.kts @@ -1,3 +0,0 @@ -dependencies { - compileOnlyApi(project(":plugin")) -} \ No newline at end of file diff --git a/module/tournaments/src/main/java/org/battleplugins/arena/module/tournaments/Tournament.java b/module/tournaments/src/main/java/org/battleplugins/arena/module/tournaments/Tournament.java index 23c7be1a..79803bcb 100644 --- a/module/tournaments/src/main/java/org/battleplugins/arena/module/tournaments/Tournament.java +++ b/module/tournaments/src/main/java/org/battleplugins/arena/module/tournaments/Tournament.java @@ -13,7 +13,7 @@ import org.battleplugins.arena.options.Teams; import org.battleplugins.arena.team.ArenaTeam; import org.battleplugins.arena.util.IntRange; -import org.battleplugins.arena.util.UnitUtil; +import org.battleplugins.arena.util.Util; import org.bukkit.Bukkit; import org.bukkit.entity.Player; import org.jetbrains.annotations.Nullable; @@ -211,7 +211,7 @@ public void onAdvance(List contestants) throws TournamentException { }, ticks); for (Player watchingPlayer : this.watchingPlayers) { - NEXT_ROUND_STARTING_IN.send(watchingPlayer, UnitUtil.toUnitString(watchingPlayer, advanceTime.toSeconds(), TimeUnit.SECONDS)); + NEXT_ROUND_STARTING_IN.send(watchingPlayer, Util.toUnitString(advanceTime.toSeconds(), TimeUnit.SECONDS)); } } } diff --git a/module/vault-integration/build.gradle.kts b/module/vault-integration/build.gradle.kts index 99481968..fd3a6bb3 100644 --- a/module/vault-integration/build.gradle.kts +++ b/module/vault-integration/build.gradle.kts @@ -3,6 +3,5 @@ repositories { } dependencies { - compileOnlyApi(project(":plugin")) compileOnly("com.github.MilkBowl:VaultAPI:1.7") } \ No newline at end of file diff --git a/plugin/build.gradle.kts b/plugin/build.gradle.kts index 719c5794..bc5de5a5 100644 --- a/plugin/build.gradle.kts +++ b/plugin/build.gradle.kts @@ -22,6 +22,8 @@ java { tasks { runServer { + dependsOn("bundledJar") + minecraftVersion("1.20.6") // Set Java 21 (1.20.6 requires Java 21) @@ -39,6 +41,21 @@ tasks { archiveClassifier.set("") } + create("bundledJar") { + dependsOn(jar) + from(sourceSets.main.get().output) + + // Bundle in our modules + project(":module").subprojects.forEach { + from(it.tasks.jar) { + into("modules") + } + } + + archiveFileName.set("BattleArena.jar") + archiveClassifier.set("") + } + javadoc { (options as CoreJavadocOptions).addBooleanOption("Xdoclint:none", true) } diff --git a/plugin/src/main/java/org/battleplugins/arena/BattleArena.java b/plugin/src/main/java/org/battleplugins/arena/BattleArena.java index d815243d..b2d25809 100644 --- a/plugin/src/main/java/org/battleplugins/arena/BattleArena.java +++ b/plugin/src/main/java/org/battleplugins/arena/BattleArena.java @@ -26,6 +26,7 @@ import org.battleplugins.arena.team.ArenaTeams; import org.battleplugins.arena.util.CommandInjector; import org.battleplugins.arena.util.LoggerHolder; +import org.battleplugins.arena.util.Util; import org.bukkit.Bukkit; import org.bukkit.command.PluginCommand; import org.bukkit.configuration.Configuration; @@ -88,6 +89,8 @@ public void onLoad() { this.arenasPath = dataFolder.resolve("arenas"); Path modulesPath = dataFolder.resolve("modules"); + Util.copyDirectories(this.getFile(), modulesPath, "modules"); + this.moduleLoader = new ArenaModuleLoader(this, this.getClassLoader(), modulesPath); try { this.moduleLoader.loadModules(); @@ -130,12 +133,7 @@ private void enable() { this.debugMode = this.config.isDebugMode(); if (Files.notExists(this.arenasPath)) { - this.saveResource("arenas/arena.yml", false); - this.saveResource("arenas/battlegrounds.yml", false); - this.saveResource("arenas/colosseum.yml", false); - this.saveResource("arenas/deathmatch.yml", false); - this.saveResource("arenas/ffa.yml", false); - this.saveResource("arenas/skirmish.yml", false); + Util.copyDirectories(this.getFile(), this.arenasPath, "arenas"); } Path dataFolder = this.getDataFolder().toPath(); @@ -166,9 +164,6 @@ private void enable() { // Enable modules this.moduleLoader.enableModules(); - // Load messages - MessageLoader.load(dataFolder.resolve("messages.yml")); - // Register base command PluginCommand command = this.getCommand("battlearena"); if (command == null) { @@ -220,6 +215,9 @@ public void onServerLoad(ServerLoadEvent event) { } private void postInitialize() { + // Load messages + MessageLoader.load(this.getDataFolder().toPath().resolve("messages.yml")); + // Load all arenas this.loadArenas(); diff --git a/plugin/src/main/java/org/battleplugins/arena/command/BACommandExecutor.java b/plugin/src/main/java/org/battleplugins/arena/command/BACommandExecutor.java index 15b2f2ab..8394f3b3 100644 --- a/plugin/src/main/java/org/battleplugins/arena/command/BACommandExecutor.java +++ b/plugin/src/main/java/org/battleplugins/arena/command/BACommandExecutor.java @@ -11,7 +11,7 @@ import org.battleplugins.arena.messages.Messages; import org.battleplugins.arena.util.InventoryBackup; import org.battleplugins.arena.util.OptionSelector; -import org.battleplugins.arena.util.UnitUtil; +import org.battleplugins.arena.util.Util; import org.bukkit.Bukkit; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; @@ -179,7 +179,7 @@ public void reload(Player player) { } long end = System.currentTimeMillis(); - Messages.RELOAD_COMPLETE.send(player, UnitUtil.toUnitString(player, end - start, TimeUnit.MILLISECONDS)); + Messages.RELOAD_COMPLETE.send(player, Util.toUnitString(end - start, TimeUnit.MILLISECONDS)); } public void sendHeader(CommandSender sender) { diff --git a/plugin/src/main/java/org/battleplugins/arena/competition/map/LiveCompetitionMap.java b/plugin/src/main/java/org/battleplugins/arena/competition/map/LiveCompetitionMap.java index bb28faf8..b415d102 100644 --- a/plugin/src/main/java/org/battleplugins/arena/competition/map/LiveCompetitionMap.java +++ b/plugin/src/main/java/org/battleplugins/arena/competition/map/LiveCompetitionMap.java @@ -12,7 +12,7 @@ import org.battleplugins.arena.config.ParseException; import org.battleplugins.arena.config.PostProcessable; import org.battleplugins.arena.util.BlockUtil; -import org.battleplugins.arena.util.FieldUtil; +import org.battleplugins.arena.util.Util; import org.battleplugins.arena.util.VoidChunkGenerator; import org.bukkit.Bukkit; import org.bukkit.World; @@ -249,7 +249,7 @@ public final Competition createDynamicCompetition(Arena arena) { LiveCompetitionMap copy = arena.getMapFactory().create(this.name, arena, this.type, worldName, this.bounds, this.spawns); // Copy additional fields for custom maps if (copy.getClass() != LiveCompetitionMap.class) { - FieldUtil.copyFields(this, copy); + Util.copyFields(this, copy); } copy.mapWorld = world; diff --git a/plugin/src/main/java/org/battleplugins/arena/competition/phase/phases/CountdownPhase.java b/plugin/src/main/java/org/battleplugins/arena/competition/phase/phases/CountdownPhase.java index 7380019f..82251d31 100644 --- a/plugin/src/main/java/org/battleplugins/arena/competition/phase/phases/CountdownPhase.java +++ b/plugin/src/main/java/org/battleplugins/arena/competition/phase/phases/CountdownPhase.java @@ -7,7 +7,7 @@ import org.battleplugins.arena.event.ArenaEventHandler; import org.battleplugins.arena.event.player.ArenaLeaveEvent; import org.battleplugins.arena.messages.Messages; -import org.battleplugins.arena.util.UnitUtil; +import org.battleplugins.arena.util.Util; import org.bukkit.Bukkit; import org.bukkit.entity.Player; import org.bukkit.scheduler.BukkitTask; @@ -63,7 +63,7 @@ private void onCountdown() { if (this.countdown % 60 == 0 || this.countdown == 30 || this.countdown == 15 || this.countdown == 10 || this.countdown <= 5) { for (ArenaPlayer arenaPlayer : this.getCompetition().getPlayers()) { Player player = arenaPlayer.getPlayer(); - String timeToStart = UnitUtil.toUnitString(player, this.countdown, TimeUnit.SECONDS); + String timeToStart = Util.toUnitString(this.countdown, TimeUnit.SECONDS); Messages.ARENA_STARTS_IN.send(player, this.competition.getArena().getName(), timeToStart); diff --git a/plugin/src/main/java/org/battleplugins/arena/config/context/EventContextProvider.java b/plugin/src/main/java/org/battleplugins/arena/config/context/EventContextProvider.java index 3c0e80aa..9ab09a28 100644 --- a/plugin/src/main/java/org/battleplugins/arena/config/context/EventContextProvider.java +++ b/plugin/src/main/java/org/battleplugins/arena/config/context/EventContextProvider.java @@ -83,9 +83,9 @@ public Map, List> provideInstance(@Nullable Path EventActionType actionType = EventActionType.get(root.value()); if (actionType == null) { - throw new ParseException("Unrecognized event action detected (" + key + ") when loading configuration section " + configurationSection.getName()) + throw new ParseException("Unrecognized event action detected (" + root.value() + ") when loading configuration section " + configurationSection.getName()) .context("Section", configuration.getName()) - .context("Provided action", key) + .context("Provided action", root.value()) .context("Valid events", EventActionType.values().stream().map(EventActionType::getName).toList().toString()) .cause(ParseException.Cause.INVALID_VALUE) .type(EventActionType.class) diff --git a/plugin/src/main/java/org/battleplugins/arena/event/action/types/BroadcastAction.java b/plugin/src/main/java/org/battleplugins/arena/event/action/types/BroadcastAction.java index 75a06289..931f00e3 100644 --- a/plugin/src/main/java/org/battleplugins/arena/event/action/types/BroadcastAction.java +++ b/plugin/src/main/java/org/battleplugins/arena/event/action/types/BroadcastAction.java @@ -50,7 +50,7 @@ public void postProcess(Arena arena, Competition competition, Resolvable reso }; for (Player player : players) { - Component component = MiniMessage.miniMessage().deserialize(resolvable.resolve().resolveToString(message)); + Component component = resolvable.resolve().resolveToComponent(MiniMessage.miniMessage().deserialize(message)); switch (messageType) { case CHAT -> player.sendMessage(component); case ACTION_BAR -> player.sendActionBar(component); diff --git a/plugin/src/main/java/org/battleplugins/arena/event/action/types/SendMessageAction.java b/plugin/src/main/java/org/battleplugins/arena/event/action/types/SendMessageAction.java index bb9bda86..9f0ab6ed 100644 --- a/plugin/src/main/java/org/battleplugins/arena/event/action/types/SendMessageAction.java +++ b/plugin/src/main/java/org/battleplugins/arena/event/action/types/SendMessageAction.java @@ -25,7 +25,7 @@ public void call(ArenaPlayer arenaPlayer, Resolvable resolvable) { .toUpperCase(Locale.ROOT) ); - Component component = MiniMessage.miniMessage().deserialize(resolvable.resolve().resolveToString(message)); + Component component = resolvable.resolve().resolveToComponent(MiniMessage.miniMessage().deserialize(message)); switch (messageType) { case CHAT -> arenaPlayer.getPlayer().sendMessage(component); case ACTION_BAR -> arenaPlayer.getPlayer().sendActionBar(component); 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 76064ac2..9a591d55 100644 --- a/plugin/src/main/java/org/battleplugins/arena/resolver/Resolver.java +++ b/plugin/src/main/java/org/battleplugins/arena/resolver/Resolver.java @@ -1,9 +1,13 @@ package org.battleplugins.arena.resolver; +import net.kyori.adventure.text.Component; + public interface Resolver { String resolveToString(String string); + Component resolveToComponent(Component component); + T resolve(ResolverKey key); boolean has(ResolverKey key); 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 dc5f4654..ce4ac763 100644 --- a/plugin/src/main/java/org/battleplugins/arena/resolver/ResolverImpl.java +++ b/plugin/src/main/java/org/battleplugins/arena/resolver/ResolverImpl.java @@ -1,5 +1,7 @@ package org.battleplugins.arena.resolver; +import net.kyori.adventure.text.Component; + import java.util.HashMap; import java.util.Map; @@ -20,6 +22,16 @@ public String resolveToString(String string) { return string; } + @Override + public Component resolveToComponent(Component component) { + for (Map.Entry, ResolverProvider> entry : this.results.entrySet()) { + String key = "%" + entry.getKey().getName().replace("-", "_") + "%"; + component = component.replaceText(builder -> builder.matchLiteral(key).replacement(entry.getValue().toComponent(this))); + } + + return component; + } + @SuppressWarnings("unchecked") @Override public T resolve(ResolverKey key) { diff --git a/plugin/src/main/java/org/battleplugins/arena/util/FieldUtil.java b/plugin/src/main/java/org/battleplugins/arena/util/FieldUtil.java deleted file mode 100644 index cda342c4..00000000 --- a/plugin/src/main/java/org/battleplugins/arena/util/FieldUtil.java +++ /dev/null @@ -1,24 +0,0 @@ -package org.battleplugins.arena.util; - -import org.battleplugins.arena.config.ArenaOption; - -import java.lang.reflect.Field; - -public class FieldUtil { - - public static void copyFields(T oldInstance, T newInstance) { - for (Field field : oldInstance.getClass().getDeclaredFields()) { - if (!field.isAnnotationPresent(ArenaOption.class)) { - continue; - } - - field.setAccessible(true); - - try { - field.set(newInstance, field.get(oldInstance)); - } catch (IllegalAccessException e) { - throw new RuntimeException("Failed to copy field " + field.getName(), e); - } - } - } -} diff --git a/plugin/src/main/java/org/battleplugins/arena/util/UnitUtil.java b/plugin/src/main/java/org/battleplugins/arena/util/UnitUtil.java deleted file mode 100644 index 3608faf0..00000000 --- a/plugin/src/main/java/org/battleplugins/arena/util/UnitUtil.java +++ /dev/null @@ -1,54 +0,0 @@ -package org.battleplugins.arena.util; - -import org.battleplugins.arena.messages.Messages; -import org.bukkit.command.CommandSender; - -import java.util.Locale; -import java.util.concurrent.TimeUnit; - -public final class UnitUtil { - - public static String toUnitString(CommandSender viewer, long amount, TimeUnit unit) { - switch (unit) { - case MILLISECONDS -> { - if (amount == 1) { - return amount + " " + Messages.MILLISECOND.asPlainText(); - } else { - return amount + " " + Messages.MILLISECONDS.asPlainText(); - } - } - case SECONDS -> { - if (amount == 1) { - return amount + " " + Messages.SECOND.asPlainText(); - } else { - return amount + " " + Messages.SECONDS.asPlainText(); - } - } - case MINUTES -> { - if (amount == 1) { - return amount + " " + Messages.MINUTE.asPlainText(); - } else { - return amount + " " + Messages.MINUTES.asPlainText(); - } - } - - case HOURS -> { - if (amount == 1) { - return amount + " " + Messages.HOUR.asPlainText(); - } else { - return amount + " " + Messages.HOURS.asPlainText(); - } - } - case DAYS -> { - if (amount == 1) { - return amount + " " + Messages.DAY.asPlainText(); - } else { - return amount + " " + Messages.DAYS.asPlainText(); - } - } - } - - // Realistically, we will only ever be using the values above - return unit.name().toLowerCase(Locale.ROOT); - } -} diff --git a/plugin/src/main/java/org/battleplugins/arena/util/Util.java b/plugin/src/main/java/org/battleplugins/arena/util/Util.java new file mode 100644 index 00000000..e4fbc473 --- /dev/null +++ b/plugin/src/main/java/org/battleplugins/arena/util/Util.java @@ -0,0 +1,120 @@ +package org.battleplugins.arena.util; + +import org.battleplugins.arena.BattleArena; +import org.battleplugins.arena.config.ArenaOption; +import org.battleplugins.arena.messages.Messages; + +import java.io.File; +import java.io.IOException; +import java.lang.reflect.Field; +import java.nio.file.FileSystem; +import java.nio.file.FileSystems; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Locale; +import java.util.Map; +import java.util.concurrent.TimeUnit; +import java.util.stream.Stream; + +public class Util { + + public static String toUnitString(long amount, TimeUnit unit) { + switch (unit) { + case MILLISECONDS -> { + if (amount == 1) { + return amount + " " + Messages.MILLISECOND.asPlainText(); + } else { + return amount + " " + Messages.MILLISECONDS.asPlainText(); + } + } + case SECONDS -> { + if (amount == 1) { + return amount + " " + Messages.SECOND.asPlainText(); + } else { + return amount + " " + Messages.SECONDS.asPlainText(); + } + } + case MINUTES -> { + if (amount == 1) { + return amount + " " + Messages.MINUTE.asPlainText(); + } else { + return amount + " " + Messages.MINUTES.asPlainText(); + } + } + + case HOURS -> { + if (amount == 1) { + return amount + " " + Messages.HOUR.asPlainText(); + } else { + return amount + " " + Messages.HOURS.asPlainText(); + } + } + case DAYS -> { + if (amount == 1) { + return amount + " " + Messages.DAY.asPlainText(); + } else { + return amount + " " + Messages.DAYS.asPlainText(); + } + } + } + + // Realistically, we will only ever be using the values above + return unit.name().toLowerCase(Locale.ROOT); + } + + public static void copyFields(T oldInstance, T newInstance) { + for (Field field : oldInstance.getClass().getDeclaredFields()) { + if (!field.isAnnotationPresent(ArenaOption.class)) { + continue; + } + + field.setAccessible(true); + + try { + field.set(newInstance, field.get(oldInstance)); + } catch (IllegalAccessException e) { + throw new RuntimeException("Failed to copy field " + field.getName(), e); + } + } + } + + public static void copyDirectories(File jarFile, Path outputPath, String directory) { + Path jarPath = jarFile.toPath(); + try { + if (Files.notExists(outputPath)) { + Files.createDirectories(outputPath); + } + + try (FileSystem fileSystem = FileSystems.newFileSystem(jarPath, Map.of())) { + Path root = fileSystem.getPath("/"); + Path directoryPath = root.resolve(directory); + if (Files.notExists(directoryPath)) { + return; + } + + try (Stream paths = Files.walk(directoryPath)) { + paths.forEach(path -> { + if (Files.isDirectory(path)) { + return; + } + + Path relativePath = directoryPath.relativize(path); + Path targetPath = outputPath.resolve(relativePath.toString()); + if (Files.exists(targetPath)) { + return; + } + + try { + Files.createDirectories(targetPath.getParent()); + Files.copy(path, targetPath); + } catch (IOException e) { + BattleArena.getInstance().error("Failed to copy module {}!", path.getFileName(), e); + } + }); + } + } + } catch (Exception e) { + BattleArena.getInstance().error("Failed to copy modules from jar!", e); + } + } +}