293 lines
10 KiB
Java
293 lines
10 KiB
Java
package com.xeroth.xeroanticheat;
|
|
|
|
import com.xeroth.xeroanticheat.checks.combat.*;
|
|
import com.xeroth.xeroanticheat.checks.misc.*;
|
|
import com.xeroth.xeroanticheat.checks.movement.*;
|
|
import com.xeroth.xeroanticheat.check.Check;
|
|
import com.xeroth.xeroanticheat.command.XACCommand;
|
|
import com.xeroth.xeroanticheat.listener.CombatListener;
|
|
import com.xeroth.xeroanticheat.listener.MiscListener;
|
|
import com.xeroth.xeroanticheat.listener.MovementListener;
|
|
import com.xeroth.xeroanticheat.manager.CheckManager;
|
|
import com.xeroth.xeroanticheat.manager.ConfigManager;
|
|
import com.xeroth.xeroanticheat.manager.DatabaseManager;
|
|
import com.xeroth.xeroanticheat.manager.PunishmentManager;
|
|
import com.xeroth.xeroanticheat.manager.ViolationManager;
|
|
import com.xeroth.xeroanticheat.protocol.PacketListener;
|
|
import com.xeroth.xeroanticheat.data.PlayerData;
|
|
import org.bukkit.Bukkit;
|
|
import org.bukkit.entity.Player;
|
|
import org.bukkit.potion.PotionEffectType;
|
|
import org.bukkit.plugin.java.JavaPlugin;
|
|
|
|
import java.util.HashMap;
|
|
import java.util.Map;
|
|
import java.util.Set;
|
|
import java.util.UUID;
|
|
import java.util.concurrent.ConcurrentHashMap;
|
|
|
|
/**
|
|
* XeroAntiCheat - Lightweight, accurate anti-cheat for Paper 1.21.x
|
|
*
|
|
* This plugin provides comprehensive cheat detection including movement,
|
|
* combat, and miscellaneous checks with minimal false positives.
|
|
*/
|
|
public final class XeroAntiCheat extends JavaPlugin {
|
|
|
|
private static XeroAntiCheat instance;
|
|
|
|
private ConfigManager configManager;
|
|
private ViolationManager violationManager;
|
|
private PunishmentManager punishmentManager;
|
|
private CheckManager checkManager;
|
|
private PacketListener packetListener;
|
|
private DatabaseManager databaseManager;
|
|
|
|
private boolean protocolLibLoaded = false;
|
|
private org.bukkit.scheduler.BukkitTask decayTask;
|
|
|
|
// Staff alert toggles
|
|
private final Map<UUID, Boolean> alertToggles = new ConcurrentHashMap<>();
|
|
|
|
// Verbose targets (per-player debug output)
|
|
private final Set<UUID> verboseTargets = ConcurrentHashMap.newKeySet();
|
|
|
|
@Override
|
|
public void onEnable() {
|
|
instance = this;
|
|
|
|
// Check for ProtocolLib
|
|
checkProtocolLib();
|
|
|
|
// Initialize managers
|
|
initializeManagers();
|
|
|
|
// Register listeners
|
|
registerListeners();
|
|
|
|
// Register commands
|
|
registerCommands();
|
|
|
|
// Register checks
|
|
registerChecks();
|
|
|
|
// Start decay task
|
|
startDecayTask();
|
|
|
|
// Start potion effect refresh task
|
|
startPotionRefreshTask();
|
|
|
|
// Start TimerCheck blink detection task
|
|
startTimerBlinkTask();
|
|
|
|
getLogger().info("XeroAntiCheat v" + getDescription().getVersion() + " enabled!");
|
|
if (protocolLibLoaded) {
|
|
getLogger().info("ProtocolLib detected - packet-level checks enabled");
|
|
} else {
|
|
getLogger().info("ProtocolLib not found - using event-based detection");
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onDisable() {
|
|
// Cancel all tasks
|
|
Bukkit.getScheduler().cancelTasks(this);
|
|
|
|
// Save any pending data
|
|
if (violationManager != null) {
|
|
violationManager.saveAll();
|
|
}
|
|
|
|
// Close database
|
|
if (databaseManager != null) {
|
|
databaseManager.close();
|
|
}
|
|
|
|
getLogger().info("XeroAntiCheat disabled!");
|
|
}
|
|
|
|
private void checkProtocolLib() {
|
|
try {
|
|
Class.forName("com.comphenix.protocol.ProtocolLibrary");
|
|
protocolLibLoaded = true;
|
|
} catch (ClassNotFoundException e) {
|
|
protocolLibLoaded = false;
|
|
}
|
|
}
|
|
|
|
private void initializeManagers() {
|
|
configManager = new ConfigManager(this);
|
|
configManager.loadConfig();
|
|
|
|
violationManager = new ViolationManager(this);
|
|
punishmentManager = new PunishmentManager(this, violationManager);
|
|
checkManager = new CheckManager(this);
|
|
|
|
databaseManager = new DatabaseManager(this);
|
|
databaseManager.initialize();
|
|
}
|
|
|
|
private void registerListeners() {
|
|
getServer().getPluginManager().registerEvents(new MovementListener(this), this);
|
|
getServer().getPluginManager().registerEvents(new CombatListener(this), this);
|
|
getServer().getPluginManager().registerEvents(new MiscListener(this), this);
|
|
|
|
if (protocolLibLoaded) {
|
|
packetListener = new PacketListener(this);
|
|
packetListener.register();
|
|
}
|
|
}
|
|
|
|
private void registerCommands() {
|
|
getCommand("xac").setExecutor(new XACCommand(this));
|
|
}
|
|
|
|
private void registerChecks() {
|
|
// Movement checks
|
|
checkManager.registerCheck(new SpeedCheck(this));
|
|
checkManager.registerCheck(new FlyCheck(this));
|
|
checkManager.registerCheck(new JesusCheck(this));
|
|
checkManager.registerCheck(new NoFallCheck(this));
|
|
checkManager.registerCheck(new TimerCheck(this));
|
|
checkManager.registerCheck(new SpiderCheck(this));
|
|
checkManager.registerCheck(new GlideCheck(this));
|
|
checkManager.registerCheck(new PhaseCheck(this));
|
|
|
|
// Combat checks
|
|
checkManager.registerCheck(new KillAuraCheck(this));
|
|
checkManager.registerCheck(new ReachCheck(this));
|
|
checkManager.registerCheck(new CriticalCheck(this));
|
|
checkManager.registerCheck(new AutoClickerCheck(this));
|
|
|
|
// VelocityCheck requires ProtocolLib
|
|
if (protocolLibLoaded) {
|
|
checkManager.registerCheck(new VelocityCheck(this));
|
|
}
|
|
|
|
// Misc checks
|
|
checkManager.registerCheck(new FastPlaceCheck(this));
|
|
checkManager.registerCheck(new ScaffoldCheck(this));
|
|
checkManager.registerCheck(new FastEatCheck(this));
|
|
checkManager.registerCheck(new InventoryMoveCheck(this));
|
|
|
|
getLogger().info("Registered " + checkManager.getRegisteredChecks().size() + " checks");
|
|
}
|
|
|
|
private void startDecayTask() {
|
|
int interval = configManager.getInt("violation.decay_interval", 30) * 20;
|
|
decayTask = Bukkit.getScheduler().runTaskTimerAsynchronously(this, () -> {
|
|
violationManager.decayAll();
|
|
}, interval, interval);
|
|
}
|
|
|
|
private void startPotionRefreshTask() {
|
|
Bukkit.getScheduler().runTaskTimer(this, () -> {
|
|
for (Player player : Bukkit.getOnlinePlayers()) {
|
|
PlayerData data = violationManager.getPlayerData(player);
|
|
if (data == null) continue;
|
|
|
|
data.setHasSpeedEffect(player.hasPotionEffect(PotionEffectType.SPEED));
|
|
data.setHasSlownessEffect(player.hasPotionEffect(PotionEffectType.SLOWNESS));
|
|
data.setHasLevitation(player.hasPotionEffect(PotionEffectType.LEVITATION));
|
|
data.setHasDolphinsGrace(player.hasPotionEffect(PotionEffectType.DOLPHINS_GRACE));
|
|
data.setHasJumpBoost(player.hasPotionEffect(PotionEffectType.JUMP_BOOST));
|
|
data.setHasSlowFalling(player.hasPotionEffect(PotionEffectType.SLOW_FALLING));
|
|
|
|
var speed = player.getPotionEffect(PotionEffectType.SPEED);
|
|
data.setSpeedLevel(speed != null ? speed.getAmplifier() + 1 : 0);
|
|
|
|
var slowness = player.getPotionEffect(PotionEffectType.SLOWNESS);
|
|
data.setSlownessLevel(slowness != null ? slowness.getAmplifier() + 1 : 0);
|
|
|
|
var jump = player.getPotionEffect(PotionEffectType.JUMP_BOOST);
|
|
data.setJumpBoostLevel(jump != null ? jump.getAmplifier() + 1 : 0);
|
|
}
|
|
}, 10L, 10L);
|
|
}
|
|
|
|
private void startTimerBlinkTask() {
|
|
Bukkit.getScheduler().runTaskTimer(this, () -> {
|
|
long now = System.currentTimeMillis();
|
|
long threshold = configManager.getInt("checks.timer.blink_threshold_ms", 500);
|
|
Check timerCheck = checkManager.getCheck("Timer");
|
|
|
|
for (Player player : Bukkit.getOnlinePlayers()) {
|
|
if (timerCheck != null && timerCheck.isBypassed(player)) continue;
|
|
PlayerData data = violationManager.getPlayerData(player);
|
|
if (data == null || data.getLastMovePacketTime() == 0) continue;
|
|
|
|
long gap = now - data.getLastMovePacketTime();
|
|
if (gap > threshold) {
|
|
violationManager.addViolation(player, "Timer", 2.0);
|
|
punishmentManager.evaluate(player, "Timer");
|
|
data.setLastMovePacketTime(now);
|
|
}
|
|
}
|
|
}, 5L, 5L);
|
|
}
|
|
|
|
/**
|
|
* Reload the plugin configuration and re-register checks
|
|
*/
|
|
public void reload() {
|
|
configManager.loadConfig();
|
|
violationManager.clearAll();
|
|
violationManager.setDecayRate(
|
|
configManager.getDouble("violation.decay_rate", 0.5));
|
|
|
|
if (decayTask != null) {
|
|
decayTask.cancel();
|
|
}
|
|
startDecayTask();
|
|
|
|
getLogger().info("Configuration reloaded!");
|
|
}
|
|
|
|
// Getters
|
|
public static XeroAntiCheat getInstance() {
|
|
return instance;
|
|
}
|
|
|
|
public ConfigManager getConfigManager() {
|
|
return configManager;
|
|
}
|
|
|
|
public ViolationManager getViolationManager() {
|
|
return violationManager;
|
|
}
|
|
|
|
public PunishmentManager getPunishmentManager() {
|
|
return punishmentManager;
|
|
}
|
|
|
|
public CheckManager getCheckManager() {
|
|
return checkManager;
|
|
}
|
|
|
|
public DatabaseManager getDatabaseManager() {
|
|
return databaseManager;
|
|
}
|
|
|
|
public boolean isProtocolLibLoaded() {
|
|
return protocolLibLoaded;
|
|
}
|
|
|
|
public boolean isAlertsEnabled(java.util.UUID uuid) {
|
|
return alertToggles.getOrDefault(uuid, true);
|
|
}
|
|
|
|
public void setAlertsEnabled(java.util.UUID uuid, boolean enabled) {
|
|
alertToggles.put(uuid, enabled);
|
|
}
|
|
|
|
public boolean isVerboseTarget(UUID uuid) {
|
|
return verboseTargets.contains(uuid);
|
|
}
|
|
|
|
public void toggleVerboseTarget(UUID uuid) {
|
|
if (!verboseTargets.remove(uuid)) {
|
|
verboseTargets.add(uuid);
|
|
}
|
|
}
|
|
}
|