-
Notifications
You must be signed in to change notification settings - Fork 1
Release diff: master → previous_release #37
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
This file was deleted.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,133 @@ | ||
| package md.thomas.asyncanticheat.bukkit; | ||
|
|
||
| import md.thomas.hopper.Dependency; | ||
| import md.thomas.hopper.FailurePolicy; | ||
| import md.thomas.hopper.LogLevel; | ||
| import md.thomas.hopper.bukkit.BukkitHopper; | ||
| import md.thomas.hopper.version.UpdatePolicy; | ||
| import org.bukkit.Bukkit; | ||
| import org.bukkit.plugin.Plugin; | ||
| import org.jetbrains.annotations.NotNull; | ||
|
|
||
| import java.io.File; | ||
| import java.util.logging.Logger; | ||
| import java.util.regex.Pattern; | ||
|
|
||
| /** | ||
| * Handles automatic downloading of PacketEvents using Hopper. | ||
| * <p> | ||
| * Downloaded plugins are automatically loaded at runtime without requiring a server restart. | ||
| */ | ||
| public final class AacHopper { | ||
|
|
||
| private static boolean downloadComplete = false; | ||
| private static boolean requiresRestart = false; | ||
| private static boolean enabled = true; | ||
|
|
||
| // Matches: packetevents.jar, packetevents-spigot-2.7.0.jar, PacketEvents-2.7.0.jar | ||
| private static final Pattern PACKETEVENTS_PATTERN = Pattern.compile( | ||
| "(?i)^packetevents([-_][\\w.-]*)?\\.jar$" | ||
| ); | ||
|
|
||
| private AacHopper() {} | ||
|
|
||
| /** | ||
| * Registers PacketEvents dependency with Hopper. | ||
| * Should be called in the plugin constructor. | ||
| * | ||
| * @param plugin the plugin instance | ||
| */ | ||
| public static void register(@NotNull Plugin plugin) { | ||
| Logger logger = plugin.getLogger(); | ||
|
|
||
| // Check if auto-download is disabled via system property (config not loaded in constructor) | ||
| if (Boolean.getBoolean("asyncanticheat.skipDependencyDownload")) { | ||
| enabled = false; | ||
| logger.info("Auto-download of dependencies is disabled"); | ||
| return; | ||
| } | ||
|
|
||
| BukkitHopper.register(plugin, deps -> { | ||
| // Check if PacketEvents is already installed | ||
| boolean hasPacketEvents = pluginJarExists(PACKETEVENTS_PATTERN); | ||
|
|
||
| if (!hasPacketEvents) { | ||
| // Primary source: Modrinth (auto-detects platform) | ||
| deps.require(Dependency.modrinth("packetevents") | ||
| .name("PacketEvents") | ||
| .minVersion("2.7.0") | ||
| .updatePolicy(UpdatePolicy.MINOR) | ||
| .onFailure(FailurePolicy.FAIL) | ||
| .build()); | ||
|
|
||
| // Fallback source: GitHub releases | ||
| deps.require(Dependency.github("retrooper/packetevents") | ||
| .name("PacketEvents") | ||
| .minVersion("2.7.0") | ||
| .assetPattern("*-spigot-*.jar") | ||
| .updatePolicy(UpdatePolicy.MINOR) | ||
| .onFailure(FailurePolicy.WARN_SKIP) | ||
| .build()); | ||
|
cursor[bot] marked this conversation as resolved.
|
||
| } | ||
| }); | ||
| } | ||
|
|
||
| /** | ||
| * Downloads all registered dependencies and automatically loads them. | ||
| * Should be called in the plugin's onLoad() method. | ||
| * | ||
| * @param plugin the plugin instance | ||
| * @return true if all dependencies are satisfied and loaded | ||
| */ | ||
| public static boolean download(@NotNull Plugin plugin) { | ||
| if (!enabled) { | ||
| downloadComplete = true; | ||
| return true; | ||
| } | ||
|
|
||
| Logger logger = plugin.getLogger(); | ||
| BukkitHopper.DownloadAndLoadResult result = BukkitHopper.downloadAndLoad(plugin, LogLevel.QUIET); | ||
|
|
||
| downloadComplete = true; | ||
| requiresRestart = !result.noRestartRequired(); | ||
|
|
||
| if (requiresRestart) { | ||
| logger.warning("Some dependencies require a server restart to load:"); | ||
| for (var failed : result.loadResult().failed()) { | ||
| logger.warning(" - " + failed.path().getFileName() + ": " + failed.error()); | ||
| } | ||
| } | ||
|
|
||
| return !requiresRestart; | ||
| } | ||
|
|
||
| /** | ||
| * @return true if a restart is required to load newly downloaded dependencies | ||
| */ | ||
| public static boolean requiresRestart() { | ||
| return requiresRestart; | ||
| } | ||
|
|
||
| /** | ||
| * @return true if the download phase has completed | ||
| */ | ||
| public static boolean isDownloadComplete() { | ||
| return downloadComplete; | ||
| } | ||
|
|
||
| /** | ||
| * Checks if a plugin jar file exists in the plugins folder using a regex pattern. | ||
| */ | ||
| private static boolean pluginJarExists(Pattern pattern) { | ||
| File pluginsFolder = Bukkit.getPluginsFolder(); | ||
| if (pluginsFolder == null || !pluginsFolder.exists()) { | ||
| return false; | ||
| } | ||
|
|
||
| File[] files = pluginsFolder.listFiles((dir, name) -> | ||
| pattern.matcher(name).matches() | ||
| ); | ||
|
|
||
| return files != null && files.length > 0; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -6,57 +6,58 @@ | |
| import md.thomas.asyncanticheat.core.AcLogger; | ||
| import md.thomas.asyncanticheat.core.AsyncAnticheatService; | ||
| import org.bstats.bukkit.Metrics; | ||
| import org.bukkit.Bukkit; | ||
| import org.bukkit.command.Command; | ||
| import org.bukkit.command.CommandSender; | ||
| import org.bukkit.command.PluginCommand; | ||
| import org.bukkit.event.EventHandler; | ||
| import org.bukkit.event.Listener; | ||
| import org.bukkit.event.player.PlayerQuitEvent; | ||
| import org.bukkit.plugin.java.JavaPlugin; | ||
| import org.jetbrains.annotations.NotNull; | ||
|
|
||
| import java.util.List; | ||
|
|
||
| public final class AsyncAnticheatBukkitPlugin extends JavaPlugin { | ||
|
|
||
| private AsyncAnticheatService service; | ||
| private RecordingManager recordingManager; | ||
| private BukkitPlayerExemptionTracker exemptionTracker; | ||
| private SchedulerUtil.ScheduledTask stateTask; | ||
| private Command registeredCommand; | ||
| private boolean packetEventsInitialized = false; | ||
|
|
||
| public AsyncAnticheatBukkitPlugin() { | ||
| // Register dependencies with Hopper for auto-download | ||
| AacHopper.register(this); | ||
| } | ||
|
|
||
| @Override | ||
| public void onLoad() { | ||
| // PacketEvents is downloaded by AacBootstrap before this class loads (Paper) | ||
| // or must be manually installed (Spigot) | ||
| // Download PacketEvents if needed (loads at runtime, no restart required) | ||
| AacHopper.download(this); | ||
|
|
||
| // Initialize PacketEvents | ||
| try { | ||
| PacketEvents.setAPI(SpigotPacketEventsBuilder.build(this)); | ||
| PacketEvents.getAPI().load(); | ||
| packetEventsInitialized = true; | ||
| } catch (Throwable t) { | ||
| getLogger().severe("[AsyncAnticheat] Failed to load PacketEvents: " + t.getMessage()); | ||
| getLogger().severe("[AsyncAnticheat] PacketEvents is required. Install it from https://modrinth.com/plugin/packetevents"); | ||
| getLogger().severe("Failed to load PacketEvents: " + t.getMessage()); | ||
| getLogger().severe("PacketEvents is required. Install it from https://modrinth.com/plugin/packetevents"); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ignored download result causes misleading error messageThe return value of |
||
| } | ||
| } | ||
|
|
||
| @Override | ||
| public void onEnable() { | ||
| if (!packetEventsInitialized) { | ||
| getLogger().severe("[AsyncAnticheat] PacketEvents not available. Disabling plugin."); | ||
| getLogger().severe("PacketEvents not available. Disabling plugin."); | ||
| getServer().getPluginManager().disablePlugin(this); | ||
| return; | ||
| } | ||
|
|
||
| final AcLogger logger = new BukkitLogger(getLogger()); | ||
| service = new AsyncAnticheatService(getDataFolder(), logger); | ||
|
|
||
| // Initialize exemption tracker with config | ||
| // Tracks player states like creative mode, flying, dead, etc. based on NCP patterns | ||
| exemptionTracker = new BukkitPlayerExemptionTracker(service.getConfig().getExemptionConfig()); | ||
| getServer().getPluginManager().registerEvents(exemptionTracker, this); | ||
|
|
||
| // Initialize PacketEvents (load was called in onLoad) | ||
| if (packetEventsInitialized) { | ||
| try { | ||
|
|
@@ -66,14 +67,14 @@ public void onEnable() { | |
| PacketListenerPriority.LOW | ||
| ); | ||
| } catch (Throwable t) { | ||
| logger.error("[AsyncAnticheat] Failed to initialize PacketEvents (Bukkit).", t); | ||
| logger.error("Failed to initialize PacketEvents.", t); | ||
| packetEventsInitialized = false; | ||
| } | ||
| } | ||
|
|
||
| // 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 | ||
|
|
@@ -85,7 +86,6 @@ public void onQuit(PlayerQuitEvent event) { | |
| }, this); | ||
|
|
||
| // Register main /aac command with subcommands | ||
| // Use CommandMap directly for Paper plugin compatibility | ||
| registerCommand(); | ||
|
|
||
| // Initialize bStats metrics (plugin ID: 20187) | ||
|
|
@@ -131,11 +131,6 @@ public void onDisable() { | |
| recordingManager.stopAll(); | ||
| recordingManager = null; | ||
| } | ||
| // Unregister command to prevent leak on reload | ||
| if (registeredCommand != null) { | ||
| registeredCommand.unregister(Bukkit.getCommandMap()); | ||
| registeredCommand = null; | ||
| } | ||
| if (service != null) { | ||
| service.stop(); | ||
| service = null; | ||
|
|
@@ -155,32 +150,11 @@ public BukkitPlayerExemptionTracker getExemptionTracker() { | |
| private void registerCommand() { | ||
| final BukkitMainCommand mainCmd = new BukkitMainCommand(service, recordingManager); | ||
|
|
||
| // Try Spigot-style registration first (plugin.yml defines the command) | ||
| // This works on Spigot servers where getCommand() returns the registered command | ||
| // Register command from plugin.yml | ||
| PluginCommand pluginCmd = getCommand("aac"); | ||
| if (pluginCmd != null) { | ||
| pluginCmd.setExecutor(mainCmd); | ||
| pluginCmd.setTabCompleter(mainCmd); | ||
| return; | ||
| } | ||
|
|
||
| // Fall back to CommandMap registration for Paper plugins | ||
| // Paper plugins use paper-plugin.yml which doesn't support getCommand() | ||
| registeredCommand = new Command("aac", "AsyncAnticheat main command", "/aac [token|record|status]", List.of("asyncanticheat")) { | ||
| @Override | ||
| public boolean execute(@NotNull CommandSender sender, @NotNull String commandLabel, @NotNull String[] args) { | ||
| return mainCmd.onCommand(sender, this, commandLabel, args); | ||
| } | ||
|
|
||
| @Override | ||
| public @NotNull List<String> tabComplete(@NotNull CommandSender sender, @NotNull String alias, @NotNull String[] args) { | ||
| List<String> result = mainCmd.onTabComplete(sender, this, alias, args); | ||
| return result != null ? result : List.of(); | ||
| } | ||
| }; | ||
|
|
||
| Bukkit.getCommandMap().register("asyncanticheat", registeredCommand); | ||
| } | ||
| } | ||
|
|
||
|
|
||
This file was deleted.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1,3 @@ | ||
| pluginVersion=0.3.0 | ||
| pluginVersion=0.4.1 | ||
|
|
||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Static enabled flag never reset to true
The static
enabledfield is set tofalsewhen the system propertyasyncanticheat.skipDependencyDownloadis set, but there's no code path to reset it totrue. If an operator sets the property, then removes it and reloads the plugin without a full server restart,enabledremainsfalseanddownload()will skip dependency downloading, potentially leaving PacketEvents unavailable.