OrbitMC commited on
Commit
d4d39bb
ยท
verified ยท
1 Parent(s): 202d0a1

Update server.js

Browse files
Files changed (1) hide show
  1. server.js +228 -276
server.js CHANGED
@@ -1,324 +1,268 @@
 
1
  const express = require('express');
2
  const http = require('http');
3
  const socketIo = require('socket.io');
4
  const mineflayer = require('mineflayer');
5
- const fetch = require('node-fetch');
6
- const { parse } = require('csv-parse/sync');
7
  const path = require('path');
8
- const dns = require('dns').promises;
9
 
10
  const app = express();
11
  const server = http.createServer(app);
12
  const io = socketIo(server);
13
 
14
- // Google Sheets configuration
15
- const SHEET_ID = '109roJQr-Y4YCLTkCqaK6iwShC-Dr2Jb-hB0qE2phNqQ';
16
- const SHEET_URL = `https://docs.google.com/spreadsheets/d/${SHEET_ID}/export?format=csv`;
 
 
 
17
 
18
- // Bot management
19
- const bots = new Map();
20
- const serverBotMap = new Map(); // Track one bot per server
 
 
 
21
 
22
- // Function to resolve domain to IP
23
- async function resolveToIP(hostname) {
24
- try {
25
- // Check if it's already an IP address
26
- if (/^(\d{1,3}\.){3}\d{1,3}$/.test(hostname)) {
27
- return hostname;
28
- }
29
-
30
- // Resolve domain to IP
31
- const addresses = await dns.resolve4(hostname);
32
- return addresses[0]; // Return first IP
33
- } catch (error) {
34
- console.error(`Failed to resolve ${hostname}:`, error.message);
35
- return hostname; // Return original if resolution fails
36
- }
37
- }
38
-
39
- class BotManager {
40
- constructor(botName, ip, port, version) {
41
- this.botName = botName;
42
- this.ip = ip;
43
- this.port = port;
44
- this.version = version || '1.20.1';
45
- this.bot = null;
46
- this.status = 'Disconnected';
47
- this.deathCount = 0;
48
- this.disconnectTime = null;
49
- this.lastReconnectTime = null;
50
- this.isManualDisconnect = false;
51
- this.startTime = Date.now();
52
- this.connectedTime = null;
53
- this.inSheet = true;
54
- this.resolvedIP = null;
55
- }
56
-
57
- async connect() {
58
- try {
59
- // Resolve domain to IP for comparison
60
- this.resolvedIP = await resolveToIP(this.ip);
61
- const serverKey = `${this.resolvedIP}:${this.port}`;
62
-
63
- // Check if server already has a bot
64
- const existingBot = serverBotMap.get(serverKey);
65
- if (existingBot && existingBot !== this.botName) {
66
- this.status = 'Server already has a bot';
67
- console.log(`Bot ${this.botName} blocked: Server ${serverKey} already has bot ${existingBot}`);
68
- return false;
69
- }
70
 
71
- this.status = 'Connecting...';
72
- this.bot = mineflayer.createBot({
73
- host: this.ip, // Use original hostname/IP for connection
74
- port: parseInt(this.port),
75
- username: this.botName,
76
- auth: 'offline',
77
- version: this.version,
78
- hideErrors: true,
79
- checkTimeoutInterval: 30000
80
- });
 
 
 
 
81
 
82
- // Register this bot for the server using resolved IP
83
- serverBotMap.set(serverKey, this.botName);
84
- console.log(`Registering bot ${this.botName} for server ${serverKey}`);
85
 
86
- this.bot.once('spawn', () => {
87
- this.status = 'Connected';
88
- this.connectedTime = Date.now();
89
- this.disconnectTime = null;
90
- console.log(`Bot ${this.botName} spawned on ${this.ip}:${this.port} (resolved: ${this.resolvedIP})`);
 
 
 
 
 
 
 
 
91
 
92
- // Start AFK behavior
93
- this.startAFK();
94
- });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
95
 
96
- this.bot.on('death', () => {
97
- this.deathCount++;
98
- this.status = 'Dead';
99
- console.log(`Bot ${this.botName} died. Total deaths: ${this.deathCount}`);
100
- this.handleDisconnect();
101
- });
102
 
103
- this.bot.on('kicked', (reason) => {
104
- console.log(`Bot ${this.botName} was kicked: ${reason}`);
105
- this.handleDisconnect();
106
- });
 
 
107
 
108
- this.bot.on('error', (err) => {
109
- console.error(`Bot ${this.botName} error:`, err.message);
110
- });
 
 
111
 
112
- this.bot.on('end', () => {
113
- this.handleDisconnect();
114
- });
115
 
116
- return true;
117
- } catch (error) {
118
- console.error(`Failed to connect bot ${this.botName}:`, error);
119
- this.status = 'Connection Failed';
120
-
121
- // Clean up server registration if connection failed
122
- if (this.resolvedIP) {
123
- const serverKey = `${this.resolvedIP}:${this.port}`;
124
- if (serverBotMap.get(serverKey) === this.botName) {
125
- serverBotMap.delete(serverKey);
126
- }
127
  }
128
-
129
- return false;
130
- }
131
- }
132
 
133
- handleDisconnect() {
134
- if (this.resolvedIP) {
135
- const serverKey = `${this.resolvedIP}:${this.port}`;
136
- if (serverBotMap.get(serverKey) === this.botName) {
137
- serverBotMap.delete(serverKey);
138
- console.log(`Unregistering bot ${this.botName} from server ${serverKey}`);
139
- }
140
- }
141
-
142
- if (this.status !== 'Dead') {
143
- this.status = 'Disconnected';
144
- }
145
- this.disconnectTime = Date.now();
146
- this.connectedTime = null;
147
- this.bot = null;
148
-
149
- console.log(`Bot ${this.botName} disconnected from ${this.ip}:${this.port}`);
150
- }
151
 
152
- disconnect() {
153
- this.isManualDisconnect = true;
154
- if (this.bot) {
155
- this.bot.quit();
156
- }
157
- this.handleDisconnect();
 
 
 
158
  }
 
159
 
160
- startAFK() {
161
- if (!this.bot) return;
 
 
 
162
 
163
- // Simple AFK movement
164
- let direction = 1;
165
- const afkInterval = setInterval(() => {
166
- if (!this.bot || this.status !== 'Connected') {
167
- clearInterval(afkInterval);
168
- return;
169
  }
170
 
171
- // Walk forward and backward
172
- this.bot.setControlState('forward', direction > 0);
173
- this.bot.setControlState('back', direction < 0);
174
-
175
  setTimeout(() => {
176
- if (this.bot) {
177
- this.bot.clearControlStates();
178
- }
179
- }, 1000);
180
-
181
- direction *= -1;
182
- }, 5000);
183
- }
184
-
185
- canReconnect() {
186
- if (this.status === 'Connected' || this.status === 'Connecting...') return false;
187
- if (!this.inSheet) return false;
188
-
189
- if (!this.lastReconnectTime) return true;
190
-
191
- const hourAgo = Date.now() - (60 * 60 * 1000);
192
- return this.lastReconnectTime < hourAgo;
193
- }
194
-
195
- async reconnect() {
196
- if (!this.canReconnect()) {
197
- return false;
198
  }
199
-
200
- this.lastReconnectTime = Date.now();
201
- this.isManualDisconnect = false;
202
- return await this.connect();
203
- }
204
-
205
- getTimeUntilReconnect() {
206
- if (!this.lastReconnectTime) return 0;
207
- const timeElapsed = Date.now() - this.lastReconnectTime;
208
- const hourInMs = 60 * 60 * 1000;
209
- const timeRemaining = Math.max(0, hourInMs - timeElapsed);
210
- return Math.ceil(timeRemaining / 1000);
211
- }
212
-
213
- getConnectedDuration() {
214
- if (!this.connectedTime || this.status !== 'Connected') return 0;
215
- return Math.floor((Date.now() - this.connectedTime) / 1000);
216
- }
217
-
218
- getInfo() {
219
- return {
220
- botName: this.botName,
221
- status: this.status,
222
- deathCount: this.deathCount,
223
- connectedDuration: this.getConnectedDuration(),
224
- canReconnect: this.canReconnect(),
225
- disconnectTime: this.disconnectTime,
226
- timeUntilReconnect: this.getTimeUntilReconnect(),
227
- inSheet: this.inSheet
228
- };
229
- }
230
  }
231
 
232
- // Fetch and parse Google Sheets data
233
- async function fetchSheetData() {
234
  try {
235
- const response = await fetch(SHEET_URL);
236
- const csvText = await response.text();
 
237
 
238
- const records = parse(csvText, {
239
- columns: true,
240
- skip_empty_lines: true
241
- });
242
 
243
- return records;
244
- } catch (error) {
245
- console.error('Error fetching sheet data:', error);
246
- return [];
247
- }
248
- }
249
-
250
- // Update bots based on sheet data
251
- async function updateBots() {
252
- const sheetData = await fetchSheetData();
253
- const activeBots = new Set();
254
-
255
- // Mark all existing bots as not in sheet initially
256
- for (const [botName, botManager] of bots.entries()) {
257
- botManager.inSheet = false;
258
- }
259
-
260
- for (const row of sheetData) {
261
- const botName = row['BOT NAME']?.trim();
262
- const ip = row['IP']?.trim();
263
- const port = row['PORT']?.trim();
264
- const version = row['Version']?.trim() || '1.20.1';
265
-
266
- if (!botName || !ip || !port) continue;
267
 
268
- activeBots.add(botName);
 
 
 
269
 
270
- // Add new bot if it doesn't exist
271
- if (!bots.has(botName)) {
272
- const botManager = new BotManager(botName, ip, port, version);
273
- bots.set(botName, botManager);
274
- console.log(`Added new bot from sheet: ${botName} for ${ip}:${port}`);
275
- } else {
276
- // Mark existing bot as still in sheet
277
- bots.get(botName).inSheet = true;
278
- }
279
- }
280
-
281
- // Remove bots that are no longer in the sheet
282
- for (const [botName, botManager] of bots.entries()) {
283
- if (!botManager.inSheet) {
284
- console.log(`Removing bot ${botName} - no longer in sheet`);
285
- botManager.disconnect();
286
- bots.delete(botName);
287
- }
288
  }
289
-
290
- console.log(`Total bots: ${bots.size}, Active servers: ${serverBotMap.size}`);
291
  }
292
 
293
- // Socket.IO events
294
  io.on('connection', (socket) => {
295
- console.log('Client connected');
296
 
297
- // Send initial bot data
298
- const sendBotData = () => {
299
- const botData = Array.from(bots.values()).map(bot => bot.getInfo());
300
- socket.emit('botUpdate', botData);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
301
  };
302
 
303
- sendBotData();
304
- const updateInterval = setInterval(sendBotData, 2000);
305
-
306
- socket.on('reconnectBot', async (botName) => {
307
- const botManager = bots.get(botName);
308
- if (botManager) {
309
- const success = await botManager.reconnect();
310
- socket.emit('reconnectResult', { botName, success });
311
- }
312
- });
313
 
314
- socket.on('refreshSheet', async () => {
315
- await updateBots();
316
- sendBotData();
317
  });
318
 
319
  socket.on('disconnect', () => {
320
- clearInterval(updateInterval);
321
- console.log('Client disconnected');
322
  });
323
  });
324
 
@@ -331,9 +275,17 @@ app.get('/', (req, res) => {
331
  // Start server
332
  const PORT = process.env.PORT || 7860;
333
  server.listen(PORT, () => {
334
- console.log(`Server running on port ${PORT}`);
 
 
 
 
 
 
 
335
  });
336
 
337
- // Initial load and periodic updates
338
- updateBots();
339
- setInterval(updateBots, 30000); // Check sheet every 30 seconds
 
 
1
+ // server.js
2
  const express = require('express');
3
  const http = require('http');
4
  const socketIo = require('socket.io');
5
  const mineflayer = require('mineflayer');
 
 
6
  const path = require('path');
 
7
 
8
  const app = express();
9
  const server = http.createServer(app);
10
  const io = socketIo(server);
11
 
12
+ // Configuration
13
+ const SERVER_HOST = 'orbitmc.progamer.me';
14
+ const SERVER_PORT = 40675;
15
+ const SERVER_VERSION = '1.21.8';
16
+ const BOT_NAMES = ['moderator_1', 'moderator_2', 'moderator_3', 'moderator_4', 'moderator_5'];
17
+ const ROTATION_DURATION = 3600; // 1 hour in seconds
18
 
19
+ // State
20
+ let currentBotIndex = 0;
21
+ let rotationStartTime = null;
22
+ let activeBot = null;
23
+ let serverStatus = { online: false, players: '0/0', latency: 0 };
24
+ const botStats = {};
25
 
26
+ // Initialize bot stats
27
+ BOT_NAMES.forEach(name => {
28
+ botStats[name] = {
29
+ name,
30
+ status: 'offline',
31
+ deaths: 0,
32
+ reconnects: 0,
33
+ uptime: 0,
34
+ position: { x: 0, y: 0, z: 0 },
35
+ health: 20,
36
+ food: 20,
37
+ isActive: false
38
+ };
39
+ });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
40
 
41
+ // Bot creation with circular movement
42
+ function createBot(botName) {
43
+ console.log(`๐Ÿš€ Starting ${botName}...`);
44
+
45
+ const bot = mineflayer.createBot({
46
+ host: SERVER_HOST,
47
+ port: SERVER_PORT,
48
+ username: botName,
49
+ auth: 'offline',
50
+ version: SERVER_VERSION,
51
+ hideErrors: false,
52
+ checkTimeoutInterval: 30000,
53
+ keepAlive: true
54
+ });
55
 
56
+ let circleInterval = null;
57
+ let centerPos = null;
58
+ let angle = 0;
59
 
60
+ bot.once('spawn', () => {
61
+ console.log(`โœ… ${botName} connected`);
62
+ botStats[botName].status = 'online';
63
+ botStats[botName].uptime = Date.now();
64
+
65
+ // Start circular movement
66
+ setTimeout(() => {
67
+ if (!bot.entity) return;
68
+
69
+ centerPos = bot.entity.position.clone();
70
+
71
+ circleInterval = setInterval(() => {
72
+ if (!bot.entity) return;
73
 
74
+ try {
75
+ const radius = 4;
76
+ angle += 0.03;
77
+
78
+ const targetX = centerPos.x + Math.cos(angle) * radius;
79
+ const targetZ = centerPos.z + Math.sin(angle) * radius;
80
+
81
+ const dx = targetX - bot.entity.position.x;
82
+ const dz = targetZ - bot.entity.position.z;
83
+ const yaw = Math.atan2(-dx, -dz);
84
+
85
+ bot.look(yaw, 0, true);
86
+ bot.setControlState('forward', true);
87
+
88
+ // Update position
89
+ botStats[botName].position = {
90
+ x: Math.floor(bot.entity.position.x),
91
+ y: Math.floor(bot.entity.position.y),
92
+ z: Math.floor(bot.entity.position.z)
93
+ };
94
+ botStats[botName].health = bot.health || 20;
95
+ botStats[botName].food = bot.food || 20;
96
+ } catch (err) {
97
+ console.error(`Movement error for ${botName}:`, err.message);
98
+ }
99
+ }, 100);
100
+ }, 2000);
101
+ });
102
 
103
+ bot.on('death', () => {
104
+ console.log(`๐Ÿ’€ ${botName} died`);
105
+ botStats[botName].deaths++;
106
+ if (circleInterval) clearInterval(circleInterval);
107
+ centerPos = null;
108
+ });
109
 
110
+ bot.on('respawn', () => {
111
+ console.log(`๐Ÿ”„ ${botName} respawned`);
112
+ if (circleInterval) clearInterval(circleInterval);
113
+ centerPos = null;
114
+ angle = 0;
115
+ });
116
 
117
+ bot.on('kicked', (reason) => {
118
+ console.log(`โ›” ${botName} kicked: ${reason}`);
119
+ botStats[botName].status = 'offline';
120
+ if (circleInterval) clearInterval(circleInterval);
121
+ });
122
 
123
+ bot.on('error', (err) => {
124
+ console.error(`โŒ ${botName} error:`, err.message);
125
+ });
126
 
127
+ bot.on('end', () => {
128
+ console.log(`๐Ÿ”Œ ${botName} disconnected`);
129
+ botStats[botName].status = 'offline';
130
+ if (circleInterval) clearInterval(circleInterval);
131
+
132
+ // Auto reconnect after 5 seconds if still active
133
+ setTimeout(() => {
134
+ if (activeBot && activeBot.username === botName) {
135
+ console.log(`๐Ÿ”„ Reconnecting ${botName}...`);
136
+ botStats[botName].reconnects++;
137
+ activeBot = createBot(botName);
138
  }
139
+ }, 5000);
140
+ });
 
 
141
 
142
+ return bot;
143
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
144
 
145
+ // Stop bot
146
+ function stopBot(bot) {
147
+ if (!bot) return;
148
+
149
+ try {
150
+ bot.quit();
151
+ console.log(`โน๏ธ Stopped ${bot.username}`);
152
+ } catch (err) {
153
+ console.error('Error stopping bot:', err.message);
154
  }
155
+ }
156
 
157
+ // Rotation manager
158
+ function rotationManager() {
159
+ setInterval(() => {
160
+ const currentBot = BOT_NAMES[currentBotIndex];
161
+ const now = Date.now();
162
 
163
+ if (!rotationStartTime || (now - rotationStartTime) >= (ROTATION_DURATION * 1000)) {
164
+ // Stop current bot
165
+ if (activeBot) {
166
+ stopBot(activeBot);
167
+ botStats[activeBot.username].isActive = false;
 
168
  }
169
 
170
+ // Start new bot
 
 
 
171
  setTimeout(() => {
172
+ activeBot = createBot(currentBot);
173
+ botStats[currentBot].isActive = true;
174
+ rotationStartTime = Date.now();
175
+
176
+ console.log(`๐Ÿ”„ Rotation: ${currentBot} is now active`);
177
+
178
+ // Move to next bot
179
+ currentBotIndex = (currentBotIndex + 1) % BOT_NAMES.length;
180
+ }, 2000);
 
 
 
 
 
 
 
 
 
 
 
 
 
181
  }
182
+ }, 5000);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
183
  }
184
 
185
+ // Check server status
186
+ async function checkServerStatus() {
187
  try {
188
+ const net = require('net');
189
+ const client = new net.Socket();
190
+ const startTime = Date.now();
191
 
192
+ client.setTimeout(5000);
 
 
 
193
 
194
+ client.connect(SERVER_PORT, SERVER_HOST, () => {
195
+ const latency = Date.now() - startTime;
196
+ serverStatus = {
197
+ online: true,
198
+ latency,
199
+ players: '?/?'
200
+ };
201
+ client.destroy();
202
+ });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
203
 
204
+ client.on('error', () => {
205
+ serverStatus = { online: false, latency: 0, players: '0/0' };
206
+ client.destroy();
207
+ });
208
 
209
+ client.on('timeout', () => {
210
+ serverStatus = { online: false, latency: 0, players: '0/0' };
211
+ client.destroy();
212
+ });
213
+ } catch (err) {
214
+ serverStatus = { online: false, latency: 0, players: '0/0' };
 
 
 
 
 
 
 
 
 
 
 
 
215
  }
 
 
216
  }
217
 
218
+ // Socket.IO
219
  io.on('connection', (socket) => {
220
+ console.log('๐Ÿ“ฑ Client connected');
221
 
222
+ const sendUpdate = () => {
223
+ const currentBot = BOT_NAMES[(currentBotIndex - 1 + BOT_NAMES.length) % BOT_NAMES.length];
224
+ const nextBot = BOT_NAMES[currentBotIndex];
225
+ const elapsed = rotationStartTime ? Math.floor((Date.now() - rotationStartTime) / 1000) : 0;
226
+ const remaining = Math.max(0, ROTATION_DURATION - elapsed);
227
+
228
+ // Update uptimes
229
+ Object.keys(botStats).forEach(name => {
230
+ if (botStats[name].status === 'online' && botStats[name].uptime) {
231
+ botStats[name].uptimeSeconds = Math.floor((Date.now() - botStats[name].uptime) / 1000);
232
+ } else {
233
+ botStats[name].uptimeSeconds = 0;
234
+ }
235
+ });
236
+
237
+ socket.emit('update', {
238
+ server: {
239
+ host: SERVER_HOST,
240
+ port: SERVER_PORT,
241
+ version: SERVER_VERSION,
242
+ status: serverStatus
243
+ },
244
+ rotation: {
245
+ current: currentBot,
246
+ next: nextBot,
247
+ elapsed,
248
+ remaining,
249
+ queue: BOT_NAMES
250
+ },
251
+ bots: Object.values(botStats)
252
+ });
253
  };
254
 
255
+ const interval = setInterval(sendUpdate, 1000);
256
+ sendUpdate();
 
 
 
 
 
 
 
 
257
 
258
+ socket.on('forceRotation', () => {
259
+ rotationStartTime = 0;
260
+ socket.emit('rotationForced', true);
261
  });
262
 
263
  socket.on('disconnect', () => {
264
+ clearInterval(interval);
265
+ console.log('๐Ÿ“ด Client disconnected');
266
  });
267
  });
268
 
 
275
  // Start server
276
  const PORT = process.env.PORT || 7860;
277
  server.listen(PORT, () => {
278
+ console.log('โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•');
279
+ console.log('๐ŸŽฎ MINECRAFT BOT MANAGER');
280
+ console.log('โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•');
281
+ console.log(`๐Ÿ“ก Server: ${SERVER_HOST}:${SERVER_PORT}`);
282
+ console.log(`๐Ÿค– Bots: ${BOT_NAMES.join(', ')}`);
283
+ console.log(`โฑ๏ธ Rotation: ${ROTATION_DURATION / 60} minutes`);
284
+ console.log(`๐ŸŒ Dashboard: http://localhost:${PORT}`);
285
+ console.log('โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•');
286
  });
287
 
288
+ // Start systems
289
+ rotationManager();
290
+ setInterval(checkServerStatus, 1000);
291
+ checkServerStatus();