diff --git a/plugin/src/main/java/org/battleplugins/arena/BattleArena.java b/plugin/src/main/java/org/battleplugins/arena/BattleArena.java index a4b2b7fd..0ff9bc49 100644 --- a/plugin/src/main/java/org/battleplugins/arena/BattleArena.java +++ b/plugin/src/main/java/org/battleplugins/arena/BattleArena.java @@ -123,7 +123,10 @@ public void onEnable() { // Cheeky little message about BattleTracker <3 if (Bukkit.getPluginManager().getPlugin("BattleTracker") == null) { - this.warn("BattleTracker not found! Arena statistics will not be tracked. You can download BattleTracker at: https://modrinth.com/project/battletracker."); + this.warn("----------------------------------------"); + this.warn("BattleTracker not found! Arena statistics will not be saved."); + this.warn("You can download BattleTracker at: https://modrinth.com/project/battletracker."); + this.warn("----------------------------------------"); } } @@ -237,8 +240,8 @@ void postInitialize() { continue; // We are not interested in starting manual events } - this.eventScheduler.scheduleEvent(arena, options); - this.info("Scheduled event for arena {} in {}m.", arena.getName(), options.getInterval()); + this.eventScheduler.scheduleEvent(arena, options, true); + this.info("Scheduled event for arena {} in {}m.", arena.getName(), options.getInterval().plus(options.getDelay())); } } } 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 8394f3b3..4c38a904 100644 --- a/plugin/src/main/java/org/battleplugins/arena/command/BACommandExecutor.java +++ b/plugin/src/main/java/org/battleplugins/arena/command/BACommandExecutor.java @@ -3,6 +3,8 @@ import net.kyori.adventure.text.Component; import net.kyori.adventure.text.JoinConfiguration; import net.kyori.adventure.text.event.ClickEvent; +import net.kyori.adventure.text.format.NamedTextColor; +import net.kyori.adventure.text.format.Style; import org.battleplugins.arena.Arena; import org.battleplugins.arena.BattleArena; import org.battleplugins.arena.competition.CompetitionType; @@ -31,66 +33,78 @@ public BACommandExecutor(String parentCommand) { } @ArenaCommand(commands = "backups", description = "Shows backups that a player has saved.", permissionNode = "backups") - public void backups(Player player, @Argument(name = "player") String playerName) { - CompletableFuture.supplyAsync(() -> player.getServer().getOfflinePlayer(playerName)).thenAcceptAsync(target -> { + public void backups(CommandSender sender, @Argument(name = "player") String playerName) { + CompletableFuture.supplyAsync(() -> Bukkit.getServer().getOfflinePlayer(playerName)).thenAcceptAsync(target -> { if (target == null) { - Messages.PLAYER_NOT_ONLINE.send(player, playerName); + Messages.PLAYER_NOT_ONLINE.send(sender, playerName); return; } // Show backups List backups = InventoryBackup.load(target.getUniqueId()); if (backups.isEmpty()) { - Messages.NO_BACKUPS.send(player, target.getName()); + Messages.NO_BACKUPS.send(sender, target.getName()); return; } - Messages.HEADER.sendCentered(player, Messages.INVENTORY_BACKUPS); + Messages.HEADER.sendCentered(sender, Messages.INVENTORY_BACKUPS); List options = backups.stream().map(backup -> new OptionSelector.Option( Messages.BACKUP_INFO.withContext(backup.getFormattedDate()), "/ba restore " + target.getName() + " " + (backups.indexOf(backup) + 1) )).toList(); - OptionSelector.sendOptions(player, options, ClickEvent.Action.SUGGEST_COMMAND); + if (sender instanceof Player player) { + OptionSelector.sendOptions(player, options, ClickEvent.Action.SUGGEST_COMMAND); + } else { + for (int i = 0; i < options.size(); i++) { + // Console cannot click messages, so send command instead + OptionSelector.Option option = options.get(i); + Component message = Component.text("[" + (i + 1) + "] ", Messages.SECONDARY_COLOR) + .append(option.message().toComponent().style(Style.style(Messages.PRIMARY_COLOR))) + .append(Component.text(" (Run: \"" + option.command() + "\" to restore)", NamedTextColor.WHITE)); + + sender.sendMessage(message); + } + } }, Bukkit.getScheduler().getMainThreadExecutor(BattleArena.getInstance())); } @ArenaCommand(commands = "restore", description = "Restores a backup for a player.", permissionNode = "restore") - public void restore(Player player, Player target, int backupIndex) { + public void restore(CommandSender sender, Player target, int backupIndex) { backupIndex--; // Restore backup List backups = InventoryBackup.load(target.getUniqueId()); if (backupIndex < 0 || backupIndex >= backups.size()) { - Messages.BACKUP_NOT_FOUND.send(player); + Messages.BACKUP_NOT_FOUND.send(sender); return; } InventoryBackup backup = backups.get(backupIndex); if (backup == null) { - Messages.BACKUP_NOT_FOUND.send(player); + Messages.BACKUP_NOT_FOUND.send(sender); return; } backup.restore(target); - Messages.BACKUP_RESTORED.send(player, target.getName()); + Messages.BACKUP_RESTORED.send(sender, target.getName()); } @ArenaCommand(commands = "backup", description = "Creates a manual backup of a player's inventory.", permissionNode = "backup") - public void backup(Player player, Player target) { + public void backup(CommandSender sender, Player target) { InventoryBackup.save(new InventoryBackup(target.getUniqueId(), target.getInventory().getContents())); - Messages.BACKUP_CREATED.send(player, target.getName()); + Messages.BACKUP_CREATED.send(sender, target.getName()); } @ArenaCommand(commands = "modules", description = "Lists all modules.", permissionNode = "modules") - public void modules(Player player) { - Messages.HEADER.sendCentered(player, Messages.MODULES); + public void modules(CommandSender sender) { + Messages.HEADER.sendCentered(sender, Messages.MODULES); // All enabled modules BattleArena.getInstance().getModules() .stream() .sorted(Comparator.comparing(module -> module.module().name())) - .forEach(module -> Messages.MODULE.send(player, Messages.wrap(module.module().name()), Messages.ENABLED)); + .forEach(module -> Messages.MODULE.send(sender, Messages.wrap(module.module().name()), Messages.ENABLED)); // All failed modules BattleArena.getInstance().getFailedModules() @@ -110,28 +124,29 @@ public void modules(Player player) { } component = component.hoverEvent(Component.join(JoinConfiguration.newlines(), hoverLines)); - player.sendMessage(component); + sender.sendMessage(component); }); } @ArenaCommand(commands = "start", description = "Starts an event manually.", permissionNode = "start") - public void event(Player player, Arena arena) { + public void event(CommandSender sender, Arena arena) { if (arena.getType() != CompetitionType.EVENT) { - Messages.NOT_EVENT.send(player); + Messages.NOT_EVENT.send(sender); return; } arena.getPlugin().getEventScheduler().startEvent(arena, new EventOptions( EventType.MANUAL, Duration.ZERO, + Duration.ZERO, Messages.MANUAL_EVENT_MESSAGE.toComponent(arena.getName(), arena.getName().toLowerCase(Locale.ROOT)) )); } @ArenaCommand(commands = "stop", description = "Stops an event manually.", permissionNode = "stop") - public void stop(Player player, Arena arena) { + public void stop(CommandSender sender, Arena arena) { if (arena.getType() != CompetitionType.EVENT) { - Messages.NOT_EVENT.send(player); + Messages.NOT_EVENT.send(sender); return; } @@ -139,47 +154,48 @@ public void stop(Player player, Arena arena) { } @ArenaCommand(commands = "stopall", description = "Stops all events manually.", permissionNode = "stopall") - public void stopAll(Player player) { + public void stopAll(CommandSender sender) { for (Arena scheduledEvent : BattleArena.getInstance().getEventScheduler().getScheduledEvents()) { BattleArena.getInstance().getEventScheduler().stopEvent(scheduledEvent); } } @ArenaCommand(commands = "schedule", description = "Schedules an event to start at the specified time.", permissionNode = "schedule") - public void schedule(Player player, Arena arena, Duration interval) { + public void schedule(CommandSender sender, Arena arena, Duration interval) { if (arena.getType() != CompetitionType.EVENT) { - Messages.NOT_EVENT.send(player); + Messages.NOT_EVENT.send(sender); return; } arena.getPlugin().getEventScheduler().scheduleEvent(arena, new EventOptions( EventType.SCHEDULED, interval, + Duration.ZERO, Messages.MANUAL_EVENT_MESSAGE.toComponent(arena.getName(), arena.getName().toLowerCase(Locale.ROOT)) - )); + ), false); } @ArenaCommand(commands = "debug", description = "Toggles debug mode.", permissionNode = "debug") - public void debug(Player player) { + public void debug(CommandSender sender) { BattleArena.getInstance().setDebugMode(!BattleArena.getInstance().isDebugMode()); - Messages.DEBUG_MODE_SET_TO.send(player, Boolean.toString(BattleArena.getInstance().isDebugMode())); + Messages.DEBUG_MODE_SET_TO.send(sender, Boolean.toString(BattleArena.getInstance().isDebugMode())); } @ArenaCommand(commands = "reload", description = "Reloads the plugin.", permissionNode = "reload") - public void reload(Player player) { - Messages.STARTING_RELOAD.send(player); + public void reload(CommandSender sender) { + Messages.STARTING_RELOAD.send(sender); long start = System.currentTimeMillis(); try { BattleArena.getInstance().reload(); } catch (Exception e) { - Messages.RELOAD_FAILED.send(player); + Messages.RELOAD_FAILED.send(sender); BattleArena.getInstance().error("Failed to reload plugin", e); return; } long end = System.currentTimeMillis(); - Messages.RELOAD_COMPLETE.send(player, Util.toUnitString(end - start, TimeUnit.MILLISECONDS)); + Messages.RELOAD_COMPLETE.send(sender, Util.toUnitString(end - start, TimeUnit.MILLISECONDS)); } public void sendHeader(CommandSender sender) { diff --git a/plugin/src/main/java/org/battleplugins/arena/competition/event/EventOptions.java b/plugin/src/main/java/org/battleplugins/arena/competition/event/EventOptions.java index 513728c0..a426e280 100644 --- a/plugin/src/main/java/org/battleplugins/arena/competition/event/EventOptions.java +++ b/plugin/src/main/java/org/battleplugins/arena/competition/event/EventOptions.java @@ -13,13 +13,15 @@ public class EventOptions { private EventType type; @ArenaOption(name = "interval", required = true, description = "The interval of the event.") private Duration interval; + @ArenaOption(name = "delay", description = "The delay until the event starts.") + private Duration delay = Duration.ZERO; @ArenaOption(name = "message", required = true, description = "The message of the event.") private Component message; public EventOptions() { } - public EventOptions(EventType type, Duration interval, Component message) { + public EventOptions(EventType type, Duration interval, Duration delay, Component message) { this.type = type; this.interval = interval; this.message = message; @@ -33,6 +35,10 @@ public Duration getInterval() { return this.interval; } + public Duration getDelay() { + return this.delay; + } + public Component getMessage() { return this.message; } diff --git a/plugin/src/main/java/org/battleplugins/arena/competition/event/EventScheduler.java b/plugin/src/main/java/org/battleplugins/arena/competition/event/EventScheduler.java index 87b102bb..b813c716 100644 --- a/plugin/src/main/java/org/battleplugins/arena/competition/event/EventScheduler.java +++ b/plugin/src/main/java/org/battleplugins/arena/competition/event/EventScheduler.java @@ -30,7 +30,7 @@ public class EventScheduler { * @param arena the arena to schedule the event in * @param options the options for the event */ - public void scheduleEvent(Arena arena, EventOptions options) { + public void scheduleEvent(Arena arena, EventOptions options, boolean initial) { ScheduledEvent scheduledEvent = this.scheduledEvents.get(arena); if (scheduledEvent != null) { scheduledEvent.task().cancel(); @@ -38,9 +38,14 @@ public void scheduleEvent(Arena arena, EventOptions options) { arena.getPlugin().info("An event is already scheduled in arena {}, cancelling the previous event.", arena.getName()); } + long timeTilStart = options.getInterval().toMillis() / 50; + if (initial) { + timeTilStart = options.getDelay().toMillis() / 50; + } + BukkitTask bukkitTask = Bukkit.getScheduler().runTaskLater(arena.getPlugin(), () -> { this.startEvent(arena, options); - }, options.getInterval().toMillis() / 50); + }, timeTilStart); this.scheduledEvents.put(arena, new ScheduledEvent(options, bukkitTask)); } @@ -126,7 +131,7 @@ public void eventEnded(Arena arena, Competition competition) { return; } - this.scheduleEvent(arena, scheduledEvent.options()); + this.scheduleEvent(arena, scheduledEvent.options(), false); arena.getPlugin().info("Event in arena {} has ended. Rescheduling event at interval.", arena.getName()); } diff --git a/plugin/src/main/resources/config.yml b/plugin/src/main/resources/config.yml index 77e2c68e..627846dd 100644 --- a/plugin/src/main/resources/config.yml +++ b/plugin/src/main/resources/config.yml @@ -32,4 +32,5 @@ events: FFA: - type: scheduled # Type of event interval: 30m # Interval until start + delay: 5m # Delay until start message: "[BattleArena] A Free for All event is starting! Run /ffa join to join!"