Files
Aluer/src/main/java/com/aluer/anticheat/movement/AntiNoSlowService.java
T
Peijun Zhao 055de65e88 安全模块大规模重构:151文件从com.aluer.security分包到8个子包
新包结构:
- com.aluer.anticheat.combat (16) — KillAura/Reach/Criticals/CrystalAura/Totem等战斗反作弊
- com.aluer.anticheat.movement (18) — Fly/Speed/Jesus/NoFall/Timer/Phase等移动反作弊
- com.aluer.anticheat.world (14) — Nuker/Xray/AutoMine/VeinMiner等世界反作弊
- com.aluer.anticheat.player (8) — FakePlayer/AltAccount/NameSpoof等玩家反作弊
- com.aluer.network (32) — DDoS/Protocol/TokenBucket/Firewall等网络安全
- com.aluer.server (10) — ChunkLoad/EntityCount/Redstone/CrashExploit等服务器保护
- com.aluer.chat (5) — ChatFlood/Advertisement/Phishing/CommandAbuse等聊天安全
- com.aluer.defense (50) — WAF/IDS/IPS/Honeypot/EDR/Forensics等纵深防御

所有package声明+跨包import已更新,323测试全绿
2026-05-22 19:29:45 +08:00

321 lines
13 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package com.aluer.anticheat.movement;
import com.aluer.config.ServerGuardConfig;
import org.springframework.stereotype.Service;
import java.time.Instant;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;
/**
* 无减速(NoSlow)检测服务 — V5.2 反作弊移动模块
*
* 检测原理:
* 1. 物品使用时移速检测 — Minecraft中玩家在使用物品(吃食物、拉弓、举盾、喝药水)时
* 会强制减速至潜行速度(约1.31 m/s)。NoSlow hack通过取消物品使用减速封包,
* 使玩家在使用物品时仍能全速移动。
* 2. 按物品类型设定速度阈值 — 不同类型的物品使用有不同的预期速度上限:
* 吃食物(1.5 m/s)、拉弓(1.2 m/s)、举盾(0.8 m/s)、喝药水(1.5 m/s)、
* 吃东西(1.5 m/s)。超过对应阈值即标记。
* 3. 连续tick检测 — 区分偶发的网络延迟导致的瞬间速度尖峰和持续性的作弊行为,
* 要求连续多tick同时"使用物品+高速移动"才判定为作弊。
* 4. 使用状态交叉验证 — 结合玩家"正在使用物品"状态与Agent上报的移动速度数据,
* 精准判断玩家是否在使用物品的同时快速移动。
*
* 配置开关:serverguard.security.super-evolution.anti-no-slow
*/
@Service
public class AntiNoSlowService {
private final ServerGuardConfig config;
/**
* 追踪每个玩家的物品使用历史记录(playerName -> 使用记录列表)
*/
private final Map<String, List<ItemUseRecord>> playerUseHistory = new ConcurrentHashMap<>();
/**
* 追踪每个玩家连续违规tick计数(playerName -> 连续违规次数)
* 用于区分瞬时延迟与持续作弊
*/
private final Map<String, Integer> playerViolationTicks = new ConcurrentHashMap<>();
/**
* 追踪每个玩家当前正在使用的物品类型(playerName -> 物品类型描述)
*/
private final Map<String, String> playerActiveItem = new ConcurrentHashMap<>();
private final AtomicLong totalChecks = new AtomicLong(0);
private final AtomicLong noSlowViolations = new AtomicLong(0);
/**
* 各物品类型在使用时的最大合法水平速度(m/s)
* 正常使用物品时玩家被强制减速至潜行速度约1.31 m/s
*/
private static final double MAX_EATING_SPEED = 1.5;
private static final double MAX_BOW_DRAW_SPEED = 1.2;
private static final double MAX_SHIELD_BLOCK_SPEED = 0.8;
private static final double MAX_POTION_DRINK_SPEED = 1.5;
private static final double MAX_FOOD_EAT_SPEED = 1.5;
private static final double MAX_DEFAULT_ITEM_USE_SPEED = 1.3;
/**
* 正常步行速度的60%阈值 — 用于快速判断
* Minecraft步行速度约4.317 m/s,60%约2.59 m/s
*/
private static final double WALK_SPEED_60_PERCENT = 2.59;
/**
* 连续违规tick阈值 — 需要连续超过此tick数才判定为作弊
* 低于此值可能是网络延迟导致的数据包积压释放
*/
private static final int MIN_CONSECUTIVE_VIOLATION_TICKS = 5;
/**
* 每个玩家保留的最大记录数
*/
private static final int MAX_RECORDS_PER_PLAYER = 40;
/**
* 无参构造函数 — 测试/默认配置使用
*/
public AntiNoSlowService() {
this(new ServerGuardConfig());
}
/**
* Spring 注入构造函数
* @param config 全局配置,提供模块开关控制
*/
public AntiNoSlowService(ServerGuardConfig config) {
this.config = config;
}
/**
* 检测玩家是否在使用物品时维持异常高速(NoSlow hack)
*
* @param playerName 玩家名称
* @param playerUUID 玩家UUID
* @param isUsingItem 玩家是否正在使用物品(右键按住)
* @param itemType 正在使用的物品类型(EATING, BOW, SHIELD, POTION, FOOD, OTHER)
* @param fromX 移动起始X坐标
* @param fromZ 移动起始Z坐标
* @param toX 移动结束X坐标
* @param toZ 移动结束Z坐标
* @param deltaSeconds 本次移动时间间隔(秒)
* @param timestamp 时间戳
* @return 检测结果
*/
public NoSlowCheckResult detect(String playerName, String playerUUID,
boolean isUsingItem, String itemType,
double fromX, double fromZ,
double toX, double toZ,
double deltaSeconds, Instant timestamp) {
// 模块开关检查 — 关闭时跳过所有检测
if (!config.getSecurity().getSuperEvolution().isAntiNoSlow()) {
return NoSlowCheckResult.clean();
}
totalChecks.incrementAndGet();
// 玩家当前未使用物品,无需检测,重置违规计数
if (!isUsingItem) {
playerViolationTicks.remove(playerName);
playerActiveItem.remove(playerName);
return NoSlowCheckResult.clean();
}
// 更新当前使用的物品类型
playerActiveItem.put(playerName, itemType != null ? itemType : "OTHER");
List<ItemUseRecord> history = playerUseHistory.computeIfAbsent(
playerName, k -> new ArrayList<>());
// 避免除零 — 默认50ms(1 tick)
if (deltaSeconds <= 0) {
deltaSeconds = 0.05;
}
// 计算XZ平面水平速度
double dx = toX - fromX;
double dz = toZ - fromZ;
double horizontalDistance = Math.sqrt(dx * dx + dz * dz);
double horizontalSpeed = horizontalDistance / deltaSeconds;
// 根据物品类型确定速度阈值
double speedThreshold = getItemSpeedThreshold(itemType);
ItemUseRecord record = new ItemUseRecord(timestamp, itemType,
horizontalSpeed, horizontalDistance, deltaSeconds);
history.add(record);
while (history.size() > MAX_RECORDS_PER_PLAYER) {
history.remove(0);
}
List<String> reasons = new ArrayList<>();
// 1. 核心检测:使用物品时水平速度超过对应物品类型阈值
if (horizontalSpeed > speedThreshold) {
// 累加违规tick计数
int violationTicks = playerViolationTicks.getOrDefault(playerName, 0) + 1;
playerViolationTicks.put(playerName, violationTicks);
// 超过连续阈值才判定为作弊
if (violationTicks >= MIN_CONSECUTIVE_VIOLATION_TICKS) {
noSlowViolations.incrementAndGet();
reasons.add("SUSTAINED_NOSLOW: " + String.format("%.2f", horizontalSpeed)
+ " m/s while using " + itemType.toLowerCase()
+ " for " + violationTicks + " consecutive ticks"
+ " (threshold: " + String.format("%.2f", speedThreshold) + " m/s)");
}
} else {
// 速度正常,重置违规计数
playerViolationTicks.remove(playerName);
}
// 2. 快速筛查:使用物品时速度超过步行速度60%
if (horizontalSpeed > WALK_SPEED_60_PERCENT) {
int violationTicks = playerViolationTicks.getOrDefault(playerName, 0);
if (violationTicks >= MIN_CONSECUTIVE_VIOLATION_TICKS) {
reasons.add("HIGH_SPEED_ITEM_USE: " + String.format("%.2f", horizontalSpeed)
+ " m/s (" + String.format("%.0f", (horizontalSpeed / 4.317) * 100)
+ "% of walk speed) while using " + itemType.toLowerCase());
}
}
// 3. 使用模式分析 — 检测高频使用+高速移动的组合模式
if (history.size() >= 10) {
long highSpeedWhileUsing = history.stream()
.filter(r -> r.horizontalSpeed > speedThreshold)
.count();
double highSpeedRatio = (double) highSpeedWhileUsing / history.size();
// 如果超过70%的使用物品时间都处于高速移动,高度可疑
if (highSpeedRatio > 0.7 && history.size() >= 10) {
reasons.add("PATTERN_HIGH_SPEED_USE: "
+ String.format("%.0f", highSpeedRatio * 100)
+ "% of item use ticks at high speed ("
+ highSpeedWhileUsing + "/" + history.size() + " ticks)");
}
}
if (reasons.size() >= 2) {
return NoSlowCheckResult.flagged(reasons);
} else if (reasons.size() == 1) {
return NoSlowCheckResult.suspicious(reasons);
}
return NoSlowCheckResult.clean();
}
/**
* 根据物品类型返回对应的速度阈值
*
* @param itemType 物品类型字符串
* @return 该物品类型允许的最大水平速度(m/s)
*/
private double getItemSpeedThreshold(String itemType) {
if (itemType == null) return MAX_DEFAULT_ITEM_USE_SPEED;
return switch (itemType.toUpperCase()) {
case "EATING" -> MAX_EATING_SPEED;
case "BOW" -> MAX_BOW_DRAW_SPEED;
case "SHIELD" -> MAX_SHIELD_BLOCK_SPEED;
case "POTION" -> MAX_POTION_DRINK_SPEED;
case "FOOD" -> MAX_FOOD_EAT_SPEED;
default -> MAX_DEFAULT_ITEM_USE_SPEED;
};
}
/**
* 玩家离线时清理追踪数据
* @param playerName 离线玩家名称
*/
public void clearPlayer(String playerName) {
playerUseHistory.remove(playerName);
playerViolationTicks.remove(playerName);
playerActiveItem.remove(playerName);
}
/**
* 获取模块运行状态
* @return 包含计数器和违规数量的Map
*/
public Map<String, Object> getStatus() {
Map<String, Object> status = new LinkedHashMap<>();
status.put("totalChecks", totalChecks.get());
status.put("noSlowViolations", noSlowViolations.get());
status.put("activeTrackedPlayers", playerUseHistory.size());
status.put("playersInViolation", playerViolationTicks.size());
// 列出当前正在使用物品的玩家
List<Map<String, String>> usingPlayers = new ArrayList<>();
for (Map.Entry<String, String> entry : playerActiveItem.entrySet()) {
Map<String, String> info = new LinkedHashMap<>();
info.put("player", entry.getKey());
info.put("item", entry.getValue());
info.put("violationTicks", String.valueOf(
playerViolationTicks.getOrDefault(entry.getKey(), 0)));
usingPlayers.add(info);
}
status.put("activeItemUsers", usingPlayers);
return status;
}
/**
* 内部物品使用记录 — 记录玩家使用物品时的移动信息
*/
private static class ItemUseRecord {
final Instant timestamp;
final String itemType;
final double horizontalSpeed;
final double horizontalDistance;
final double deltaSeconds;
ItemUseRecord(Instant timestamp, String itemType,
double horizontalSpeed, double horizontalDistance,
double deltaSeconds) {
this.timestamp = timestamp;
this.itemType = itemType;
this.horizontalSpeed = horizontalSpeed;
this.horizontalDistance = horizontalDistance;
this.deltaSeconds = deltaSeconds;
}
}
/**
* NoSlow检测结果 — 不可变结果类
*/
public static class NoSlowCheckResult {
private final boolean flagged;
private final boolean suspicious;
private final List<String> reasons;
private NoSlowCheckResult(boolean flagged, boolean suspicious, List<String> reasons) {
this.flagged = flagged;
this.suspicious = suspicious;
this.reasons = reasons;
}
/** 无异常 — 正常使用物品并减速 */
public static NoSlowCheckResult clean() {
return new NoSlowCheckResult(false, false, List.of());
}
/** 可疑 — 短暂超速但不足够确认为NoSlow hack */
public static NoSlowCheckResult suspicious(List<String> reasons) {
return new NoSlowCheckResult(false, true, reasons);
}
/** 已标记 — 确认NoSlow hack,持续在使用物品时高速移动 */
public static NoSlowCheckResult flagged(List<String> reasons) {
return new NoSlowCheckResult(true, false, reasons);
}
public boolean isFlagged() { return flagged; }
public boolean isSuspicious() { return suspicious; }
public boolean isClean() { return !flagged && !suspicious; }
public List<String> getReasons() { return reasons; }
}
}