node1 / start.sh
Nodiw52992's picture
Update start.sh
7e4140e verified
Raw
History Blame Contribute Delete
12.1 kB
#!/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 = `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Eaglercraft Console</title>
<style>
* { box-sizing: border-box; margin: 0; padding: 0; }
body {
background: #1e1e1e; color: #d4d4d4;
font-family: 'Consolas', monospace;
display: flex; flex-direction: column;
height: 100vh; padding: 16px; gap: 12px;
}
h2 { color: #569cd6; font-size: 1.1rem; }
#logs {
flex: 1; overflow-y: auto; white-space: pre-wrap; word-break: break-all;
background: #0d0d0d; padding: 12px;
border: 1px solid #333; border-radius: 4px;
font-size: 0.82rem; line-height: 1.45;
}
form { display: flex; gap: 8px; }
input[type=text] {
flex: 1; padding: 9px 12px;
background: #2d2d2d; color: #fff;
border: 1px solid #444; border-radius: 4px; font-family: inherit;
}
button {
padding: 9px 20px; background: #0e639c; color: #fff;
border: none; border-radius: 4px; cursor: pointer; font-family: inherit;
}
button:hover { background: #1177bb; }
</style>
</head>
<body>
<h2>&#9654; Eaglercraft Live Console</h2>
<div id="logs">Connecting to server log…</div>
<form action="/command" method="POST" target="_blank"
onsubmit="setTimeout(()=>this.reset(),10)">
<input type="text" name="cmd"
placeholder="e.g. op YourName | say Hello!"
autocomplete="off" autofocus>
<button type="submit">Send</button>
</form>
<script>
const logs = document.getElementById('logs');
let last = '';
async function poll() {
try {
const t = await fetch('/logs').then(r => r.text());
if (t !== last) {
last = t;
const atBottom =
logs.scrollHeight - logs.clientHeight <= logs.scrollTop + 60;
logs.textContent = t;
if (atBottom) logs.scrollTop = logs.scrollHeight;
}
} catch(_) {}
setTimeout(poll, 1000);
}
poll();
</script>
</body>
</html>`;
// ── 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