跳到主要内容

服务器事件

服务器事件用于监听 Minecraft 服务器内的玩家活动,包括玩家登录、消息发送、死亡、进出等事件。这些事件是实现游戏内外联动的核心功能。

事件列表

player_login

功能描述: 当玩家尝试登录服务器时触发,支持自定义登录验证逻辑。

语法:

bus.on("player_login", function(server, playerName, playerUuid) {
// 处理玩家登录逻辑
// 可以返回登录结果来控制登录行为
});

参数:

  • server (Bridge): 服务器桥接对象
  • playerName (string): 玩家游戏名
  • playerUuid (string): 玩家唯一标识符

返回值:

  • voidundefined: 不拦截登录,玩家正常登录
  • PlayerLoginResultPacket: 拦截登录,使用自定义登录结果
    • kicked: true: 玩家被踢出服务器
    • kicked: false: 玩家不被踢出,但拦截 EasyBot 后续操作

使用示例:

// 基础登录监听
bus.on("player_login", function(server, playerName, playerUuid) {
logger.info("玩家 {0} ({1}) 正在登录服务器", playerName, playerUuid);

// 记录登录时间
const loginTime = new Date().toLocaleString();
logger.info("登录时间: {0}", loginTime);
});

// 自定义登录验证
bus.on("player_login", function(server, playerName, playerUuid) {
// 检查玩家是否在黑名单中
if (isPlayerBanned(playerName)) {
return {
kicked: true,
reason: "您已被服务器封禁"
};
}

// 检查服务器是否维护中
if (isServerMaintenance()) {
return {
kicked: true,
reason: "服务器维护中,请稍后再试"
};
}

// 允许正常登录
return undefined;
});

相关 API:


report_player

功能描述: 当服务器上报玩家信息时触发,用于同步玩家数据到 EasyBot。

语法:

bus.on("report_player", function(server, playerName, playerUuid, playerIp) {
// 处理玩家数据更新逻辑
});

参数:

  • server (Bridge): 服务器桥接对象
  • playerName (string): 玩家游戏名
  • playerUuid (string): 玩家唯一标识符
  • playerIp (string): 玩家 IP 地址

返回值: 无(返回值会被系统忽略)

使用示例:

bus.on("report_player", function(server, playerName, playerUuid, playerIp) {
logger.info("更新玩家数据: {0} ({1}) IP: {2}", playerName, playerUuid, playerIp);

// 记录玩家信息到数据库
db.Set(`player:${playerUuid}:name`, playerName);
db.Set(`player:${playerUuid}:ip`, playerIp);
db.Set(`player:${playerUuid}:lastSeen`, new Date().toISOString());

// 检查IP变化
const lastIp = db.GetString(`player:${playerUuid}:lastIp`);
if (lastIp && lastIp !== playerIp) {
logger.warning("玩家 {0} IP 发生变化: {1} -> {2}", playerName, lastIp, playerIp);
}
db.Set(`player:${playerUuid}:lastIp`, playerIp);
});

注意事项:

  • 此事件仅用于通知,无法阻止 EasyBot 更新内部玩家数据
  • 适用于自定义玩家数据统计和分析

player_message

功能描述: 当玩家在服务器发送消息时触发,支持消息过滤和自定义处理。

语法:

bus.on("player_message", function(server, packet) {
// 处理玩家消息逻辑
// 可以返回 true 来拦截消息同步
});

参数:

  • server (Bridge): 服务器桥接对象
  • packet (SyncMessagePacket): 消息数据包,包含消息内容、发送者等信息

返回值:

  • voidundefined: 允许 EasyBot 继续处理消息同步
  • true: 拦截消息同步,EasyBot 跳过内部处理流程

使用示例:

// 基础消息监听
bus.on("player_message", function(server, packet) {
logger.info("玩家消息: [{0}] {1}: {2}",
server.Session, packet.playerName, packet.message);
});

// 消息过滤和处理
bus.on("player_message", function(server, packet) {
const message = packet.message;
const playerName = packet.playerName;

// 过滤敏感词
if (containsBadWords(message)) {
logger.warning("检测到敏感词,来自玩家: {0}", playerName);

// 向玩家发送警告
server.SendRunCommandAsync(playerName, "tell " + playerName + " 请注意言辞", false);

// 拦截消息同步
return true;
}

// 处理特殊命令
if (message.startsWith("!help")) {
server.SendRunCommandAsync(playerName, "tell " + playerName + " 输入 /help 查看帮助", false);
return true;
}

// 允许正常同步
return undefined;
});

相关 API:


player_death

功能描述: 当玩家在服务器死亡时触发,支持自定义死亡消息处理。

语法:

bus.on("player_death", function(server, packet) {
// 处理玩家死亡逻辑
// 可以返回 true 来拦截死亡事件同步
});

参数:

  • server (Bridge): 服务器桥接对象
  • packet (SyncDeathMessagePacket): 死亡消息数据包,包含死亡详情、玩家信息等

返回值:

  • voidundefined: 允许 EasyBot 继续处理死亡事件同步
  • true: 拦截死亡事件同步,EasyBot 跳过内部处理流程

使用示例:

// 基础死亡事件监听
bus.on("player_death", function(server, packet) {
logger.info("玩家死亡: [{0}] {1} - {2}",
server.Session, packet.playerName, packet.deathMessage);
});

// 死亡统计和特殊处理
bus.on("player_death", function(server, packet) {
const playerName = packet.playerName;
const deathMessage = packet.deathMessage;

// 统计死亡次数
const deathCount = db.GetNumber(`player:${playerName}:deaths`, 0) + 1;
db.Set(`player:${playerName}:deaths`, deathCount);

// 记录死亡原因
db.Set(`player:${playerName}:lastDeath`, {
message: deathMessage,
time: new Date().toISOString(),
count: deathCount
});

// 特殊死亡处理
if (deathMessage.includes("fell from a high place")) {
server.SendMessageToAllPlayer(`§e${playerName} 又摔死了!这是第 ${deathCount} 次摔死`);
return true; // 拦截默认死亡消息
}

// 死亡次数里程碑
if (deathCount % 10 === 0) {
server.SendMessageToAllPlayer(`§c${playerName} 已经死亡 ${deathCount} 次了!`);
}

return undefined;
});

player_join_or_exit

功能描述: 当玩家加入或退出服务器时触发,支持自定义进出消息处理。

语法:

bus.on("player_join_or_exit", function(server, packet) {
// 处理玩家进出逻辑
// 可以返回 true 来拦截进出事件同步
});

参数:

  • server (Bridge): 服务器桥接对象
  • packet (SyncEnterExitMessagePacket): 进出事件数据包,包含玩家状态、时间戳等信息

返回值:

  • voidundefined: 允许 EasyBot 继续处理进出事件同步
  • true: 拦截进出事件同步,EasyBot 跳过内部处理流程

使用示例:

// 基础进出事件监听
bus.on("player_join_or_exit", function(server, packet) {
const action = packet.isJoin ? "加入" : "退出";
logger.info("玩家{0}: [{1}] {2}", action, server.Session, packet.playerName);
});

// 自定义欢迎和告别消息
bus.on("player_join_or_exit", function(server, packet) {
const playerName = packet.playerName;

if (packet.isJoin) {
// 玩家加入
const joinCount = db.GetNumber(`player:${playerName}:joinCount`, 0) + 1;
db.Set(`player:${playerName}:joinCount`, joinCount);

// 首次加入特殊欢迎
if (joinCount === 1) {
server.SendMessageToAllPlayer(`§a欢迎新玩家 ${playerName} 首次加入服务器!`);
server.SendRunCommandAsync(playerName, "give " + playerName + " minecraft:bread 10", false);
return true; // 拦截默认加入消息
}

// 记录加入时间
db.Set(`player:${playerName}:lastJoin`, new Date().toISOString());

} else {
// 玩家退出
const joinTime = db.GetString(`player:${playerName}:lastJoin`);
if (joinTime) {
const playTime = new Date() - new Date(joinTime);
const minutes = Math.floor(playTime / 60000);

if (minutes > 0) {
server.SendMessageToAllPlayer(`§e${playerName} 离开了服务器,本次游戏时长: ${minutes} 分钟`);
return true; // 拦截默认退出消息
}
}
}

return undefined;
});

实用示例

1. 玩家活动统计系统

// 玩家活动统计
let playerStats = {};

bus.on("player_login", function(server, playerName, playerUuid) {
// 初始化玩家统计
if (!playerStats[playerUuid]) {
playerStats[playerUuid] = {
name: playerName,
loginCount: 0,
messageCount: 0,
deathCount: 0,
firstSeen: new Date(),
lastSeen: new Date()
};
}

playerStats[playerUuid].loginCount++;
playerStats[playerUuid].lastSeen = new Date();

logger.info("玩家 {0} 第 {1} 次登录", playerName, playerStats[playerUuid].loginCount);
});

bus.on("player_message", function(server, packet) {
const playerUuid = packet.playerUuid;
if (playerStats[playerUuid]) {
playerStats[playerUuid].messageCount++;
}
});

bus.on("player_death", function(server, packet) {
const playerUuid = packet.playerUuid;
if (playerStats[playerUuid]) {
playerStats[playerUuid].deathCount++;
}
});

2. 聊天监控和管理

// 聊天监控系统
let chatHistory = [];
let spamDetection = {};

bus.on("player_message", function(server, packet) {
const playerName = packet.playerName;
const message = packet.message;
const now = Date.now();

// 记录聊天历史
chatHistory.push({
server: server.Session,
player: playerName,
message: message,
time: now
});

// 保持历史记录在合理范围内
if (chatHistory.length > 1000) {
chatHistory = chatHistory.slice(-500);
}

// 刷屏检测
if (!spamDetection[playerName]) {
spamDetection[playerName] = [];
}

spamDetection[playerName].push(now);
spamDetection[playerName] = spamDetection[playerName].filter(time => now - time < 10000); // 10秒内

if (spamDetection[playerName].length > 5) {
logger.warning("检测到玩家 {0} 可能在刷屏", playerName);
server.SendRunCommandAsync(playerName, "tell " + playerName + " 请不要刷屏", false);
return true; // 拦截消息
}

return undefined;
});

3. 新手引导系统

// 新手引导系统
bus.on("player_login", function(server, playerName, playerUuid) {
const isNewPlayer = !db.Exists(`player:${playerUuid}:welcomed`);

if (isNewPlayer) {
// 标记为已欢迎
db.Set(`player:${playerUuid}:welcomed`, true);

// 延迟发送欢迎消息
setTimeout(function() {
server.SendRunCommandAsync(playerName, "tell " + playerName + " §a欢迎来到服务器!输入 /help 查看帮助", false);
server.SendRunCommandAsync(playerName, "give " + playerName + " minecraft:bread 5", false);
server.SendRunCommandAsync(playerName, "give " + playerName + " minecraft:wooden_sword 1", false);
}, 2000);
}
});

bus.on("player_join_or_exit", function(server, packet) {
if (packet.isJoin) {
const playerName = packet.playerName;
const joinCount = db.GetNumber(`player:${playerName}:joinCount`, 0) + 1;
db.Set(`player:${playerName}:joinCount`, joinCount);

// 新手提示
if (joinCount <= 3) {
setTimeout(function() {
server.SendRunCommandAsync(playerName, "tell " + playerName + " §e提示: 按 T 键打开聊天,输入消息与其他玩家交流", false);
}, 5000);
}
}
});

4. 服务器安全监控

// 安全监控系统
let securityAlerts = {};

bus.on("player_login", function(server, playerName, playerUuid) {
// 检查玩家名称是否可疑
if (playerName.match(/^[a-zA-Z0-9_]{1,2}$/) || playerName.includes("bot")) {
logger.warning("可疑玩家名称: {0} ({1})", playerName, playerUuid);

return {
kicked: true,
reason: "可疑的玩家名称"
};
}
});

bus.on("report_player", function(server, playerName, playerUuid, playerIp) {
// IP 变化监控
const lastIp = db.GetString(`player:${playerUuid}:ip`);
if (lastIp && lastIp !== playerIp) {
logger.info("玩家 {0} IP 变化: {1} -> {2}", playerName, lastIp, playerIp);

// 检查是否为已知的代理IP
if (isProxyIP(playerIp)) {
logger.warning("玩家 {0} 使用代理IP: {1}", playerName, playerIp);
}
}

db.Set(`player:${playerUuid}:ip`, playerIp);
});

bus.on("player_message", function(server, packet) {
const message = packet.message.toLowerCase();

// 检测恶意链接
if (message.includes("http") && (message.includes("hack") || message.includes("cheat"))) {
logger.error("检测到可疑链接,来自玩家: {0}", packet.playerName);
server.SendRunCommandAsync(packet.playerName, "kick " + packet.playerName + " 发送可疑链接", false);
return true;
}

return undefined;
});

相关 API 参考

注意事项

  1. 返回值控制: 注意区分不同事件的返回值含义和作用
  2. 性能考虑: player_message 是高频事件,避免在其中执行耗时操作
  3. 数据一致性: 使用 report_player 事件同步玩家数据时注意数据一致性
  4. 错误处理: 在事件处理函数中使用 try-catch 避免异常影响其他监听器
  5. 权限检查: 执行服务器命令前确保有足够的权限