diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index dca880a..f388ab7 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -39,11 +39,16 @@ jobs: path: build/libs/async-anticheat-${{ steps.version.outputs.version }}.jar if-no-files-found: error + - name: Copy JAR with stable name + run: cp build/libs/async-anticheat-${{ steps.version.outputs.version }}.jar build/libs/async-anticheat.jar + - name: Upload JAR to Release if: github.event_name == 'release' uses: softprops/action-gh-release@v2 with: - files: build/libs/async-anticheat-${{ steps.version.outputs.version }}.jar + files: | + build/libs/async-anticheat-${{ steps.version.outputs.version }}.jar + build/libs/async-anticheat.jar env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/asyncanticheat-bukkit/src/main/java/md/thomas/asyncanticheat/bukkit/AsyncAnticheatBukkitPlugin.java b/asyncanticheat-bukkit/src/main/java/md/thomas/asyncanticheat/bukkit/AsyncAnticheatBukkitPlugin.java index 274a452..ebc9b46 100644 --- a/asyncanticheat-bukkit/src/main/java/md/thomas/asyncanticheat/bukkit/AsyncAnticheatBukkitPlugin.java +++ b/asyncanticheat-bukkit/src/main/java/md/thomas/asyncanticheat/bukkit/AsyncAnticheatBukkitPlugin.java @@ -15,7 +15,7 @@ public final class AsyncAnticheatBukkitPlugin extends JavaPlugin { private AsyncAnticheatService service; - private BukkitDevModeManager devMode; + private RecordingManager recordingManager; private BukkitPlayerExemptionTracker exemptionTracker; private SchedulerUtil.ScheduledTask stateTask; private boolean packetEventsInitialized = false; @@ -56,26 +56,25 @@ public void onEnable() { } } - devMode = new BukkitDevModeManager(this, service); - // Ensure dev sessions are stopped immediately on logout (no orphaned repeating tasks) + // Initialize recording manager for in-game cheat recording + recordingManager = new RecordingManager(this, service.getConfig(), service.getServerId()); + + // Ensure recordings are stopped immediately on logout getServer().getPluginManager().registerEvents(new Listener() { @EventHandler public void onQuit(PlayerQuitEvent event) { - if (devMode != null) { - devMode.stopSilent(event.getPlayer().getUniqueId()); + if (recordingManager != null) { + recordingManager.handlePlayerQuit(event.getPlayer().getUniqueId()); } } }, this); - final PluginCommand cmd = getCommand("aacdev"); - if (cmd != null) { - final BukkitDevModeCommand executor = new BukkitDevModeCommand(devMode, service); - cmd.setExecutor(executor); - cmd.setTabCompleter(executor); - } - final PluginCommand linkCmd = getCommand("aac"); - if (linkCmd != null) { - linkCmd.setExecutor(new BukkitLinkCommand(service)); + // Register main /aac command with subcommands + final PluginCommand aacCmd = getCommand("aac"); + if (aacCmd != null) { + final BukkitMainCommand mainCmd = new BukkitMainCommand(service, recordingManager); + aacCmd.setExecutor(mainCmd); + aacCmd.setTabCompleter(mainCmd); } service.start(); @@ -113,12 +112,10 @@ public void onDisable() { stateTask.cancel(); stateTask = null; } - // IMPORTANT: devMode.stopAll() enqueues DEV_MARKER stop events via service.tryEnqueue(...). - // service.stop() triggers a best-effort final flush/upload on a daemon thread. - // Therefore, stopAll MUST run before stop() so stop markers are included in the final upload. - if (devMode != null) { - devMode.stopAll("plugin_disable"); - devMode = null; + // Stop all active recordings and submit them + if (recordingManager != null) { + recordingManager.stopAll(); + recordingManager = null; } if (service != null) { service.stop(); diff --git a/asyncanticheat-bukkit/src/main/java/md/thomas/asyncanticheat/bukkit/BukkitDevModeCommand.java b/asyncanticheat-bukkit/src/main/java/md/thomas/asyncanticheat/bukkit/BukkitDevModeCommand.java deleted file mode 100644 index 07b24d9..0000000 --- a/asyncanticheat-bukkit/src/main/java/md/thomas/asyncanticheat/bukkit/BukkitDevModeCommand.java +++ /dev/null @@ -1,105 +0,0 @@ -package md.thomas.asyncanticheat.bukkit; - -import md.thomas.asyncanticheat.core.AsyncAnticheatService; -import org.bukkit.command.Command; -import org.bukkit.command.CommandExecutor; -import org.bukkit.command.CommandSender; -import org.bukkit.command.TabCompleter; -import org.bukkit.entity.Player; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.ArrayList; -import java.util.List; - -final class BukkitDevModeCommand implements CommandExecutor, TabCompleter { - - private final BukkitDevModeManager dev; - private final AsyncAnticheatService service; - - BukkitDevModeCommand(@NotNull BukkitDevModeManager dev, @NotNull AsyncAnticheatService service) { - this.dev = dev; - this.service = service; - } - - @Override - public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) { - if (!(sender instanceof Player player)) { - sender.sendMessage("[AsyncAnticheat] This command must be run by a player."); - return true; - } - if (!service.getConfig().isDevModeEnabled()) { - sender.sendMessage("[AsyncAnticheat] Dev mode is disabled. Enable it in config.yml: dev.enabled: true"); - return true; - } - if (!sender.hasPermission("asyncanticheat.dev")) { - sender.sendMessage("[AsyncAnticheat] Missing permission: asyncanticheat.dev"); - return true; - } - - if (args.length == 0) { - return help(sender); - } - - final String sub = args[0].toLowerCase(); - switch (sub) { - case "start" -> { - if (args.length < 3) return help(sender); - int duration = parseInt(args[1], service.getConfig().getDevDefaultDurationSeconds()); - String cheatLabel = args[2]; - int warmup = args.length >= 4 ? parseInt(args[3], service.getConfig().getDevDefaultWarmupSeconds()) : service.getConfig().getDevDefaultWarmupSeconds(); - int toggle = args.length >= 5 ? parseInt(args[4], service.getConfig().getDevDefaultToggleSeconds()) : service.getConfig().getDevDefaultToggleSeconds(); - dev.start(player, cheatLabel, duration, warmup, toggle); - return true; - } - case "stop" -> { - dev.stop(player, "manual"); - return true; - } - case "status" -> { - final BukkitDevModeManager.Session s = dev.getSession(player.getUniqueId()); - if (s == null) { - sender.sendMessage("[AsyncAnticheat Dev] No active session."); - } else { - sender.sendMessage("[AsyncAnticheat Dev] Active: label=" + s.label + " state=" + s.cheatState + - " elapsed=" + s.elapsedSeconds + "s/" + s.durationSeconds + "s toggle=" + s.toggleSeconds + "s warmup=" + s.warmupSeconds + "s"); - } - return true; - } - default -> { - return help(sender); - } - } - } - - private static int parseInt(@NotNull String s, int def) { - try { - return Integer.parseInt(s); - } catch (Exception ignored) { - return def; - } - } - - private static boolean help(@NotNull CommandSender sender) { - sender.sendMessage("AsyncAnticheat dev mode (labeled recording)"); - sender.sendMessage("/aacdev start