commit 3c99a8e60a83b3a7564937c5647d055cd1413f3b Author: tcsenpai Date: Mon Nov 4 22:25:54 2024 +0800 first commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9e9089a --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +old/ +.env +package-lock.json +node_modules/ + diff --git a/backup.sh b/backup.sh new file mode 100755 index 0000000..e16e3af --- /dev/null +++ b/backup.sh @@ -0,0 +1,7 @@ +#!/bin/bash +rm -rf old/* +cp -r public old/ +cp server.js old/ + +cat /dev/null > server.js +cat /dev/null > public/index.html diff --git a/env.example b/env.example new file mode 100644 index 0000000..4d69fe0 --- /dev/null +++ b/env.example @@ -0,0 +1,15 @@ +# Server Configuration +PORT=3000 + +# Admin Credentials +ADMIN_USERNAME=admin +ADMIN_PASSWORD=setyourpassword + +# RCON Configuration +RCON_HOST=localhost +RCON_PORT=25575 +RCON_PASSWORD=yourrconpassword + +# Paths +MODS_PATH=/your/server/mods +SERVER_PATH=/your/server diff --git a/package.json b/package.json new file mode 100644 index 0000000..c664aec --- /dev/null +++ b/package.json @@ -0,0 +1,20 @@ +{ + "name": "mine", + "version": "1.0.0", + "description": "", + "main": "server.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "start": "node server.js" + }, + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "dotenv": "^16.4.5", + "express": "^4.21.1", + "express-basic-auth": "^1.2.1", + "multer": "^1.4.5-lts.1", + "rcon-client": "^4.2.5" + } +} diff --git a/public/index.html b/public/index.html new file mode 100644 index 0000000..0646502 --- /dev/null +++ b/public/index.html @@ -0,0 +1,305 @@ + + + + + + Minecraft Server Control Panel + + + +
Authentication failed. Please reload the page and try again.
+
+
+

Minecraft Server Control Panel

+
+ +
+
+

Server Status

+ Offline +
+
+

Players

+ 0/20 +
+
+

TPS

+ 20 +
+
+

Uptime

+ 0:00:00 +
+
+ +
+ +
+ + +
+ +
+ + + + +
+ +
+

Installed Mods

+
+
+
+ + + + diff --git a/server.js b/server.js new file mode 100644 index 0000000..78c536a --- /dev/null +++ b/server.js @@ -0,0 +1,176 @@ +require('dotenv').config(); +const express = require('express'); +const { Rcon } = require('rcon-client'); +const fs = require('fs'); +const path = require('path'); +const basicAuth = require('express-basic-auth'); +const app = express(); +const port = process.env.PORT || 3000; + +app.use(express.json()); + +// Basic Authentication +const users = { + [process.env.ADMIN_USERNAME]: process.env.ADMIN_PASSWORD +}; + +app.use(basicAuth({ + users: users, + challenge: true, + realm: 'Minecraft Server Control Panel' +})); + +app.use(express.static('public')); + +// RCON configuration +const config = { + host: process.env.RCON_HOST, + port: parseInt(process.env.RCON_PORT), + password: process.env.RCON_PASSWORD, + modsPath: process.env.MODS_PATH, + serverPath: process.env.SERVER_PATH +}; + +let rcon = null; +let serverStats = { + players: 0, + maxPlayers: 0, + tps: 20, + uptime: 0, + mods: [] +}; + +async function connectRcon() { + if (rcon === null || !rcon.connected) { + rcon = await Rcon.connect({ + host: config.host, + port: config.port, + password: config.password + }); + console.log('RCON authenticated'); + } + return rcon; +} + +async function getMaxPlayers() { + try { + const propertiesPath = path.join(config.serverPath || '.', 'server.properties'); + if (fs.existsSync(propertiesPath)) { + const content = fs.readFileSync(propertiesPath, 'utf8'); + const maxPlayersMatch = content.match(/max-players=(\d+)/); + if (maxPlayersMatch) { + return parseInt(maxPlayersMatch[1]); + } + } + + const rcon = await connectRcon(); + const response = await rcon.send('list'); + const match = response.match(/\d+\/(\d+)/); + if (match) { + return parseInt(match[1]); + } + } catch (error) { + console.error('Error getting max players:', error); + } + return 20; +} + +function formatBytes(bytes) { + if (bytes === 0) return '0 Bytes'; + const k = 1024; + const sizes = ['Bytes', 'KB', 'MB', 'GB']; + const i = Math.floor(Math.log(bytes) / Math.log(k)); + return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]; +} + +async function updateServerStats() { + try { + const rcon = await connectRcon(); + + const listResponse = await rcon.send('list'); + const playerMatch = listResponse.match(/There are (\d+)\/(\d+) players online/); + if (playerMatch) { + serverStats.players = parseInt(playerMatch[1]); + serverStats.maxPlayers = parseInt(playerMatch[2]); + } else { + serverStats.maxPlayers = await getMaxPlayers(); + } + + try { + const tpsResponse = await rcon.send('tps'); + const tpsMatch = tpsResponse.match(/\d+\.\d+/); + if (tpsMatch) { + serverStats.tps = parseFloat(tpsMatch[0]); + } + } catch (e) { + serverStats.tps = 20; + } + + try { + const modsDir = config.modsPath; + if (fs.existsSync(modsDir)) { + serverStats.mods = fs.readdirSync(modsDir) + .filter(file => file.endsWith('.jar')) + .map(file => ({ + name: file.replace('.jar', ''), + size: formatBytes(fs.statSync(path.join(modsDir, file)).size) + })); + } + } catch (e) { + console.error('Error reading mods:', e); + } + + } catch (error) { + console.error('Error updating server stats:', error); + rcon = null; + } +} + +app.get('/api/stats', async (req, res) => { + try { + await updateServerStats(); + res.json(serverStats); + } catch (error) { + res.status(500).json({ error: error.message }); + } +}); + +app.post('/api/command', async (req, res) => { + const { command } = req.body; + + if (!command) { + return res.status(400).json({ success: false, error: 'No command provided' }); + } + + try { + const rcon = await connectRcon(); + const response = await rcon.send(command); + res.json({ success: true, response }); + } catch (error) { + res.status(500).json({ success: false, error: error.message }); + } +}); + +// Validate required environment variables +const requiredEnvVars = [ + 'ADMIN_USERNAME', + 'ADMIN_PASSWORD', + 'RCON_HOST', + 'RCON_PORT', + 'RCON_PASSWORD', + 'MODS_PATH', + 'SERVER_PATH' +]; + +const missingEnvVars = requiredEnvVars.filter(varName => !process.env[varName]); +if (missingEnvVars.length > 0) { + console.error('Missing required environment variables:', missingEnvVars.join(', ')); + process.exit(1); +} + +// Start server +app.listen(port, () => { + console.log(`Server running at http://localhost:${port}`); + updateServerStats(); // Initial stats update + setInterval(updateServerStats, 5000); // Update every 5 seconds +});