#!/bin/bash set -e # ───────────────────────────────────────────── # 1. Resolve Working Directory # ───────────────────────────────────────────── if [ -d "/data" ]; then echo "[System] Persistent /data bucket found. Taking ownership..." sudo chown -R minecraft:minecraft /data 2>/dev/null || true sudo chmod -R 775 /data 2>/dev/null || true cd /data else echo "[Warning] No /data bucket found. World will reset on restart!" mkdir -p /app/server cd /app/server fi WORKDIR=$(pwd) echo "[System] Working directory: $WORKDIR" # ───────────────────────────────────────────── # 2. Clean Up Old/Corrupted Files # ───────────────────────────────────────────── echo "[System] Cleaning up old jars..." # Only remove specific old plugin jars, NOT the whole plugins dir yet rm -f plugins/Via*.jar rm -f plugins/EaglerX*.jar rm -f server.jar echo "[System] Clearing Paperclip cache..." rm -rf cache mkdir -p logs mkdir -p plugins/EaglercraftXServer # ───────────────────────────────────────────── # 3. Install Node.js Dependencies # ───────────────────────────────────────────── echo "[System] Installing WebSocket proxy dependencies..." npm install http-proxy --save 2>&1 | tail -1 # ───────────────────────────────────────────── # 4. Download Paper 1.12.2 # ───────────────────────────────────────────── echo "[System] Downloading Paper 1.12.2..." curl -fsSL \ -A "Mozilla/5.0" \ -o server.jar \ "https://api.papermc.io/v2/projects/paper/versions/1.12.2/builds/1620/downloads/paper-1.12.2-1620.jar" if [ ! -s server.jar ]; then echo "[Error] server.jar download failed or is empty. Aborting." exit 1 fi echo "[System] server.jar downloaded successfully ($(du -sh server.jar | cut -f1))." # ───────────────────────────────────────────── # 5. Download Plugins # ───────────────────────────────────────────── echo "[System] Downloading EaglerXServer & EaglerXRewind..." curl -fsSL -A "Mozilla/5.0" \ -o plugins/EaglerXServer.jar \ "https://github.com/lax1dude/eaglerxserver/releases/download/v1.0.8/EaglerXServer.jar" curl -fsSL -A "Mozilla/5.0" \ -o plugins/EaglerXRewind.jar \ "https://github.com/lax1dude/eaglerxserver/releases/download/v1.0.8/EaglerXRewind.jar" echo "[System] Downloading ViaVersion suite..." curl -fsSL -A "Mozilla/5.0" \ -o plugins/ViaVersion.jar \ "https://github.com/ViaVersion/ViaVersion/releases/download/5.8.1/ViaVersion-5.8.1.jar" curl -fsSL -A "Mozilla/5.0" \ -o plugins/ViaBackwards.jar \ "https://cdn.modrinth.com/data/NpvuJQoq/versions/8YdMHUIl/ViaBackwards-5.9.0-SNAPSHOT.jar" curl -fsSL -A "Mozilla/5.0" \ -o plugins/ViaRewind.jar \ "https://cdn.modrinth.com/data/TbHIxhx5/versions/r3AZ5yTY/ViaRewind-4.1.0-SNAPSHOT.jar" # Verify all plugins downloaded echo "[System] Verifying plugin downloads..." for jar in \ plugins/EaglerXServer.jar \ plugins/EaglerXRewind.jar \ plugins/ViaVersion.jar \ plugins/ViaBackwards.jar \ plugins/ViaRewind.jar do if [ ! -s "$jar" ]; then echo "[Error] $jar is missing or empty! Aborting." exit 1 fi echo " [OK] $jar ($(du -sh "$jar" | cut -f1))" done # ───────────────────────────────────────────── # 6. Server Configuration # ───────────────────────────────────────────── echo "[System] Writing eula.txt and server.properties..." echo "eula=true" > eula.txt cat > server.properties << 'PROPS' server-port=25565 online-mode=false motd=Eaglercraft Server max-players=60 PROPS # ───────────────────────────────────────────── # 7. EaglerXServer Listener Config # ───────────────────────────────────────────── echo "[System] Writing EaglercraftXServer listener config..." cat > plugins/EaglercraftXServer/listeners.yml << 'LISTENEOF' listeners: - address: 127.0.0.1:25566 motd: "&dEaglercraftX\n&ePowered by EaglerXServer" max_players: 60 online_mode: false forward_ip: true LISTENEOF # ───────��───────────────────────────────────── # 8. Generate Node.js Web Console # ───────────────────────────────────────────── echo "[System] Writing webconsole.js..." cat > webconsole.js << 'NODEEOF' 'use strict'; const http = require('http'); const fs = require('fs'); const path = require('path'); const { spawn, exec } = require('child_process'); const httpProxy = require('http-proxy'); const LOG_FILE = path.join(__dirname, 'logs', 'latest.log'); const MC_PORT = 25566; // EaglerXServer internal WS port const WEB_PORT = 7860; // Exposed port // ── Banner ──────────────────────────────────────────────────────────────────── console.log('========================================='); console.log(' Eaglercraft Server + Web Console '); console.log('========================================='); // ── Start Minecraft ─────────────────────────────────────────────────────────── const mcProcess = spawn('java', [ '-Xms2G', '-Xmx2G', '-XX:+UseG1GC', '-jar', 'server.jar', 'nogui' ], { cwd: __dirname }); const logStream = fs.createWriteStream(LOG_FILE, { flags: 'w' }); mcProcess.stdout.pipe(logStream, { end: false }); mcProcess.stderr.pipe(logStream, { end: false }); mcProcess.stdout.pipe(process.stdout); mcProcess.stderr.pipe(process.stderr); mcProcess.on('exit', (code) => { console.log(`[MC] Server process exited with code ${code}`); logStream.end(); }); // ── WebSocket Proxy ─────────────────────────────────────────────────────────── const proxy = httpProxy.createProxyServer({ target: `ws://127.0.0.1:${MC_PORT}`, ws: true }); proxy.on('error', (err, req, socket) => { console.warn('[Proxy] Routing error (server may still be booting):', err.message); if (socket && socket.writable) socket.destroy(); }); // ── HTML Page ───────────────────────────────────────────────────────────────── const PAGE_HTML = ` Eaglercraft Console

▶ Eaglercraft Live Console

Connecting to server log…
`; // ── HTTP Server ─────────────────────────────────────────────────────────────── const server = http.createServer((req, res) => { // ── GET / → console page if (req.method === 'GET' && req.url === '/') { res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' }); return res.end(PAGE_HTML); } // ── GET /logs → last 200 lines of log if (req.method === 'GET' && req.url === '/logs') { exec(`tail -n 200 "${LOG_FILE}"`, (err, stdout) => { res.writeHead(200, { 'Content-Type': 'text/plain; charset=utf-8' }); res.end(stdout || '(Waiting for server output…)'); }); return; } // ── POST /command → write to MC stdin if (req.method === 'POST' && req.url === '/command') { let body = ''; req.on('data', chunk => { body += chunk; }); req.on('end', () => { const cmd = new URLSearchParams(body).get('cmd') || ''; const safe = cmd.trim().replace(/[\r\n]+/g, ''); // no newline injection if (safe && mcProcess.stdin.writable) { mcProcess.stdin.write(safe + '\n'); console.log(`[Console] Command sent: ${safe}`); } res.writeHead(200, { 'Content-Type': 'text/plain' }); res.end('ok'); }); return; } res.writeHead(404); res.end('Not found'); }); // ── Upgrade → proxy WebSocket to Eaglercraft server.on('upgrade', (req, socket, head) => { console.log(`[Proxy] WS upgrade from ${socket.remoteAddress} → MC:${MC_PORT}`); proxy.ws(req, socket, head); }); server.listen(WEB_PORT, '0.0.0.0', () => { console.log(`[WebConsole] Listening on http://0.0.0.0:${WEB_PORT}`); }); NODEEOF # ───────────────────────────────────────────── # 9. Launch # ───────────────────────────────────────────── echo "[System] Handing off to Node.js master controller..." exec node webconsole.js