API improvements

- fixed `Game.getLatestData()` method
- added `Patch.install()` method
- added downloaded archive auto-deletion in `AbstractInstaller`
- highly improved `promisify()` function
- added `source` field for the patch info
This commit is contained in:
Observer KRypt0n_ 2021-12-24 21:05:58 +02:00
parent bd41bd1f46
commit 843196e0e1
No known key found for this signature in database
GPG key ID: DC5D4EC1303465DA
8 changed files with 215 additions and 15 deletions

View file

@ -130,12 +130,13 @@ This is our current roadmap goals. You can find older ones [here](ROADMAP.md)
* <s>Ability to parse current installed voice packs and get selected one</s>
* <s>Ability to get latest available voice packs</s>
* <s>Ability to download and install updates</s>
* Make `Patch` class to control patch-related features
* <s>Make `Patch` class to control patch-related features</s>
* <s>Ability to get current installed patch</s>
* <s>Ability to get latest available patch</s>
* Ability to download and install it
* <s>Ability to download and install it</s>
* Add project binaries bundling
* AppImage
* <s>AppImage</s> *(seems to be impossible)*
* One-time small installation script because then launcher will have auto updates
#### Launcher functions

View file

@ -6,7 +6,6 @@ import Launcher from '../ts/Launcher';
import Configs from '../ts/Configs';
import constants from '../ts/Constants';
import promisify from '../ts/core/promisify';
import Process from '../ts/neutralino/Process';
promisify(async () => {
Configs.defaults({

View file

@ -183,6 +183,9 @@ export default class constants
public static readonly versionsUri: string = `${this.uri.api}/resource?key=gcStgarh&launcher_id=10`;
public static readonly backgroundUri: string = `${this.uri.api}/content?filter_adv=true&launcher_id=10&key=gcStgarh&language=`;
/**
* Get a URI to the specified patch repository archive
*/
public static getPatchUri(source: 'origin' | 'additional'): string
{
return `${this.uri.patch[source]}/archive/master.zip`;

View file

@ -58,7 +58,7 @@ export default class Game
if (response.ok)
{
const json: ServerResponse = await (response as any).json();
const json: ServerResponse = JSON.parse(await response.body());
if (json.message == 'OK')
resolve(json.data);

View file

@ -1,13 +1,125 @@
import type {
PatchState,
PatchInfo
} from './types/Patch';
import type { PatchInfo } from './types/Patch';
import md5 from 'js-md5';
import constants from './Constants';
import Game from './Game';
import fetch from './core/Fetch';
import AbstractInstaller from './core/AbstractInstaller';
import promisify from './core/promisify';
import Process from './neutralino/Process';
declare const Neutralino;
class Stream extends AbstractInstaller
{
protected userUnpackFinishCallback?: () => void;
protected onPatchFinish?: () => void;
protected patchFinished: boolean = false;
public constructor(uri: string, version: string|null = null)
{
super(uri, constants.paths.launcherDir);
/**
* We'll make our own AbstractInstaller unpacking finish event
* and provide some hack to call another user-provided function
* if he wants to make something after patch's archive unpacking
*/
this.onUnpackFinish = async () => {
if (this.userUnpackFinishCallback)
this.userUnpackFinishCallback();
// Find patch version if it wasn't provided
if (version === null)
version = (await Patch.latest).version;
const patchDir = `${await constants.paths.launcherDir}/dawn/${version.replaceAll('.', '')}`;
/**
* Patch out the testing phase content from the shell files
* if active and make sure the shell files are executable
*/
const pipeline = promisify({
callbacks: [
/**
* Remove test version restrictions from the main patch
*/
() => Neutralino.os.execCommand(`cd '${patchDir}' && sed -i '/^echo "If you would like to test this patch, modify this script and remove the line below this one."/,+5d' patch.sh`),
/**
* Remove test version restrictions from the anti-login crash patch
*/
() => Neutralino.os.execCommand(`cd '${patchDir}' && sed -i '/^echo " necessary afterwards (Friday?). If that's the case, comment the line below."/,+2d' patch_anti_logincrash.sh`),
/**
* Make the main patch executable
*/
() => Neutralino.os.execCommand(`chmod +x '${patchDir}/patch.sh'`),
/**
* Make the anti-login crash patch executable
*/
() => Neutralino.os.execCommand(`chmod +x '${patchDir}/patch_anti_logincrash.sh'`),
/**
* Execute the main patch installation script
*/
(): Promise<void> => {
return new Promise(async (resolve) => {
Process.run(`yes yes | bash '${patchDir}/patch.sh'`, {
cwd: await constants.paths.gameDir
}).then((process) => {
process.finish(() => resolve());
});
});
},
/**
* Execute the anti-login crash patch installation script
*/
(): Promise<void> => {
return new Promise(async (resolve) => {
Process.run(`yes | bash '${patchDir}/patch_anti_logincrash.sh'`, {
cwd: await constants.paths.gameDir
}).then((process) => {
process.finish(() => resolve());
});
});
}
]
});
// When all the things above was done
pipeline.then(() => {
this.patchFinished = true;
if (this.onPatchFinish)
this.onPatchFinish();
});
};
}
public unpackFinish(callback: () => void)
{
this.userUnpackFinishCallback = callback;
if (this.unpackFinished)
callback();
}
/**
* Specify event that will be called when the patch will be applied
*/
public patchFinish(callback: () => void)
{
this.onPatchFinish = callback;
if (this.patchFinished)
callback();
}
}
export default class Patch
{
@ -95,7 +207,8 @@ export default class Patch
resolve({
version: version,
state: 'preparation',
applied: false
applied: false,
source: source
});
}
@ -118,7 +231,8 @@ export default class Patch
let patchInfo: PatchInfo = {
version: version,
state: response.includes(stableMark) ? 'stable' : 'testing',
applied: false
applied: false,
source: source
};
const originalPlayer = /if \[ "\${sum}" != "([a-z0-9]{32})" \]; then/mg.exec(response);
@ -138,4 +252,18 @@ export default class Patch
});
});
}
/**
* Get patch installation stream
*/
public static install(): Promise<Stream>
{
return new Promise((resolve, reject) => {
this.latest
.then((patch) => resolve(new Stream(constants.getPatchUri(patch.source ?? 'origin'), patch.version)))
.catch((err) => reject(err));
});
}
}
export { Stream };

View file

@ -2,6 +2,8 @@ import constants from '../Constants';
import Downloader from './Downloader';
import Archive from './Archive';
declare const Neutralino;
export default abstract class Installer
{
/**
@ -75,6 +77,8 @@ export default abstract class Installer
stream.finish(() => {
this.unpackFinished = true;
Neutralino.filesystem.removeFile(archivePath);
if (this.onUnpackFinish)
this.onUnpackFinish();

View file

@ -1,9 +1,69 @@
type callback = () => any;
type PromiseOptions = {
callbacks: callback[];
/**
* If true, then all the callbacks will be called
* at the same time and promisify will be resolved
* when all of them will be finished
*
* Otherwise, callbacks will be called one after the other
* and promisify will be resolved with the last one
*/
callAtOnce?: boolean;
/**
* [callAtOnce: true] updates interval in ms
*
* @default 100
*/
interval?: number;
};
/**
* Make a promise from a synchronous function and run it
* Make a promise from a function(s) and run it
*/
export default function promisify(callback: () => any): Promise<any>
export default function promisify(callback: callback|PromiseOptions): Promise<any>
{
return new Promise((resolve) => {
resolve(callback());
return new Promise(async (resolve) => {
if (typeof callback === 'function')
resolve(await Promise.resolve(callback()));
else
{
let outputs = {};
if (callback.callAtOnce)
{
let remained = callback.callbacks.length;
for (let i = 0; i < callback.callbacks.length; ++i)
promisify(callback.callbacks[i]).then((output) => {
outputs[i] = output;
--remained;
});
const updater = () => {
if (remained > 0)
setTimeout(updater, callback.interval ?? 100);
else resolve(outputs);
};
setTimeout(updater, callback.interval ?? 100);
}
else for (let i = 0; i < callback.callbacks.length; ++i)
outputs[i] = await promisify(callback.callbacks[i]());
resolve(outputs);
}
});
};
export type {
PromiseOptions,
callback
};

View file

@ -14,6 +14,11 @@ type PatchInfo = {
* If the patch was applied
*/
applied: boolean;
/**
* Source where this info was got from
*/
source?: 'origin' | 'additional';
};
export type {