diff --git a/README.md b/README.md
index d9b1d5e..6706308 100644
--- a/README.md
+++ b/README.md
@@ -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
* Add `svelte-i18n`
* Telemetry checking
+* Tooltips for some options
+* Debugger
* 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)
* Use `LauncherLib.getGameVersion` function instead of the `config.json`'s `version` property *(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
diff --git a/src/components/DXVKSelectionList.svelte b/src/components/DXVKSelectionList.svelte
index 50284cf..1baaaa7 100644
--- a/src/components/DXVKSelectionList.svelte
+++ b/src/components/DXVKSelectionList.svelte
@@ -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);
}}
>
diff --git a/src/components/RunnerSelectionList.svelte b/src/components/RunnerSelectionList.svelte
index 7041969..8f5d4c1 100644
--- a/src/components/RunnerSelectionList.svelte
+++ b/src/components/RunnerSelectionList.svelte
@@ -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;
diff --git a/src/pages/index.ts b/src/pages/index.ts
index ec5eb75..8334840 100644
--- a/src/pages/index.ts
+++ b/src/pages/index.ts
@@ -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({
diff --git a/src/pages/settings.ts b/src/pages/settings.ts
index da15863..53ce02b 100644
--- a/src/pages/settings.ts
+++ b/src/pages/settings.ts
@@ -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')!
});
diff --git a/src/ts/Game.ts b/src/ts/Game.ts
index 9720c97..d04ab16 100644
--- a/src/ts/Game.ts
+++ b/src/ts/Game.ts
@@ -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] : ''}`
+ });
+
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
{
+ 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
{
+ 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);
});
});
diff --git a/src/ts/Launcher.ts b/src/ts/Launcher.ts
index 93ab051..967c6a4 100644
--- a/src/ts/Launcher.ts
+++ b/src/ts/Launcher.ts
@@ -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();
})
diff --git a/src/ts/Patch.ts b/src/ts/Patch.ts
index c26ea9f..6ed562e 100644
--- a/src/ts/Patch.ts
+++ b/src/ts/Patch.ts
@@ -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
{
+ 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 => {
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
{
+ Debug.log({
+ function: 'Patch.install',
+ message: 'Installing the patch...'
+ });
+
return new Promise((resolve, reject) => {
this.latest
.then((patch) => {
diff --git a/src/ts/Voice.ts b/src/ts/Voice.ts
index 94ec4f7..ae511f5 100644
--- a/src/ts/Voice.ts
+++ b/src/ts/Voice.ts
@@ -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
{
+ 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) => {
diff --git a/src/ts/core/AbstractInstaller.ts b/src/ts/core/AbstractInstaller.ts
index f8187fb..ddbf409 100644
--- a/src/ts/core/AbstractInstaller.ts
+++ b/src/ts/core/AbstractInstaller.ts
@@ -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)
{
+ const debugThread = new DebugThread('AbstractInstaller', {
+ message: {
+ 'uri': uri,
+ 'unpack dir': typeof unpackDir === 'string' ? unpackDir : ''
+ }
+ });
+
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();
}
-}
+};
diff --git a/src/ts/core/Archive.ts b/src/ts/core/Archive.ts
index f72eed2..b62b9d0 100644
--- a/src/ts/core/Archive.ts
+++ b/src/ts/core/Archive.ts
@@ -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
{
+ 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 };
diff --git a/src/ts/core/DXVK.ts b/src/ts/core/DXVK.ts
index a9a76c1..2448ad5 100644
--- a/src/ts/core/DXVK.ts
+++ b/src/ts/core/DXVK.ts
@@ -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
{
+ 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 => 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();
+ });
});
}
}
diff --git a/src/ts/core/Debug.ts b/src/ts/core/Debug.ts
new file mode 100644
index 0000000..6d6ecce
--- /dev/null
+++ b/src/ts/core/Debug.ts
@@ -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
+};
diff --git a/src/ts/core/Domain.ts b/src/ts/core/Domain.ts
index 7539666..6956f2c 100644
--- a/src/ts/core/Domain.ts
+++ b/src/ts/core/Domain.ts
@@ -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
{
+ 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);
}
});
});
diff --git a/src/ts/core/Downloader.ts b/src/ts/core/Downloader.ts
index a8ee28a..bd37ddc 100644
--- a/src/ts/core/Downloader.ts
+++ b/src/ts/core/Downloader.ts
@@ -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 };
diff --git a/src/ts/core/Prefix.ts b/src/ts/core/Prefix.ts
index 100b55f..312635e 100644
--- a/src/ts/core/Prefix.ts
+++ b/src/ts/core/Prefix.ts
@@ -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
{
+ 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);
+ });
});
}
});
diff --git a/src/ts/core/Runners.ts b/src/ts/core/Runners.ts
index 927fa49..be63fe8 100644
--- a/src/ts/core/Runners.ts
+++ b/src/ts/core/Runners.ts
@@ -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
{
+ 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();
+ });
});
});
}
diff --git a/src/ts/launcher/State.ts b/src/ts/launcher/State.ts
index 9463d52..32322eb 100644
--- a/src/ts/launcher/State.ts
+++ b/src/ts/launcher/State.ts
@@ -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' ?
diff --git a/src/ts/launcher/states/Install.ts b/src/ts/launcher/states/Install.ts
index b3df921..c48492a 100644
--- a/src/ts/launcher/states/Install.ts
+++ b/src/ts/launcher/states/Install.ts
@@ -5,8 +5,6 @@ import Prefix from '../../core/Prefix';
export default (launcher: Launcher): Promise => {
return new Promise(async (resolve) => {
- launcher.state!.launchButton!.style['display'] = 'none';
-
Prefix.exists().then((exists) => {
if (!exists)
{
diff --git a/src/ts/launcher/states/Launch.ts b/src/ts/launcher/states/Launch.ts
index f2f1570..d06c1a9 100644
--- a/src/ts/launcher/states/Launch.ts
+++ b/src/ts/launcher/states/Launch.ts
@@ -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 => {
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 => {
icon: icon,
importance: 'critical'
});
+
+ debugThread.log('Telemetry is not disabled!');
}
// Otherwise run the game
@@ -51,7 +56,7 @@ export default (): Promise => {
}
}
- console.log(`Wine executable: ${wineExeutable}`);
+ debugThread.log(`Wine executable path: ${wineExeutable}`);
// Some special variables
let env: any = {};
@@ -77,9 +82,9 @@ export default (): Promise => {
*/
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 => {
/*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
diff --git a/src/ts/neutralino/Process.ts b/src/ts/neutralino/Process.ts
index b336a60..24b211f 100644
--- a/src/ts/neutralino/Process.ts
+++ b/src/ts/neutralino/Process.ts
@@ -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));
});
}
diff --git a/src/ts/types/Debug.d.ts b/src/ts/types/Debug.d.ts
new file mode 100644
index 0000000..f8715bd
--- /dev/null
+++ b/src/ts/types/Debug.d.ts
@@ -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 };