Last active
May 13, 2020 06:50
-
-
Save alitaheri/b4bb09638f88bdd7d3e3456cc215900f to your computer and use it in GitHub Desktop.
Minecraft Backup Script
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const { resolve } = require('path'); | |
const { existsSync, writeFileSync, readFileSync } = require('fs'); | |
const { promisify } = require('util'); | |
const exec = promisify(require('child_process').exec); | |
const { Rcon } = require('rcon-client'); | |
const moment = require('moment'); | |
const configFile = resolve(__dirname, 'backup-config.json'); | |
if (!existsSync(configFile)) { | |
console.log('Missing config file. The file backup-config.json has been created, please provide the correct config'); | |
const configSample = { | |
world: 'my-world-name', | |
rcon: { host: 'localhost', port: 25575, password: 'rcon-pass' }, | |
mega: { path: '/' }, | |
backup: { interval: 30 * 60 * 1000 }, | |
}; | |
writeFileSync(configFile, JSON.stringify(configSample, null, ' '), 'utf8'); | |
process.exit(1); | |
} | |
const config = JSON.parse(readFileSync(configFile, { encoding: 'utf8' })); | |
let rcon = null; | |
let playersWereOnlineOnPreviousAttempt = null; | |
function log(message) { | |
if (process.argv.includes('--verbose') || process.argv.includes('--once')) { | |
console.log(message); | |
} | |
} | |
async function kill(error) { | |
const message = (error && error.message) || error || 'Unknown'; | |
console.log(`An Error has occurred: ${message}`); | |
try { | |
console.log('Attempting to restore "save-on"'); | |
const failsafeConnection = await Rcon.connect({ | |
host: config.rcon.host, | |
port: config.rcon.port, | |
password: config.rcon.password, | |
}); | |
await failsafeConnection.send('save-on'); | |
} finally { | |
process.exit(1); | |
} | |
} | |
function execute(command) { | |
log('Executing bash command: ' + command); | |
return exec(command, { cwd: __dirname }); | |
} | |
async function getConnection() { | |
if (rcon) { | |
return rcon; | |
} | |
try { | |
rcon = await Rcon.connect({ | |
host: config.rcon.host, | |
port: config.rcon.port, | |
password: config.rcon.password, | |
}); | |
} catch (error) { | |
await kill(error); | |
} | |
rcon.on('end', () => kill('RCON closed unexpectedly')); | |
return rcon; | |
} | |
async function sendRCONCommand(command) { | |
const rcon = await getConnection(); | |
try { | |
log('Sending RCON command: ' + command); | |
return await rcon.send(command); | |
} catch (error) { | |
await kill(error); | |
} | |
} | |
async function makeBackup() { | |
try { | |
await sendRCONCommand('save-off'); | |
await sendRCONCommand('save-all'); | |
log('Waiting 10 seconds for the save to complete'); | |
await new Promise(resolve => setTimeout(resolve, 10 * 1000)); | |
const worldFolder = resolve(__dirname, config.world); | |
const backupFile = resolve(__dirname, 'backup.tar.gz'); | |
const backupName = `${config.world}/${moment().utc().format('YYYY-MM-DD HH:mm')}.tar.gz`; | |
log('Making a compressed archive of the world: ' + config.world); | |
await execute(`tar -czf "${backupFile}" "${worldFolder}"`); | |
await execute(`mega-put -c "${backupFile}" "${config.mega.path}/${backupName}"`) | |
await sendRCONCommand('save-on'); | |
await sendRCONCommand(`say "Successfully Made a Backup [${backupName}]"`); | |
} catch (error) { | |
await kill(error); | |
} | |
} | |
async function attemptBackup() { | |
const noPlayersOnline = (await sendRCONCommand('list')).startsWith('There are 0 '); | |
if (!noPlayersOnline) { | |
log('There are players online, making backup of their progress'); | |
playersWereOnlineOnPreviousAttempt = true; | |
await makeBackup(); | |
return; | |
} | |
if (playersWereOnlineOnPreviousAttempt === null) { | |
log('It is unknown whether there were any players online or not before this backup attemp, making one anyway.'); | |
playersWereOnlineOnPreviousAttempt = false; | |
await makeBackup(); | |
return; | |
} | |
if (playersWereOnlineOnPreviousAttempt) { | |
log('There are no players online, but there were previously. Making backup to ensure their progress is saved'); | |
playersWereOnlineOnPreviousAttempt = false; | |
await makeBackup(); | |
return; | |
} | |
log('There has not been any players online for 2 attempts, skipping backup'); | |
} | |
if (process.argv.includes('--once')) { | |
makeBackup().finally(() => process.kill(0)); | |
} else { | |
setInterval(attemptBackup, config.backup.interval); | |
if (process.argv.includes('--preemptive')) { | |
makeBackup(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment