事件机制
MSL 采用事件驱动的架构,通过事件系统实现插件与服务器之间的解耦通信。本章将详细介绍事件机制的工作原理和使用方法。
事件概述
事件是 MSL 插件系统的核心概念之一。当服务器发生特定行为时(如玩家加入、服务器启动完成等),MSL 会触发相应的事件,所有监听该事件的插件都会收到通知并执行相应的处理逻辑。
事件的优势
- 解耦:插件不需要轮询检查状态变化,只需监听感兴趣的事件
- 高效:事件只在发生时触发,不会浪费系统资源
- 灵活:多个插件可以同时监听同一事件,互不干扰
- 可扩展:插件可以触发自定义事件,实现插件间通信
原生事件
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);
});参数:
| 参数 | 类型 | 说明 |
|---|---|---|
time | string | 事件发生的时间(格式:HH:mm:ss) |
player | string | 玩家游戏名称 |
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);
});参数:
| 参数 | 类型 | 说明 |
|---|---|---|
time | string | 事件发生的时间(格式:HH:mm:ss) |
player | string | 玩家游戏名称 |
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} 不当言论`);
}
});参数:
| 参数 | 类型 | 说明 |
|---|---|---|
time | string | 事件发生的时间(格式:HH:mm:ss) |
player | string | 玩家游戏名称 |
message | string | 聊天消息内容 |
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}`);
}
});参数:
| 参数 | 类型 | 说明 |
|---|---|---|
time | string | 事件发生的时间(格式:HH:mm:ss) |
player | string | 玩家游戏名称 |
command | string | 指令名称(不含 /) |
args | string[] | 指令参数数组 |
插件事件
pluginLoaded
插件加载完成时触发。
javascript
plugin_onEvent("pluginLoaded", (pluginName) => {
plugin_log("INFO", `插件 ${pluginName} 已加载`);
});参数:
| 参数 | 类型 | 说明 |
|---|---|---|
pluginName | string | 加载的插件名称 |
触发时机: 当一个插件成功加载并执行完毕后触发。
日志事件
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}`);
// 可以在这里实现自动报警功能
}
});参数:
| 参数 | 类型 | 说明 |
|---|---|---|
line | string | 服务器日志的一行内容 |
注意
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 | 无 | 服务器启动完成 |
playerJoin | time, player | 玩家加入服务器 |
playerQuit | time, player | 玩家离开服务器 |
playerSendMessage | time, player, message | 玩家发送聊天消息 |
playerSendCommand | time, player, command, args | 玩家执行指令 |
pluginLoaded | pluginName | 插件加载完成 |
serverLog | line | 服务器输出日志 |
下一步
- 注册指令 - 学习如何注册自定义指令
- 执行指令 - 学习如何执行 Minecraft 指令
- API 参考 - plugin_onEvent - 查看详细 API 文档