wpms commited on
Commit
f39bfe4
·
verified ·
1 Parent(s): 48087ca

Update index.js

Browse files
Files changed (1) hide show
  1. index.js +262 -44
index.js CHANGED
@@ -10,7 +10,9 @@ const io = new Server(server, {
10
  cors: {
11
  origin: "*",
12
  methods: ["GET", "POST"]
13
- }
 
 
14
  });
15
 
16
  const PORT = process.env.PORT || 7860;
@@ -31,7 +33,7 @@ app.get('/health', (req, res) => {
31
  app.get('/api/bots', (req, res) => {
32
  const bots = Array.from(activeBots.entries()).map(([id, data]) => ({
33
  id,
34
- username: data.bot.username,
35
  host: data.host,
36
  port: data.port,
37
  connected: data.connected,
@@ -46,6 +48,12 @@ io.on('connection', (socket) => {
46
 
47
  let currentBot = null;
48
  let botId = null;
 
 
 
 
 
 
49
 
50
  // Connect bot to Minecraft server
51
  socket.on('connect-bot', (config) => {
@@ -56,22 +64,47 @@ io.on('connection', (socket) => {
56
  return;
57
  }
58
 
 
 
 
 
 
59
  // Disconnect existing bot for this socket
60
  if (currentBot) {
 
61
  currentBot.quit();
62
  activeBots.delete(botId);
63
  }
64
 
 
 
 
 
 
 
65
  botId = `${socket.id}_${Date.now()}`;
66
 
 
 
 
 
 
 
67
  try {
 
 
68
  const bot = mineflayer.createBot({
69
  host: host,
70
  port: parseInt(port),
71
  username: username,
72
  version: version || false,
73
  auth: 'offline',
74
- hideErrors: true
 
 
 
 
 
75
  });
76
 
77
  currentBot = bot;
@@ -83,30 +116,43 @@ io.on('connection', (socket) => {
83
  port: port,
84
  connected: false,
85
  players: [],
86
- chatHistory: []
 
 
87
  };
88
 
89
  activeBots.set(botId, botData);
90
 
91
  // Bot Events
92
  bot.on('login', () => {
 
93
  botData.connected = true;
 
94
  socket.emit('bot-status', {
95
  status: 'online',
96
  username: bot.username,
97
  host: host,
98
  port: port
99
  });
100
- console.log(`Bot ${username} connected to ${host}:${port}`);
101
  });
102
 
103
  bot.on('spawn', () => {
 
104
  socket.emit('bot-spawned', {
105
  position: bot.entity?.position,
106
  health: bot.health,
107
  food: bot.food
108
  });
109
  updatePlayersList(bot, socket);
 
 
 
 
 
 
 
 
 
110
  });
111
 
112
  bot.on('chat', (username, message) => {
@@ -164,89 +210,225 @@ io.on('connection', (socket) => {
164
  });
165
 
166
  bot.on('kicked', (reason) => {
 
 
167
  socket.emit('bot-kicked', {
168
- reason: reason.toString(),
169
  timestamp: new Date().toISOString()
170
  });
171
- cleanupBot(botId);
 
 
 
 
172
  });
173
 
174
  bot.on('error', (err) => {
175
- console.error('Bot error:', err.message);
176
  socket.emit('bot-error', {
177
  message: err.message,
178
  timestamp: new Date().toISOString()
179
  });
 
 
 
 
 
 
 
 
 
 
 
 
 
180
  });
181
 
182
  bot.on('end', (reason) => {
 
 
 
 
 
183
  socket.emit('bot-disconnected', {
184
  reason: reason || 'Connection closed',
185
  timestamp: new Date().toISOString()
186
  });
187
- cleanupBot(botId);
 
 
 
 
 
 
 
188
  });
189
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
190
  } catch (error) {
 
191
  socket.emit('error', { message: error.message });
 
 
 
 
192
  }
193
- });
194
 
195
- // Send chat message
196
- socket.on('send-chat', (message) => {
197
- if (currentBot && currentBot.chat) {
198
- currentBot.chat(message);
199
- socket.emit('chat-sent', {
200
- username: currentBot.username,
201
- message: message,
202
  timestamp: new Date().toISOString()
203
  });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
204
  }
205
  });
206
 
207
  // Send command
208
  socket.on('send-command', (command) => {
209
- if (currentBot && currentBot.chat) {
210
- currentBot.chat(command);
 
 
 
 
211
  }
212
  });
213
 
214
  // Bot actions
215
  socket.on('bot-action', (action) => {
216
- if (!currentBot) return;
217
-
218
- switch(action.type) {
219
- case 'jump':
220
- currentBot.setControlState('jump', true);
221
- setTimeout(() => currentBot.setControlState('jump', false), 500);
222
- break;
223
- case 'sneak':
224
- currentBot.setControlState('sneak', true);
225
- setTimeout(() => currentBot.setControlState('sneak', false), 1000);
226
- break;
227
- case 'look':
228
- if (action.x !== undefined && action.y !== undefined && action.z !== undefined) {
229
- const Vec3 = require('vec3');
230
- currentBot.lookAt(new Vec3(action.x, action.y, action.z));
231
- }
232
- break;
233
- case 'move':
234
- if (action.direction) {
235
- currentBot.setControlState(action.direction, true);
236
- setTimeout(() => currentBot.setControlState(action.direction, false), 500);
237
- }
238
- break;
 
 
 
 
239
  }
240
  });
241
 
242
  // Disconnect bot
243
  socket.on('disconnect-bot', () => {
 
 
 
 
 
 
 
244
  if (currentBot) {
245
- currentBot.quit();
 
 
246
  cleanupBot(botId);
247
  currentBot = null;
248
  botId = null;
249
  }
 
 
 
 
 
250
  });
251
 
252
  // Get players list
@@ -259,8 +441,17 @@ io.on('connection', (socket) => {
259
  // Disconnect handler
260
  socket.on('disconnect', () => {
261
  console.log('Client disconnected:', socket.id);
 
 
 
 
 
 
 
262
  if (currentBot) {
263
- currentBot.quit();
 
 
264
  cleanupBot(botId);
265
  }
266
  });
@@ -278,9 +469,23 @@ function updatePlayersList(bot, socket) {
278
  socket.emit('players-list', players);
279
  }
280
 
 
 
 
 
 
 
 
 
 
 
 
 
 
281
  function cleanupBot(id) {
282
  if (activeBots.has(id)) {
283
  const data = activeBots.get(id);
 
284
  if (data.bot) {
285
  try {
286
  data.bot.quit();
@@ -291,6 +496,19 @@ function cleanupBot(id) {
291
  }
292
  }
293
 
 
 
 
 
 
 
 
 
 
 
 
 
 
294
  // Start server
295
  server.listen(PORT, () => {
296
  console.log(`🎮 Minecraft Bot Controller running on port ${PORT}`);
 
10
  cors: {
11
  origin: "*",
12
  methods: ["GET", "POST"]
13
+ },
14
+ pingTimeout: 60000,
15
+ pingInterval: 25000
16
  });
17
 
18
  const PORT = process.env.PORT || 7860;
 
33
  app.get('/api/bots', (req, res) => {
34
  const bots = Array.from(activeBots.entries()).map(([id, data]) => ({
35
  id,
36
+ username: data.bot?.username || 'unknown',
37
  host: data.host,
38
  port: data.port,
39
  connected: data.connected,
 
48
 
49
  let currentBot = null;
50
  let botId = null;
51
+ let reconnectTimer = null;
52
+ let reconnectAttempts = 0;
53
+ const MAX_RECONNECT_ATTEMPTS = 5;
54
+ const RECONNECT_DELAY = 5000;
55
+ let botConfig = null;
56
+ let isIntentionalDisconnect = false;
57
 
58
  // Connect bot to Minecraft server
59
  socket.on('connect-bot', (config) => {
 
64
  return;
65
  }
66
 
67
+ // Save config for reconnect
68
+ botConfig = { username, host, port, version };
69
+ isIntentionalDisconnect = false;
70
+ reconnectAttempts = 0;
71
+
72
  // Disconnect existing bot for this socket
73
  if (currentBot) {
74
+ isIntentionalDisconnect = true;
75
  currentBot.quit();
76
  activeBots.delete(botId);
77
  }
78
 
79
+ // Clear any pending reconnect
80
+ if (reconnectTimer) {
81
+ clearTimeout(reconnectTimer);
82
+ reconnectTimer = null;
83
+ }
84
+
85
  botId = `${socket.id}_${Date.now()}`;
86
 
87
+ createBotConnection(config, socket);
88
+ });
89
+
90
+ function createBotConnection(config, socket) {
91
+ const { username, host, port = 25565, version } = config;
92
+
93
  try {
94
+ console.log(`Creating bot ${username} for ${host}:${port}...`);
95
+
96
  const bot = mineflayer.createBot({
97
  host: host,
98
  port: parseInt(port),
99
  username: username,
100
  version: version || false,
101
  auth: 'offline',
102
+ hideErrors: false,
103
+ checkTimeoutInterval: 30000,
104
+ closeTimeout: 10000,
105
+ keepAlive: true,
106
+ // Add some delay to avoid rate limiting
107
+ connectTimeout: 30000
108
  });
109
 
110
  currentBot = bot;
 
116
  port: port,
117
  connected: false,
118
  players: [],
119
+ chatHistory: [],
120
+ spawnTimeout: null,
121
+ keepAliveInterval: null
122
  };
123
 
124
  activeBots.set(botId, botData);
125
 
126
  // Bot Events
127
  bot.on('login', () => {
128
+ console.log(`Bot ${username} logged in to ${host}:${port}`);
129
  botData.connected = true;
130
+ reconnectAttempts = 0;
131
  socket.emit('bot-status', {
132
  status: 'online',
133
  username: bot.username,
134
  host: host,
135
  port: port
136
  });
 
137
  });
138
 
139
  bot.on('spawn', () => {
140
+ console.log(`Bot ${username} spawned`);
141
  socket.emit('bot-spawned', {
142
  position: bot.entity?.position,
143
  health: bot.health,
144
  food: bot.food
145
  });
146
  updatePlayersList(bot, socket);
147
+
148
+ // Start keep-alive mechanism
149
+ startKeepAlive(bot, botData);
150
+
151
+ // Clear spawn timeout
152
+ if (botData.spawnTimeout) {
153
+ clearTimeout(botData.spawnTimeout);
154
+ botData.spawnTimeout = null;
155
+ }
156
  });
157
 
158
  bot.on('chat', (username, message) => {
 
210
  });
211
 
212
  bot.on('kicked', (reason) => {
213
+ console.log(`Bot ${username} kicked:`, reason);
214
+ const reasonStr = reason.toString ? reason.toString() : JSON.stringify(reason);
215
  socket.emit('bot-kicked', {
216
+ reason: reasonStr,
217
  timestamp: new Date().toISOString()
218
  });
219
+
220
+ // Try to reconnect if not intentional
221
+ if (!isIntentionalDisconnect) {
222
+ attemptReconnect(socket, `Kicked: ${reasonStr}`);
223
+ }
224
  });
225
 
226
  bot.on('error', (err) => {
227
+ console.error(`Bot ${username} error:`, err.message);
228
  socket.emit('bot-error', {
229
  message: err.message,
230
  timestamp: new Date().toISOString()
231
  });
232
+
233
+ // Only attempt reconnect for connection errors, not auth errors
234
+ if (err.message && (
235
+ err.message.includes('ETIMEDOUT') ||
236
+ err.message.includes('ECONNRESET') ||
237
+ err.message.includes('ECONNREFUSED') ||
238
+ err.message.includes('timeout') ||
239
+ err.message.includes('disconnected')
240
+ )) {
241
+ if (!isIntentionalDisconnect) {
242
+ attemptReconnect(socket, `Error: ${err.message}`);
243
+ }
244
+ }
245
  });
246
 
247
  bot.on('end', (reason) => {
248
+ console.log(`Bot ${username} disconnected. Reason:`, reason, 'Intentional:', isIntentionalDisconnect);
249
+
250
+ // Stop keep-alive
251
+ stopKeepAlive(botData);
252
+
253
  socket.emit('bot-disconnected', {
254
  reason: reason || 'Connection closed',
255
  timestamp: new Date().toISOString()
256
  });
257
+
258
+ // Cleanup but keep the bot data for potential reconnect
259
+ cleanupBotListeners(botId);
260
+
261
+ // Attempt reconnect if not intentional disconnect
262
+ if (!isIntentionalDisconnect) {
263
+ attemptReconnect(socket, reason || 'Connection closed');
264
+ }
265
  });
266
 
267
+ // Spawn timeout - if bot doesn't spawn within 30 seconds, reconnect
268
+ botData.spawnTimeout = setTimeout(() => {
269
+ if (!botData.connected) {
270
+ console.log(`Bot ${username} spawn timeout, reconnecting...`);
271
+ socket.emit('bot-error', {
272
+ message: 'Spawn timeout, reconnecting...',
273
+ timestamp: new Date().toISOString()
274
+ });
275
+ if (!isIntentionalDisconnect) {
276
+ bot.quit();
277
+ attemptReconnect(socket, 'Spawn timeout');
278
+ }
279
+ }
280
+ }, 30000);
281
+
282
  } catch (error) {
283
+ console.error('Failed to create bot:', error);
284
  socket.emit('error', { message: error.message });
285
+
286
+ if (!isIntentionalDisconnect) {
287
+ attemptReconnect(socket, `Create error: ${error.message}`);
288
+ }
289
  }
290
+ }
291
 
292
+ function attemptReconnect(socket, reason) {
293
+ if (reconnectAttempts >= MAX_RECONNECT_ATTEMPTS) {
294
+ console.log(`Max reconnect attempts reached for ${botConfig?.username}`);
295
+ socket.emit('bot-error', {
296
+ message: `Max reconnect attempts reached. Please check server details or try again later.`,
 
 
297
  timestamp: new Date().toISOString()
298
  });
299
+ return;
300
+ }
301
+
302
+ reconnectAttempts++;
303
+ console.log(`Reconnect attempt ${reconnectAttempts}/${MAX_RECONNECT_ATTEMPTS} for ${botConfig?.username} in ${RECONNECT_DELAY}ms`);
304
+
305
+ socket.emit('bot-reconnecting', {
306
+ attempt: reconnectAttempts,
307
+ maxAttempts: MAX_RECONNECT_ATTEMPTS,
308
+ delay: RECONNECT_DELAY,
309
+ reason: reason,
310
+ timestamp: new Date().toISOString()
311
+ });
312
+
313
+ reconnectTimer = setTimeout(() => {
314
+ if (botConfig && !isIntentionalDisconnect) {
315
+ console.log(`Executing reconnect for ${botConfig.username}...`);
316
+ createBotConnection(botConfig, socket);
317
+ }
318
+ }, RECONNECT_DELAY);
319
+ }
320
+
321
+ function startKeepAlive(bot, botData) {
322
+ // Send periodic position updates to keep connection alive
323
+ botData.keepAliveInterval = setInterval(() => {
324
+ if (bot && bot.entity) {
325
+ // Small random movement to prevent AFK kick
326
+ try {
327
+ const random = Math.random();
328
+ if (random < 0.1) {
329
+ // 10% chance to look around
330
+ const yaw = Math.random() * Math.PI * 2;
331
+ bot.look(yaw, 0, false);
332
+ }
333
+ } catch (e) {
334
+ // Ignore look errors
335
+ }
336
+ }
337
+ }, 15000); // Every 15 seconds
338
+ }
339
+
340
+ function stopKeepAlive(botData) {
341
+ if (botData.keepAliveInterval) {
342
+ clearInterval(botData.keepAliveInterval);
343
+ botData.keepAliveInterval = null;
344
+ }
345
+ if (botData.spawnTimeout) {
346
+ clearTimeout(botData.spawnTimeout);
347
+ botData.spawnTimeout = null;
348
+ }
349
+ }
350
+
351
+ // Send chat message
352
+ socket.on('send-chat', (message) => {
353
+ if (currentBot && currentBot.chat && botConfig) {
354
+ try {
355
+ currentBot.chat(message);
356
+ socket.emit('chat-sent', {
357
+ username: currentBot.username,
358
+ message: message,
359
+ timestamp: new Date().toISOString()
360
+ });
361
+ } catch (e) {
362
+ socket.emit('error', { message: 'Failed to send chat: ' + e.message });
363
+ }
364
  }
365
  });
366
 
367
  // Send command
368
  socket.on('send-command', (command) => {
369
+ if (currentBot && currentBot.chat && botConfig) {
370
+ try {
371
+ currentBot.chat(command);
372
+ } catch (e) {
373
+ socket.emit('error', { message: 'Failed to send command: ' + e.message });
374
+ }
375
  }
376
  });
377
 
378
  // Bot actions
379
  socket.on('bot-action', (action) => {
380
+ if (!currentBot || !botConfig) return;
381
+
382
+ try {
383
+ switch(action.type) {
384
+ case 'jump':
385
+ currentBot.setControlState('jump', true);
386
+ setTimeout(() => currentBot.setControlState('jump', false), 500);
387
+ break;
388
+ case 'sneak':
389
+ currentBot.setControlState('sneak', true);
390
+ setTimeout(() => currentBot.setControlState('sneak', false), 1000);
391
+ break;
392
+ case 'look':
393
+ if (action.x !== undefined && action.y !== undefined && action.z !== undefined) {
394
+ const Vec3 = require('vec3');
395
+ currentBot.lookAt(new Vec3(action.x, action.y, action.z));
396
+ }
397
+ break;
398
+ case 'move':
399
+ if (action.direction) {
400
+ currentBot.setControlState(action.direction, true);
401
+ setTimeout(() => currentBot.setControlState(action.direction, false), 500);
402
+ }
403
+ break;
404
+ }
405
+ } catch (e) {
406
+ socket.emit('error', { message: 'Action failed: ' + e.message });
407
  }
408
  });
409
 
410
  // Disconnect bot
411
  socket.on('disconnect-bot', () => {
412
+ isIntentionalDisconnect = true;
413
+
414
+ if (reconnectTimer) {
415
+ clearTimeout(reconnectTimer);
416
+ reconnectTimer = null;
417
+ }
418
+
419
  if (currentBot) {
420
+ try {
421
+ currentBot.quit();
422
+ } catch (e) {}
423
  cleanupBot(botId);
424
  currentBot = null;
425
  botId = null;
426
  }
427
+
428
+ socket.emit('bot-disconnected', {
429
+ reason: 'Disconnected by user',
430
+ timestamp: new Date().toISOString()
431
+ });
432
  });
433
 
434
  // Get players list
 
441
  // Disconnect handler
442
  socket.on('disconnect', () => {
443
  console.log('Client disconnected:', socket.id);
444
+ isIntentionalDisconnect = true;
445
+
446
+ if (reconnectTimer) {
447
+ clearTimeout(reconnectTimer);
448
+ reconnectTimer = null;
449
+ }
450
+
451
  if (currentBot) {
452
+ try {
453
+ currentBot.quit();
454
+ } catch (e) {}
455
  cleanupBot(botId);
456
  }
457
  });
 
469
  socket.emit('players-list', players);
470
  }
471
 
472
+ function cleanupBotListeners(id) {
473
+ if (activeBots.has(id)) {
474
+ const data = activeBots.get(id);
475
+ stopKeepAlive(data);
476
+ if (data.bot) {
477
+ try {
478
+ data.bot.removeAllListeners();
479
+ } catch (e) {}
480
+ }
481
+ activeBots.delete(id);
482
+ }
483
+ }
484
+
485
  function cleanupBot(id) {
486
  if (activeBots.has(id)) {
487
  const data = activeBots.get(id);
488
+ stopKeepAlive(data);
489
  if (data.bot) {
490
  try {
491
  data.bot.quit();
 
496
  }
497
  }
498
 
499
+ function stopKeepAlive(botData) {
500
+ if (botData) {
501
+ if (botData.keepAliveInterval) {
502
+ clearInterval(botData.keepAliveInterval);
503
+ botData.keepAliveInterval = null;
504
+ }
505
+ if (botData.spawnTimeout) {
506
+ clearTimeout(botData.spawnTimeout);
507
+ botData.spawnTimeout = null;
508
+ }
509
+ }
510
+ }
511
+
512
  // Start server
513
  server.listen(PORT, () => {
514
  console.log(`🎮 Minecraft Bot Controller running on port ${PORT}`);