Files
XeroAntiCheat/src/main/java/com/xeroth/xeroanticheat/manager/ViolationManager.java
Axel 8190b39160 XeroAntiCheat v1.0.9 bug fixes
- BUG-1: Added alerts.cooldown_ms (default 5s) to throttle warn() and
  sendAlert() in PunishmentManager, preventing chat spam during high-frequency
  flags. Verbose output in ViolationManager also respects the cooldown.
  Kick/ban punishments always fire immediately regardless of cooldown.
- BUG-2: Fixed FlyCheck false positive with Jump Boost I - condition was
  incorrectly flagging players with Jump Boost level 1. Now exempts all
  jump boost levels from the sustained-flight flag.
- BUG-3: Optimized SpiderCheck by caching player.getLocation() in a single
  variable instead of calling it 3 times.
2026-03-15 03:51:27 -03:00

166 lines
5.0 KiB
Java

package com.xeroth.xeroanticheat.manager;
import com.xeroth.xeroanticheat.XeroAntiCheat;
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.entity.Player;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
/**
* Manages violation levels for all players and checks.
* Thread-safe implementation with temporal decay support.
*/
public class ViolationManager {
private final XeroAntiCheat plugin;
private final Map<UUID, PlayerData> playerDataCache = new ConcurrentHashMap<>();
private final MiniMessage miniMessage = MiniMessage.miniMessage();
private volatile double decayRate;
public ViolationManager(XeroAntiCheat plugin) {
this.plugin = plugin;
this.decayRate = plugin.getConfigManager().getDouble("violation.decay_rate", 0.5);
}
public void setDecayRate(double rate) {
this.decayRate = rate;
}
/**
* Get or create player data for a player
*/
public PlayerData getPlayerData(Player player) {
return playerDataCache.computeIfAbsent(player.getUniqueId(), k -> new PlayerData(player));
}
/**
* Get player data by UUID
*/
public PlayerData getPlayerData(UUID uuid) {
return playerDataCache.get(uuid);
}
/**
* Add violation to a player for a specific check
*/
public void addViolation(Player player, String checkName, double weight) {
PlayerData data = getPlayerData(player);
if (data == null) return;
data.incrementViolation(checkName, weight);
data.setLastFlaggedCheck(checkName);
data.setLastFlagTime(System.currentTimeMillis());
double newVl = data.getViolationLevel(checkName);
if (plugin.isVerboseTarget(player.getUniqueId())) {
long cooldownMs = plugin.getConfigManager().getInt("alerts.cooldown_ms", 5000);
long now = System.currentTimeMillis();
if ((now - data.getLastWarnTime(checkName)) >= cooldownMs) {
Component verbose = miniMessage.deserialize(
"<gray>[<white>VERBOSE<gray>] <yellow>" + player.getName()
+ " <gray>» <white>" + checkName
+ " <gray>+<green>" + String.format("%.1f", weight)
+ " <gray>(VL: <white>" + String.format("%.1f", newVl) + "<gray>)"
);
for (Player staff : Bukkit.getOnlinePlayers()) {
if (staff.hasPermission("xac.command.verbose") || staff.hasPermission("xac.admin")) {
staff.sendMessage(verbose);
}
}
data.setLastWarnTime(checkName, now);
}
}
if (plugin.getConfigManager().isDebug()) {
plugin.getLogger().info(player.getName() + " violated " + checkName + " (VL: " + newVl + ")");
}
}
/**
* Get violation level for a player and check
*/
public double getViolationLevel(Player player, String checkName) {
PlayerData data = getPlayerData(player);
if (data == null) return 0.0;
return data.getViolationLevel(checkName);
}
/**
* Decay all violation levels for all players
*/
public void decayAll() {
for (PlayerData data : playerDataCache.values()) {
for (String checkName : data.getViolationLevels().keySet()) {
data.decayViolation(checkName, decayRate);
}
}
}
/**
* Clear all violation levels for a player
*/
public void clearPlayer(UUID uuid) {
playerDataCache.remove(uuid);
}
/**
* Clear all violation levels for all players
*/
public void clearAll() {
playerDataCache.clear();
}
/**
* Remove player data on quit
*/
public void removePlayer(UUID uuid) {
playerDataCache.remove(uuid);
}
/**
* Save all pending data (for shutdown)
*/
public void saveAll() {
// Currently just clearing, but could be extended to persist to disk
if (plugin.getConfigManager().isDebug()) {
plugin.getLogger().info("Saved " + playerDataCache.size() + " player data records");
}
}
/**
* Check if a player has any violations above threshold
*/
public boolean hasViolations(Player player, double threshold) {
PlayerData data = getPlayerData(player);
if (data == null) return false;
for (double vl : data.getViolationLevels().values()) {
if (vl >= threshold) return true;
}
return false;
}
/**
* Get total violations for a player
*/
public double getTotalViolations(Player player) {
PlayerData data = getPlayerData(player);
if (data == null) return 0.0;
double total = 0.0;
for (double vl : data.getViolationLevels().values()) {
total += vl;
}
return total;
}
}