- 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.
166 lines
5.0 KiB
Java
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;
|
|
}
|
|
}
|