package de.serverpulse.spigot.alerts; import de.serverpulse.spigot.SpigotPlugin; import de.serverpulse.models.AlertSeverity; import de.serverpulse.spigot.utils.MsgUtil; import org.bukkit.Bukkit; import org.bukkit.World; import org.bukkit.entity.Entity; import org.bukkit.entity.Item; import org.bukkit.entity.Monster; import java.util.HashMap; import java.util.Map; /** * Verwaltet alle Warnungen, Cooldowns und Notfallmaßnahmen. * Verhindert Alert-Spam durch einen Cooldown-Mechanismus. */ public class AlertManager { private final SpigotPlugin plugin; private final long pluginStartTimeMs; private final long startupGracePeriodMs; // Cooldown: alertType -> letzter Trigger-Zeitpunkt (ms) private final Map alertCooldowns = new HashMap<>(); // Standard-Cooldown: 5 Minuten private static final long DEFAULT_COOLDOWN_MS = 5 * 60 * 1000; // Zähler für kritische Ereignisse (für Auto-Diagnose) private int criticalEventCount = 0; // Item-Clear läuft gerade? private boolean itemClearRunning = false; public AlertManager(SpigotPlugin plugin) { this.plugin = plugin; this.pluginStartTimeMs = System.currentTimeMillis(); this.startupGracePeriodMs = plugin.getSpigotConfig().getStartupCheckDelayMinutes() * 60_000L; } // ────────────────────────────────────────── // ALERT AUSLÖSEN // ────────────────────────────────────────── /** * Löst eine Warnung aus, wenn kein Cooldown aktiv ist. */ public void triggerAlert(String alertType, AlertSeverity severity, String worldName, String message, Double currentValue, Double threshold) { if (isStartupGracePeriodActive()) { plugin.debug("Startup-Delay aktiv: Alert unterdrückt (" + alertType + ")"); return; } // Cooldown prüfen String cooldownKey = alertType + (worldName != null ? "_" + worldName : ""); long now = System.currentTimeMillis(); if (alertCooldowns.containsKey(cooldownKey)) { long lastAlert = alertCooldowns.get(cooldownKey); if (now - lastAlert < DEFAULT_COOLDOWN_MS) { plugin.debug("Alert-Cooldown aktiv für: " + cooldownKey); return; } } alertCooldowns.put(cooldownKey, now); // Alert loggen plugin.getLogger().warning("[" + severity.getName() + "] " + message); // In DB speichern if (plugin.getDatabaseManager().isConnected()) { plugin.getDatabaseManager().saveAlert( alertType, severity.getName(), worldName, message, currentValue, threshold ); } // Discord-Benachrichtigung senden if (plugin.getSpigotConfig().isDiscordEnabled()) { plugin.getDiscordWebhook().sendAlert(alertType, severity, worldName, message, currentValue, threshold); } // Alert an BungeeCord weitergeben if (plugin.getMessenger() != null) { plugin.getMessenger().sendAlert(alertType, severity.getName(), message); } // Kritische Events zählen für Auto-Diagnose if (severity == AlertSeverity.CRITICAL) { criticalEventCount++; if (plugin.getSpigotConfig().isAutoDiagnosisEnabled()) { if (criticalEventCount >= plugin.getSpigotConfig().getAutoDiagnosisTrigger()) { triggerDiagnosisReport(); criticalEventCount = 0; } } } } // ────────────────────────────────────────── // NOTFALLMASSNAHMEN // ────────────────────────────────────────── /** * Löst einen Item-Clear mit Countdown aus */ public void triggerItemClear(String worldName) { if (itemClearRunning) return; itemClearRunning = true; int countdown = plugin.getSpigotConfig().getItemClearCountdown(); String broadcastMsg = plugin.getSpigotConfig().getItemClearMessage(); // Countdown-Broadcasts if (plugin.getSpigotConfig().isItemClearBroadcast()) { for (int i = countdown; i > 0; i -= (i > 10 ? 10 : (i > 5 ? 5 : 1))) { final int secondsLeft = i; Bukkit.getScheduler().runTaskLater(plugin, () -> { String msg = broadcastMsg.replace("{seconds}", String.valueOf(secondsLeft)); Bukkit.broadcastMessage(MsgUtil.colorize(msg)); }, (countdown - i) * 20L); } } // Clear ausführen nach Countdown Bukkit.getScheduler().runTaskLater(plugin, () -> { World world = Bukkit.getWorld(worldName); if (world != null) { int cleared = 0; for (Entity entity : world.getEntities()) { if (entity instanceof Item) { entity.remove(); cleared++; } } plugin.getLogger().info("Item-Clear ausgeführt in " + worldName + ": " + cleared + " Items entfernt."); if (plugin.getSpigotConfig().isItemClearBroadcast()) { Bukkit.broadcastMessage(MsgUtil.colorize( "&a[ServerPulse] Item-Clear abgeschlossen! " + cleared + " Items wurden entfernt.")); } triggerAlert("ITEM_CLEAR_EXECUTED", AlertSeverity.INFO, worldName, "Automatischer Item-Clear: " + cleared + " Items in " + worldName + " entfernt.", (double) cleared, null); } itemClearRunning = false; }, countdown * 20L); } /** * Führt einen Mob-Clear in einer Welt durch */ public void triggerMobClear(String worldName) { boolean hostileOnly = plugin.getSpigotConfig().isMobClearHostileOnly(); World world = Bukkit.getWorld(worldName); if (world == null) return; Bukkit.getScheduler().runTask(plugin, () -> { int cleared = 0; for (Entity entity : world.getEntities()) { if (entity instanceof Monster) { entity.remove(); cleared++; } } plugin.getLogger().info("Mob-Clear ausgeführt in " + worldName + ": " + cleared + " Mobs entfernt."); triggerAlert("MOB_CLEAR_EXECUTED", AlertSeverity.INFO, worldName, "Automatischer Mob-Clear: " + cleared + " Mobs in " + worldName + " entfernt.", (double) cleared, null); }); } /** * Erstellt automatisch einen Diagnose-Report */ private void triggerDiagnosisReport() { plugin.getLogger().warning("[ServerPulse] Erstelle automatischen Diagnose-Report..."); StringBuilder report = new StringBuilder(); report.append("=== AUTOMATISCHER DIAGNOSE-REPORT ===\n"); report.append("Zeitpunkt: ").append(MsgUtil.timestamp()).append("\n\n"); // Performance if (plugin.getPerformanceMonitor().getLastSnapshot() != null) { var snap = plugin.getPerformanceMonitor().getLastSnapshot(); report.append("Performance:\n"); report.append(" TPS: ").append(String.format("%.2f", snap.getTps())).append("\n"); report.append(" MSPT: ").append(String.format("%.2f", snap.getMspt())).append("ms\n"); report.append(" RAM: ").append(snap.getRamUsedMb()).append("MB / ").append(snap.getRamMaxMb()).append("MB\n"); report.append(" Spieler: ").append(snap.getOnlinePlayers()).append("\n"); report.append(" Chunks: ").append(snap.getLoadedChunks()).append("\n\n"); } // Entities pro Welt report.append("Entities pro Welt:\n"); for (var entry : plugin.getEntityMonitor().getAllLastSnapshots().entrySet()) { var snap = entry.getValue(); report.append(" ").append(entry.getKey()).append(": ") .append("Gesamt=").append(snap.getTotal()) .append(", Monster=").append(snap.getMonsters()) .append(", Items=").append(snap.getItems()) .append("\n"); } String reportContent = report.toString(); // In DB speichern if (plugin.getDatabaseManager().isConnected()) { plugin.getDatabaseManager().saveReport("AUTO_DIAGNOSIS", "ServerPulse", reportContent); } plugin.getLogger().warning(reportContent); } /** * Setzt alle Alert-Cooldowns zurück (z.B. nach reload) */ public void clearCooldowns() { alertCooldowns.clear(); criticalEventCount = 0; } public int getCriticalEventCount() { return criticalEventCount; } private boolean isStartupGracePeriodActive() { return System.currentTimeMillis() - pluginStartTimeMs < startupGracePeriodMs; } }