Skip to content

事件机制

MSL 采用事件驱动的架构,通过事件系统实现插件与服务器之间的解耦通信。本章将详细介绍事件机制的工作原理和使用方法。

事件概述

事件是 MSL 插件系统的核心概念之一。当服务器发生特定行为时(如玩家加入、服务器启动完成等),MSL 会触发相应的事件,所有监听该事件的插件都会收到通知并执行相应的处理逻辑。

事件的优势

  1. 解耦:插件不需要轮询检查状态变化,只需监听感兴趣的事件
  2. 高效:事件只在发生时触发,不会浪费系统资源
  3. 灵活:多个插件可以同时监听同一事件,互不干扰
  4. 可扩展:插件可以触发自定义事件,实现插件间通信

原生事件

MSL 内置了以下原生事件,无需手动触发:

服务器生命周期事件

serverStart

服务器进程启动时触发。

javascript
plugin_onEvent("serverStart", () => {
    plugin_log("INFO", "Minecraft 服务器进程已启动");
});

参数:

触发时机: 当 Minecraft 服务器进程被创建时立即触发,此时服务器可能还在启动中。


serverStop

服务器进程停止时触发。

javascript
plugin_onEvent("serverStop", () => {
    plugin_log("INFO", "Minecraft 服务器进程已停止");
});

参数:

触发时机: 当 Minecraft 服务器进程退出时触发,无论是正常关闭还是崩溃。


serverDone

服务器启动完成时触发。

javascript
plugin_onEvent("serverDone", () => {
    plugin_log("INFO", "服务器启动完成,玩家可以加入了!");
    
    // 执行启动后的初始化操作
    plugin_executeCommand("say 服务器已就绪");
});

参数:

触发时机: 当服务器日志中出现 Done (xxx.xxxs)! 信息时触发,表示服务器已完成启动,可以接受玩家连接。


玩家事件

playerJoin

玩家加入服务器时触发。

javascript
plugin_onEvent("playerJoin", (time, player) => {
    plugin_log("INFO", `[${time}] 玩家 ${player} 加入了服务器`);
    
    // 发送欢迎消息
    plugin_executeCommand(`tellraw ${player} {"text":"欢迎来到服务器!","color":"green"}`);
    
    // 记录玩家数据
    const joinData = plugin_pull("playerJoins") || {};
    joinData[player] = (joinData[player] || 0) + 1;
    plugin_push("playerJoins", joinData);
});

参数:

参数类型说明
timestring事件发生的时间(格式:HH:mm:ss)
playerstring玩家游戏名称

playerQuit

玩家离开服务器时触发。

javascript
plugin_onEvent("playerQuit", (time, player) => {
    plugin_log("INFO", `[${time}] 玩家 ${player} 离开了服务器`);
    
    // 记录离线时间
    const quitTimes = plugin_pull("playerQuitTimes") || {};
    quitTimes[player] = new Date().toISOString();
    plugin_push("playerQuitTimes", quitTimes);
});

参数:

参数类型说明
timestring事件发生的时间(格式:HH:mm:ss)
playerstring玩家游戏名称

playerSendMessage

玩家发送聊天消息时触发。

javascript
plugin_onEvent("playerSendMessage", (time, player, message) => {
    plugin_log("INFO", `[${time}] ${player}: ${message}`);
    
    // 聊天记录
    const chatLog = plugin_pull("chatLog") || [];
    chatLog.push({ time, player, message, timestamp: Date.now() });
    plugin_push("chatLog", chatLog);
    
    // 关键词过滤
    const bannedWords = ["badword1", "badword2"];
    if (bannedWords.some(word => message.toLowerCase().includes(word))) {
        plugin_executeCommand(`kick ${player} 不当言论`);
    }
});

参数:

参数类型说明
timestring事件发生的时间(格式:HH:mm:ss)
playerstring玩家游戏名称
messagestring聊天消息内容

playerSendCommand

玩家执行指令时触发。

javascript
plugin_onEvent("playerSendCommand", (time, player, command, args) => {
    plugin_log("INFO", `[${time}] ${player} 执行了 /${command} ${args.join(" ")}`);
    
    // 指令日志
    const commandLog = plugin_pull("commandLog") || [];
    commandLog.push({
        time,
        player,
        command,
        args,
        fullCommand: `/${command} ${args.join(" ")}`
    });
    plugin_push("commandLog", commandLog);
    
    // 监控敏感指令
    const sensitiveCommands = ["op", "deop", "ban", "pardon"];
    if (sensitiveCommands.includes(command.toLowerCase())) {
        plugin_log("WARN", `敏感指令执行: ${player} 执行了 /${command}`);
    }
});

参数:

参数类型说明
timestring事件发生的时间(格式:HH:mm:ss)
playerstring玩家游戏名称
commandstring指令名称(不含 /)
argsstring[]指令参数数组

插件事件

pluginLoaded

插件加载完成时触发。

javascript
plugin_onEvent("pluginLoaded", (pluginName) => {
    plugin_log("INFO", `插件 ${pluginName} 已加载`);
});

参数:

参数类型说明
pluginNamestring加载的插件名称

触发时机: 当一个插件成功加载并执行完毕后触发。


日志事件

serverLog

服务器输出每一行日志时触发。

javascript
plugin_onEvent("serverLog", (line) => {
    // 监控警告日志
    if (line.includes("WARN")) {
        plugin_log("WARN", `服务器警告: ${line}`);
    }
    
    // 监控错误日志
    if (line.includes("ERROR") || line.includes("Exception")) {
        plugin_log("ERROR", `服务器错误: ${line}`);
        
        // 可以在这里实现自动报警功能
    }
});

参数:

参数类型说明
linestring服务器日志的一行内容

注意

serverLog 事件非常频繁,不建议在此事件中执行耗时操作。同时,此事件不会输出调试日志以避免刷屏。

自定义事件

除了原生事件,插件还可以触发和监听自定义事件,实现插件间的通信。

触发自定义事件

使用 plugin_triggerEvent 触发自定义事件:

javascript
// 触发一个简单事件
plugin_triggerEvent("myCustomEvent");

// 触发带参数的事件
plugin_triggerEvent("playerAchievement", "Steve", "获得成就:第一次死亡");

监听自定义事件

使用 plugin_onEvent 监听自定义事件:

javascript
// 监听自定义事件
plugin_onEvent("playerAchievement", (player, achievement) => {
    plugin_log("INFO", `${player} ${achievement}`);
    
    // 广播成就
    plugin_executeCommand(`say ${player} ${achievement}`);
});

插件间通信示例

插件 A(触发事件):

javascript
// my-economy.js
plugin_onEvent("playerJoin", (time, player) => {
    // 检查玩家经济数据
    const economy = plugin_pull("economy") || {};
    const balance = economy[player] || 0;
    
    // 触发自定义事件,通知其他插件
    plugin_triggerEvent("economyChecked", player, balance);
});

插件 B(监听事件):

javascript
// my-welcome.js
plugin_onEvent("economyChecked", (player, balance) => {
    // 根据余额发送不同的欢迎消息
    if (balance > 1000) {
        plugin_executeCommand(`tellraw ${player} {"text":"尊贵的VIP玩家,欢迎回来!","color":"gold"}`);
    } else {
        plugin_executeCommand(`tellraw ${player} {"text":"欢迎来到服务器!","color":"white"}`);
    }
});

事件监听最佳实践

1. 错误处理

始终在事件回调中添加错误处理:

javascript
plugin_onEvent("playerJoin", (time, player) => {
    try {
        // 可能出错的操作
        processPlayerJoin(player);
    } catch (error) {
        plugin_log("ERROR", `处理玩家加入事件失败: ${error.message}`);
    }
});

2. 避免阻塞

事件回调应该是非阻塞的,避免执行耗时操作:

javascript
// 不推荐:阻塞式操作
plugin_onEvent("playerJoin", (time, player) => {
    // 模拟耗时操作
    const start = Date.now();
    while (Date.now() - start < 1000) {}  // 阻塞 1 秒
});

// 推荐:使用定时器异步处理
plugin_onEvent("playerJoin", (time, player) => {
    setTimeout(() => {
        // 异步执行耗时操作
        processPlayerData(player);
    }, 0);
});

3. 事件命名规范

使用有意义的事件名称,避免冲突:

javascript
// 推荐:使用插件名作为前缀
plugin_triggerEvent("myPlugin:playerLevelUp", player, level);

// 不推荐:通用名称可能冲突
plugin_triggerEvent("levelUp", player, level);

4. 文档化自定义事件

关于格式

文档化的核心是开发者方便,而不是统一和规范。团队之间可以协商一致的文档格式。

为你的自定义事件编写文档:

javascript
/**
 * 自定义事件:myPlugin:playerLevelUp
 * 
 * 参数:
 * - player (string): 玩家名称
 * - level (number): 新等级
 * - experience (number): 当前经验值
 * 
 * 触发时机:当玩家等级提升时
 */
plugin_triggerEvent("myPlugin:playerLevelUp", player, newLevel, currentExp);

事件列表速查

事件参数触发时机
serverStart服务器进程启动
serverStop服务器进程停止
serverDone服务器启动完成
playerJointime, player玩家加入服务器
playerQuittime, player玩家离开服务器
playerSendMessagetime, player, message玩家发送聊天消息
playerSendCommandtime, player, command, args玩家执行指令
pluginLoadedpluginName插件加载完成
serverLogline服务器输出日志

下一步