#!/usr/bin/env node // // Refresh the spawn atlas from a ServUO tree, from the command line. // // npm run atlas:import # use the configured path // npm run atlas:import -- --servuo # override it for this run // npm run atlas:import -- --force # reimport even if unchanged // npm run atlas:import -- --approve # apply a staged refresh // npm run atlas:import -- --status # report without changing anything // // The server does this itself on every boot (see `shardAtlas.refreshOnBoot`), so // this is for operators who want to apply a map change without a restart, and // for approving a refresh that was staged because it would remove a facet. // // All the logic lives in `src/model/shardAtlas/shardAtlas.model.js`; this file // is argument parsing and output formatting. const db = () => require('../src/utils/db') function parseArgs(argv) { const args = {} for (let i = 0; i < argv.length; i += 1) { const flag = argv[i] if (flag === '--servuo') args.servuo = argv[++i] else if (flag === '--force') args.force = true else if (flag === '--approve') args.approve = true else if (flag === '--reject') args.reject = true else if (flag === '--status') args.status = true else if (flag === '--help' || flag === '-h') args.help = true } return args } const USAGE = ` Refresh the spawn atlas from a ServUO tree. node scripts/importSpawnAtlas.js [options] --servuo Use this tree for this run instead of the configured path. --force Reimport even when the source files are unchanged. --approve Apply a refresh that was staged for removing a facet. --reject Keep the current atlas and dismiss the staged refresh. --status Report atlas and source state; change nothing. With no options this imports only if the tree differs from what is loaded. ` function describe(result) { switch (result.status) { case 'skipped': return ( 'No ServUO path configured — nothing to import.\n' + 'Set one with SERVUO_PATH, the admin panel, or --servuo .\n' ) case 'unavailable': return `ServUO tree unavailable: ${result.reason}\n` case 'unchanged': return `Atlas is already up to date${result.reason ? ` (${result.reason})` : ''}.\n` case 'needsReview': { return ( 'Refresh NOT applied — it would remove ' + `${result.removedFacets.length} facet(s): ${result.removedFacets.join(', ')}.\n` + 'This is what a half-copied or mid-update tree looks like, so it has been\n' + 'staged for review. The current atlas is unchanged.\n' + 'Apply it with --approve, or dismiss it with --reject.\n' ) } case 'imported': { const c = result.counts const added = result.addedFacets?.length ? ` Added facets: ${result.addedFacets.join(', ')}.` : '' const removed = result.removedFacets?.length ? ` Removed facets: ${result.removedFacets.join(', ')}.` : '' return ( `Atlas imported: ${c.points} points, ${c.creatures} creatures, ` + `${c.pointTypes} point/type rows, ${c.regions} regions, ` + `${c.landmarks} landmarks, ${c.champions} champion altars.${added}${removed}\n` ) } case 'failed': return `Atlas refresh failed: ${result.reason}\n` default: return `${JSON.stringify(result, null, 2)}\n` } } async function main() { const args = parseArgs(process.argv.slice(2)) if (args.help) { process.stdout.write(USAGE) return } const shardAtlas = require('../src/model/shardAtlas/shardAtlas.model') // `--servuo` is a per-run override and deliberately does NOT persist to the // configured path; changing where the atlas permanently reads from is an // admin action, not a side effect of a one-off import. const override = { path: args.servuo ?? '' } if (args.status) { process.stdout.write(`${JSON.stringify(await shardAtlas.status(override), null, 2)}\n`) return } if (args.reject) { process.stdout.write(`${JSON.stringify(await shardAtlas.rejectPending(), null, 2)}\n`) return } const result = args.approve ? await shardAtlas.approvePending(override) : await shardAtlas.refresh({ ...override, force: Boolean(args.force) }) process.stdout.write(describe(result)) if (result.status === 'failed') process.exitCode = 1 } if (require.main === module) { main() .catch((err) => { process.stderr.write(`atlas:import failed: ${err.message}\n`) process.exitCode = 1 }) .finally(() => db().close()) } module.exports = { describe, parseArgs }