package com.xeroth.xeroanticheat.manager; import com.xeroth.xeroanticheat.XeroAntiCheat; import com.xeroth.xeroanticheat.check.Check; import com.xeroth.xeroanticheat.data.PlayerData; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.minimessage.MiniMessage; import org.bukkit.Bukkit; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.io.PrintWriter; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Set; import java.util.UUID; import java.util.logging.Level; /** * Handles punishment execution and logging. */ public class PunishmentManager { private final XeroAntiCheat plugin; private final ViolationManager violationManager; private final MiniMessage miniMessage = MiniMessage.miniMessage(); private SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); private File logsFolder; private File punishmentsLog; public PunishmentManager(XeroAntiCheat plugin, ViolationManager violationManager) { this.plugin = plugin; this.violationManager = violationManager; initializeLogs(); } private void initializeLogs() { logsFolder = new File(plugin.getDataFolder(), "logs"); if (!logsFolder.exists()) { logsFolder.mkdirs(); } punishmentsLog = new File(logsFolder, "punishments.log"); if (!punishmentsLog.exists()) { try { punishmentsLog.createNewFile(); } catch (IOException e) { plugin.getLogger().log(Level.SEVERE, "Could not create punishments log", e); } } } /** * Evaluate violations and apply appropriate punishment */ public void evaluate(Player player, String checkName) { if (!plugin.getConfigManager().isEnabled()) return; PlayerData data = violationManager.getPlayerData(player); if (data == null) return; double vl = data.getViolationLevel(checkName); String checkPath = "checks." + checkName.toLowerCase() + "."; int warnVl = plugin.getConfigManager().getInt(checkPath + "warn_vl", 10); int kickVl = plugin.getConfigManager().getInt(checkPath + "kick_vl", 25); int tempbanVl = plugin.getConfigManager().getInt(checkPath + "tempban_vl", 50); int permbanVl = plugin.getConfigManager().getInt(checkPath + "permban_vl", 100); Check check = plugin.getCheckManager().getCheck(checkName); String category = check != null ? check.getCategory() : "misc"; long cooldownMs = plugin.getConfigManager().getInt("alerts.cooldown_ms", 5000); long now = System.currentTimeMillis(); long lastWarn = data.getLastWarnTime(checkName); boolean cooledDown = (now - lastWarn) >= cooldownMs; boolean isPunishment = vl >= kickVl; if (cooledDown || isPunishment) { sendAlert(player, checkName, vl, category); data.setLastWarnTime(checkName, now); } if (vl >= permbanVl) { punish(player, checkName, "PERMBAN", permbanVl); } else if (vl >= tempbanVl) { punish(player, checkName, "TEMPBAN", tempbanVl); } else if (vl >= kickVl) { punish(player, checkName, "KICK", kickVl); } else if (vl >= warnVl && cooledDown) { warn(player, checkName); } } /** * Send warning to player */ private void warn(Player player, String checkName) { Component message = miniMessage.deserialize( "Warning: Suspicious behavior detected (" + checkName + ")" ); player.sendMessage(message); } /** * Apply punishment based on type */ private void punish(Player player, String checkName, String type, double vl) { String reason = plugin.getConfigManager().getString("punishments.default_reason", "Suspicious activity"); String playerName = player.getName(); switch (type) { case "KICK" -> { String kickCmd = plugin.getConfigManager().getString("punishments.kick_command", "kick %player% &c[XAC] Illegal activity detected"); kickCmd = kickCmd.replace("%player%", playerName).replace("%reason%", reason); executeCommand(kickCmd); logPunishment(type, player, checkName, vl); plugin.getMetricsManager().recordPunishment(); } case "TEMPBAN" -> { String tempbanCmd = plugin.getConfigManager().getString("punishments.tempban_command", "tempban %player% 30d %reason%"); tempbanCmd = tempbanCmd.replace("%player%", playerName).replace("%reason%", reason); executeCommand(tempbanCmd); logPunishment(type, player, checkName, vl); plugin.getMetricsManager().recordPunishment(); } case "PERMBAN" -> { String permbanCmd = plugin.getConfigManager().getString("punishments.permban_command", "ban %player% %reason%"); permbanCmd = permbanCmd.replace("%player%", playerName).replace("%reason%", reason); executeCommand(permbanCmd); logPunishment(type, player, checkName, vl); plugin.getMetricsManager().recordPunishment(); } } } /** * Execute a console command */ private void executeCommand(String command) { final String cmd = command.startsWith("/") ? command.substring(1) : command; Bukkit.getScheduler().runTask(plugin, () -> { Bukkit.dispatchCommand(Bukkit.getConsoleSender(), cmd); }); } /** * Send alert to staff members */ public void sendAlert(Player player, String checkName, double vl, String category) { if (!plugin.getConfigManager().isAlertsEnabled()) return; String format = plugin.getConfigManager().getAlertsFormat(); format = format.replace("%player%", player.getName()) .replace("%check%", checkName) .replace("%vl%", String.valueOf((int) vl)) .replace("%category%", category); Component message = miniMessage.deserialize(format); String categoryPerm = "xac.alerts." + category; for (Player staff : Bukkit.getOnlinePlayers()) { boolean hasPermission = staff.hasPermission("xac.alerts") || staff.hasPermission("xac.admin") || staff.hasPermission(categoryPerm); if (!hasPermission) continue; if (!plugin.isAlertsEnabled(staff.getUniqueId())) continue; staff.sendMessage(message); } plugin.getLogger().info( player.getName() + " failed " + checkName + " [" + category + "] (VL: " + (int) vl + ")" ); } /** * Log punishment to file and database */ private void logPunishment(String type, Player player, String checkName, double vl) { final String timestamp = dateFormat.format(new Date()); final String playerName = player.getName(); final String playerUuid = player.getUniqueId().toString(); final String logLine = String.format("[%s] %s | %s | %s | %s | %.1f", timestamp, type, playerName, playerUuid, checkName, vl); Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> { try (FileWriter fw = new FileWriter(punishmentsLog, true); PrintWriter pw = new PrintWriter(fw)) { pw.println(logLine); } catch (IOException e) { plugin.getLogger().log(Level.WARNING, "Could not write to punishments log", e); } DatabaseManager db = plugin.getDatabaseManager(); boolean dbEnabled = plugin.getConfigManager().getBoolean("database.enabled", true); if (db != null && db.isAvailable() && dbEnabled) { db.insertPunishment(timestamp, type, playerUuid, playerName, checkName, vl); } }); } /** * Manually punish a player for a check */ public void manualPunish(Player player, String checkName) { PlayerData data = violationManager.getPlayerData(player); if (data == null) return; // Set VL to kick threshold and evaluate data.setViolationLevel(checkName, plugin.getConfigManager().getInt("checks." + checkName.toLowerCase() + ".kick_vl", 25)); evaluate(player, checkName); } }