Files
Aluer/src/main/java/com/aluer/plugin/AluerPlugin.java
T
Peijun Zhao 030ced87f9 V5.0 Paper插件架构:深度植入Minecraft服务器进程,实现完美实时防护
核心变更:
- 新增 PaperMC 插件层(14个新文件):主插件入口 AluerPlugin + 10个 Bukkit 事件监听器 + DataBridge 数据桥接 + InternalCommandExecutor 内部命令执行器
- 运行时注入服务器进程,直接访问 Bukkit/Paper API,取消 RCON 中间层
- 9种反作弊实时检测(Speed/Fly/Jesus/NoFall/KillAura/Reach/AutoClicker/Scaffold/Nuker)全部迁移为事件驱动
- 聊天安全(刷屏/广告/钓鱼)与命令滥用检测由 AsyncPlayerChatEvent/PlayerCommandPreprocessEvent 直接拦截
- AlertType 枚举扩展至 47 项告警类型,覆盖反作弊/玩家行为/服务器保护/聊天安全/网络/主机全部维度
- 双模式兼容:serverguard.mode=plugin(嵌入式)| external(传统外部监控),旧模式代码完整保留
- Maven 构建切换为 shade 插件,生成 Paper 插件兼容的扁平 JAR
- 278 项全量测试通过,0 失败
2026-05-16 15:50:48 +08:00

186 lines
7.4 KiB
Java

package com.aluer.plugin;
import com.aluer.ServerGuardApplication;
import com.aluer.plugin.bridge.DataBridge;
import com.aluer.plugin.listener.*;
import org.bukkit.Bukkit;
import org.bukkit.event.HandlerList;
import org.bukkit.plugin.java.JavaPlugin;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ConfigurableApplicationContext;
/**
* Aluer ServerGuard — PaperMC 插件主入口
*
* 插件加载时启动嵌入式 Spring Boot 上下文,
* 注册所有 Bukkit 事件监听器,通过 DataBridge
* 将服务器内部实时数据推送至安全分析引擎。
*
* 架构:
* Bukkit Events → Event Listeners → DataBridge → Security Services → AI → AutoExecutor → Bukkit API
*/
public class AluerPlugin extends JavaPlugin {
private static final Logger logger = LoggerFactory.getLogger(AluerPlugin.class);
/** 全局单例引用,供监听器在 Spring 上下文就绪前获取插件实例 */
private static AluerPlugin instance;
/** Spring Boot 应用上下文,插件关闭时需 gracefully shutdown */
private ConfigurableApplicationContext springContext;
/** 数据桥接中心,连接 Bukkit 事件与安全服务 */
private DataBridge dataBridge;
/** Spring 上下文是否已就绪 */
private volatile boolean springReady = false;
// ─── 生命周期 ────────────────────────────────────────
@Override
public void onLoad() {
instance = this;
logger.info("Aluer ServerGuard v4.0.0 — PaperMC Plugin loading (LOAD STARTUP)");
}
@Override
public void onEnable() {
long startMs = System.currentTimeMillis();
// 步骤 1:启动嵌入式 Spring Boot(异步,避免阻塞 Paper 主线程)
logger.info("Starting embedded Spring Boot context...");
new Thread(() -> {
try {
// 设置 Paper 插件模式标志
System.setProperty("serverguard.mode", "plugin");
System.setProperty("server.mode", "plugin");
springContext = ServerGuardApplication.bootstrap();
dataBridge = springContext.getBean(DataBridge.class);
springReady = true;
logger.info("Spring Boot context ready — all security services initialized");
} catch (Exception e) {
logger.error("Failed to start Spring Boot context: {}", e.getMessage(), e);
}
}, "aluer-spring-boot").start();
// 步骤 2:等待 Spring 上下文就绪(最长 30 秒)
int waited = 0;
while (!springReady && waited < 30) {
try {
Thread.sleep(1000);
waited++;
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
break;
}
}
if (!springReady) {
logger.error("Spring Boot failed to start within 30s — plugin will operate in degraded mode");
}
// 步骤 3:注册 Bukkit 事件监听器
registerEventListeners();
// 步骤 4:注册插件命令
registerCommands();
long elapsed = System.currentTimeMillis() - startMs;
logger.info("Aluer ServerGuard enabled in {}ms — real-time protection active", elapsed);
logger.info(" Listeners: 10 event handlers registered");
logger.info(" Commands: /aluer, /aluerstatus, /aluerplayers, /aluerblock, /aluerunblock, /aluerscan, /aluerwhitelist");
logger.info(" Web Console: http://0.0.0.0:8080/");
}
@Override
public void onDisable() {
logger.info("Shutting down Aluer ServerGuard...");
// 注销所有 Bukkit 事件监听器
HandlerList.unregisterAll(this);
// 关闭 Spring Boot 上下文
if (springContext != null) {
springContext.close();
springContext = null;
}
instance = null;
logger.info("Aluer ServerGuard disabled — protection stopped");
}
// ─── 事件监听器注册 ──────────────────────────────────
private void registerEventListeners() {
// 注意:监听器在构造函数中接收 DataBridge 引用
// 如果 Spring 未就绪则使用 null(监听器内部做空安全处理)
DataBridge bridge = springReady ? dataBridge : null;
// 核心监听器:玩家、战斗、聊天、命令
Bukkit.getPluginManager().registerEvents(new PlayerEventListener(this, bridge), this);
Bukkit.getPluginManager().registerEvents(new CombatEventListener(this, bridge), this);
Bukkit.getPluginManager().registerEvents(new ChatEventListener(this, bridge), this);
Bukkit.getPluginManager().registerEvents(new CommandEventListener(this, bridge), this);
// 方块与物品监听器
Bukkit.getPluginManager().registerEvents(new BlockEventListener(this, bridge), this);
Bukkit.getPluginManager().registerEvents(new InventoryEventListener(this, bridge), this);
// 实体与世界监听器
Bukkit.getPluginManager().registerEvents(new EntityEventListener(this, bridge), this);
Bukkit.getPluginManager().registerEvents(new WorldEventListener(this, bridge), this);
// 包级监听器(Paper API 特有,用于精确的包速率控制)
if (Bukkit.getServer().getClass().getPackageName().contains("paper")) {
Bukkit.getPluginManager().registerEvents(new PacketEventListener(this, bridge), this);
}
}
// ─── 命令注册 ────────────────────────────────────────
private void registerCommands() {
AluerCommandExecutor executor = new AluerCommandExecutor(this, dataBridge);
var aluerCmd = getCommand("aluer");
if (aluerCmd != null) {
aluerCmd.setExecutor(executor);
aluerCmd.setTabCompleter(executor);
}
var statusCmd = getCommand("aluerstatus");
if (statusCmd != null) statusCmd.setExecutor(executor);
var playersCmd = getCommand("aluerplayers");
if (playersCmd != null) playersCmd.setExecutor(executor);
var blockCmd = getCommand("aluerblock");
if (blockCmd != null) blockCmd.setExecutor(executor);
var unblockCmd = getCommand("aluerunblock");
if (unblockCmd != null) unblockCmd.setExecutor(executor);
var scanCmd = getCommand("aluerscan");
if (scanCmd != null) scanCmd.setExecutor(executor);
var whitelistCmd = getCommand("aluerwhitelist");
if (whitelistCmd != null) whitelistCmd.setExecutor(executor);
}
// ─── 公开访问器 ──────────────────────────────────────
/** 获取插件单例实例 */
public static AluerPlugin getInstance() {
return instance;
}
/** 获取 Spring 应用上下文(可能为 null,在 Spring 就绪前) */
public ConfigurableApplicationContext getSpringContext() {
return springContext;
}
/** 获取 DataBridge(可能为 null,在 Spring 就绪前) */
public DataBridge getDataBridge() {
return dataBridge;
}
/** Spring 上下文是否已完全初始化 */
public boolean isSpringReady() {
return springReady;
}
}