服务器事件
服务器事件用于监听 Minecraft 服务器内的玩家活动,包括玩家登录、消息发送、死亡、进出等事件。这些事件是实现游戏内外联动的核心功能。
事件列表
player_login
功能描述: 当玩家尝试登录服务器时触发,支持自定义登录验证逻辑。
语法:
bus.on("player_login", function(server, playerName, playerUuid) {
// 处理玩家登录逻辑
// 可以返回登录结果来控制登录行为
});
参数:
server
(Bridge): 服务器 桥接对象playerName
(string): 玩家游戏名playerUuid
(string): 玩家唯一标识符
返回值:
void
或undefined
: 不拦截登录,玩家正常登录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:
- Bridge.NotifyPlayerBindSuccess() - 通知玩家绑定成功
- Bridge.NotifyPlayerUnBind() - 通知玩家解绑
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): 消息数据包,包含消息内容、发送者等信息
返回值:
void
或undefined
: 允许 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:
- Bridge.SendRunCommandAsync() - 执行服务器命令
- Bridge.SendMessageToAllPlayer() - 发送广播消息
player_death
功能描述: 当玩家在服务器死亡时触发,支持自定义死亡消息处理。
语法:
bus.on("player_death", function(server, packet) {
// 处理玩家死亡逻辑
// 可以返回 true 来拦截死亡事件同步
});
参数:
server
(Bridge): 服务器桥接对象packet
(SyncDeathMessagePacket): 死亡消息数据包,包含死亡详情、玩家信息等
返回值:
void
或undefined
: 允许 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): 进出事件数据包,包含玩家状态、时间戳等信息
返回值:
void
或undefined
: 允许 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 参考
- Bridge API - 桥接对象的完整 API 文档
- Sessions API - 会话管理相关功能
- Database API - 数据库操作接口
- 事件介绍 - 事件系统基础概念
注意事项
- 返回值控制: 注意区分不同事件的返回值含义和作用
- 性能考虑:
player_message
是高频事件,避免在其中执行耗时操作 - 数据一致性: 使用
report_player
事件同步玩家数据时注意数据一致性 - 错误处理: 在事件处理函数中使用 try-catch 避免异常影响其他监听器
- 权限检查: 执行服务器命令前确保有足够的权限