Made default runner and dxvk auto-installation

- Wine-GE runners family moved to the top of the runners list
- now installed runner will be automatically selected
- launcher controls were slightly moved down
- fixed double resolved unpack directory logging in AbstractInstaller
- fixed `Prefix.create()` method work
- added `State.update()` method logging
- now settings button will be hidden when user are doing something
- added `InstallWine` and `InstallDXVK` scripts; added related launcher states
This commit is contained in:
Observer KRypt0n_ 2021-12-29 21:34:23 +02:00
parent 2a4dcc03e3
commit 81676f4b7c
No known key found for this signature in database
GPG key ID: DC5D4EC1303465DA
12 changed files with 323 additions and 123 deletions

View file

@ -169,11 +169,11 @@ This is our current roadmap goals. You can find older ones [here](ROADMAP.md)
* <s>Splash screen</s>
* <s>Theming system</s>
* <s>Game pre-installation</s>
* Launcher auto-updates
* <s>Default runner and DXVK auto-installation</s>
* 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

View file

@ -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
}
]
}
]

View file

@ -47,6 +47,10 @@
disabledRunners[runner.name] = false;
progress[runner.name] = undefined;
selectedVersion = runner.name;
Runners.current(runner);
});
});
};

View file

@ -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

View file

@ -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();
});
});
}

View file

@ -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}`,

View file

@ -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 = <HTMLElement>document.getElementById('launch');
this.predownloadButton = <HTMLElement>document.getElementById('predownload');
this.settingsButton = <HTMLElement>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<string>
{
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);
}
});
}
};

View file

@ -7,7 +7,7 @@ export default (launcher: Launcher): Promise<void> => {
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<void> => {
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());
}
});
});

View file

@ -0,0 +1,56 @@
import type Launcher from '../../Launcher';
import DXVK from '../../core/DXVK';
import constants from '../../Constants';
export default (launcher: Launcher): Promise<void> => {
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();
});
});
});
});
});
});
};

View file

@ -0,0 +1,57 @@
import type Launcher from '../../Launcher';
import Runners from '../../core/Runners';
import DXVK from '../../core/DXVK';
export default (launcher: Launcher): Promise<void> => {
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();
});
});
});
});
});
});
};

View file

@ -144,7 +144,7 @@ export default (): Promise<void> => {
Window.current.show();
// todo
// TODO
resolve();
});

View file

@ -13,6 +13,8 @@
*/
type LauncherState =
| 'runner-installation-required'
| 'dxvk-installation-required'
| 'patch-unavailable'
| 'test-patch-available'
| 'patch-available'