V5.0 安全模块大扩展:Meteor Client 全功能反作弊 + AI行为分析 + 服务器保护

新增 45 个安全模块,覆盖 Meteor Client 全部 hack 类别:

【反作弊基础扩展 6 模块】
- AntiTimerService: 游戏加速检测(移动tick间隔分析)
- AntiVelocityService: 击退修改检测(伤害后速度分析)
- AntiPhaseService: 穿墙检测(固体方块射线追踪)
- AntiBlinkService: 闪烁检测(伤害-断连-重连时序)
- AntiFastBreakService: 快速破坏检测(方块标准破坏时间对比)
- AntiElytraFlyService: 鞘翅飞行检测(速度/高度操控分析)

【Meteor Combat 10 模块】
- AntiCriticalsService: 自动暴击检测(跳跃/地面状态验证)
- AntiAutoCrystalService: 水晶Aura检测(放置/爆炸时序分析)
- AntiAutoTotemService: 自动图腾检测(副手装备速度分析)
- AntiSurroundService: 自动包围检测(脚下方块精确放置模式)
- AntiAutoTrapService: 自动陷阱检测(目标周围方块网格分析)
- AntiAutoArmorService: 自动装备检测(多槽位同tick交换)
- AntiChestSwapService: 自动胸甲交换检测(鞘翅-胸甲乒乓切换)
- AntiAutoLogService: 自动断线检测(伤害-断连500ms时序)
- AntiHitboxesService: 扩展碰撞箱检测(射线-边界边缘命中率)
- AntiBowAimBotService: 弓箭自瞄检测(移动目标精度分析)

【Meteor Movement 10 模块】
- AntiNoSlowService: 无减速检测(物品使用+移动速度组合)
- AntiSpiderService: 爬墙检测(无攀爬方块垂直上升)
- AntiStepService: 自动跨步检测(非跳跃1格上升)
- AntiPacketFlyService: 数据包飞行检测(连续悬浮/弹跳模式)
- AntiAirJumpService: 空中跳跃检测(离地跳跃包)
- AntiLongJumpService: 远跳检测(跳跃后水平速度衰减分析)
- AntiAntiHungerService: 免饥饿检测(活动量vs饥饿消耗率)
- AntiFastFallService: 快速下落检测(超终端速度持续下落)
- AntiVClipService: 垂直穿墙检测(固体方块间瞬移)
- AntiNoFallService 增强: 新增GroundSpoof/数据包地面/坠落累积/瞬移坠落检测

【Meteor World+Player 10 模块】
- AntiSpeedMineService: 加速挖掘检测(方块标准破坏时间对比)
- AntiFastUseService: 快速使用检测(物品冷却时间分析)
- AntiNoInteractService: 交互绕过检测(持剑开箱等异常操作)
- AntiAutoMineService: 自动挖掘检测(挖掘间隔方差/视角追踪)
- AntiVeinMinerService: 连锁挖矿检测(相邻矿石快速序列)
- AntiAutoToolService: 自动工具检测(同tick切工具+开挖)
- AntiFakePlayerService: 假人检测(心跳响应/交互缺失/移动模式)
- AntiPistonAuraService: 活塞光环检测(战斗上下文活塞激活率)
- AntiAnchorService: 锚定检测(受击零位移+封闭空间)
- AntiStashFinderService: 仓库扫描检测(网格区块加载+零交互)

【网络协议安全 5 模块】
- ProtocolStateValidator: 协议状态机验证(四态转换违规检测)
- TokenBucketRateLimiter: 令牌桶速率限制器(可复用速率控制基元)
- BotFingerprintDetector: 机器人指纹检测(五维行为指纹分析)
- NBTExploitPrevention: NBT漏洞防护(深度/大小/书页/告示牌验证)
- ConnectionHandshakeValidator: 握手完整性验证(协议版本/状态/主机名)

【服务器保护 4 模块】
- ChunkLoadRateLimiter: 区块加载限制器(WARN/LIMIT/BLOCK三级响应)
- EntityCountEnforcer: 实体数量执行器(分块限制+优先级清理)
- RedstoneUpdateLimiter: 红石更新限制器(三级渐进+观察者钟检测)
- CrashExploitSignatureDB: 崩溃漏洞签名库(12种已知漏洞+4级自动响应)

【AI/ML 行为分析 4 模块】
- BehavioralProfilingEngine: 行为分析引擎(5维特征+香农熵+Z分数)
- ThreatScoreAggregator: 威胁分数聚合器(指数衰减+4级上报)
- MovementPatternAnalyzer: 移动模式分析器(FFT频谱+旋转分析)
- CombatPatternRecognizer: 战斗模式识别器(CPS方差+多目标熵+扫视分析)

配置更新:AlertType新增39个枚举值,SuperEvolutionConfig新增39个开关,
application.yml新增39个配置项。323项全量测试通过,0失败。
This commit is contained in:
Peijun Zhao
2026-05-16 18:02:49 +08:00
parent 67e873525b
commit 1b66197804
54 changed files with 23497 additions and 1 deletions
@@ -0,0 +1,330 @@
package com.aluer.security;
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;
/**
* 穿墙/方块剪切(Phase)检测服务 — V5.0 反作弊扩展模块
*
* 检测原理:
* 1. 固体方块穿越检测 — 玩家两次连续移动位置之间如果存在固体方块,
* 则玩家穿过了方块,这是Phase hack的典型特征。通过步进采样两点间
* 路径上的方块状态进行检测。
* 2. 移动距离异常检测 — 正常玩家无法在单tick内移动超过一定距离。
* 如果玩家单次移动穿越了多个固体方块,几乎可以确定为Phase hack。
* 3. 地狱门Phase检测 — Phase hack的一个常见变种是利用地狱门传送瞬间
* 将玩家位置修改到门的另一端(通常是固体方块内部)。
* 检测玩家在地狱门附近出现的不可能位置。
* 4. 方块内停留检测 — 正常游戏机制会阻止玩家停留在固体方块内部。
* 如果检测到玩家在固体方块内部持续存在,说明存在Phase hack。
*
* 注意:此模块接收来自Agent监听器的方块状态数据,
* 由于Paper API为provided scope,实际的isSolid/isOccluding检查
* 由Agent在Minecraft进程内部完成并传递结果。
*
* 配置开关:serverguard.security.super-evolution.anti-phase
*/
@Service
public class AntiPhaseService {
private final ServerGuardConfig config;
/**
* 追踪每个玩家的移动轨迹(playerName -> 最近的位置记录)
* 用于检测两点间是否穿越了固体方块
*/
private final Map<String, PlayerPosition> playerLastPosition = new ConcurrentHashMap<>();
/**
* 追踪每个玩家的可疑穿墙事件(playerName -> 事件列表)
* 用于累积检测模式和判定作弊行为
*/
private final Map<String, List<PhaseEvent>> playerPhaseEvents = new ConcurrentHashMap<>();
/**
* 追踪在地狱门附近的玩家及其进入时间(playerName -> 最近门户交互时间)
* 用于检测地狱门Phase exploit
*/
private final Map<String, Instant> playerPortalInteraction = new ConcurrentHashMap<>();
private final AtomicLong totalChecks = new AtomicLong(0);
private final AtomicLong flaggedCount = new AtomicLong(0);
/**
* 单次移动允许的最大距离(方块)— 生存模式正常移动不会超过此值
* 超过此值表明可能穿过了固体方块
*/
private static final double MAX_NORMAL_MOVE_DISTANCE = 10.0;
/**
* 路径采样步长(方块)— 在两点间进行路径采样时的步长
* 较小值提供更精细的检测,较大值提高性能
*/
private static final double PATH_SAMPLE_STEP = 0.5;
/**
* 触发标记所需的最小穿墙事件数
*/
private static final int MIN_PHASE_EVENTS = 2;
/**
* 穿墙事件时间窗口(毫秒)— 在此窗口内的事件视为同一作弊会话
*/
private static final long PHASE_WINDOW_MS = 30_000;
/**
* 地狱门Phase检测窗口(秒)— 门户交互后此时间内出现的不可能位置
*/
private static final long PORTAL_PHASE_WINDOW_MS = 3_000;
/**
* 方块内部检测所需的连续检查次数
*/
private static final int MIN_INSIDE_BLOCK_CHECKS = 3;
/**
* 每个玩家保留的最大穿墙事件记录数
*/
private static final int MAX_PHASE_EVENTS = 20;
/**
* 无参构造函数 — 测试/默认配置使用
*/
public AntiPhaseService() {
this(new ServerGuardConfig());
}
/**
* Spring 注入构造函数
* @param config 全局配置,提供模块开关控制
*/
public AntiPhaseService(ServerGuardConfig config) {
this.config = config;
}
/**
* 检测玩家移动是否穿越了固体方块(Phase hack)
*
* @param playerName 玩家名称
* @param playerUUID 玩家UUID
* @param fromX 移动起始X坐标
* @param fromY 移动起始Y坐标
* @param fromZ 移动起始Z坐标
* @param toX 移动结束X坐标
* @param toY 移动结束Y坐标
* @param toZ 移动结束Z坐标
* @param solidBlocksBetween 两点间固体方块数量(由Agent在Minecraft进程中统计)
* @param isPlayerInsideBlock 移动后玩家是否在固体方块内部(由Agent检测)
* @param isNearPortal 玩家是否在地狱门附近
* @param gameMode 玩家游戏模式
* @param timestamp 时间戳
* @return 检测结果
*/
public PhaseCheckResult detect(String playerName, String playerUUID,
double fromX, double fromY, double fromZ,
double toX, double toY, double toZ,
int solidBlocksBetween, boolean isPlayerInsideBlock,
boolean isNearPortal, String gameMode,
Instant timestamp) {
// 模块开关检查 — 关闭时跳过所有检测直接返回安全结果
if (!config.getSecurity().getSuperEvolution().isAntiPhase()) {
return PhaseCheckResult.clean();
}
// 创造和观察者模式不受方块限制,直接跳过
if ("creative".equalsIgnoreCase(gameMode) || "spectator".equalsIgnoreCase(gameMode)) {
return PhaseCheckResult.clean();
}
totalChecks.incrementAndGet();
List<String> reasons = new ArrayList<>();
// 获取或创建玩家事件历史
List<PhaseEvent> events = playerPhaseEvents.computeIfAbsent(
playerName, k -> new ArrayList<>());
// 计算移动距离
double dx = toX - fromX;
double dy = toY - fromY;
double dz = toZ - fromZ;
double distance = Math.sqrt(dx * dx + dy * dy + dz * dz);
// 1. 固体方块穿越检测 — 两点间存在固体方块
if (solidBlocksBetween > 0) {
PhaseEvent event = new PhaseEvent(timestamp, fromX, fromY, fromZ, toX, toY, toZ,
solidBlocksBetween, isPlayerInsideBlock, isNearPortal, distance);
events.add(event);
// 控制列表大小
while (events.size() > MAX_PHASE_EVENTS) {
events.remove(0);
}
// 检查是否为地狱门Phase exploit
Instant portalTime = playerPortalInteraction.get(playerName);
boolean isPortalPhase = portalTime != null
&& timestamp.toEpochMilli() - portalTime.toEpochMilli() < PORTAL_PHASE_WINDOW_MS;
if (isPortalPhase && isNearPortal) {
reasons.add("PORTAL_PHASE: moved through " + solidBlocksBetween
+ " solid block(s) within " + PORTAL_PHASE_WINDOW_MS / 1000
+ "s of portal interaction (distance: " + String.format("%.2f", distance) + " blocks)");
} else {
reasons.add("SOLID_BLOCK_PHASE: moved through " + solidBlocksBetween
+ " solid block(s) over " + String.format("%.2f", distance) + " blocks");
}
// 统计时间窗口内的穿墙事件数
Instant windowStart = timestamp.minusMillis(PHASE_WINDOW_MS);
long recentPhases = events.stream()
.filter(e -> e.timestamp.isAfter(windowStart) && e.solidBlocksCrossed > 0)
.count();
if (recentPhases >= MIN_PHASE_EVENTS) {
reasons.add("REPEATED_PHASE: " + recentPhases + " phase events in "
+ PHASE_WINDOW_MS / 1000 + "s");
}
}
// 2. 方块内部停留检测 — 玩家在固体方块内部持续存在
if (isPlayerInsideBlock) {
reasons.add("INSIDE_BLOCK: player detected inside solid block at ("
+ String.format("%.1f", toX) + ", "
+ String.format("%.1f", toY) + ", "
+ String.format("%.1f", toZ) + ")");
}
// 3. 移动距离异常检测 — 单次移动距离过大
if (distance > MAX_NORMAL_MOVE_DISTANCE && solidBlocksBetween > 0) {
reasons.add("IMPOSSIBLE_DISTANCE: moved " + String.format("%.2f", distance)
+ " blocks through " + solidBlocksBetween + " solid blocks (max normal: "
+ String.format("%.1f", MAX_NORMAL_MOVE_DISTANCE) + ")");
}
// 更新玩家最后位置
playerLastPosition.put(playerName, new PlayerPosition(toX, toY, toZ, timestamp));
// 更新地狱门交互追踪
if (isNearPortal) {
playerPortalInteraction.put(playerName, timestamp);
}
// 汇总结果
if (reasons.size() >= 2) {
flaggedCount.incrementAndGet();
return PhaseCheckResult.flagged(reasons);
} else if (reasons.size() == 1) {
return PhaseCheckResult.suspicious(reasons);
}
return PhaseCheckResult.clean();
}
/**
* 记录玩家与地狱门的交互(Agent在检测到门户事件时调用)
* @param playerName 玩家名称
* @param timestamp 交互时间戳
*/
public void recordPortalInteraction(String playerName, Instant timestamp) {
playerPortalInteraction.put(playerName, timestamp);
}
/**
* 玩家离线时清理其追踪数据,释放内存
* @param playerName 离线玩家名称
*/
public void clearPlayer(String playerName) {
playerLastPosition.remove(playerName);
playerPhaseEvents.remove(playerName);
playerPortalInteraction.remove(playerName);
}
/**
* 获取模块运行状态
* @return 包含计数器和活跃追踪玩家数的Map
*/
public Map<String, Object> getStatus() {
Map<String, Object> status = new LinkedHashMap<>();
status.put("totalChecks", totalChecks.get());
status.put("flaggedCount", flaggedCount.get());
status.put("activeTrackedPlayers", playerLastPosition.size());
return status;
}
/**
* 内部玩家位置记录
*/
private static class PlayerPosition {
final double x, y, z;
final Instant timestamp;
PlayerPosition(double x, double y, double z, Instant timestamp) {
this.x = x; this.y = y; this.z = z;
this.timestamp = timestamp;
}
}
/**
* 内部穿墙事件记录
*/
private static class PhaseEvent {
final Instant timestamp;
final double fromX, fromY, fromZ;
final double toX, toY, toZ;
final int solidBlocksCrossed;
final boolean insideBlock;
final boolean nearPortal;
final double distance;
PhaseEvent(Instant timestamp, double fromX, double fromY, double fromZ,
double toX, double toY, double toZ, int solidBlocksCrossed,
boolean insideBlock, boolean nearPortal, double distance) {
this.timestamp = timestamp;
this.fromX = fromX; this.fromY = fromY; this.fromZ = fromZ;
this.toX = toX; this.toY = toY; this.toZ = toZ;
this.solidBlocksCrossed = solidBlocksCrossed;
this.insideBlock = insideBlock;
this.nearPortal = nearPortal;
this.distance = distance;
}
}
/**
* Phase检测结果 — 不可变结果类
*/
public static class PhaseCheckResult {
private final boolean flagged;
private final boolean suspicious;
private final List<String> reasons;
private PhaseCheckResult(boolean flagged, boolean suspicious, List<String> reasons) {
this.flagged = flagged;
this.suspicious = suspicious;
this.reasons = reasons;
}
/** 无异常 — 正常移动路径 */
public static PhaseCheckResult clean() {
return new PhaseCheckResult(false, false, List.of());
}
/** 可疑 — 检测到穿墙但证据不够充分(可能为网络延迟/边界情况) */
public static PhaseCheckResult suspicious(List<String> reasons) {
return new PhaseCheckResult(false, true, reasons);
}
/** 已标记 — 多项规则命中,确认为穿墙作弊 */
public static PhaseCheckResult flagged(List<String> reasons) {
return new PhaseCheckResult(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; }
}
}