diff --git a/README.md b/README.md
index acd76f1..fbeceff 100644
--- a/README.md
+++ b/README.md
@@ -169,11 +169,11 @@ This is our current roadmap goals. You can find older ones [here](ROADMAP.md)
* Splash screen
* Theming system
* Game pre-installation
-* Launcher auto-updates
+* Default runner and DXVK auto-installation
* Statistics window
-* Chengelog window
-* Default runner auto-installation
* Ability to change the temp directory where the launcher should download some files
+* Launcher auto-updates
+* Changelog window
### Features
diff --git a/public/runners.json b/public/runners.json
index cf1093c..42ce4cc 100644
--- a/public/runners.json
+++ b/public/runners.json
@@ -1,4 +1,69 @@
[
+ {
+ "title": "Wine-GE",
+ "runners": [
+ {
+ "family": "Wine-GE",
+ "name": "lutris-ge-6.21-1-x86_64",
+ "title": "Wine-6.21-GE-1",
+ "uri": "https://github.com/GloriousEggroll/wine-ge-custom/releases/download/6.21-GE-1/wine-lutris-ge-6.21-1-x86_64.tar.xz",
+ "files": {
+ "wine": "bin/wine64",
+ "wineserver": "bin/wineserver",
+ "winecfg": "lib64/wine/x86_64-windows/winecfg.exe"
+ },
+ "recommended": true
+ },
+ {
+ "family": "Wine-GE",
+ "name": "lutris-ge-6.20-1-x86_64",
+ "title": "Wine-6.20-GE-1",
+ "uri": "https://github.com/GloriousEggroll/wine-ge-custom/releases/download/6.20-GE-1/wine-lutris-ge-6.20-1-x86_64.tar.xz",
+ "files": {
+ "wine": "bin/wine64",
+ "wineserver": "bin/wineserver",
+ "winecfg": "lib64/wine/x86_64-windows/winecfg.exe"
+ },
+ "recommended": true
+ },
+ {
+ "family": "Wine-GE",
+ "name": "lutris-ge-6.19-1-x86_64",
+ "title": "Wine-6.19-GE-1",
+ "uri": "https://github.com/GloriousEggroll/wine-ge-custom/releases/download/6.19-GE-1/wine-lutris-ge-6.19-1-x86_64.tar.xz",
+ "files": {
+ "wine": "bin/wine64",
+ "wineserver": "bin/wineserver",
+ "winecfg": "lib64/wine/x86_64-windows/winecfg.exe"
+ },
+ "recommended": true
+ },
+ {
+ "family": "Wine-GE",
+ "name": "lutris-ge-6.18-1-x86_64",
+ "title": "Wine-6.18-GE-1",
+ "uri": "https://github.com/GloriousEggroll/wine-ge-custom/releases/download/6.18-GE-1/wine-lutris-ge-6.18-1-x86_64.tar.xz",
+ "files": {
+ "wine": "bin/wine64",
+ "wineserver": "bin/wineserver",
+ "winecfg": "lib64/wine/x86_64-windows/winecfg.exe"
+ },
+ "recommended": true
+ },
+ {
+ "family": "Wine-GE",
+ "name": "lutris-ge-6.16-1-x86_64",
+ "title": "Wine-6.16-GE-1",
+ "uri": "https://github.com/GloriousEggroll/wine-ge-custom/releases/download/6.16-GE-1/lutris-ge-6.16-1-x86_64.tar.xz",
+ "files": {
+ "wine": "bin/wine64",
+ "wineserver": "bin/wineserver",
+ "winecfg": "lib64/wine/x86_64-windows/winecfg.exe"
+ },
+ "recommended": true
+ }
+ ]
+ },
{
"title": "Lutris",
"runners": [
@@ -159,70 +224,5 @@
"recommended": false
}
]
- },
- {
- "title": "Wine-GE",
- "runners": [
- {
- "family": "Wine-GE",
- "name": "lutris-ge-6.21-1-x86_64",
- "title": "Wine-6.21-GE-1",
- "uri": "https://github.com/GloriousEggroll/wine-ge-custom/releases/download/6.21-GE-1/wine-lutris-ge-6.21-1-x86_64.tar.xz",
- "files": {
- "wine": "bin/wine64",
- "wineserver": "bin/wineserver",
- "winecfg": "lib64/wine/x86_64-windows/winecfg.exe"
- },
- "recommended": true
- },
- {
- "family": "Wine-GE",
- "name": "lutris-ge-6.20-1-x86_64",
- "title": "Wine-6.20-GE-1",
- "uri": "https://github.com/GloriousEggroll/wine-ge-custom/releases/download/6.20-GE-1/wine-lutris-ge-6.20-1-x86_64.tar.xz",
- "files": {
- "wine": "bin/wine64",
- "wineserver": "bin/wineserver",
- "winecfg": "lib64/wine/x86_64-windows/winecfg.exe"
- },
- "recommended": true
- },
- {
- "family": "Wine-GE",
- "name": "lutris-ge-6.19-1-x86_64",
- "title": "Wine-6.19-GE-1",
- "uri": "https://github.com/GloriousEggroll/wine-ge-custom/releases/download/6.19-GE-1/wine-lutris-ge-6.19-1-x86_64.tar.xz",
- "files": {
- "wine": "bin/wine64",
- "wineserver": "bin/wineserver",
- "winecfg": "lib64/wine/x86_64-windows/winecfg.exe"
- },
- "recommended": false
- },
- {
- "family": "Wine-GE",
- "name": "lutris-ge-6.18-1-x86_64",
- "title": "Wine-6.18-GE-1",
- "uri": "https://github.com/GloriousEggroll/wine-ge-custom/releases/download/6.18-GE-1/wine-lutris-ge-6.18-1-x86_64.tar.xz",
- "files": {
- "wine": "bin/wine64",
- "wineserver": "bin/wineserver",
- "winecfg": "lib64/wine/x86_64-windows/winecfg.exe"
- },
- "recommended": false
- },
- {
- "family": "Wine-GE",
- "name": "lutris-ge-6.16-1-x86_64",
- "title": "Wine-6.16-GE-1",
- "uri": "https://github.com/GloriousEggroll/wine-ge-custom/releases/download/6.16-GE-1/lutris-ge-6.16-1-x86_64.tar.xz",
- "files": {
- "wine": "bin/wine64",
- "wineserver": "bin/wineserver",
- "winecfg": "lib64/wine/x86_64-windows/winecfg.exe"
- },
- "recommended": false
- }
- ]
}
]
diff --git a/src/components/RunnerSelectionList.svelte b/src/components/RunnerSelectionList.svelte
index 8f5d4c1..5fbb15b 100644
--- a/src/components/RunnerSelectionList.svelte
+++ b/src/components/RunnerSelectionList.svelte
@@ -47,6 +47,10 @@
disabledRunners[runner.name] = false;
progress[runner.name] = undefined;
+
+ selectedVersion = runner.name;
+
+ Runners.current(runner);
});
});
};
diff --git a/src/sass/index.sass b/src/sass/index.sass
index d2384ab..8c1f4c9 100644
--- a/src/sass/index.sass
+++ b/src/sass/index.sass
@@ -41,7 +41,7 @@
height: 64px
right: 128px
- bottom: 64px
+ bottom: 48px
font-size: 22px
@@ -53,7 +53,7 @@
height: 52px
right: 386px
- bottom: 70px
+ bottom: 54px
border-radius: 32px
@@ -98,7 +98,7 @@
position: absolute
left: 48px
- bottom: 116px
+ bottom: 100px
color: white
font-size: 18px
@@ -119,7 +119,7 @@
padding: 0
left: 48px
- bottom: 68px
+ bottom: 52px
width: 720px
height: 36px
diff --git a/src/ts/core/AbstractInstaller.ts b/src/ts/core/AbstractInstaller.ts
index fb096da..a973d2c 100644
--- a/src/ts/core/AbstractInstaller.ts
+++ b/src/ts/core/AbstractInstaller.ts
@@ -114,13 +114,7 @@ export default abstract class Installer
if (this.onDownloadFinish)
this.onDownloadFinish();
- Promise.resolve(unpackDir)
- .then((unpackDir) => {
- if (shouldResolve)
- debugThread.log(`Resolved unpack dir: ${unpackDir}`);
-
- unpackArchive();
- });
+ unpackArchive();
});
});
}
diff --git a/src/ts/core/Prefix.ts b/src/ts/core/Prefix.ts
index 312635e..2ec01ed 100644
--- a/src/ts/core/Prefix.ts
+++ b/src/ts/core/Prefix.ts
@@ -104,7 +104,7 @@ export default class Prefix
this.getWinetricks().then(async (winetricks) => {
let installationProgress = 0;
- const process = await Process.run(`./'${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}`,
diff --git a/src/ts/launcher/State.ts b/src/ts/launcher/State.ts
index 1a8bbe4..36cef7b 100644
--- a/src/ts/launcher/State.ts
+++ b/src/ts/launcher/State.ts
@@ -6,6 +6,9 @@ import Window from '../neutralino/Window';
import Game from '../Game';
import Patch from '../Patch';
import Voice from '../Voice';
+import Runners from '../core/Runners';
+import { DebugThread } from '../core/Debug';
+import DXVK from '../core/DXVK';
declare const Neutralino;
@@ -15,15 +18,17 @@ export default class State
public launchButton: HTMLElement;
public predownloadButton: HTMLElement;
+ public settingsButton: HTMLElement;
protected _state: LauncherState = 'game-launch-available';
protected events = {
+ 'runner-installation-required': import('./states/InstallWine'),
+ 'dxvk-installation-required': import('./states/InstallDXVK'),
'game-launch-available': import('./states/Launch'),
'game-installation-available': import('./states/Install'),
'game-update-available': import('./states/Install'),
-
'game-voice-update-required': import('./states/InstallVoice'),
'test-patch-available': import('./states/ApplyPatch'),
@@ -36,16 +41,19 @@ export default class State
this.launchButton = document.getElementById('launch');
this.predownloadButton = document.getElementById('predownload');
+ this.settingsButton = document.getElementById('settings');
this.launchButton.onclick = () => {
if (this.events[this._state])
{
this.launchButton.style['display'] = 'none';
+ this.settingsButton.style['display'] = 'none';
this.events[this._state].then((event) => {
event.default(this.launcher).then(() => {
this.update().then(() => {
this.launchButton.style['display'] = 'block';
+ this.settingsButton.style['display'] = 'block';
});
});
});
@@ -55,6 +63,7 @@ export default class State
this.predownloadButton.onclick = () => {
this.launchButton.style['display'] = 'none';
this.predownloadButton.style['display'] = 'none';
+ this.settingsButton.style['display'] = 'none';
const module = this._state === 'game-pre-installation-available' ?
'Predownload' : 'PredownloadVoice';
@@ -63,6 +72,7 @@ export default class State
module.default(this.launcher).then(() => {
this.update().then(() => {
this.launchButton.style['display'] = 'block';
+ this.settingsButton.style['display'] = 'block';
});
});
});
@@ -95,6 +105,16 @@ export default class State
switch(state)
{
+ case 'runner-installation-required':
+ this.launchButton.textContent = 'Install wine';
+
+ break;
+
+ case 'dxvk-installation-required':
+ this.launchButton.textContent = 'Install DXVK';
+
+ break;
+
case 'game-launch-available':
this.launchButton.textContent = 'Launch';
@@ -148,42 +168,120 @@ export default class State
*/
public update(): Promise
{
+ const debugThread = new DebugThread('State.update', 'Updating launcher state');
+
return new Promise(async (resolve) => {
- let state: LauncherState;
+ let state: LauncherState|null = null;
- const gameCurrent = await Game.current;
- const gameLatest = await Game.getLatestData();
- const patch = await Patch.latest;
- const voiceData = await Voice.current;
+ const runner = await Runners.current();
+ const dxvk = await DXVK.current();
- if (gameCurrent === null)
- state = 'game-installation-available';
-
- else if (gameCurrent != gameLatest.game.latest.version)
- 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)
+ // Check if the wine is installed
+ if (runner === null)
{
- state = patch.state == 'preparation' ?
- 'patch-unavailable' : (patch.state == 'testing' ?
- 'test-patch-available' : 'patch-available');
+ debugThread.log('Runner is not specified');
+
+ state = 'runner-installation-required';
+
+ Runners.list().then((list) => {
+ for (const family of list)
+ for (const runner of family.runners)
+ if (runner.installed && runner.recommended)
+ {
+ debugThread.log(`Automatically selected runner ${runner.title} (${runner.name})`);
+
+ state = null;
+
+ Runners.current(runner).then(() => {
+ this.update().then(resolve);
+ });
+
+ return;
+ }
+ });
+
+ if (state !== null)
+ {
+ debugThread.log('No recommended runner installed');
+
+ this.set(state);
+
+ resolve(state);
+ }
}
- else if (gameLatest.pre_download_game && !await Game.isUpdatePredownloaded())
- state = 'game-pre-installation-available';
+ // Check if the DXVK is installed
+ else if (dxvk === null)
+ {
+ debugThread.log('DXVK is not specified');
- else if (gameLatest.pre_download_game && !await Voice.isUpdatePredownloaded(await Voice.selected))
- state = 'game-voice-pre-installation-available';
+ state = 'dxvk-installation-required';
- else state = 'game-launch-available';
+ DXVK.list().then((list) => {
+ for (const dxvk of list)
+ if (dxvk.installed && dxvk.recommended)
+ {
+ debugThread.log(`Automatically selected DXVK ${dxvk.version}`);
- this.set(state);
+ state = null;
- resolve(state);
+ DXVK.current(dxvk).then(() => {
+ this.update().then(resolve);
+ });
+
+ return;
+ }
+ });
+
+ if (state !== null)
+ {
+ debugThread.log('No recommended DXVK installed');
+
+ this.set(state);
+
+ resolve(state);
+ }
+ }
+
+ // Otherwise select some launcher state
+ else
+ {
+ const gameCurrent = await Game.current;
+ const gameLatest = await Game.getLatestData();
+ const patch = await Patch.latest;
+ const voiceData = await Voice.current;
+
+ if (gameCurrent === null)
+ state = 'game-installation-available';
+
+ else if (gameCurrent != gameLatest.game.latest.version)
+ 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' ?
+ 'patch-unavailable' : (patch.state == 'testing' ?
+ 'test-patch-available' : 'patch-available');
+ }
+
+ else if (gameLatest.pre_download_game && !await Game.isUpdatePredownloaded())
+ state = 'game-pre-installation-available';
+
+ else if (gameLatest.pre_download_game && !await Voice.isUpdatePredownloaded(await Voice.selected))
+ state = 'game-voice-pre-installation-available';
+
+ else state = 'game-launch-available';
+
+ debugThread.log(`Updated state: ${state}`);
+
+ this.set(state);
+
+ resolve(state);
+ }
});
}
};
diff --git a/src/ts/launcher/states/CreatePrefix.ts b/src/ts/launcher/states/CreatePrefix.ts
index eec3f42..790e12c 100644
--- a/src/ts/launcher/states/CreatePrefix.ts
+++ b/src/ts/launcher/states/CreatePrefix.ts
@@ -7,7 +7,7 @@ export default (launcher: Launcher): Promise => {
return new Promise(async (resolve) => {
const prefixDir = await constants.paths.prefix.current;
- Prefix.exists().then((exists) => {
+ Prefix.exists(prefixDir).then((exists) => {
if (exists)
resolve();
@@ -28,23 +28,12 @@ export default (launcher: Launcher): Promise => {
Prefix.create(prefixDir, (output, current, total) => {
progressLabel = output;
- if (progressLabel.length > 70)
- progressLabel = progressLabel.substring(0, 70) + '...';
+ if (progressLabel.length > 80)
+ progressLabel = progressLabel.substring(0, 80) + '...';
launcher.progressBar!.update(current, total, 1);
})
- .then((result) => {
- if (result === true)
- resolve();
-
- else
- {
- // TODO
- console.error('There\'s no wine version installed to use to create the prefix');
-
- resolve();
- }
- });
+ .then(() => resolve());
}
});
});
diff --git a/src/ts/launcher/states/InstallDXVK.ts b/src/ts/launcher/states/InstallDXVK.ts
new file mode 100644
index 0000000..59adb0f
--- /dev/null
+++ b/src/ts/launcher/states/InstallDXVK.ts
@@ -0,0 +1,56 @@
+import type Launcher from '../../Launcher';
+
+import DXVK from '../../core/DXVK';
+import constants from '../../Constants';
+
+export default (launcher: Launcher): Promise => {
+ return new Promise(async (resolve) => {
+ // Create prefix if it is not created
+ import('./CreatePrefix').then((module) => {
+ module.default(launcher).then(() => {
+ // And then download the DXVK
+ DXVK.download('1.9.2').then((stream) => {
+ launcher.progressBar?.init({
+ label: 'Downloading DXVK 1.9.2...',
+ 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);
+ });
+
+ let unpacking = true;
+
+ stream?.unpackStart(() => {
+ launcher.progressBar?.init({
+ label: () => unpacking ? 'Unpacking DXVK 1.9.2...' : 'Applying DXVK 1.9.2...',
+ showSpeed: true,
+ showEta: true,
+ showPercents: true,
+ showTotals: true
+ });
+ });
+
+ stream?.unpackProgress((current: number, total: number, difference: number) => {
+ launcher.progressBar?.update(current, total, difference);
+ });
+
+ stream?.unpackFinish(async () => {
+ unpacking = true;
+
+ DXVK.apply(await constants.paths.prefix.current, '1.9.2').then(() => {
+ launcher.progressBar?.hide();
+
+ resolve();
+ });
+ });
+ });
+ });
+ });
+ });
+};
diff --git a/src/ts/launcher/states/InstallWine.ts b/src/ts/launcher/states/InstallWine.ts
new file mode 100644
index 0000000..36cb190
--- /dev/null
+++ b/src/ts/launcher/states/InstallWine.ts
@@ -0,0 +1,57 @@
+import type Launcher from '../../Launcher';
+
+import Runners from '../../core/Runners';
+import DXVK from '../../core/DXVK';
+
+export default (launcher: Launcher): Promise => {
+ return new Promise(async (resolve) => {
+ Runners.download('lutris-ge-6.21-1-x86_64').then((stream) => {
+ launcher.progressBar?.init({
+ label: 'Downloading Wine-GE 6.21-1...',
+ 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 Wine-GE 6.21-1...',
+ showSpeed: true,
+ showEta: true,
+ showPercents: true,
+ showTotals: true
+ });
+ });
+
+ stream?.unpackProgress((current: number, total: number, difference: number) => {
+ launcher.progressBar?.update(current, total, difference);
+ });
+
+ stream?.unpackFinish(() => {
+ // Create prefix if it is not created
+ import('./CreatePrefix').then((module) => {
+ module.default(launcher).then(() => {
+ // Download DXVK if it wasn't downloaded
+ DXVK.current().then((dxvk) => {
+ if (dxvk === null)
+ {
+ import('./InstallDXVK').then((module) => {
+ module.default(launcher).then(() => resolve());
+ });
+ }
+
+ else resolve();
+ });
+ });
+ });
+ });
+ });
+ });
+};
diff --git a/src/ts/launcher/states/Launch.ts b/src/ts/launcher/states/Launch.ts
index 52bb7bd..1bebf85 100644
--- a/src/ts/launcher/states/Launch.ts
+++ b/src/ts/launcher/states/Launch.ts
@@ -144,7 +144,7 @@ export default (): Promise => {
Window.current.show();
- // todo
+ // TODO
resolve();
});
diff --git a/src/ts/types/Launcher.ts b/src/ts/types/Launcher.ts
index 1597759..0409298 100644
--- a/src/ts/types/Launcher.ts
+++ b/src/ts/types/Launcher.ts
@@ -13,6 +13,8 @@
*/
type LauncherState =
+ | 'runner-installation-required'
+ | 'dxvk-installation-required'
| 'patch-unavailable'
| 'test-patch-available'
| 'patch-available'