From 2ed09ccbe389b27ac698b845e49da5a823d59bad Mon Sep 17 00:00:00 2001 From: TheAfroOfDoom Date: Sun, 4 Feb 2024 15:36:57 -0500 Subject: [PATCH] add `sync.down` script to extract `world.zip` to client saves directory - closes #53 - creates a backup in a `tmp` directory so we don't unintentionally lose local world changes - tested by running the following steps: 1. sync-up world with `yarn sync` 2. move player to a different spot in the world (e.g. turn around 180 degrees) 3. sync-down world with `yarn down` 4. open world and see that player is in the original spot before we turned them around --- .gitignore | 4 ++ package-scripts/sync-world.js | 49 ++++++++++++++--- package.json | 2 + yarn.lock | 101 +++++++++++++++++++++++++++++++++- 4 files changed, 147 insertions(+), 9 deletions(-) diff --git a/.gitignore b/.gitignore index 616fbe5e0..521ccbe41 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,7 @@ resourcepack/assets/animated_java resourcepack/assets/minecraft/models/item/animated_java_empty.json resourcepack/assets/minecraft/models/item/white_dye.json resourcepack/resourcepack.ajmeta + +## temp files +# old world folders that have been backed up after running `yarn down` +tmp diff --git a/package-scripts/sync-world.js b/package-scripts/sync-world.js index a35682881..634491789 100644 --- a/package-scripts/sync-world.js +++ b/package-scripts/sync-world.js @@ -1,6 +1,8 @@ const archiver = require('archiver'); +const chalk = require('chalk'); const dotenv = require('dotenv'); -const { createWriteStream, remove } = require('fs-extra'); +const extract = require('extract-zip'); +const { createWriteStream, remove, stat, mkdir, exists } = require('fs-extra'); const minimist = require('minimist'); const { assertEnvironmentVariables } = require('./utils'); @@ -13,11 +15,12 @@ const minecraftPath = process.env.MINECRAFT_PATH; const worldName = process.env.WORLD_NAME; const minecraftWorldPath = `${minecraftPath}/saves/${worldName}`; +const worldBackupPath = './world.zip'; +const tempBackupPath = './tmp/world-backups'; -const syncUp = async () => { - await remove('./world.zip'); - return new Promise((resolve, reject) => { - const output = createWriteStream('./world.zip'); +const backupWorld = async (dest, useColor = true) => + new Promise((resolve, reject) => { + const output = createWriteStream(dest); const archive = archiver('zip'); archive.on('error', function (err) { @@ -32,7 +35,10 @@ const syncUp = async () => { }); output.on('close', function () { - console.log('Finished world backup'); + const prefix = 'Finished world backup to:'; + const prefixFormatted = useColor ? chalk.greenBright(prefix) : prefix; + const destFormatted = chalk.blueBright(dest); + console.log(prefixFormatted, destFormatted); resolve(); }); @@ -40,10 +46,39 @@ const syncUp = async () => { archive.directory(minecraftWorldPath, false); archive.finalize(); }); + +/** Backup name will be the minecraft save directory's last modified date */ +const createTempWorldBackupName = async () => { + const modifiedTime = (await stat(minecraftWorldPath)).mtime; + const formattedTime = modifiedTime.toISOString().replaceAll(':', '.'); + return `world-${formattedTime}.zip`; +}; + +const syncUp = async () => { + await remove(worldBackupPath); + await backupWorld(worldBackupPath); }; const syncDown = async () => { - throw new Error('Unimplemented'); + // Create tmp/world-backups folder if it doesn't exist + if (!(await exists(tempBackupPath))) { + await mkdir(tempBackupPath, { recursive: true }); + } + + console.log('Backing up your current world as a precautionary measure...'); + const name = await createTempWorldBackupName(); + const path = `${tempBackupPath}/${name}`; + await backupWorld(path, false); + + await remove(minecraftWorldPath); + await extract(worldBackupPath, { dir: minecraftWorldPath }); + + const srcFormatted = chalk.blueBright(worldBackupPath); + const destFormatted = chalk.blueBright(minecraftWorldPath); + const successMessage = chalk.greenBright( + `Extracted ${srcFormatted} to ${destFormatted}`, + ); + console.log(successMessage); }; const main = async () => { diff --git a/package.json b/package.json index d886faab5..81bcc5f4a 100644 --- a/package.json +++ b/package.json @@ -2,6 +2,7 @@ "name": "omega-flowey-minecraft-remastered", "packageManager": "yarn@3.6.3", "scripts": { + "down": "nps sync.world.down", "lint": "nps lint", "lint.fix": "nps lint.fix", "start": "nps", @@ -16,6 +17,7 @@ "chokidar": "3.5.3", "dotenv": "16.3.1", "eslint": "8.56.0", + "extract-zip": "2.0.1", "find-process": "1.4.7", "fs-extra": "11.2.0", "glob": "10.3.10", diff --git a/yarn.lock b/yarn.lock index c53ad607a..abc0e92c4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -149,6 +149,24 @@ __metadata: languageName: node linkType: hard +"@types/node@npm:*": + version: 20.11.16 + resolution: "@types/node@npm:20.11.16" + dependencies: + undici-types: ~5.26.4 + checksum: 51f0831c1219bf4698e7430aeb9892237bd851deeb25ce23c5bb0ceefcc77c3b114e48f4e98d9fc26def5a87ba9d8079f0281dd37bee691140a93f133812c152 + languageName: node + linkType: hard + +"@types/yauzl@npm:^2.9.1": + version: 2.10.3 + resolution: "@types/yauzl@npm:2.10.3" + dependencies: + "@types/node": "*" + checksum: 5ee966ea7bd6b2802f31ad4281c92c4c0b6dfa593c378a2582c58541fa113bec3d70eb0696b34ad95e8e6861a884cba6c3e351285816693ed176222f840a8c08 + languageName: node + linkType: hard + "@ungap/structured-clone@npm:^1.2.0": version: 1.2.0 resolution: "@ungap/structured-clone@npm:1.2.0" @@ -406,7 +424,7 @@ __metadata: languageName: node linkType: hard -"buffer-crc32@npm:^0.2.1": +"buffer-crc32@npm:^0.2.1, buffer-crc32@npm:~0.2.3": version: 0.2.13 resolution: "buffer-crc32@npm:0.2.13" checksum: 06252347ae6daca3453b94e4b2f1d3754a3b146a111d81c68924c22d91889a40623264e95e67955b1cb4a68cbedf317abeabb5140a9766ed248973096db5ce1c @@ -834,6 +852,15 @@ __metadata: languageName: node linkType: hard +"end-of-stream@npm:^1.1.0": + version: 1.4.4 + resolution: "end-of-stream@npm:1.4.4" + dependencies: + once: ^1.4.0 + checksum: 530a5a5a1e517e962854a31693dbb5c0b2fc40b46dad2a56a2deec656ca040631124f4795823acc68238147805f8b021abbe221f4afed5ef3c8e8efc2024908b + languageName: node + linkType: hard + "env-paths@npm:^2.2.0": version: 2.2.1 resolution: "env-paths@npm:2.2.1" @@ -1011,6 +1038,23 @@ __metadata: languageName: node linkType: hard +"extract-zip@npm:2.0.1": + version: 2.0.1 + resolution: "extract-zip@npm:2.0.1" + dependencies: + "@types/yauzl": ^2.9.1 + debug: ^4.1.1 + get-stream: ^5.1.0 + yauzl: ^2.10.0 + dependenciesMeta: + "@types/yauzl": + optional: true + bin: + extract-zip: cli.js + checksum: 8cbda9debdd6d6980819cc69734d874ddd71051c9fe5bde1ef307ebcedfe949ba57b004894b585f758b7c9eeeea0e3d87f2dda89b7d25320459c2c9643ebb635 + languageName: node + linkType: hard + "fast-deep-equal@npm:^3.1.1, fast-deep-equal@npm:^3.1.3": version: 3.1.3 resolution: "fast-deep-equal@npm:3.1.3" @@ -1048,6 +1092,15 @@ __metadata: languageName: node linkType: hard +"fd-slicer@npm:~1.1.0": + version: 1.1.0 + resolution: "fd-slicer@npm:1.1.0" + dependencies: + pend: ~1.2.0 + checksum: c8585fd5713f4476eb8261150900d2cb7f6ff2d87f8feb306ccc8a1122efd152f1783bdb2b8dc891395744583436bfd8081d8e63ece0ec8687eeefea394d4ff2 + languageName: node + linkType: hard + "file-entry-cache@npm:^6.0.1": version: 6.0.1 resolution: "file-entry-cache@npm:6.0.1" @@ -1242,6 +1295,15 @@ __metadata: languageName: node linkType: hard +"get-stream@npm:^5.1.0": + version: 5.2.0 + resolution: "get-stream@npm:5.2.0" + dependencies: + pump: ^3.0.0 + checksum: 8bc1a23174a06b2b4ce600df38d6c98d2ef6d84e020c1ddad632ad75bac4e092eeb40e4c09e0761c35fc2dbc5e7fff5dab5e763a383582c4a167dd69a905bd12 + languageName: node + linkType: hard + "glob-parent@npm:^6.0.2": version: 6.0.2 resolution: "glob-parent@npm:6.0.2" @@ -2189,6 +2251,7 @@ __metadata: chokidar: 3.5.3 dotenv: 16.3.1 eslint: 8.56.0 + extract-zip: 2.0.1 find-process: 1.4.7 fs-extra: 11.2.0 glob: 10.3.10 @@ -2202,7 +2265,7 @@ __metadata: languageName: unknown linkType: soft -"once@npm:^1.3.0": +"once@npm:^1.3.0, once@npm:^1.3.1, once@npm:^1.4.0": version: 1.4.0 resolution: "once@npm:1.4.0" dependencies: @@ -2445,6 +2508,13 @@ __metadata: languageName: node linkType: hard +"pend@npm:~1.2.0": + version: 1.2.0 + resolution: "pend@npm:1.2.0" + checksum: 6c72f5243303d9c60bd98e6446ba7d30ae29e3d56fdb6fae8767e8ba6386f33ee284c97efe3230a0d0217e2b1723b8ab490b1bbf34fcbb2180dbc8a9de47850d + languageName: node + linkType: hard + "picomatch@npm:^2.0.4, picomatch@npm:^2.2.1": version: 2.3.1 resolution: "picomatch@npm:2.3.1" @@ -2550,6 +2620,16 @@ __metadata: languageName: node linkType: hard +"pump@npm:^3.0.0": + version: 3.0.0 + resolution: "pump@npm:3.0.0" + dependencies: + end-of-stream: ^1.1.0 + once: ^1.3.1 + checksum: e42e9229fba14732593a718b04cb5e1cfef8254544870997e0ecd9732b189a48e1256e4e5478148ecb47c8511dca2b09eae56b4d0aad8009e6fac8072923cfc9 + languageName: node + linkType: hard + "punycode@npm:^2.1.0": version: 2.3.1 resolution: "punycode@npm:2.3.1" @@ -3263,6 +3343,13 @@ __metadata: languageName: node linkType: hard +"undici-types@npm:~5.26.4": + version: 5.26.5 + resolution: "undici-types@npm:5.26.5" + checksum: 3192ef6f3fd5df652f2dc1cd782b49d6ff14dc98e5dced492aa8a8c65425227da5da6aafe22523c67f035a272c599bb89cfe803c1db6311e44bed3042fc25487 + languageName: node + linkType: hard + "unique-filename@npm:^3.0.0": version: 3.0.0 resolution: "unique-filename@npm:3.0.0" @@ -3451,6 +3538,16 @@ __metadata: languageName: node linkType: hard +"yauzl@npm:^2.10.0": + version: 2.10.0 + resolution: "yauzl@npm:2.10.0" + dependencies: + buffer-crc32: ~0.2.3 + fd-slicer: ~1.1.0 + checksum: 7f21fe0bbad6e2cb130044a5d1d0d5a0e5bf3d8d4f8c4e6ee12163ce798fee3de7388d22a7a0907f563ac5f9d40f8699a223d3d5c1718da90b0156da6904022b + languageName: node + linkType: hard + "yocto-queue@npm:^0.1.0": version: 0.1.0 resolution: "yocto-queue@npm:0.1.0"