@baipiaodajun/mcbots 1.0.5 → 1.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -0
- package/package.json +1 -1
- package/server.js +120 -35
package/README.md
CHANGED
|
@@ -1,6 +1,16 @@
|
|
|
1
1
|
# 🛡️ Minecraft 保活机器人及监控面板 - @baipiaodajun/mcbots
|
|
2
2
|
|
|
3
|
+
## 特性
|
|
4
|
+
|
|
5
|
+
- 多服务器配置:支持通过 SERVERS_JSON 文件一次性定义多个 MC 服务器,并为每个服务器自动创建对应的机器人连接。
|
|
6
|
+
- 智能网络检测:支持连接超时监控,自动在 30 秒后尝试重连。
|
|
7
|
+
- 实时监控面板:机器人运行状态即时更新,便于统一管理与观察。
|
|
8
|
+
- 多实例支持:同一服务器可同时连接并管理多个机器人。
|
|
9
|
+
- 可配置行为变量:通过设置 CHAT 与 MOVE 环境变量,灵活控制机器人聊天或移动行为。
|
|
10
|
+
|
|
11
|
+
|
|
3
12
|
## 用法
|
|
13
|
+
|
|
4
14
|
### nodejs
|
|
5
15
|
新建一个 index.js 文件,内容如下:
|
|
6
16
|
其中内容替换成你自己的MC服务器地址和端口,其他可以根据情况改变,可以添加多台服务器。
|
package/package.json
CHANGED
package/server.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
const mineflayer = require('mineflayer');
|
|
2
2
|
const express = require('express');
|
|
3
|
+
const net = require('net');
|
|
3
4
|
const PORT = process.env.SERVER_PORT || process.env.PORT || 3000 ;
|
|
4
5
|
const CHAT = process.env.CHAT || false ;
|
|
5
6
|
const MOVE = process.env.MOVE || false ;
|
|
@@ -30,21 +31,54 @@ if (process && process.env && process.env.SERVERS_JSON) {
|
|
|
30
31
|
process.exit(1);
|
|
31
32
|
}
|
|
32
33
|
}
|
|
34
|
+
/**
|
|
35
|
+
* 测试 IP 和端口是否可达
|
|
36
|
+
* @param {string} host - IP 或域名
|
|
37
|
+
* @param {number} port - 端口号
|
|
38
|
+
* @param {number} timeoutMs - 超时时间(毫秒)
|
|
39
|
+
* @returns {Promise<boolean>}
|
|
40
|
+
*/
|
|
41
|
+
function testConnection(host, port, timeoutMs = 5000) {
|
|
42
|
+
return new Promise((resolve) => {
|
|
43
|
+
const socket = new net.Socket();
|
|
44
|
+
|
|
45
|
+
// 成功连接
|
|
46
|
+
socket.once('connect', () => {
|
|
47
|
+
socket.destroy();
|
|
48
|
+
resolve(true);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
// 出错或拒绝连接
|
|
52
|
+
socket.once('error', () => {
|
|
53
|
+
socket.destroy();
|
|
54
|
+
resolve(false);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
// 超时
|
|
58
|
+
socket.setTimeout(timeoutMs, () => {
|
|
59
|
+
socket.destroy();
|
|
60
|
+
resolve(false);
|
|
61
|
+
});
|
|
33
62
|
|
|
63
|
+
// 尝试连接
|
|
64
|
+
socket.connect(port, host);
|
|
65
|
+
});
|
|
66
|
+
}
|
|
34
67
|
// 配置常量
|
|
35
68
|
const BOT_CONFIG = {
|
|
36
69
|
reconnectDelay: 5000,
|
|
37
70
|
maxReconnectAttempts: 5,
|
|
38
71
|
healthCheckInterval: 60000,
|
|
39
72
|
viewDistance: 4,
|
|
73
|
+
connectTimeout: 2000,
|
|
40
74
|
chat: CHAT,
|
|
41
75
|
move: MOVE
|
|
42
76
|
};
|
|
43
77
|
|
|
44
78
|
const SERVER_CONFIG = {
|
|
45
79
|
statusCheckInterval: 30000,
|
|
46
|
-
maxFailedAttempts:
|
|
47
|
-
resetTimeout:
|
|
80
|
+
maxFailedAttempts: 3,
|
|
81
|
+
resetTimeout: 30000
|
|
48
82
|
};
|
|
49
83
|
|
|
50
84
|
// 全局状态存储
|
|
@@ -80,7 +114,9 @@ class MinecraftBotManager {
|
|
|
80
114
|
this.lastUpdate = Date.now();
|
|
81
115
|
this.status = 'initializing';
|
|
82
116
|
this.monitoringInterval = null;
|
|
83
|
-
|
|
117
|
+
this.timeout= null;
|
|
118
|
+
this.reachable=false;
|
|
119
|
+
this.lastReachable = null;
|
|
84
120
|
// 机器人名称池
|
|
85
121
|
this.botNames = Array.from({ length: 20 }, () => generateUsername());
|
|
86
122
|
|
|
@@ -103,41 +139,62 @@ class MinecraftBotManager {
|
|
|
103
139
|
const randomNum = Math.floor(Math.random() * 1000);
|
|
104
140
|
return `${baseName}${randomNum}`;
|
|
105
141
|
}
|
|
106
|
-
|
|
142
|
+
async testmc() {
|
|
143
|
+
const reachable = await testConnection(this.host, this.port, 3000);
|
|
144
|
+
this.reachable = reachable;
|
|
145
|
+
|
|
146
|
+
// 只有状态发生变化时才通知
|
|
147
|
+
if (this.reachable !== this.lastReachable) {
|
|
148
|
+
if (!this.reachable) {
|
|
149
|
+
console.log(`[${this.host}:${this.port}] MC服务器网络不可达`);
|
|
150
|
+
} else {
|
|
151
|
+
console.log(`[${this.host}:${this.port}] MC服务器恢复可达`);
|
|
152
|
+
}
|
|
153
|
+
this.lastReachable = this.reachable;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
107
156
|
// 创建Minecraft机器人
|
|
108
157
|
async createBot(botName) {
|
|
109
158
|
if (this.currentBots >= this.maxBots) {
|
|
110
159
|
console.log(`[${this.host}:${this.port}] 已达到最大机器人限制: ${this.maxBots}`);
|
|
111
160
|
return null;
|
|
112
161
|
}
|
|
162
|
+
await this.testmc();
|
|
163
|
+
if (!this.reachable) {
|
|
164
|
+
return null;
|
|
165
|
+
}
|
|
113
166
|
if(!botName){
|
|
114
167
|
botName = this.generateBotName();
|
|
115
168
|
}
|
|
116
169
|
try {
|
|
117
170
|
console.log(`[${this.host}:${this.port}] 创建机器人: ${botName}`);
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
this.failedAttempts = 0;
|
|
134
|
-
this.updateStatus();
|
|
171
|
+
const bot = mineflayer.createBot({
|
|
172
|
+
host: this.host,
|
|
173
|
+
port: this.port,
|
|
174
|
+
username: botName,
|
|
175
|
+
version: this.version,
|
|
176
|
+
viewDistance: BOT_CONFIG.viewDistance,
|
|
177
|
+
auth: 'offline'
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
this.timeout = setTimeout(() => {
|
|
181
|
+
console.error(`[${this.host}:${this.port}] ${botName}连接超时,放弃等待`);
|
|
182
|
+
if (typeof bot.end === 'function') {
|
|
183
|
+
bot.end();
|
|
184
|
+
}
|
|
185
|
+
}, BOT_CONFIG.connectTimeout);
|
|
135
186
|
|
|
136
|
-
|
|
187
|
+
// 设置机器人事件处理
|
|
188
|
+
this.setupBotEvents(bot, botName);
|
|
137
189
|
|
|
190
|
+
this.activeBots.set(botName, bot);
|
|
191
|
+
this.currentBots++;
|
|
192
|
+
// this.failedAttempts = 0;
|
|
193
|
+
this.updateStatus();
|
|
194
|
+
return bot;
|
|
138
195
|
} catch (error) {
|
|
139
196
|
console.log(`[${this.host}:${this.port}] 创建机器人 ${botName} 失败:`, error.message);
|
|
140
|
-
this.handleBotFailure();
|
|
197
|
+
this.handleBotFailure(botName);
|
|
141
198
|
return null;
|
|
142
199
|
}
|
|
143
200
|
}
|
|
@@ -146,6 +203,10 @@ class MinecraftBotManager {
|
|
|
146
203
|
setupBotEvents(bot, botName) {
|
|
147
204
|
bot.on('login', () => {
|
|
148
205
|
console.log(`[${this.host}:${this.port}] 机器人 ${botName} 登录成功`);
|
|
206
|
+
if (this.timeout) {
|
|
207
|
+
clearTimeout(this.timeout);
|
|
208
|
+
this.timeout = null;
|
|
209
|
+
}
|
|
149
210
|
this.updateStatus();
|
|
150
211
|
});
|
|
151
212
|
|
|
@@ -163,16 +224,21 @@ class MinecraftBotManager {
|
|
|
163
224
|
|
|
164
225
|
bot.on('error', (error) => {
|
|
165
226
|
console.log(`[${this.host}:${this.port}] 机器人 ${botName} 错误:`, error.message);
|
|
166
|
-
if (this.activeBots.has(botName)) {
|
|
227
|
+
if (this.activeBots.has(botName) && this.failedAttempts <= SERVER_CONFIG.maxFailedAttempts) {
|
|
167
228
|
this.handleBotDisconnect(botName);
|
|
168
229
|
} else {
|
|
169
|
-
this.handleBotFailure();
|
|
230
|
+
this.handleBotFailure(botName);
|
|
170
231
|
}
|
|
171
232
|
});
|
|
172
233
|
|
|
173
234
|
bot.on('end', (reason) => {
|
|
174
235
|
console.log(`[${this.host}:${this.port}] 机器人 ${botName} 断开连接:`, reason);
|
|
175
|
-
this.
|
|
236
|
+
console.log(`[${this.host}:${this.port}] ${botName}:失败次数${this.failedAttempts}`);
|
|
237
|
+
if (this.failedAttempts < SERVER_CONFIG.maxFailedAttempts) {
|
|
238
|
+
this.handleBotDisconnect(botName);
|
|
239
|
+
} else {
|
|
240
|
+
this.handleBotFailure(botName);
|
|
241
|
+
}
|
|
176
242
|
});
|
|
177
243
|
|
|
178
244
|
bot.on('kicked', (reason) => {
|
|
@@ -221,9 +287,13 @@ class MinecraftBotManager {
|
|
|
221
287
|
// 记录断开连接时间,用于调试
|
|
222
288
|
console.log(`[${this.host}:${this.port}] 当前活跃机器人数量: ${this.currentBots}, 目标: ${this.minBots}`);
|
|
223
289
|
|
|
290
|
+
if(this.failedAttempts > SERVER_CONFIG.maxFailedAttempts){
|
|
291
|
+
console.log(`[${this.host}:${this.port}] 机器人 ${botName} 重连次数太多,跳过`);
|
|
292
|
+
return;
|
|
293
|
+
}
|
|
224
294
|
// 延迟重连,避免频繁重连
|
|
225
295
|
setTimeout(() => {
|
|
226
|
-
this.
|
|
296
|
+
this.reconnect(botName);
|
|
227
297
|
}, BOT_CONFIG.reconnectDelay);
|
|
228
298
|
} else {
|
|
229
299
|
console.log(`[${this.host}:${this.port}] 机器人 ${botName} 不在活跃列表中,无需处理`);
|
|
@@ -231,15 +301,19 @@ class MinecraftBotManager {
|
|
|
231
301
|
}
|
|
232
302
|
|
|
233
303
|
// 处理机器人失败
|
|
234
|
-
handleBotFailure() {
|
|
304
|
+
handleBotFailure(botName) {
|
|
235
305
|
this.failedAttempts++;
|
|
236
306
|
this.updateStatus();
|
|
237
|
-
|
|
238
307
|
if (this.failedAttempts >= SERVER_CONFIG.maxFailedAttempts) {
|
|
239
|
-
console.log(`[${this.host}:${this.port}]
|
|
308
|
+
console.log(`[${this.host}:${this.port}] 失败次数过多,${SERVER_CONFIG.resetTimeout / 1000}秒后再次尝试`);
|
|
240
309
|
setTimeout(() => {
|
|
241
310
|
this.failedAttempts = 0;
|
|
242
|
-
|
|
311
|
+
if(botName){
|
|
312
|
+
this.reconnect(botName);
|
|
313
|
+
}
|
|
314
|
+
else{
|
|
315
|
+
this.maintainBots();
|
|
316
|
+
}
|
|
243
317
|
}, SERVER_CONFIG.resetTimeout);
|
|
244
318
|
return;
|
|
245
319
|
}
|
|
@@ -261,15 +335,26 @@ class MinecraftBotManager {
|
|
|
261
335
|
for (let i = 0; i < neededBots; i++) {
|
|
262
336
|
setTimeout(() => {
|
|
263
337
|
this.createBot(botName);
|
|
264
|
-
}, i *
|
|
338
|
+
}, i * 8000); // 每隔8秒启动一个
|
|
265
339
|
}
|
|
266
|
-
}
|
|
340
|
+
}
|
|
341
|
+
// else if (neededBots > 0) {
|
|
342
|
+
// console.log(`[${this.host}:${this.port}] 由于失败次数过多,暂停创建新机器人`);
|
|
343
|
+
// }
|
|
344
|
+
this.updateStatus();
|
|
345
|
+
}
|
|
346
|
+
reconnect(botName){
|
|
347
|
+
if (this.failedAttempts < SERVER_CONFIG.maxFailedAttempts) {
|
|
348
|
+
console.log(`[${this.host}:${this.port}] 第${this.failedAttempts}次重连 ${botName} 机器人`);
|
|
349
|
+
this.failedAttempts++;
|
|
350
|
+
setTimeout(() => {
|
|
351
|
+
this.createBot(botName);
|
|
352
|
+
}, 5000);
|
|
353
|
+
} else{
|
|
267
354
|
console.log(`[${this.host}:${this.port}] 由于失败次数过多,暂停创建新机器人`);
|
|
268
355
|
}
|
|
269
|
-
|
|
270
356
|
this.updateStatus();
|
|
271
357
|
}
|
|
272
|
-
|
|
273
358
|
// 更新状态
|
|
274
359
|
updateStatus() {
|
|
275
360
|
const serverInfo = globalServerStatus.servers.get(`${this.host}:${this.port}`);
|