From d382de1262176891d7c841387ca5a8f5aa967113 Mon Sep 17 00:00:00 2001 From: NorbiPeti Date: Sat, 19 Nov 2022 02:12:16 +0100 Subject: [PATCH] Still using Docker, added support for proper shutdown - It still doesn't work with tsc-watch in Docker, hopefully the final image will work --- .docker/docker-compose.yml | 2 +- .docker/mcserver/proxy/config.yml | 2 +- .docker/runner/Dockerfile | 2 +- src/index.ts | 50 ++++++++++++++++++++++++------- src/package-lock.json | 13 ++++++++ src/package.json | 1 + src/tsconfig.json | 5 ++-- 7 files changed, 59 insertions(+), 16 deletions(-) diff --git a/.docker/docker-compose.yml b/.docker/docker-compose.yml index 3c1058e..f45525a 100644 --- a/.docker/docker-compose.yml +++ b/.docker/docker-compose.yml @@ -8,7 +8,7 @@ services: - DGID=${DGID} image: dockermc environment: - - MC_VERSION=1.19 + - MC_VERSION=1.19.2 - DGID=${DGID} volumes: - ../src:/src diff --git a/.docker/mcserver/proxy/config.yml b/.docker/mcserver/proxy/config.yml index 6de7ecc..b7c13de 100644 --- a/.docker/mcserver/proxy/config.yml +++ b/.docker/mcserver/proxy/config.yml @@ -15,7 +15,7 @@ listeners: host: 0.0.0.0:25577 max_players: 1 tab_size: 60 - force_default_server: false + force_default_server: true remote_ping_cache: -1 network_compression_threshold: 256 permissions: diff --git a/.docker/runner/Dockerfile b/.docker/runner/Dockerfile index 3c4e798..7782137 100644 --- a/.docker/runner/Dockerfile +++ b/.docker/runner/Dockerfile @@ -25,7 +25,7 @@ RUN groupadd -g $DGID hostDocker || : RUN usermod -aG $DGID node USER node -ENTRYPOINT ["npm", "run", "start:watch"] +ENTRYPOINT ["node_modules/.bin/tsc-watch", "--target", "es2017", "--outDir", "./dist", "--onSuccess", "node -r source-map-support/register .", "--noClear"] FROM dev AS prod diff --git a/src/index.ts b/src/index.ts index 9ca4dbd..6169bd8 100644 --- a/src/index.ts +++ b/src/index.ts @@ -7,38 +7,58 @@ import debounce from 'debounce'; const running: { watch: chokidar.FSWatcher, server1: boolean, - serverName: () => string, - prevServerName: () => string + serverName: () => 'server1' | 'server2', + prevServerName: () => 'server1' | 'server2' } = { watch: null, server1: false, - serverName: () => 'server' + (running.server1 ? '1' : '2'), - prevServerName: () => 'server' + (running.server1 ? '2' : '1') + serverName: () => (running.server1 ? 'server1' : 'server2'), + prevServerName: () => (running.server1 ? 'server2' : 'server1') }; +let isShuttingDown = false; + async function main() { console.log("Checking for server updates...", "MC", process.env.MC_VERSION); await updateServer('paper'); await updateServer('waterfall'); console.log('Starting server proxy...'); - exec('docker compose -f /docker-compose.server.yml up -d bungee', loggingCallback); + execCompose('bungee', 'up'); const listener = async (changedPath) => { console.log(`Plugin change detected at ${changedPath}, switching to ${running.prevServerName()}...`); await startNextServer(); }; running.watch = chokidar.watch(`/mcserver/plugins/*.jar`).on('change', debounce(listener, 500)); await startNextServer(); + process.on('SIGTERM', () => shutdown()); + process.on('SIGINT', () => shutdown()); +} + +async function shutdown() { + if (isShuttingDown) return; + isShuttingDown = true; + console.log("Shutting down..."); + await running.watch?.close(); + await new Promise(resolve => execCompose(running.serverName(), 'stop').on('exit', () => resolve())); + await new Promise(resolve => execCompose('bungee', 'stop').on('exit', () => resolve())); + console.log("Shutdown complete"); + process.exit(); } async function updateServer(project: 'paper' | 'waterfall') { - const res: { builds: Build[] } = await (await fetch(`https://api.papermc.io/v2/projects/${project}/versions/${process.env.MC_VERSION}/builds`)).json() as any; + let mcver = process.env.MC_VERSION; + mcver = project === 'waterfall' ? mcver.split('.').slice(0, 2).join('.') : mcver; + const res: { builds?: Build[] } = await (await fetch(`https://api.papermc.io/v2/projects/${project}/versions/${mcver}/builds`)).json() as any; + if (!res.builds) { + throw new Error(`No builds found for MC version ${mcver}`); + } const lastBuild = res.builds[res.builds.length - 1]; - console.log(`Latest ${project} build is ${lastBuild.build} at ${new Date(Date.parse(lastBuild.time)).toLocaleString()}`); + console.log(`Latest ${project} build is ${lastBuild.build} at ${new Date(Date.parse(lastBuild.time)).toLocaleString()}`); // TODO: Delete all old builds try { await promises.access('/mcserver/' + lastBuild.downloads.application.name) } catch { console.log("Build not found locally, downloading..."); - const newVerRes = await fetch(`https://api.papermc.io/v2/projects/${project}/versions/${process.env.MC_VERSION}/builds/${lastBuild.build}/downloads/${lastBuild.downloads.application.name}`); + const newVerRes = await fetch(`https://api.papermc.io/v2/projects/${project}/versions/${mcver}/builds/${lastBuild.build}/downloads/${lastBuild.downloads.application.name}`); if (newVerRes.status > 299) { console.log("Error while downloading", await newVerRes.json()); throw new Error("Error while downloading"); @@ -57,14 +77,15 @@ async function startNextServer() { console.log('Copying config files...'); await promises.cp('/mcserver/configs', `/mcserver/${running.serverName()}`, {recursive: true}); console.log("Starting server", running.server1 ? 'one...' : 'two...'); - exec('docker compose -f /docker-compose.server.yml up -d ' + running.serverName(), loggingCallback); + execCompose(running.serverName(), 'up'); await waitForStartup(); await stopPrevServer(); + console.log("Server", running.server1 ? 'one' : 'two', "reachable at localhost:25565"); } async function stopPrevServer() { console.log("Stopping previous server..."); - exec('docker compose -f /docker-compose.server.yml stop ' + running.prevServerName(), loggingCallback); + execCompose(running.prevServerName(), 'stop'); } function waitForStartup() { @@ -88,11 +109,18 @@ function loggingCallback(error?: ExecException, stdout?: string, stderr?: string if (stderr) { console.error(stderr); } - if (error) { + if (error && error.signal !== 'SIGTERM') { // docker compose stop results in SIGTERM if not running I guess console.error(error); } } +function execCompose(container: 'bungee' | 'server1' | 'server2', command: 'up' | 'stop') { + return exec(`docker compose -f /docker-compose.server.yml ${command}${command === 'up' ? ' -d' : ''} ${container}`, loggingCallback); +} + +// Begin reading from stdin so the process does not exit. +process.stdin.resume(); + main().catch(reason => console.error('An error occurred while running DockerMC.', reason)); type Build = { build: number, time: string, changes: { summary: string }[], downloads: { application: { name: string }, 'mojang-mappings': { name: string } } }; diff --git a/src/package-lock.json b/src/package-lock.json index cddb9d2..4c4ab57 100644 --- a/src/package-lock.json +++ b/src/package-lock.json @@ -14,12 +14,19 @@ "read-last-lines": "^1.8.0" }, "devDependencies": { + "@types/debounce": "^1.2.1", "@types/node": "^18.0.0", "source-map-support": "^0.5.21", "tsc-watch": "^5.0.3", "typescript": "^4.7.4" } }, + "node_modules/@types/debounce": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@types/debounce/-/debounce-1.2.1.tgz", + "integrity": "sha512-epMsEE85fi4lfmJUH/89/iV/LI+F5CvNIvmgs5g5jYFPfhO2S/ae8WSsLOKWdwtoaZw9Q2IhJ4tQ5tFCcS/4HA==", + "dev": true + }, "node_modules/@types/node": { "version": "18.0.0", "resolved": "https://registry.npmjs.org/@types/node/-/node-18.0.0.tgz", @@ -583,6 +590,12 @@ } }, "dependencies": { + "@types/debounce": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@types/debounce/-/debounce-1.2.1.tgz", + "integrity": "sha512-epMsEE85fi4lfmJUH/89/iV/LI+F5CvNIvmgs5g5jYFPfhO2S/ae8WSsLOKWdwtoaZw9Q2IhJ4tQ5tFCcS/4HA==", + "dev": true + }, "@types/node": { "version": "18.0.0", "resolved": "https://registry.npmjs.org/@types/node/-/node-18.0.0.tgz", diff --git a/src/package.json b/src/package.json index c02feb1..a8feb10 100644 --- a/src/package.json +++ b/src/package.json @@ -20,6 +20,7 @@ "author": "NorbiPeti", "license": "", "devDependencies": { + "@types/debounce": "^1.2.1", "@types/node": "^18.0.0", "source-map-support": "^0.5.21", "tsc-watch": "^5.0.3", diff --git a/src/tsconfig.json b/src/tsconfig.json index 49f1dbe..d24fdf3 100644 --- a/src/tsconfig.json +++ b/src/tsconfig.json @@ -2,7 +2,8 @@ "compilerOptions": { "outDir": "dist", "rootDir": ".", - "moduleResolution": "Node" + "moduleResolution": "Node", + "allowSyntheticDefaultImports": true }, - "include": ["."], + "include": ["."] }