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,378 @@
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;
/**
* 数据包飞行(PacketFly)检测服务 — V5.2 反作弊移动模块
*
* 检测原理:
* 1. 无辅助持续上升检测 — PacketFly是最先进的飞行hack之一,通过操控移动数据包序列
* 欺骗服务端,实现无鞘翅、无缓降、无创造模式的持续飞行。
* 检测玩家在生存模式下没有任何飞行辅助物品(鞘翅/烟花/浮空效果)的持续垂直上升。
* 2. 震荡模式检测 — PacketFly最典型的特征是"上下震荡"模式:
* 快速的上下来回移动(约±0.5-2.0方块/tick的振荡),目的是重置服务端的摔落距离计算,
* 从而避免被反飞行检测。正常玩家不会产生这种高频的垂直振荡模式。
* 3. 永不落地检测 — PacketFly用户可以无限期保持在空中不接触地面,
* 远超正常玩家通过跳跃、鞘翅滑翔等方式能维持的最大空中时间。
* 4. 总垂直位移异常检测 — 在生存模式下,累计的总上升高度不应超过物理极限。
* PacketFly用户可以在几分钟内上升数百方块而没有任何合法的上升手段。
*
* 配置开关:serverguard.security.super-evolution.anti-packet-fly
*/
@Service
public class AntiPacketFlyService {
private final ServerGuardConfig config;
/**
* 追踪每个玩家的移动历史(playerName -> 移动记录列表)
*/
private final Map<String, List<PacketFlyRecord>> playerMoveHistory = new ConcurrentHashMap<>();
/**
* 追踪每个玩家的连续空中tick(playerName -> 连续不落地tick计数)
*/
private final Map<String, Integer> playerAirTicks = new ConcurrentHashMap<>();
/**
* 追踪每个玩家的累计上升高度(playerName -> 累计上升方块数)
*/
private final Map<String, Double> playerTotalRise = new ConcurrentHashMap<>();
/**
* 追踪每个玩家最近一次接触地面的时间(playerName -> 时间戳)
*/
private final Map<String, Instant> playerLastGroundContact = new ConcurrentHashMap<>();
/**
* 追踪每个玩家的振荡检测数据(playerName -> Y方向变化列表)
*/
private final Map<String, List<Double>> playerDyHistory = new ConcurrentHashMap<>();
private final AtomicLong totalChecks = new AtomicLong(0);
private final AtomicLong packetFlyViolations = new AtomicLong(0);
/**
* 单tick最大合法垂直上升(生存模式无飞行辅助)— 约为0.42 blocks/tick(跳跃峰值)
*/
private static final double MAX_SURVIVAL_VERTICAL_RISE = 0.5;
/**
* 持续空中tick阈值 — 超过此值且持续上升则标记
* 正常玩家从最高处跳下也最多在空中停留约12 ticks
*/
private static final int MAX_AIR_TICKS_NORMAL = 20;
/**
* 振荡检测窗口大小 — 在此窗口内检测方向反转次数
*/
private static final int OSCILLATION_WINDOW = 10;
/**
* 振荡检测:方向反转次数阈值 — 在窗口内反转超过此值即为振荡模式
*/
private static final int MAX_DIRECTION_REVERSALS = 4;
/**
* 累计上升高度异常阈值 — 生存模式无辅助累计上升超过此值可疑
*/
private static final double MAX_CUMULATIVE_RISE = 50.0;
/**
* 永不落地时间阈值(毫秒)— 超过30秒不落地即为异常
*/
private static final long MAX_AIR_TIME_MS = 30_000;
/**
* 每个玩家保留的最大记录数
*/
private static final int MAX_RECORDS_PER_PLAYER = 60;
/**
* 无参构造函数 — 测试/默认配置使用
*/
public AntiPacketFlyService() {
this(new ServerGuardConfig());
}
/**
* Spring 注入构造函数
* @param config 全局配置,提供模块开关控制
*/
public AntiPacketFlyService(ServerGuardConfig config) {
this.config = config;
}
/**
* 检测玩家是否使用PacketFly数据包操控飞行
*
* @param playerName 玩家名称
* @param playerUUID 玩家UUID
* @param fromX 移动起始X坐标
* @param fromY 移动起始Y坐标
* @param fromZ 移动起始Z坐标
* @param toX 移动结束X坐标
* @param toY 移动结束Y坐标
* @param toZ 移动结束Z坐标
* @param onGround 玩家是否在地面
* @param gameMode 游戏模式(survival/creative/spectator)
* @param hasElytra 是否装备鞘翅
* @param hasLevitation 是否有浮空效果
* @param isUsingFirework 是否正在使用烟花(鞘翅助推)
* @param timestamp 时间戳
* @return 检测结果
*/
public PacketFlyCheckResult detect(String playerName, String playerUUID,
double fromX, double fromY, double fromZ,
double toX, double toY, double toZ,
boolean onGround, String gameMode,
boolean hasElytra, boolean hasLevitation,
boolean isUsingFirework, Instant timestamp) {
// 模块开关检查 — 关闭时跳过所有检测
if (!config.getSecurity().getSuperEvolution().isAntiPacketFly()) {
return PacketFlyCheckResult.clean();
}
totalChecks.incrementAndGet();
// 创造/旁观模式允许飞行,跳过
if ("creative".equalsIgnoreCase(gameMode) || "spectator".equalsIgnoreCase(gameMode)) {
return PacketFlyCheckResult.clean();
}
double dy = toY - fromY;
double dx = toX - fromX;
double dz = toZ - fromZ;
double horizontalDist = Math.sqrt(dx * dx + dz * dz);
List<PacketFlyRecord> history = playerMoveHistory.computeIfAbsent(
playerName, k -> new ArrayList<>());
List<Double> dyHistory = playerDyHistory.computeIfAbsent(
playerName, k -> new ArrayList<>());
PacketFlyRecord record = new PacketFlyRecord(timestamp, fromX, fromY, fromZ,
toX, toY, toZ, dy, horizontalDist, onGround, hasElytra,
hasLevitation, isUsingFirework);
history.add(record);
dyHistory.add(dy);
while (history.size() > MAX_RECORDS_PER_PLAYER) {
history.remove(0);
}
while (dyHistory.size() > 30) {
dyHistory.remove(0);
}
// 更新空中tick计数
if (!onGround) {
int airTicks = playerAirTicks.getOrDefault(playerName, 0) + 1;
playerAirTicks.put(playerName, airTicks);
} else {
playerAirTicks.remove(playerName);
playerLastGroundContact.put(playerName, timestamp);
// 落地时重置累计上升
playerTotalRise.remove(playerName);
}
// 累计上升高度(只计正方向)
if (dy > 0) {
double totalRise = playerTotalRise.getOrDefault(playerName, 0.0) + dy;
playerTotalRise.put(playerName, totalRise);
}
// 确定玩家是否有合法飞行手段
boolean canFlyLegitimately = hasElytra || hasLevitation || isUsingFirework;
List<String> reasons = new ArrayList<>();
// 1. 核心检测:无合法手段的持续上升
if (!onGround && dy > MAX_SURVIVAL_VERTICAL_RISE && !canFlyLegitimately) {
reasons.add("IMPOSSIBLE_RISE: dy=" + String.format("%.3f", dy)
+ " blocks/tick in survival with no flight items"
+ " (max normal: " + String.format("%.2f", MAX_SURVIVAL_VERTICAL_RISE) + ")");
}
// 2. 振荡模式检测 — PacketFly的典型特征
if (dyHistory.size() >= OSCILLATION_WINDOW) {
int reversals = 0;
double prevSign = Math.signum(dyHistory.get(dyHistory.size() - OSCILLATION_WINDOW));
for (int i = dyHistory.size() - OSCILLATION_WINDOW + 1; i < dyHistory.size(); i++) {
double currentSign = Math.signum(dyHistory.get(i));
if (currentSign != 0 && prevSign != 0 && currentSign != prevSign) {
reversals++;
}
if (currentSign != 0) {
prevSign = currentSign;
}
}
// 高频振荡且振幅明显 — PacketFly特征
if (reversals >= MAX_DIRECTION_REVERSALS) {
// 检查振荡幅度是否显著
double maxDy = 0;
int start = Math.max(0, dyHistory.size() - OSCILLATION_WINDOW);
for (int i = start; i < dyHistory.size(); i++) {
maxDy = Math.max(maxDy, Math.abs(dyHistory.get(i)));
}
if (maxDy > 0.3) {
reasons.add("PACKET_FLY_OSCILLATION: " + reversals
+ " direction reversals in " + OSCILLATION_WINDOW
+ " ticks (amplitude: " + String.format("%.2f", maxDy)
+ ", typical PacketFly bounce pattern)");
}
}
}
// 3. 永不落地检测
int airTicks = playerAirTicks.getOrDefault(playerName, 0);
if (airTicks >= MAX_AIR_TICKS_NORMAL && !canFlyLegitimately) {
// 检查是否持续上升(而非下落)
long risingTicks = history.stream()
.filter(r -> r.dy > 0.05)
.count();
if (risingTicks >= MAX_AIR_TICKS_NORMAL / 2) {
reasons.add("NEVER_LANDS: " + airTicks
+ " ticks in air without touching ground"
+ " (" + risingTicks + " ticks rising, no flight items)");
}
}
// 4. 累计上升高度异常
double totalRise = playerTotalRise.getOrDefault(playerName, 0.0);
if (totalRise > MAX_CUMULATIVE_RISE && !canFlyLegitimately) {
packetFlyViolations.incrementAndGet();
reasons.add("EXCESSIVE_CUMULATIVE_RISE: " + String.format("%.1f", totalRise)
+ " blocks total rise without flight items"
+ " (threshold: " + String.format("%.0f", MAX_CUMULATIVE_RISE) + ")");
}
// 5. 空中持续时间过长(基于时间戳)
Instant lastGround = playerLastGroundContact.get(playerName);
if (lastGround != null && !canFlyLegitimately) {
long airTimeMs = timestamp.toEpochMilli() - lastGround.toEpochMilli();
if (airTimeMs > MAX_AIR_TIME_MS) {
reasons.add("PROLONGED_AIR_TIME: " + (airTimeMs / 1000)
+ " seconds without ground contact (no flight items)");
}
}
if (reasons.size() >= 2) {
return PacketFlyCheckResult.flagged(reasons);
} else if (reasons.size() == 1) {
return PacketFlyCheckResult.suspicious(reasons);
}
return PacketFlyCheckResult.clean();
}
/**
* 玩家离线时清理追踪数据
* @param playerName 离线玩家名称
*/
public void clearPlayer(String playerName) {
playerMoveHistory.remove(playerName);
playerAirTicks.remove(playerName);
playerTotalRise.remove(playerName);
playerLastGroundContact.remove(playerName);
playerDyHistory.remove(playerName);
}
/**
* 获取模块运行状态
* @return 包含计数器和违规数量的Map
*/
public Map<String, Object> getStatus() {
Map<String, Object> status = new LinkedHashMap<>();
status.put("totalChecks", totalChecks.get());
status.put("packetFlyViolations", packetFlyViolations.get());
status.put("activeTrackedPlayers", playerMoveHistory.size());
// 列出空中玩家
List<Map<String, Object>> airbornePlayers = new ArrayList<>();
for (Map.Entry<String, Integer> entry : playerAirTicks.entrySet()) {
if (entry.getValue() > MAX_AIR_TICKS_NORMAL) {
Map<String, Object> info = new LinkedHashMap<>();
info.put("player", entry.getKey());
info.put("airTicks", entry.getValue());
info.put("totalRise", String.format("%.1f",
playerTotalRise.getOrDefault(entry.getKey(), 0.0)));
airbornePlayers.add(info);
}
}
status.put("extendedAirbornePlayers", airbornePlayers);
return status;
}
/**
* 内部PacketFly移动记录 — 记录玩家单次移动的状态数据
*/
private static class PacketFlyRecord {
final Instant timestamp;
final double fromX, fromY, fromZ;
final double toX, toY, toZ;
final double dy;
final double horizontalDist;
final boolean onGround;
final boolean hasElytra;
final boolean hasLevitation;
final boolean isUsingFirework;
PacketFlyRecord(Instant timestamp, double fromX, double fromY, double fromZ,
double toX, double toY, double toZ, double dy,
double horizontalDist, boolean onGround,
boolean hasElytra, boolean hasLevitation,
boolean isUsingFirework) {
this.timestamp = timestamp;
this.fromX = fromX; this.fromY = fromY; this.fromZ = fromZ;
this.toX = toX; this.toY = toY; this.toZ = toZ;
this.dy = dy;
this.horizontalDist = horizontalDist;
this.onGround = onGround;
this.hasElytra = hasElytra;
this.hasLevitation = hasLevitation;
this.isUsingFirework = isUsingFirework;
}
}
/**
* PacketFly检测结果 — 不可变结果类
*/
public static class PacketFlyCheckResult {
private final boolean flagged;
private final boolean suspicious;
private final List<String> reasons;
private PacketFlyCheckResult(boolean flagged, boolean suspicious, List<String> reasons) {
this.flagged = flagged;
this.suspicious = suspicious;
this.reasons = reasons;
}
/** 无异常 — 正常的地面/合法飞行移动 */
public static PacketFlyCheckResult clean() {
return new PacketFlyCheckResult(false, false, List.of());
}
/** 可疑 — 短暂的异常但不满足所有检测条件 */
public static PacketFlyCheckResult suspicious(List<String> reasons) {
return new PacketFlyCheckResult(false, true, reasons);
}
/** 已标记 — 确认PacketFly hack,持续无合法手段的飞行 */
public static PacketFlyCheckResult flagged(List<String> reasons) {
return new PacketFlyCheckResult(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; }
}
}