diff --git a/pom.xml b/pom.xml index ed15152..feae0eb 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ com.xeroth xeroanticheat - 1.0.8 + 1.0.9 jar XeroAntiCheat diff --git a/src/main/java/com/xeroth/xeroanticheat/checks/movement/FlyCheck.java b/src/main/java/com/xeroth/xeroanticheat/checks/movement/FlyCheck.java index 53e9b91..5c51c4e 100644 --- a/src/main/java/com/xeroth/xeroanticheat/checks/movement/FlyCheck.java +++ b/src/main/java/com/xeroth/xeroanticheat/checks/movement/FlyCheck.java @@ -67,8 +67,7 @@ public class FlyCheck extends Check { // If moving up or staying at same height while not supposed to if (velocity.getY() > 0.1 || Math.abs(velocity.getY()) < 0.01) { if (data.getAirTicks() > fallBuffer) { - // Additional check: see if player has jump boost - if (!data.hasJumpBoost() || data.getJumpBoostLevel() <= 1) { + if (!data.hasJumpBoost()) { flag(data, player); } } diff --git a/src/main/java/com/xeroth/xeroanticheat/checks/movement/SpiderCheck.java b/src/main/java/com/xeroth/xeroanticheat/checks/movement/SpiderCheck.java index 18224f0..fb9ac8a 100644 --- a/src/main/java/com/xeroth/xeroanticheat/checks/movement/SpiderCheck.java +++ b/src/main/java/com/xeroth/xeroanticheat/checks/movement/SpiderCheck.java @@ -48,9 +48,10 @@ public class SpiderCheck extends Check { // Get blocks around player using block coordinates (no Location mutation) org.bukkit.World world = player.getWorld(); - int blockX = player.getLocation().getBlockX(); - int blockY = player.getLocation().getBlockY(); - int blockZ = player.getLocation().getBlockZ(); + org.bukkit.Location loc = player.getLocation(); + int blockX = loc.getBlockX(); + int blockY = loc.getBlockY(); + int blockZ = loc.getBlockZ(); Material feetBlock = world.getBlockAt(blockX, blockY - 1, blockZ).getType(); Material bodyBlock = world.getBlockAt(blockX, blockY, blockZ).getType(); diff --git a/src/main/java/com/xeroth/xeroanticheat/data/PlayerData.java b/src/main/java/com/xeroth/xeroanticheat/data/PlayerData.java index b641594..0308931 100644 --- a/src/main/java/com/xeroth/xeroanticheat/data/PlayerData.java +++ b/src/main/java/com/xeroth/xeroanticheat/data/PlayerData.java @@ -111,6 +111,9 @@ public class PlayerData { // SpeedCheck tracking private int speedViolationTicks = 0; + // Alert cooldown tracking + private final Map lastWarnTime = new ConcurrentHashMap<>(); + public PlayerData(Player player) { this.uuid = player.getUniqueId(); this.name = player.getName(); @@ -618,6 +621,14 @@ public class PlayerData { this.speedViolationTicks = 0; } + public long getLastWarnTime(String checkName) { + return lastWarnTime.getOrDefault(checkName, 0L); + } + + public void setLastWarnTime(String checkName, long time) { + lastWarnTime.put(checkName, time); + } + public void clearPositionHistory() { positionHistory.clear(); rotationHistory.clear(); diff --git a/src/main/java/com/xeroth/xeroanticheat/manager/ConfigManager.java b/src/main/java/com/xeroth/xeroanticheat/manager/ConfigManager.java index 5f174eb..c020edf 100644 --- a/src/main/java/com/xeroth/xeroanticheat/manager/ConfigManager.java +++ b/src/main/java/com/xeroth/xeroanticheat/manager/ConfigManager.java @@ -171,6 +171,7 @@ public class ConfigManager { DEFAULTS.put("alerts.enabled", true); DEFAULTS.put("alerts.format", "[XAC] %player% failed %check% (VL: %vl%)"); DEFAULTS.put("alerts.staff_format", "[%time%] %message%"); + DEFAULTS.put("alerts.cooldown_ms", 5000); // Commands DEFAULTS.put("commands.reload_permission", "xac.admin"); diff --git a/src/main/java/com/xeroth/xeroanticheat/manager/PunishmentManager.java b/src/main/java/com/xeroth/xeroanticheat/manager/PunishmentManager.java index 37a17dc..3b12675 100644 --- a/src/main/java/com/xeroth/xeroanticheat/manager/PunishmentManager.java +++ b/src/main/java/com/xeroth/xeroanticheat/manager/PunishmentManager.java @@ -76,7 +76,17 @@ public class PunishmentManager { Check check = plugin.getCheckManager().getCheck(checkName); String category = check != null ? check.getCategory() : "misc"; - sendAlert(player, checkName, vl, category); + 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); @@ -84,7 +94,7 @@ public class PunishmentManager { punish(player, checkName, "TEMPBAN", tempbanVl); } else if (vl >= kickVl) { punish(player, checkName, "KICK", kickVl); - } else if (vl >= warnVl) { + } else if (vl >= warnVl && cooledDown) { warn(player, checkName); } } diff --git a/src/main/java/com/xeroth/xeroanticheat/manager/ViolationManager.java b/src/main/java/com/xeroth/xeroanticheat/manager/ViolationManager.java index cc5fd55..638b28a 100644 --- a/src/main/java/com/xeroth/xeroanticheat/manager/ViolationManager.java +++ b/src/main/java/com/xeroth/xeroanticheat/manager/ViolationManager.java @@ -62,16 +62,21 @@ public class ViolationManager { double newVl = data.getViolationLevel(checkName); if (plugin.isVerboseTarget(player.getUniqueId())) { - Component verbose = miniMessage.deserialize( - "[VERBOSE] " + player.getName() - + " » " + checkName - + " +" + String.format("%.1f", weight) - + " (VL: " + String.format("%.1f", newVl) + ")" - ); - for (Player staff : Bukkit.getOnlinePlayers()) { - if (staff.hasPermission("xac.command.verbose") || staff.hasPermission("xac.admin")) { - staff.sendMessage(verbose); + long cooldownMs = plugin.getConfigManager().getInt("alerts.cooldown_ms", 5000); + long now = System.currentTimeMillis(); + if ((now - data.getLastWarnTime(checkName)) >= cooldownMs) { + Component verbose = miniMessage.deserialize( + "[VERBOSE] " + player.getName() + + " » " + checkName + + " +" + String.format("%.1f", weight) + + " (VL: " + String.format("%.1f", newVl) + ")" + ); + for (Player staff : Bukkit.getOnlinePlayers()) { + if (staff.hasPermission("xac.command.verbose") || staff.hasPermission("xac.admin")) { + staff.sendMessage(verbose); + } } + data.setLastWarnTime(checkName, now); } } diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index 003bfea..bb14374 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -315,6 +315,11 @@ alerts: # Staff-only alert format staff_format: "[%time%] %message%" + + # Minimum milliseconds between alert/warn messages for the same player+check. + # Prevents chat spam when a player is flagging at high frequency. + # Default: 5000ms (5 seconds). Set to 0 to disable throttling. + cooldown_ms: 5000 # ========================================== # COMMANDS diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 2d173c3..c7507c2 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -1,5 +1,5 @@ name: XeroAntiCheat -version: 1.0.8 +version: 1.0.9 main: com.xeroth.xeroanticheat.XeroAntiCheat author: Xeroth description: Lightweight, accurate anti-cheat for Paper 1.21.x