diff --git a/neutralino.config.json b/neutralino.config.json index d630b22..4b15c2d 100644 --- a/neutralino.config.json +++ b/neutralino.config.json @@ -27,7 +27,7 @@ "maximize": false, "hidden": true, "resizable": false, - "exitProcessOnClose": true + "exitProcessOnClose": false }, "browser": {}, "cloud": {} diff --git a/src/pages/index.ts b/src/pages/index.ts index 93c7e2c..ec5eb75 100644 --- a/src/pages/index.ts +++ b/src/pages/index.ts @@ -1,11 +1,21 @@ import '../i18n'; import App from '../index.svelte'; +import Archive from '../ts/core/Archive'; +import Downloader from '../ts/core/Downloader'; declare const Neutralino; Neutralino.init(); + Neutralino.events.on('ready', () => import('../defaultSettings')); +Neutralino.events.on('windowClose', () => { + Downloader.closeStreams(true); + Archive.closeStreams(true); + + Neutralino.app.exit(); +}); + const app = new App({ target: document.getElementById('app')! }); diff --git a/src/ts/Launcher.ts b/src/ts/Launcher.ts index 855f8c4..94580da 100644 --- a/src/ts/Launcher.ts +++ b/src/ts/Launcher.ts @@ -21,7 +21,7 @@ export default class Launcher this.progressBar = new ProgressBar(this); // Progress bar test - this.progressBar.init({ + /*this.progressBar.init({ label: 'Abobus', showSpeed: true, showEta: true, @@ -42,7 +42,7 @@ export default class Launcher } }; - t(0); + t(0);*/ }); } diff --git a/src/ts/core/Archive.ts b/src/ts/core/Archive.ts index 19481c1..2f14611 100644 --- a/src/ts/core/Archive.ts +++ b/src/ts/core/Archive.ts @@ -10,6 +10,16 @@ declare const NL_CWD; class Stream { + protected _id: number = -1; + + /** + * ID of the archive unpacker process + */ + public get id(): number + { + return this._id; + } + /** * The interval in ms between progress event calls */ @@ -67,6 +77,8 @@ class Stream Neutralino.os.execCommand(command, { background: true + }).then((result) => { + this._id = result.pid; }); const updateProgress = async () => { @@ -156,10 +168,20 @@ class Stream if (this.throwedError) callback(); } + + /** + * Close unpacking stream + */ + public close(forced: boolean = false) + { + Neutralino.os.execCommand(`kill ${forced ? '-9' : '-15'} ${this._id}`); + } } export default class Archive { + protected static streams: Stream[] = []; + /** * Get type of archive * @@ -259,7 +281,23 @@ export default class Archive */ public static unpack(path: string, unpackDir: string|null = null): Promise { - return new Promise((resolve) => resolve(new Stream(path, unpackDir))); + return new Promise((resolve) => { + const stream = new Stream(path, unpackDir); + + this.streams.push(stream); + + resolve(stream); + }); + } + + /** + * Close every open archive unpacking stream + */ + public static closeStreams(forced: boolean = false) + { + this.streams.forEach((stream) => { + stream.close(forced); + }); } } diff --git a/src/ts/core/Downloader.ts b/src/ts/core/Downloader.ts index d62c6ee..a8ee28a 100644 --- a/src/ts/core/Downloader.ts +++ b/src/ts/core/Downloader.ts @@ -4,6 +4,16 @@ declare const Neutralino; class Stream { + protected _id: number = -1; + + /** + * ID of the curl process + */ + public get id(): number + { + return this._id; + } + /** * The interval in ms between progress event calls */ @@ -31,12 +41,14 @@ class Stream Neutralino.os.execCommand(`curl -s -L -N -o "${output}" "${uri}"`, { background: true + }).then((result) => { + this._id = result.pid; }); const updateProgress = () => { Neutralino.filesystem.getStats(output).then((stats) => { if (this.onProgress) - this.onProgress(stats.size, this.total, this.previous - stats.size); + this.onProgress(stats.size, this.total, stats.size - this.previous); this.previous = stats.size; @@ -94,10 +106,20 @@ class Stream if (this.finished) callback(); } + + /** + * Close downloading stream + */ + public close(forced: boolean = false) + { + Neutralino.os.execCommand(`kill ${forced ? '-9' : '-15'} ${this._id}`); + } } export default class Downloader { + protected static streams: Stream[] = []; + /** * Download file * @@ -110,11 +132,28 @@ export default class Downloader { return new Promise(async (resolve) => { fetch(uri).then((response) => { - resolve(new Stream(uri, output ?? this.fileFromUri(uri), response.length!)); + const stream = new Stream(uri, output ?? this.fileFromUri(uri), response.length!); + + this.streams.push(stream); + + resolve(stream); }); }); } + /** + * Close every open downloading stream + */ + public static closeStreams(forced: boolean = false) + { + this.streams.forEach((stream) => { + stream.close(forced); + }); + } + + /** + * Get a file name from the URI + */ public static fileFromUri(uri: string): string { const file = uri.split('/').pop()!.split('#')[0].split('?')[0]; diff --git a/src/ts/launcher/State.ts b/src/ts/launcher/State.ts index 8d4b1a4..b72efe8 100644 --- a/src/ts/launcher/State.ts +++ b/src/ts/launcher/State.ts @@ -8,12 +8,12 @@ export default class State public launchButton: HTMLElement; - protected _state: LauncherState = 'game-launch-available'; + protected _state: LauncherState = 'game-installation-available'; protected events = { 'game-launch-available': import('./states/Launch'), - 'game-install-available': import('./states/Install'), + 'game-installation-available': import('./states/Install'), 'game-update-available': import('./states/Install') }; @@ -25,7 +25,7 @@ export default class State this.launchButton.onclick = () => { if (this.events[this._state]) - this.events[this._state].then((event) => event.default()); + this.events[this._state].then((event) => event.default(this.launcher)); }; } diff --git a/src/ts/launcher/states/Install.ts b/src/ts/launcher/states/Install.ts index 3b422aa..9c0e6d3 100644 --- a/src/ts/launcher/states/Install.ts +++ b/src/ts/launcher/states/Install.ts @@ -1,39 +1,64 @@ import type Launcher from '../../Launcher'; import Game from '../../Game'; +import constants from '../../Constants'; +import Runners from '../../core/Runners'; + +declare const Neutralino; export default (launcher: Launcher): Promise => { return new Promise(async (resolve) => { - Game.update(await Game.current).then((stream) => { - launcher.progressBar?.init({ - label: 'Downloading game...', - showSpeed: true, - showEta: true, - showPercents: true, - showTotals: true + const prefixDir = await constants.paths.prefix.current; + + Neutralino.filesystem.getStats(prefixDir) + .then(() => updateGame()) + .catch(() => { + Runners.createPrefix(prefixDir).then((result) => { + if (result === true) + updateGame(); + + else + { + // TODO + console.error('There\'s no wine version installed to use to create the prefix'); + + resolve(); + } + }); }); - stream?.downloadStart(() => launcher.progressBar?.show()); - - stream?.downloadProgress((current: number, total: number, difference: number) => { - launcher.progressBar?.update(current, total, difference); - }); - - stream?.unpackStart(() => { + const updateGame = async () => { + Game.update(await Game.current).then((stream) => { launcher.progressBar?.init({ - label: 'Unpacking game...', + label: 'Downloading game...', showSpeed: true, showEta: true, showPercents: true, showTotals: true }); + + stream?.downloadStart(() => launcher.progressBar?.show()); + + stream?.downloadProgress((current: number, total: number, difference: number) => { + launcher.progressBar?.update(current, total, difference); + }); + + stream?.unpackStart(() => { + launcher.progressBar?.init({ + label: 'Unpacking game...', + showSpeed: true, + showEta: true, + showPercents: true, + showTotals: true + }); + }); + + stream?.unpackProgress((current: number, total: number, difference: number) => { + launcher.progressBar?.update(current, total, difference); + }); + + stream?.unpackFinish(() => resolve()); }); - - stream?.unpackProgress((current: number, total: number, difference: number) => { - launcher.progressBar?.update(current, total, difference); - }); - - stream?.unpackFinish(() => resolve()); - }); + }; }); }; diff --git a/src/ts/neutralino/Process.ts b/src/ts/neutralino/Process.ts index 1284486..f024e46 100644 --- a/src/ts/neutralino/Process.ts +++ b/src/ts/neutralino/Process.ts @@ -106,8 +106,8 @@ class Process public running(): Promise { return new Promise((resolve) => { - Neutralino.os.execCommand(`ps -p ${this.id}`).then((output) => { - resolve(output.stdOut.includes(this.id)); + Neutralino.os.execCommand(`ps -p ${this.id} -S`).then((output) => { + resolve(output.stdOut.includes(this.id) && !output.stdOut.includes('Z ')); }); }); }