2021-06-18 02:02:50 -04:00
|
|
|
import { ensureDirSync } from 'fs-extra';
|
2021-07-20 16:37:38 -04:00
|
|
|
import { appendFileSync, lstatSync } from 'fs';
|
2021-06-19 18:23:44 -04:00
|
|
|
import { parse, resolve } from 'path';
|
|
|
|
|
import walkSync from 'walk-sync';
|
|
|
|
|
import { fileURLToPath } from 'url';
|
2021-06-22 19:25:41 -04:00
|
|
|
import { Game } from '@game';
|
2021-07-20 16:37:38 -04:00
|
|
|
import { isStarted, stop, render } from '@ui';
|
2021-06-26 03:11:18 -04:00
|
|
|
import { writeFileSync } from 'fs';
|
2021-07-19 01:53:30 -04:00
|
|
|
import ansi from 'sisteransi';
|
2021-07-20 16:37:38 -04:00
|
|
|
import { spawn } from 'child_process';
|
|
|
|
|
import cluster from 'cluster';
|
|
|
|
|
import chokidar from 'chokidar';
|
2021-06-26 03:11:18 -04:00
|
|
|
|
2021-07-20 16:37:38 -04:00
|
|
|
const hotReload = true;
|
|
|
|
|
let restartTimer: NodeJS.Timeout = null;
|
2021-06-26 03:11:18 -04:00
|
|
|
|
2021-07-20 16:37:38 -04:00
|
|
|
let worker: cluster.Worker = null;
|
|
|
|
|
function createWorker() {
|
|
|
|
|
start();
|
|
|
|
|
|
|
|
|
|
function start() {
|
|
|
|
|
if (cluster.isMaster) {
|
|
|
|
|
worker = cluster.fork();
|
|
|
|
|
worker.on('message', (message) => {
|
|
|
|
|
if(message === 'ipc-restart') {
|
|
|
|
|
worker.on('exit', () => {
|
|
|
|
|
setTimeout(createWorker, 0);
|
|
|
|
|
})
|
|
|
|
|
worker.kill();
|
|
|
|
|
} else if (message === 'ipc-quit') {
|
|
|
|
|
worker.on('exit', () => {
|
|
|
|
|
process.exit(0);
|
|
|
|
|
})
|
|
|
|
|
worker.kill();
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
2021-06-26 03:11:18 -04:00
|
|
|
}
|
2021-07-20 16:37:38 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// from any cluster context, gracefully exit if needed, and begin anew!
|
|
|
|
|
export function restart() {
|
|
|
|
|
if (cluster.isWorker) {
|
|
|
|
|
process.send('ipc-restart');
|
|
|
|
|
} else if(worker) {
|
|
|
|
|
worker.on('exit', () => {
|
|
|
|
|
setTimeout(createWorker, 0);
|
|
|
|
|
})
|
|
|
|
|
worker.kill();
|
|
|
|
|
} else {
|
|
|
|
|
createWorker();
|
2021-06-26 03:11:18 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-20 16:37:38 -04:00
|
|
|
export function quit() {
|
|
|
|
|
if (cluster.isWorker) {
|
|
|
|
|
process.send('ipc-quit');
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-06-14 22:03:55 -04:00
|
|
|
|
2021-07-20 16:37:38 -04:00
|
|
|
if (cluster.isWorker) {
|
|
|
|
|
begin();
|
|
|
|
|
} else {
|
|
|
|
|
// TODO make hotreload actually bring a popup on the client
|
|
|
|
|
// so that the user should press enter to enable a reload.
|
|
|
|
|
if(hotReload) {
|
|
|
|
|
chokidar.watch('./out').on('all', (evt, path) => {
|
|
|
|
|
appendFileSync('log.log', evt + ' ' + path + '\n');
|
|
|
|
|
if(restartTimer) clearTimeout(restartTimer)
|
|
|
|
|
restartTimer = setTimeout(() => {
|
|
|
|
|
restart();
|
|
|
|
|
restartTimer = null;
|
|
|
|
|
}, 1000);
|
|
|
|
|
})
|
|
|
|
|
} else {
|
|
|
|
|
createWorker();
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-06-15 20:05:07 -04:00
|
|
|
|
2021-07-20 16:37:38 -04:00
|
|
|
async function begin() {
|
|
|
|
|
console.clear();
|
2021-06-18 02:02:50 -04:00
|
|
|
|
2021-07-20 16:37:38 -04:00
|
|
|
function gracefulShutdown() {
|
|
|
|
|
if (isStarted()) {
|
|
|
|
|
stop();
|
|
|
|
|
}
|
|
|
|
|
console.log('shutting down gracefully...');
|
|
|
|
|
if (Game.current) {
|
|
|
|
|
console.log('saving world...');
|
|
|
|
|
Game.current.sync();
|
|
|
|
|
}
|
|
|
|
|
console.log('exitting');
|
|
|
|
|
process.stdout.write(ansi.cursor.show);
|
2021-06-22 19:25:41 -04:00
|
|
|
|
2021-07-20 16:37:38 -04:00
|
|
|
process.exit(0);
|
|
|
|
|
}
|
2021-06-19 18:23:44 -04:00
|
|
|
|
2021-07-20 16:37:38 -04:00
|
|
|
process.on('exit', gracefulShutdown);
|
2021-06-22 19:25:41 -04:00
|
|
|
|
2021-07-20 16:37:38 -04:00
|
|
|
const saveFile = process.argv[2] || 'data/world01.json';
|
2021-06-22 19:25:41 -04:00
|
|
|
|
2021-07-20 16:37:38 -04:00
|
|
|
ensureDirSync(parse(saveFile).dir);
|
2021-06-22 19:25:41 -04:00
|
|
|
|
2021-07-16 20:49:29 -04:00
|
|
|
|
2021-07-20 16:37:38 -04:00
|
|
|
// TODO extract extension loading into separate file
|
|
|
|
|
console.log('df-idle: Loading extensions');
|
|
|
|
|
const extensionsPath = resolve(parse(fileURLToPath(import.meta.url)).dir, '../content');
|
2021-07-16 20:49:29 -04:00
|
|
|
|
2021-07-20 16:37:38 -04:00
|
|
|
const extensions = walkSync(extensionsPath)
|
|
|
|
|
.map(path => [path, resolve(extensionsPath, path)])
|
|
|
|
|
.filter(path => lstatSync(path[1]).isFile())
|
|
|
|
|
.filter(path => parse(path[1]).ext === '.js');
|
|
|
|
|
|
|
|
|
|
console.log('found', extensions.length, 'extensions');
|
|
|
|
|
|
|
|
|
|
for (const path of extensions) {
|
|
|
|
|
console.log('=== [', path[0], '] ===');
|
|
|
|
|
await import(path[1]);
|
|
|
|
|
console.log();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
console.log('Setup Complete.');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for (let seconds = 2; seconds > 0; seconds--) {
|
|
|
|
|
process.stdout.write('Starting DF-Idle in ' + seconds + '\r');
|
|
|
|
|
await new Promise(res => setTimeout(res, 1000));
|
|
|
|
|
}
|
|
|
|
|
console.clear();
|
|
|
|
|
|
|
|
|
|
// TODO move render logic into game, so that the ui doesnt exist until the game does...
|
|
|
|
|
// maybe, i mean, an argument could be made for not that, because the game
|
|
|
|
|
// isnt necessarily the entire thing, its just one instance of a save file.
|
|
|
|
|
// But probably the initial menu screens will be their own thing entirely.
|
|
|
|
|
const game = Game.create(saveFile);
|
2021-07-16 20:49:29 -04:00
|
|
|
|
2021-07-20 16:37:38 -04:00
|
|
|
}
|