Compare commits
2 commits
v1.0-beta1
...
master
Author | SHA1 | Date | |
---|---|---|---|
d382de1262 | |||
9257f94dbe |
11 changed files with 96 additions and 16 deletions
|
@ -8,7 +8,7 @@ services:
|
||||||
- DGID=${DGID}
|
- DGID=${DGID}
|
||||||
image: dockermc
|
image: dockermc
|
||||||
environment:
|
environment:
|
||||||
- MC_VERSION=1.19
|
- MC_VERSION=1.19.2
|
||||||
- DGID=${DGID}
|
- DGID=${DGID}
|
||||||
volumes:
|
volumes:
|
||||||
- ../src:/src
|
- ../src:/src
|
||||||
|
|
22
.docker/io.github.TBMCPlugins.FlatMC.yml
Normal file
22
.docker/io.github.TBMCPlugins.FlatMC.yml
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
app-id: io.github.TBMCPlugins.FlatMC
|
||||||
|
runtime: org.freedesktop.Sdk # Has to be SDK instead of Platform because we need the NodeJS SDK extension
|
||||||
|
runtime-version: '21.08'
|
||||||
|
sdk: org.freedesktop.Sdk
|
||||||
|
command: start.sh
|
||||||
|
modules:
|
||||||
|
- name: HelloTeszt
|
||||||
|
buildsystem: simple
|
||||||
|
build-commands:
|
||||||
|
- npm install --offline
|
||||||
|
- npm run build
|
||||||
|
- install -d dist /app/bin/dist
|
||||||
|
- install -D start.sh /app/bin/start.sh
|
||||||
|
sources:
|
||||||
|
- type: dir
|
||||||
|
path: ../src
|
||||||
|
- type: file
|
||||||
|
path: runner/start.sh
|
||||||
|
sdk-extensions:
|
||||||
|
- org.freedesktop.Sdk.Extension.node16
|
||||||
|
build-options:
|
||||||
|
append-path: /usr/lib/sdk/node16/bin
|
8
.docker/mcserver/configs/ops.json
Executable file
8
.docker/mcserver/configs/ops.json
Executable file
|
@ -0,0 +1,8 @@
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"uuid": "bd0c72e7-f5a6-49be-a407-ad69e72a3431",
|
||||||
|
"name": "NorbiPeti",
|
||||||
|
"level": 4,
|
||||||
|
"bypassesPlayerLimit": false
|
||||||
|
}
|
||||||
|
]
|
|
@ -15,7 +15,7 @@ listeners:
|
||||||
host: 0.0.0.0:25577
|
host: 0.0.0.0:25577
|
||||||
max_players: 1
|
max_players: 1
|
||||||
tab_size: 60
|
tab_size: 60
|
||||||
force_default_server: false
|
force_default_server: true
|
||||||
remote_ping_cache: -1
|
remote_ping_cache: -1
|
||||||
network_compression_threshold: 256
|
network_compression_threshold: 256
|
||||||
permissions:
|
permissions:
|
||||||
|
|
|
@ -25,7 +25,7 @@ RUN groupadd -g $DGID hostDocker || :
|
||||||
RUN usermod -aG $DGID node
|
RUN usermod -aG $DGID node
|
||||||
USER 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
|
FROM dev AS prod
|
||||||
|
|
||||||
|
|
5
.docker/runner/start.sh
Normal file
5
.docker/runner/start.sh
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
#!/bin/sh
|
||||||
|
echo "Starting runner"
|
||||||
|
export PATH=$PATH:/usr/lib/sdk/node16/bin
|
||||||
|
npm rum start
|
||||||
|
echo "Exiting runner"
|
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -5,3 +5,5 @@ node_modules
|
||||||
.docker/mcserver/server*
|
.docker/mcserver/server*
|
||||||
.docker/mcserver/*.jar
|
.docker/mcserver/*.jar
|
||||||
.docker/mcserver/plugins
|
.docker/mcserver/plugins
|
||||||
|
.docker/.flatpak-builder
|
||||||
|
.docker/build
|
||||||
|
|
50
src/index.ts
50
src/index.ts
|
@ -7,38 +7,58 @@ import debounce from 'debounce';
|
||||||
const running: {
|
const running: {
|
||||||
watch: chokidar.FSWatcher,
|
watch: chokidar.FSWatcher,
|
||||||
server1: boolean,
|
server1: boolean,
|
||||||
serverName: () => string,
|
serverName: () => 'server1' | 'server2',
|
||||||
prevServerName: () => string
|
prevServerName: () => 'server1' | 'server2'
|
||||||
} = {
|
} = {
|
||||||
watch: null,
|
watch: null,
|
||||||
server1: false,
|
server1: false,
|
||||||
serverName: () => 'server' + (running.server1 ? '1' : '2'),
|
serverName: () => (running.server1 ? 'server1' : 'server2'),
|
||||||
prevServerName: () => 'server' + (running.server1 ? '2' : '1')
|
prevServerName: () => (running.server1 ? 'server2' : 'server1')
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let isShuttingDown = false;
|
||||||
|
|
||||||
async function main() {
|
async function main() {
|
||||||
console.log("Checking for server updates...", "MC", process.env.MC_VERSION);
|
console.log("Checking for server updates...", "MC", process.env.MC_VERSION);
|
||||||
await updateServer('paper');
|
await updateServer('paper');
|
||||||
await updateServer('waterfall');
|
await updateServer('waterfall');
|
||||||
console.log('Starting server proxy...');
|
console.log('Starting server proxy...');
|
||||||
exec('docker compose -f /docker-compose.server.yml up -d bungee', loggingCallback);
|
execCompose('bungee', 'up');
|
||||||
const listener = async (changedPath) => {
|
const listener = async (changedPath) => {
|
||||||
console.log(`Plugin change detected at ${changedPath}, switching to ${running.prevServerName()}...`);
|
console.log(`Plugin change detected at ${changedPath}, switching to ${running.prevServerName()}...`);
|
||||||
await startNextServer();
|
await startNextServer();
|
||||||
};
|
};
|
||||||
running.watch = chokidar.watch(`/mcserver/plugins/*.jar`).on('change', debounce(listener, 500));
|
running.watch = chokidar.watch(`/mcserver/plugins/*.jar`).on('change', debounce(listener, 500));
|
||||||
await startNextServer();
|
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<void>(resolve => execCompose(running.serverName(), 'stop').on('exit', () => resolve()));
|
||||||
|
await new Promise<void>(resolve => execCompose('bungee', 'stop').on('exit', () => resolve()));
|
||||||
|
console.log("Shutdown complete");
|
||||||
|
process.exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
async function updateServer(project: 'paper' | 'waterfall') {
|
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];
|
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 {
|
try {
|
||||||
await promises.access('/mcserver/' + lastBuild.downloads.application.name)
|
await promises.access('/mcserver/' + lastBuild.downloads.application.name)
|
||||||
} catch {
|
} catch {
|
||||||
console.log("Build not found locally, downloading...");
|
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) {
|
if (newVerRes.status > 299) {
|
||||||
console.log("Error while downloading", await newVerRes.json());
|
console.log("Error while downloading", await newVerRes.json());
|
||||||
throw new Error("Error while downloading");
|
throw new Error("Error while downloading");
|
||||||
|
@ -57,14 +77,15 @@ async function startNextServer() {
|
||||||
console.log('Copying config files...');
|
console.log('Copying config files...');
|
||||||
await promises.cp('/mcserver/configs', `/mcserver/${running.serverName()}`, {recursive: true});
|
await promises.cp('/mcserver/configs', `/mcserver/${running.serverName()}`, {recursive: true});
|
||||||
console.log("Starting server", running.server1 ? 'one...' : 'two...');
|
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 waitForStartup();
|
||||||
await stopPrevServer();
|
await stopPrevServer();
|
||||||
|
console.log("Server", running.server1 ? 'one' : 'two', "reachable at localhost:25565");
|
||||||
}
|
}
|
||||||
|
|
||||||
async function stopPrevServer() {
|
async function stopPrevServer() {
|
||||||
console.log("Stopping previous server...");
|
console.log("Stopping previous server...");
|
||||||
exec('docker compose -f /docker-compose.server.yml stop ' + running.prevServerName(), loggingCallback);
|
execCompose(running.prevServerName(), 'stop');
|
||||||
}
|
}
|
||||||
|
|
||||||
function waitForStartup() {
|
function waitForStartup() {
|
||||||
|
@ -88,11 +109,18 @@ function loggingCallback(error?: ExecException, stdout?: string, stderr?: string
|
||||||
if (stderr) {
|
if (stderr) {
|
||||||
console.error(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);
|
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));
|
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 } } };
|
type Build = { build: number, time: string, changes: { summary: string }[], downloads: { application: { name: string }, 'mojang-mappings': { name: string } } };
|
||||||
|
|
13
src/package-lock.json
generated
13
src/package-lock.json
generated
|
@ -14,12 +14,19 @@
|
||||||
"read-last-lines": "^1.8.0"
|
"read-last-lines": "^1.8.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@types/debounce": "^1.2.1",
|
||||||
"@types/node": "^18.0.0",
|
"@types/node": "^18.0.0",
|
||||||
"source-map-support": "^0.5.21",
|
"source-map-support": "^0.5.21",
|
||||||
"tsc-watch": "^5.0.3",
|
"tsc-watch": "^5.0.3",
|
||||||
"typescript": "^4.7.4"
|
"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": {
|
"node_modules/@types/node": {
|
||||||
"version": "18.0.0",
|
"version": "18.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.0.0.tgz",
|
||||||
|
@ -583,6 +590,12 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"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": {
|
"@types/node": {
|
||||||
"version": "18.0.0",
|
"version": "18.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.0.0.tgz",
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
"author": "NorbiPeti",
|
"author": "NorbiPeti",
|
||||||
"license": "",
|
"license": "",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@types/debounce": "^1.2.1",
|
||||||
"@types/node": "^18.0.0",
|
"@types/node": "^18.0.0",
|
||||||
"source-map-support": "^0.5.21",
|
"source-map-support": "^0.5.21",
|
||||||
"tsc-watch": "^5.0.3",
|
"tsc-watch": "^5.0.3",
|
||||||
|
|
|
@ -2,7 +2,8 @@
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"outDir": "dist",
|
"outDir": "dist",
|
||||||
"rootDir": ".",
|
"rootDir": ".",
|
||||||
"moduleResolution": "Node"
|
"moduleResolution": "Node",
|
||||||
|
"allowSyntheticDefaultImports": true
|
||||||
},
|
},
|
||||||
"include": ["."],
|
"include": ["."]
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue