mirror of
https://github.com/an-anime-team/an-anime-game-launcher.git
synced 2024-12-29 21:18:17 +03:00
Added debugger
- disabled already selected dxvk/runner re-selection - fixed `Voice.current` output - made `Debug` and `DebugThread` classes - fixed `winecfg` usage during DXVK installation - `bash` usage was changed to the `./` - added voice update state identification - fixed shaders usage in game launching script - added debugging to the `[launchder]/logs` folder
This commit is contained in:
parent
fca10371b8
commit
cec860e69c
22 changed files with 449 additions and 43 deletions
|
@ -164,12 +164,12 @@ This is our current roadmap goals. You can find older ones [here](ROADMAP.md)
|
|||
* Rewrite sass code, provide more flexible theming ability
|
||||
* <s>Add `svelte-i18n`</s>
|
||||
* <s>Telemetry checking</s>
|
||||
* <s>Tooltips for some options</s>
|
||||
* <s>Debugger</s>
|
||||
* Game pre-installation
|
||||
* Launcher auto-updates
|
||||
* Statistics window
|
||||
* Debugger
|
||||
* Loading screen
|
||||
* Tooltips for some options
|
||||
* Splash screen
|
||||
* Theming system
|
||||
* Chengelog window
|
||||
* Default runner auto-installation
|
||||
|
@ -180,7 +180,6 @@ This is our current roadmap goals. You can find older ones [here](ROADMAP.md)
|
|||
* <s>Use `LauncherLib.getGameVersion` function instead of the `config.json`'s `version` property</s> *(deprecated due to the new core functions)*
|
||||
* Add downloading pause button
|
||||
* Fix button flickering at start when the launcher's state updates
|
||||
* Game's update pre-installation
|
||||
* Screenshots explorer
|
||||
* Add Patch category in settings menu with
|
||||
- Always participate in patches testing
|
||||
|
|
|
@ -98,7 +98,7 @@
|
|||
class:list-item-disabled={disabledDxvks[dxvk.version]}
|
||||
|
||||
on:click|self={() => {
|
||||
if (installedDxvks[dxvk.version])
|
||||
if (installedDxvks[dxvk.version] && selectedVersion !== dxvk.version)
|
||||
selectDxvk(dxvk);
|
||||
}}
|
||||
>
|
||||
|
|
|
@ -78,7 +78,7 @@
|
|||
class:list-item-disabled={disabledRunners[runner.name]}
|
||||
|
||||
on:click|self={() => {
|
||||
if (installedRunners[runner.name])
|
||||
if (installedRunners[runner.name] && selectedVersion !== runner.name)
|
||||
{
|
||||
selectedVersion = runner.name;
|
||||
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
import '../i18n';
|
||||
import App from '../index.svelte';
|
||||
import constants from '../ts/Constants';
|
||||
import Archive from '../ts/core/Archive';
|
||||
import Debug from '../ts/core/Debug';
|
||||
import Downloader from '../ts/core/Downloader';
|
||||
|
||||
declare const Neutralino;
|
||||
|
@ -13,7 +15,26 @@ Neutralino.events.on('windowClose', () => {
|
|||
Downloader.closeStreams(true);
|
||||
Archive.closeStreams(true);
|
||||
|
||||
Neutralino.app.exit();
|
||||
constants.paths.launcherDir.then(async (path) => {
|
||||
const time = new Date;
|
||||
|
||||
Neutralino.filesystem.getStats(`${path}/logs`)
|
||||
.then(() => saveLog())
|
||||
.catch(async () => {
|
||||
await Neutralino.filesystem.createDirectory(`${path}/logs`);
|
||||
|
||||
saveLog();
|
||||
});
|
||||
|
||||
const saveLog = async () => {
|
||||
const log = Debug.get().join("\r\n");
|
||||
|
||||
if (log != '')
|
||||
await Neutralino.filesystem.writeFile(`${path}/logs/${time.getDay()}-${time.getMonth()}-${time.getFullYear()}-${time.getHours()}-${time.getMinutes()}-${time.getSeconds()}.log`, log);
|
||||
|
||||
Neutralino.app.exit();
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
const app = new App({
|
||||
|
|
|
@ -1,10 +1,18 @@
|
|||
import '../i18n';
|
||||
import Debug from '../ts/core/Debug';
|
||||
|
||||
import App from '../settings.svelte';
|
||||
|
||||
declare const Neutralino;
|
||||
|
||||
Neutralino.init();
|
||||
|
||||
Neutralino.events.on('windowClose', async () => {
|
||||
await Neutralino.storage.setData('log', JSON.stringify(Debug.getRecords()));
|
||||
|
||||
Neutralino.app.exit();
|
||||
});
|
||||
|
||||
const app = new App({
|
||||
target: document.getElementById('app')!
|
||||
});
|
||||
|
|
|
@ -10,6 +10,7 @@ import fetch from './core/Fetch';
|
|||
import AbstractInstaller from './core/AbstractInstaller';
|
||||
import Domain from './core/Domain';
|
||||
import promisify from './core/promisify';
|
||||
import Debug, { DebugThread } from './core/Debug';
|
||||
|
||||
declare const Neutralino;
|
||||
|
||||
|
@ -42,6 +43,11 @@ export default class Game
|
|||
const buffer = new TextDecoder('ascii').decode(new Uint8Array(config));
|
||||
const version = /([1-9]+\.[0-9]+\.[0-9]+)_[\d]+_[\d]+/.exec(buffer);
|
||||
|
||||
Debug.log({
|
||||
function: 'Game.current',
|
||||
message: `Current game version: ${version !== null ? version[1] : '<unknown>'}`
|
||||
});
|
||||
|
||||
resolve(version !== null ? version[1] : null);
|
||||
})
|
||||
.catch(() => resolve(null));
|
||||
|
@ -140,6 +146,12 @@ export default class Game
|
|||
*/
|
||||
public static update(version: string|null = null): Promise<Stream|null>
|
||||
{
|
||||
Debug.log(
|
||||
version !== null ?
|
||||
`Updating the game from the ${version} version` :
|
||||
'Installing the game'
|
||||
);
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
(version === null ? this.latest : this.getDiff(version))
|
||||
.then((data: Latest|Diff|null) => resolve(data === null ? null : new Stream(data.path)))
|
||||
|
@ -152,6 +164,8 @@ export default class Game
|
|||
*/
|
||||
public static isTelemetryDisabled(): Promise<boolean>
|
||||
{
|
||||
const debugThread = new DebugThread('Game.isTelemetryDisabled', 'Checking if the telemetry servers are disabled');
|
||||
|
||||
return new Promise(async (resolve) => {
|
||||
const pipeline = promisify({
|
||||
callbacks: await constants.uri.telemetry.map((domain) => {
|
||||
|
@ -168,6 +182,8 @@ export default class Game
|
|||
|
||||
Object.values(result).forEach((value) => disabled ||= value as boolean);
|
||||
|
||||
debugThread.log(`Telemetry is ${disabled ? 'not ' : ''}disabled`);
|
||||
|
||||
resolve(disabled === false);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -6,6 +6,9 @@ import Configs from './Configs';
|
|||
|
||||
import ProgressBar from './launcher/ProgressBar';
|
||||
import State from './launcher/State';
|
||||
import Debug from './core/Debug';
|
||||
|
||||
declare const Neutralino;
|
||||
|
||||
export default class Launcher
|
||||
{
|
||||
|
@ -36,7 +39,8 @@ export default class Launcher
|
|||
title: 'Settings',
|
||||
width: 900,
|
||||
height: 600,
|
||||
enableInspector: true
|
||||
enableInspector: true,
|
||||
exitProcessOnClose: false
|
||||
});
|
||||
|
||||
if (window.status)
|
||||
|
@ -45,6 +49,14 @@ export default class Launcher
|
|||
|
||||
this.settingsMenu.finish(() => {
|
||||
this.settingsMenu = undefined;
|
||||
|
||||
Neutralino.storage.getData('log')
|
||||
.then((data) => {
|
||||
Debug.merge(JSON.parse(data));
|
||||
|
||||
Neutralino.storage.setData('log', undefined);
|
||||
})
|
||||
.catch(() => {});
|
||||
|
||||
Window.current.show();
|
||||
})
|
||||
|
|
|
@ -8,6 +8,7 @@ import fetch from './core/Fetch';
|
|||
import AbstractInstaller from './core/AbstractInstaller';
|
||||
import promisify from './core/promisify';
|
||||
import Process from './neutralino/Process';
|
||||
import Debug, { DebugThread } from './core/Debug';
|
||||
|
||||
declare const Neutralino;
|
||||
|
||||
|
@ -132,6 +133,8 @@ export default class Patch
|
|||
*/
|
||||
public static get latest(): Promise<PatchInfo>
|
||||
{
|
||||
const debugThread = new DebugThread('Patch.latest', 'Getting the latest patch information');
|
||||
|
||||
return new Promise(async (resolve, reject) => {
|
||||
const getLatestPatchInfo = (versions: string[], source: 'origin' | 'additional'): Promise<PatchInfo> => {
|
||||
return new Promise(async (resolve) => {
|
||||
|
@ -145,7 +148,12 @@ export default class Patch
|
|||
resolve(await getLatestPatchInfo(versions.slice(1), 'origin'));
|
||||
|
||||
// Otherwise - return found info
|
||||
else resolve(patchInfo);
|
||||
else
|
||||
{
|
||||
debugThread.log({ message: patchInfo });
|
||||
|
||||
resolve(patchInfo);
|
||||
}
|
||||
})
|
||||
.catch(async (error) => {
|
||||
// If we couldn't connect to the origin repo
|
||||
|
@ -271,6 +279,11 @@ export default class Patch
|
|||
*/
|
||||
public static install(): Promise<Stream|null>
|
||||
{
|
||||
Debug.log({
|
||||
function: 'Patch.install',
|
||||
message: 'Installing the patch...'
|
||||
});
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
this.latest
|
||||
.then((patch) => {
|
||||
|
|
|
@ -5,6 +5,7 @@ import constants from './Constants';
|
|||
import Game from './Game';
|
||||
import AbstractInstaller from './core/AbstractInstaller';
|
||||
import Configs from './Configs';
|
||||
import Debug from './core/Debug';
|
||||
|
||||
declare const Neutralino;
|
||||
|
||||
|
@ -41,23 +42,36 @@ export default class Voice
|
|||
// Parse installed voice packages
|
||||
Neutralino.filesystem.readDirectory(await constants.paths.voiceDir)
|
||||
.then((files) => {
|
||||
files = files
|
||||
.filter((file) => file.type == 'DIRECTORY')
|
||||
files = files.filter((file) => file.type == 'DIRECTORY')
|
||||
.map((file) => file.entry);
|
||||
|
||||
Object.keys(langs).forEach((folder) => {
|
||||
if (files.includes(folder) && langs[folder] !== undefined)
|
||||
if (files.includes(folder))
|
||||
installedVoice.installed.push(langs[folder]);
|
||||
});
|
||||
|
||||
parseActiveVoice();
|
||||
})
|
||||
.catch(() => {});
|
||||
.catch(() => parseActiveVoice());
|
||||
|
||||
// Parse active voice package
|
||||
Neutralino.filesystem.readFile(persistentPath)
|
||||
.then((lang) => installedVoice.active = langs[lang] ?? null)
|
||||
.catch(() => {});
|
||||
const parseActiveVoice = () => {
|
||||
Neutralino.filesystem.readFile(persistentPath)
|
||||
.then((lang) => {
|
||||
installedVoice.active = langs[lang] ?? null;
|
||||
|
||||
resolve(installedVoice);
|
||||
Debug.log({
|
||||
function: 'Voice.current',
|
||||
message: {
|
||||
'active voice': installedVoice.active,
|
||||
'installed voices': installedVoice.installed.join(', ')
|
||||
}
|
||||
});
|
||||
|
||||
resolve(installedVoice);
|
||||
})
|
||||
.catch(() => resolve(installedVoice));
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -106,6 +120,12 @@ export default class Voice
|
|||
*/
|
||||
public static update(lang: string|null = null, version: string|null = null): Promise<Stream|null>
|
||||
{
|
||||
Debug.log(
|
||||
version !== null ?
|
||||
`Updating the voice package from the ${version} version` :
|
||||
'Installing the voice package'
|
||||
);
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
(version === null ? this.latest : this.getDiff(version))
|
||||
.then((data: VoicePack[]|null) => {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import constants from '../Constants';
|
||||
import Downloader from './Downloader';
|
||||
import Archive from './Archive';
|
||||
import { DebugThread } from './Debug';
|
||||
|
||||
declare const Neutralino;
|
||||
|
||||
|
@ -33,6 +34,13 @@ export default abstract class Installer
|
|||
|
||||
public constructor(uri: string, unpackDir: string|Promise<string>)
|
||||
{
|
||||
const debugThread = new DebugThread('AbstractInstaller', {
|
||||
message: {
|
||||
'uri': uri,
|
||||
'unpack dir': typeof unpackDir === 'string' ? unpackDir : '<promise>'
|
||||
}
|
||||
});
|
||||
|
||||
constants.paths.launcherDir.then((launcherDir) => {
|
||||
const archivePath = `${launcherDir}/${Downloader.fileFromUri(uri)}`;
|
||||
|
||||
|
@ -79,6 +87,8 @@ export default abstract class Installer
|
|||
this.unpackFinished = true;
|
||||
|
||||
Neutralino.filesystem.removeFile(archivePath);
|
||||
|
||||
debugThread.log('Installation finished');
|
||||
|
||||
if (this.onUnpackFinish)
|
||||
this.onUnpackFinish();
|
||||
|
@ -86,8 +96,15 @@ export default abstract class Installer
|
|||
});
|
||||
};
|
||||
|
||||
const shouldResolve = typeof unpackDir !== 'string';
|
||||
|
||||
Promise.resolve(unpackDir)
|
||||
.then((unpackDir) => unpackArchive(unpackDir));
|
||||
.then((unpackDir) => {
|
||||
if (shouldResolve)
|
||||
debugThread.log(`Resolved unpack dir: ${unpackDir}`);
|
||||
|
||||
unpackArchive(unpackDir);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -164,4 +181,4 @@ export default abstract class Installer
|
|||
if (this.unpackFinished)
|
||||
callback();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -5,6 +5,7 @@ import type {
|
|||
ArchiveInfo
|
||||
} from '../types/Archive';
|
||||
|
||||
import { DebugThread } from './Debug';
|
||||
import promisify from './promisify';
|
||||
|
||||
declare const Neutralino;
|
||||
|
@ -52,6 +53,13 @@ class Stream
|
|||
this.unpackDir = unpackDir;
|
||||
this.started = true;
|
||||
|
||||
const debugThread = new DebugThread('Archive/Stream', {
|
||||
message: {
|
||||
'path': path,
|
||||
'unpack dir': unpackDir
|
||||
}
|
||||
});
|
||||
|
||||
if (this.onStart)
|
||||
this.onStart();
|
||||
|
||||
|
@ -83,6 +91,8 @@ class Stream
|
|||
this._id = result.pid;
|
||||
});
|
||||
|
||||
debugThread.log(`Unpacking started with command: ${command}`);
|
||||
|
||||
const updateProgress = async () => {
|
||||
let difference: number = 0;
|
||||
let pool: any[] = [];
|
||||
|
@ -122,6 +132,8 @@ class Stream
|
|||
{
|
||||
this.finished = true;
|
||||
|
||||
debugThread.log('Unpacking finished');
|
||||
|
||||
if (this.onFinish)
|
||||
this.onFinish();
|
||||
}
|
||||
|
@ -222,6 +234,8 @@ export default class Archive
|
|||
*/
|
||||
public static getInfo(path: string): Promise<ArchiveInfo|null>
|
||||
{
|
||||
const debugThread = new DebugThread('Archive.getInfo', `Getting info about archive: ${path}`);
|
||||
|
||||
return new Promise(async (resolve) => {
|
||||
let archive: ArchiveInfo = {
|
||||
size: {
|
||||
|
@ -252,6 +266,15 @@ export default class Archive
|
|||
});
|
||||
}
|
||||
|
||||
debugThread.log({
|
||||
message: {
|
||||
'type': archive.type,
|
||||
'compressed size': archive.size.compressed,
|
||||
'uncompressed size': archive.size.uncompressed,
|
||||
'files amount': archive.files.length
|
||||
}
|
||||
});
|
||||
|
||||
resolve(archive);
|
||||
|
||||
break;
|
||||
|
@ -276,11 +299,22 @@ export default class Archive
|
|||
});
|
||||
}
|
||||
|
||||
debugThread.log({
|
||||
message: {
|
||||
'type': archive.type,
|
||||
'compressed size': archive.size.compressed,
|
||||
'uncompressed size': archive.size.uncompressed,
|
||||
'files amount': archive.files.length
|
||||
}
|
||||
});
|
||||
|
||||
resolve(archive);
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
debugThread.log(`Unsupported archive type: ${archive.type}`);
|
||||
|
||||
resolve(null);
|
||||
|
||||
break;
|
||||
|
@ -314,7 +348,7 @@ export default class Archive
|
|||
stream.close(forced);
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export { Stream };
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ import AbstractInstaller from './AbstractInstaller';
|
|||
import Process from '../neutralino/Process';
|
||||
import promisify from './promisify';
|
||||
import Runners from './Runners';
|
||||
import { DebugThread } from './Debug';
|
||||
|
||||
declare const Neutralino;
|
||||
|
||||
|
@ -133,13 +134,19 @@ export default class DXVK
|
|||
*/
|
||||
public static delete(dxvk: TDXVK|TDXVK['version']): Promise<void>
|
||||
{
|
||||
const debugThread = new DebugThread('DXVK.delete', `Deleting dxvk ${typeof dxvk === 'string' ? dxvk : dxvk.version}`);
|
||||
|
||||
return new Promise(async (resolve) => {
|
||||
const version = typeof dxvk !== 'string' ?
|
||||
dxvk.version : dxvk;
|
||||
|
||||
Process.run(`rm -rf '${Process.addSlashes(await constants.paths.dxvksDir + '/dxvk-' + version)}'`)
|
||||
.then((process) => {
|
||||
process.finish(() => resolve());
|
||||
process.finish(() => {
|
||||
debugThread.log('Deletion completed');
|
||||
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -152,6 +159,8 @@ export default class DXVK
|
|||
return new Promise(async (resolve) => {
|
||||
const version = typeof dxvk !== 'string' ?
|
||||
dxvk.version : dxvk;
|
||||
|
||||
const debugThread = new DebugThread('DXVK.apply', `Applying dxvk ${version}`);
|
||||
|
||||
const dxvkDir = `${await constants.paths.dxvksDir}/dxvk-${version}`;
|
||||
const runner = await Runners.current();
|
||||
|
@ -168,22 +177,40 @@ export default class DXVK
|
|||
* And then run it
|
||||
*/
|
||||
(): Promise<void> => new Promise(async (resolve) => {
|
||||
Process.run(`bash '${dxvkDir}/setup_dxvk.sh' install`, {
|
||||
const alias = runner ? `alias winecfg=\\'${runnerDir}/${runner.files.winecfg}\\'\\n` : '';
|
||||
|
||||
Process.run(`eval $'${alias ? alias : ''}./setup_dxvk.sh install'`, {
|
||||
cwd: dxvkDir,
|
||||
env: {
|
||||
WINE: runner ? `${runnerDir}/${runner.files.wine}` : 'wine',
|
||||
WINECFG: runner ? `${runnerDir}/${runner.files.winecfg}` : 'winecfg',
|
||||
WINESERVER: runner ? `${runnerDir}/${runner.files.wineserver}` : 'wineserver',
|
||||
WINEPREFIX: prefix
|
||||
}
|
||||
}).then((process) => {
|
||||
process.finish(() => resolve());
|
||||
let processOutput = '';
|
||||
|
||||
process.output((output) => processOutput += output);
|
||||
|
||||
process.finish(() => {
|
||||
debugThread.log({
|
||||
message: [
|
||||
'Setup script output:',
|
||||
...processOutput.split(/\r\n|\r|\n/)
|
||||
]
|
||||
});
|
||||
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
})
|
||||
]
|
||||
});
|
||||
|
||||
pipeline.then(() => resolve());
|
||||
pipeline.then(() => {
|
||||
debugThread.log('Applying completed');
|
||||
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
119
src/ts/core/Debug.ts
Normal file
119
src/ts/core/Debug.ts
Normal file
|
@ -0,0 +1,119 @@
|
|||
import type { DebugOptions, LogRecord } from '../types/Debug';
|
||||
|
||||
class DebugThread
|
||||
{
|
||||
protected thread: number;
|
||||
protected funcName: string|null;
|
||||
|
||||
public constructor(funcName: string|null = null, options: DebugOptions|string|null = null)
|
||||
{
|
||||
// Generate some random thread id
|
||||
this.thread = 1000 + Math.round(Math.random() * 8999);
|
||||
|
||||
this.funcName = funcName;
|
||||
|
||||
if (options !== null)
|
||||
this.log(options);
|
||||
}
|
||||
|
||||
public log(options: DebugOptions|string)
|
||||
{
|
||||
Debug.log({
|
||||
thread: this.thread,
|
||||
function: this.funcName ?? '',
|
||||
|
||||
...(typeof options === 'string' ? { message: options } : options)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class Debug
|
||||
{
|
||||
protected static logOutput: LogRecord[] = [];
|
||||
|
||||
protected static formatTime(time: number): string
|
||||
{
|
||||
const prefixTime = (time: number): string => {
|
||||
return time < 10 ? `0${time}` : time.toString();
|
||||
};
|
||||
|
||||
const date = new Date(time);
|
||||
|
||||
return `${prefixTime(date.getHours())}:${prefixTime(date.getMinutes())}:${prefixTime(date.getSeconds())}.${date.getMilliseconds()}`;
|
||||
}
|
||||
|
||||
public static log(options: DebugOptions|string)
|
||||
{
|
||||
const time = Date.now();
|
||||
|
||||
let output: LogRecord = {
|
||||
time: time,
|
||||
log: [
|
||||
`[${this.formatTime(time)}]`
|
||||
]
|
||||
};
|
||||
|
||||
if (typeof options === 'string')
|
||||
output.log[0] += ` ${options}`;
|
||||
|
||||
else
|
||||
{
|
||||
// Add thread id
|
||||
if (options.thread)
|
||||
output.log[0] += `[thread: ${options.thread}]`;
|
||||
|
||||
// Add function name
|
||||
if (options.function)
|
||||
output.log[0] += `[${options.function}]`;
|
||||
|
||||
// Add log message if it is a single line
|
||||
if (typeof options.message === 'string')
|
||||
output.log[0] += ` ${options.message}`;
|
||||
|
||||
// message: [a, b, c, d]
|
||||
else if (Array.isArray(options.message))
|
||||
options.message.forEach((line) => {
|
||||
if (line !== '')
|
||||
output.log.push(` - ${line}`);
|
||||
});
|
||||
|
||||
// message: { a: b, c: d }
|
||||
else Object.keys(options.message).forEach((key) => {
|
||||
output.log.push(` - [${key}] ${options.message[key]}`);
|
||||
});
|
||||
}
|
||||
|
||||
this.logOutput.push(output);
|
||||
}
|
||||
|
||||
public static merge(records: LogRecord[])
|
||||
{
|
||||
this.logOutput.unshift(...records);
|
||||
this.logOutput.sort((a, b) => a.time - b.time);
|
||||
}
|
||||
|
||||
public static getRecords(): LogRecord[]
|
||||
{
|
||||
return this.logOutput;
|
||||
}
|
||||
|
||||
public static get(): string[]
|
||||
{
|
||||
let output: string[] = [];
|
||||
|
||||
this.logOutput.forEach((record) => {
|
||||
record.log.forEach((line) => output.push(line));
|
||||
});
|
||||
|
||||
return output;
|
||||
}
|
||||
}
|
||||
|
||||
export default Debug;
|
||||
|
||||
export { DebugThread };
|
||||
|
||||
export type {
|
||||
DebugOptions,
|
||||
LogRecord
|
||||
};
|
|
@ -1,11 +1,14 @@
|
|||
import type { DomainInfo } from '../types/Domain';
|
||||
|
||||
import Process from '../neutralino/Process';
|
||||
import { DebugThread } from './Debug';
|
||||
|
||||
export default class Domain
|
||||
{
|
||||
public static getInfo(uri: string): Promise<DomainInfo>
|
||||
{
|
||||
const debugThread = new DebugThread('Domain.getInfo', `Getting info about uri: ${uri}`);
|
||||
|
||||
return new Promise(async (resolve) => {
|
||||
const process = await Process.run(`ping -n 1 -w 1 -B ${uri}`);
|
||||
|
||||
|
@ -23,12 +26,19 @@ export default class Domain
|
|||
|
||||
if (regex !== null)
|
||||
{
|
||||
resolve({
|
||||
process.outputInterval = null;
|
||||
process.runningInterval = null;
|
||||
|
||||
const info: DomainInfo = {
|
||||
uri: regex[1],
|
||||
remoteIp: regex[2],
|
||||
localIp: regex[3],
|
||||
available: regex[2] !== regex[3]
|
||||
});
|
||||
};
|
||||
|
||||
debugThread.log({ message: info });
|
||||
|
||||
resolve(info);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import { DebugThread } from './Debug';
|
||||
import fetch from './Fetch';
|
||||
|
||||
declare const Neutralino;
|
||||
|
@ -36,15 +37,27 @@ class Stream
|
|||
this.total = total;
|
||||
this.started = true;
|
||||
|
||||
const debugThread = new DebugThread('Downloader/Stream', {
|
||||
message: {
|
||||
'uri': uri,
|
||||
'output file': output,
|
||||
'total size': total
|
||||
}
|
||||
});
|
||||
|
||||
if (this.onStart)
|
||||
this.onStart();
|
||||
|
||||
Neutralino.os.execCommand(`curl -s -L -N -o "${output}" "${uri}"`, {
|
||||
const command = `curl -s -L -N -o "${output}" "${uri}"`;
|
||||
|
||||
Neutralino.os.execCommand(command, {
|
||||
background: true
|
||||
}).then((result) => {
|
||||
this._id = result.pid;
|
||||
});
|
||||
|
||||
debugThread.log(`Downloading started with command: ${command}`);
|
||||
|
||||
const updateProgress = () => {
|
||||
Neutralino.filesystem.getStats(output).then((stats) => {
|
||||
if (this.onProgress)
|
||||
|
@ -56,6 +69,8 @@ class Stream
|
|||
{
|
||||
this.finished = true;
|
||||
|
||||
debugThread.log('Downloading finished');
|
||||
|
||||
if (this.onFinish)
|
||||
this.onFinish();
|
||||
}
|
||||
|
@ -166,6 +181,6 @@ export default class Downloader
|
|||
|
||||
else return 'index.html';
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export { Stream };
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import constants from '../Constants';
|
||||
import Process from '../neutralino/Process';
|
||||
import Debug, { DebugThread } from './Debug';
|
||||
import Downloader from './Downloader';
|
||||
import Runners from './Runners';
|
||||
|
||||
|
@ -16,8 +17,22 @@ export default class Prefix
|
|||
path ??= await constants.paths.prefix.current;
|
||||
|
||||
Neutralino.filesystem.getStats(`${path}/drive_c`)
|
||||
.then(() => resolve(true))
|
||||
.catch(() => resolve(false));
|
||||
.then(() => {
|
||||
Debug.log({
|
||||
function: 'Prefix.exists',
|
||||
message: `Prefix exists here: ${path}`
|
||||
});
|
||||
|
||||
resolve(true);
|
||||
})
|
||||
.catch(() => {
|
||||
Debug.log({
|
||||
function: 'Prefix.exists',
|
||||
message: `Prefix doesn't exist here: ${path}`
|
||||
});
|
||||
|
||||
resolve(false);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -52,6 +67,8 @@ export default class Prefix
|
|||
*/
|
||||
public static create(path: string, progress?: (output: string, current: number, total: number) => void): Promise<boolean>
|
||||
{
|
||||
const debugThread = new DebugThread('Prefix.create', 'Creating wine prefix');
|
||||
|
||||
const installationSteps = [
|
||||
// corefonts
|
||||
'Executing w_do_call corefonts',
|
||||
|
@ -74,14 +91,20 @@ export default class Prefix
|
|||
return new Promise((resolve) => {
|
||||
Runners.current().then((runner) => {
|
||||
if (runner === null)
|
||||
{
|
||||
debugThread.log('Runner doesn\'t selected');
|
||||
|
||||
resolve(false);
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
debugThread.log(`Using runner: ${runner.title} (${runner.name})`);
|
||||
|
||||
this.getWinetricks().then(async (winetricks) => {
|
||||
let installationProgress = 0;
|
||||
|
||||
const process = await Process.run(`bash '${Process.addSlashes(winetricks)}' corefonts usetakefocus=n`, {
|
||||
const process = await Process.run(`./'${Process.addSlashes(winetricks)}' corefonts usetakefocus=n`, {
|
||||
env: {
|
||||
WINE: `${await constants.paths.runnersDir}/${runner.name}/${runner.files.wine}`,
|
||||
WINESERVER: `${await constants.paths.runnersDir}/${runner.name}/${runner.files.wineserver}`,
|
||||
|
@ -91,6 +114,7 @@ export default class Prefix
|
|||
|
||||
process.outputInterval = null;
|
||||
|
||||
// If progress specified
|
||||
if (progress)
|
||||
{
|
||||
process.outputInterval = 1500;
|
||||
|
@ -116,7 +140,11 @@ export default class Prefix
|
|||
});
|
||||
}
|
||||
|
||||
process.finish(() => resolve(true));
|
||||
process.finish(() => {
|
||||
debugThread.log('Prefix creation completed');
|
||||
|
||||
resolve(true);
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
|
|
@ -6,8 +6,8 @@ import type {
|
|||
import constants from '../Constants';
|
||||
import Configs from '../Configs';
|
||||
import AbstractInstaller from './AbstractInstaller';
|
||||
import Downloader from './Downloader';
|
||||
import Process from '../neutralino/Process';
|
||||
import { DebugThread } from './Debug';
|
||||
|
||||
declare const Neutralino;
|
||||
|
||||
|
@ -140,13 +140,19 @@ class Runners
|
|||
*/
|
||||
public static delete(runner: Runner|Runner['name']): Promise<void>
|
||||
{
|
||||
const debugThread = new DebugThread('Runners.delete', `Deleting runner ${typeof runner === 'string' ? runner : runner.name}`);
|
||||
|
||||
return new Promise(async (resolve) => {
|
||||
const name = typeof runner !== 'string' ?
|
||||
runner.name : runner;
|
||||
|
||||
Process.run(`rm -rf '${Process.addSlashes(await constants.paths.runnersDir + '/' + name)}'`)
|
||||
.then((process) => {
|
||||
process.finish(() => resolve());
|
||||
process.finish(() => {
|
||||
debugThread.log('Runner deleted');
|
||||
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import Window from '../neutralino/Window';
|
|||
|
||||
import Game from '../Game';
|
||||
import Patch from '../Patch';
|
||||
import Voice from '../Voice';
|
||||
|
||||
export default class State
|
||||
{
|
||||
|
@ -35,8 +36,12 @@ export default class State
|
|||
this.launchButton.onclick = () => {
|
||||
if (this.events[this._state])
|
||||
{
|
||||
this.launchButton.style['display'] = 'none';
|
||||
|
||||
this.events[this._state].then((event) => {
|
||||
event.default(this.launcher).then(() => {
|
||||
this.launchButton.style['display'] = 'block';
|
||||
|
||||
this.update();
|
||||
});
|
||||
});
|
||||
|
@ -118,6 +123,7 @@ export default class State
|
|||
const gameCurrent = await Game.current;
|
||||
const gameLatest = (await Game.latest).version;
|
||||
const patch = await Patch.latest;
|
||||
const voiceData = await Voice.current;
|
||||
|
||||
if (gameCurrent === null)
|
||||
state = 'game-installation-available';
|
||||
|
@ -125,6 +131,10 @@ export default class State
|
|||
else if (gameCurrent != gameLatest)
|
||||
state = 'game-update-available';
|
||||
|
||||
// TODO: update this thing if the user selected another voice language
|
||||
else if (voiceData.installed.length === 0)
|
||||
state = 'game-voice-update-required';
|
||||
|
||||
else if (!patch.applied)
|
||||
{
|
||||
state = patch.state == 'preparation' ?
|
||||
|
|
|
@ -5,8 +5,6 @@ import Prefix from '../../core/Prefix';
|
|||
|
||||
export default (launcher: Launcher): Promise<void> => {
|
||||
return new Promise(async (resolve) => {
|
||||
launcher.state!.launchButton!.style['display'] = 'none';
|
||||
|
||||
Prefix.exists().then((exists) => {
|
||||
if (!exists)
|
||||
{
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import Configs from '../../Configs';
|
||||
import constants from '../../Constants';
|
||||
import { DebugThread } from '../../core/Debug';
|
||||
import Notifications from '../../core/Notifications';
|
||||
import Runners from '../../core/Runners';
|
||||
import Game from '../../Game';
|
||||
|
@ -9,6 +10,8 @@ declare const Neutralino;
|
|||
|
||||
export default (): Promise<void> => {
|
||||
return new Promise(async (resolve) => {
|
||||
const debugThread = new DebugThread('State/Launch', 'Starting the game');
|
||||
|
||||
const telemetry = await Game.isTelemetryDisabled();
|
||||
|
||||
// If telemetry servers are not disabled
|
||||
|
@ -22,6 +25,8 @@ export default (): Promise<void> => {
|
|||
icon: icon,
|
||||
importance: 'critical'
|
||||
});
|
||||
|
||||
debugThread.log('Telemetry is not disabled!');
|
||||
}
|
||||
|
||||
// Otherwise run the game
|
||||
|
@ -51,7 +56,7 @@ export default (): Promise<void> => {
|
|||
}
|
||||
}
|
||||
|
||||
console.log(`Wine executable: ${wineExeutable}`);
|
||||
debugThread.log(`Wine executable path: ${wineExeutable}`);
|
||||
|
||||
// Some special variables
|
||||
let env: any = {};
|
||||
|
@ -77,9 +82,9 @@ export default (): Promise<void> => {
|
|||
*/
|
||||
const shaders = await Configs.get('shaders');
|
||||
|
||||
if (shaders !== null)
|
||||
if (shaders !== 'none')
|
||||
{
|
||||
const userShadersFile = `${constants.paths.shadersDir}/${shaders}/vkBasalt.conf`;
|
||||
const userShadersFile = `${constants.paths.shadersDir}/public/${shaders}/vkBasalt.conf`;
|
||||
const launcherShadersFile = `${await constants.paths.launcherDir}/vkBasalt.conf`;
|
||||
|
||||
env['ENABLE_VKBASALT'] = 1;
|
||||
|
@ -114,9 +119,7 @@ export default (): Promise<void> => {
|
|||
/*if (LauncherLib.getConfig('gamemode'))
|
||||
command = `gamemoderun ${command}`;*/
|
||||
|
||||
const command = `${wineExeutable} launcher.bat`;
|
||||
|
||||
console.log(`Execution command: ${command}`);
|
||||
const command = `'${Process.addSlashes(wineExeutable)}' launcher.bat`;
|
||||
|
||||
/**
|
||||
* Starting the game
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import Debug, { DebugThread } from "../core/Debug";
|
||||
|
||||
declare const Neutralino;
|
||||
declare const NL_CWD;
|
||||
|
||||
|
@ -49,6 +51,8 @@ class Process
|
|||
|
||||
public constructor(pid: number, outputFile: string|null = null)
|
||||
{
|
||||
const debugThread = new DebugThread('Process/Stream', 'Opened process stream');
|
||||
|
||||
this.id = pid;
|
||||
this.outputFile = outputFile;
|
||||
|
||||
|
@ -66,6 +70,8 @@ class Process
|
|||
{
|
||||
this._finished = true;
|
||||
|
||||
debugThread.log('Process stopped');
|
||||
|
||||
if (this.onFinish)
|
||||
this.onFinish(this);
|
||||
}
|
||||
|
@ -86,7 +92,19 @@ class Process
|
|||
this.outputOffset = output.length;
|
||||
|
||||
if (this._finished)
|
||||
{
|
||||
if (output !== '')
|
||||
{
|
||||
debugThread.log({
|
||||
message: [
|
||||
'Process output:',
|
||||
...output.split(/\r\n|\r|\n/)
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
Neutralino.filesystem.removeFile(this.outputFile);
|
||||
}
|
||||
|
||||
else if (this.outputInterval)
|
||||
setTimeout(updateOutput, this.outputInterval);
|
||||
|
@ -184,6 +202,15 @@ class Process
|
|||
background: true
|
||||
});
|
||||
|
||||
Debug.log({
|
||||
function: 'Process.run',
|
||||
message: {
|
||||
'running command': command,
|
||||
'cwd': options.cwd,
|
||||
...options.env
|
||||
}
|
||||
});
|
||||
|
||||
resolve(new Process(process.pid, tmpFile));
|
||||
});
|
||||
}
|
||||
|
|
23
src/ts/types/Debug.d.ts
vendored
Normal file
23
src/ts/types/Debug.d.ts
vendored
Normal file
|
@ -0,0 +1,23 @@
|
|||
type DebugOptions = {
|
||||
/**
|
||||
* Some random-generated thread id
|
||||
*/
|
||||
thread?: number;
|
||||
|
||||
/**
|
||||
* Some function name
|
||||
*/
|
||||
function?: string;
|
||||
|
||||
/**
|
||||
* Some log message
|
||||
*/
|
||||
message: string|string[]|object;
|
||||
};
|
||||
|
||||
type LogRecord = {
|
||||
time: number;
|
||||
log: string[];
|
||||
};
|
||||
|
||||
export type { DebugOptions, LogRecord };
|
Loading…
Reference in a new issue