mirror of
https://github.com/an-anime-team/an-anime-game-launcher.git
synced 2024-12-30 05:28:15 +03:00
API improvements
- updated empathize to 1.4.0; due to it was removed `Launcher.isPackageAvailable()` method which now included in empathize as `Package.exists()` - `Game.isTelemetryDisabled()` now rejects an Error object when `iputils` package (`ping` command) is not installed - due to the change above was slightly rewritten `Launch.ts` script
This commit is contained in:
parent
1808cdf39f
commit
fcc67af03a
8 changed files with 251 additions and 280 deletions
|
@ -11,7 +11,7 @@
|
||||||
"check": "svelte-check --tsconfig ./tsconfig.json"
|
"check": "svelte-check --tsconfig ./tsconfig.json"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@empathize/framework": "^1.3.5",
|
"@empathize/framework": "^1.4.0",
|
||||||
"js-md5": "^0.7.3",
|
"js-md5": "^0.7.3",
|
||||||
"semver": "^7.3.5",
|
"semver": "^7.3.5",
|
||||||
"svelte-i18n": "^3.3.13",
|
"svelte-i18n": "^3.3.13",
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { _, locale } from 'svelte-i18n';
|
import { _, locale } from 'svelte-i18n';
|
||||||
|
|
||||||
|
import { Package } from '../empathize';
|
||||||
|
|
||||||
import type { Shader } from '../ts/types/Shaders';
|
import type { Shader } from '../ts/types/Shaders';
|
||||||
|
|
||||||
import Shaders from '../ts/launcher/Shaders';
|
import Shaders from '../ts/launcher/Shaders';
|
||||||
import Launcher from '../ts/Launcher';
|
|
||||||
|
|
||||||
import SelectionBox from './SelectionBox.svelte';
|
import SelectionBox from './SelectionBox.svelte';
|
||||||
|
|
||||||
|
@ -23,7 +24,7 @@
|
||||||
shadersOptions['custom'] = 'settings.shaders.items.shaders.items.custom';
|
shadersOptions['custom'] = 'settings.shaders.items.shaders.items.custom';
|
||||||
});
|
});
|
||||||
|
|
||||||
Launcher.isPackageAvailable('reshade').then((available) => reshadeInstalled = available);
|
Package.exists('reshade').then((available) => reshadeInstalled = available);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
|
|
|
@ -9,7 +9,7 @@ import {
|
||||||
Windows,
|
Windows,
|
||||||
|
|
||||||
// OS API
|
// OS API
|
||||||
Process, Tray, IPC, Notification, Archive,
|
Process, Tray, IPC, Notification, Archive, Package,
|
||||||
|
|
||||||
// Network API
|
// Network API
|
||||||
fetch, Domain, Downloader,
|
fetch, Domain, Downloader,
|
||||||
|
@ -42,7 +42,7 @@ export {
|
||||||
Windows,
|
Windows,
|
||||||
|
|
||||||
// OS API
|
// OS API
|
||||||
Process, Tray, IPC, Notification, Archive,
|
Process, Tray, IPC, Notification, Archive, Package,
|
||||||
|
|
||||||
// Network API
|
// Network API
|
||||||
fetch, Domain, Downloader,
|
fetch, Domain, Downloader,
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
import { onMount } from 'svelte';
|
import { onMount } from 'svelte';
|
||||||
import { _, locale, locales } from 'svelte-i18n';
|
import { _, locale, locales } from 'svelte-i18n';
|
||||||
|
|
||||||
import { Windows, Configs, Debug, IPC, Process, path } from './empathize';
|
import { Windows, Configs, Debug, IPC, Process, path, Package } from './empathize';
|
||||||
|
|
||||||
import constants from './ts/Constants';
|
import constants from './ts/Constants';
|
||||||
import Launcher from './ts/Launcher';
|
import Launcher from './ts/Launcher';
|
||||||
|
@ -86,7 +86,7 @@
|
||||||
tooltip: 'settings.enhancements.items.gamemode.tooltip.enabled'
|
tooltip: 'settings.enhancements.items.gamemode.tooltip.enabled'
|
||||||
};
|
};
|
||||||
|
|
||||||
Launcher.isPackageAvailable('gamemoderun').then((available) => {
|
Package.exists('gamemoderun').then((available) => {
|
||||||
gamemode.disabled = !available;
|
gamemode.disabled = !available;
|
||||||
|
|
||||||
if (gamemode.disabled)
|
if (gamemode.disabled)
|
||||||
|
|
|
@ -7,7 +7,7 @@ import type {
|
||||||
|
|
||||||
import type { Stream as DownloadingStream } from '@empathize/framework/dist/network/Downloader';
|
import type { Stream as DownloadingStream } from '@empathize/framework/dist/network/Downloader';
|
||||||
|
|
||||||
import { fetch, Domain, promisify, Downloader, Cache, Debug } from '../empathize';
|
import { fetch, Domain, promisify, Downloader, Cache, Debug, Package } from '../empathize';
|
||||||
import { DebugThread } from '@empathize/framework/dist/meta/Debug';
|
import { DebugThread } from '@empathize/framework/dist/meta/Debug';
|
||||||
|
|
||||||
import constants from './Constants';
|
import constants from './Constants';
|
||||||
|
@ -246,31 +246,45 @@ export default class Game
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if the telemetry servers are disabled
|
* Check if the telemetry servers are disabled
|
||||||
|
*
|
||||||
|
* @returns throws Error object when iputils package (ping command) is not available
|
||||||
*/
|
*/
|
||||||
public static isTelemetryDisabled(): Promise<boolean>
|
public static isTelemetryDisabled(): Promise<boolean>
|
||||||
{
|
{
|
||||||
const debugThread = new DebugThread('Game.isTelemetryDisabled', 'Checking if the telemetry servers are disabled');
|
const debugThread = new DebugThread('Game.isTelemetryDisabled', 'Checking if the telemetry servers are disabled');
|
||||||
|
|
||||||
return new Promise(async (resolve) => {
|
return new Promise(async (resolve, reject) => {
|
||||||
const pipeline = promisify({
|
// If ping command is not available - throw an error
|
||||||
callbacks: await constants.uri.telemetry.map((domain) => {
|
if (!await Package.exists('ping'))
|
||||||
return new Promise((resolve) => {
|
{
|
||||||
Domain.getInfo(domain).then((info) => resolve(info.available));
|
debugThread.log('iputils package is not installed');
|
||||||
});
|
|
||||||
}),
|
|
||||||
callAtOnce: true,
|
|
||||||
interval: 500
|
|
||||||
});
|
|
||||||
|
|
||||||
pipeline.then((result) => {
|
reject(new Error('iputils package is not installed'));
|
||||||
let disabled = false;
|
}
|
||||||
|
|
||||||
Object.values(result).forEach((value) => disabled ||= value as boolean);
|
// Otherwise - check if telemetry is disabled
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const pipeline = promisify({
|
||||||
|
callbacks: await constants.uri.telemetry.map((domain) => {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
Domain.getInfo(domain).then((info) => resolve(info.available));
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
callAtOnce: true,
|
||||||
|
interval: 500
|
||||||
|
});
|
||||||
|
|
||||||
debugThread.log(`Telemetry is ${disabled ? 'not ' : ''}disabled`);
|
pipeline.then((result) => {
|
||||||
|
let disabled = false;
|
||||||
|
|
||||||
resolve(disabled === false);
|
Object.values(result).forEach((value) => disabled ||= value as boolean);
|
||||||
});
|
|
||||||
|
debugThread.log(`Telemetry is ${disabled ? 'not ' : ''}disabled`);
|
||||||
|
|
||||||
|
resolve(disabled === false);
|
||||||
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -154,45 +154,4 @@ export default class Launcher
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if some binary file or package downloaded
|
|
||||||
*/
|
|
||||||
public static isPackageAvailable(name: string): Promise<boolean>
|
|
||||||
{
|
|
||||||
return new Promise(async (resolve) => {
|
|
||||||
let available = false;
|
|
||||||
|
|
||||||
let paths: string[] = (await Neutralino.os.getEnv('PATH')).split(':');
|
|
||||||
|
|
||||||
// Add "/usr/share" if it is not included
|
|
||||||
// because we use these paths to check if some library exists in system
|
|
||||||
if (!paths.includes('/usr/share'))
|
|
||||||
paths.push('/usr/share');
|
|
||||||
|
|
||||||
// Sort them by length because obviously
|
|
||||||
// "/usr/bin" more important than some randomly generated
|
|
||||||
// yaml or npm folder for its globally downloaded packages
|
|
||||||
paths = paths.sort((a, b) => a.length - b.length);
|
|
||||||
|
|
||||||
for (const path of paths)
|
|
||||||
{
|
|
||||||
// Becasue await Neutralino.filesystem.getStats will throw an erro
|
|
||||||
// if the specified path doesn't exist
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (await Neutralino.filesystem.getStats(`${path}/${name}`))
|
|
||||||
{
|
|
||||||
available = true;
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
catch {}
|
|
||||||
}
|
|
||||||
|
|
||||||
resolve(available);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { Notification } from '../../../empathize';
|
import { Notification, Package } from '../../../empathize';
|
||||||
|
|
||||||
|
import type Launcher from '../../Launcher';
|
||||||
|
|
||||||
import Launcher from '../../Launcher';
|
|
||||||
import Patch from '../../Patch';
|
import Patch from '../../Patch';
|
||||||
import constants from '../../Constants';
|
import constants from '../../Constants';
|
||||||
import Locales from '../Locales';
|
import Locales from '../Locales';
|
||||||
|
@ -8,7 +9,7 @@ import Locales from '../Locales';
|
||||||
export default (launcher: Launcher): Promise<void> => {
|
export default (launcher: Launcher): Promise<void> => {
|
||||||
return new Promise(async (resolve) => {
|
return new Promise(async (resolve) => {
|
||||||
// Show an error notification if xdelta3 package is not installed
|
// Show an error notification if xdelta3 package is not installed
|
||||||
if (!await Launcher.isPackageAvailable('xdelta3'))
|
if (!await Package.exists('xdelta3'))
|
||||||
{
|
{
|
||||||
Notification.show({
|
Notification.show({
|
||||||
...(Locales.translate('notifications.xdelta3_package_required') as { title: string, body: string }),
|
...(Locales.translate('notifications.xdelta3_package_required') as { title: string, body: string }),
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
import { Process, Windows, Configs, Notification, path } from '../../../empathize';
|
import { Process, Windows, Configs, Notification, path, Package } from '../../../empathize';
|
||||||
import { DebugThread } from '@empathize/framework/dist/meta/Debug';
|
import { DebugThread } from '@empathize/framework/dist/meta/Debug';
|
||||||
|
|
||||||
import Launcher from '../../Launcher';
|
import type Launcher from '../../Launcher';
|
||||||
|
|
||||||
import constants from '../../Constants';
|
import constants from '../../Constants';
|
||||||
import Runners from '../../core/Runners';
|
import Runners from '../../core/Runners';
|
||||||
import Game from '../../Game';
|
import Game from '../../Game';
|
||||||
|
@ -13,240 +14,235 @@ export default (launcher: Launcher): Promise<void> => {
|
||||||
return new Promise(async (resolve) => {
|
return new Promise(async (resolve) => {
|
||||||
const debugThread = new DebugThread('State/Launch', 'Starting the game');
|
const debugThread = new DebugThread('State/Launch', 'Starting the game');
|
||||||
|
|
||||||
// Show an error notification if ping is not available
|
// Check if telemetry servers are disabled
|
||||||
if (!await Launcher.isPackageAvailable('ping'))
|
Game.isTelemetryDisabled()
|
||||||
{
|
.then(async (telemetryDisabled) => {
|
||||||
Notification.show({
|
// If telemetry servers are not disabled
|
||||||
...(Locales.translate('notifications.iputils_package_required') as { title: string, body: string }),
|
if (!telemetryDisabled)
|
||||||
icon: `${constants.paths.appDir}/public/images/baal64-transparent.png`,
|
|
||||||
importance: 'critical'
|
|
||||||
});
|
|
||||||
|
|
||||||
debugThread.log('iputils package is not installed!');
|
|
||||||
|
|
||||||
resolve();
|
|
||||||
}
|
|
||||||
|
|
||||||
const telemetry = await Game.isTelemetryDisabled();
|
|
||||||
|
|
||||||
// If telemetry servers are not disabled
|
|
||||||
if (!telemetry)
|
|
||||||
{
|
|
||||||
Notification.show({
|
|
||||||
...(Locales.translate('notifications.telemetry_not_disabled') as { title: string, body: string }),
|
|
||||||
icon: `${constants.paths.appDir}/public/images/baal64-transparent.png`,
|
|
||||||
importance: 'critical'
|
|
||||||
});
|
|
||||||
|
|
||||||
debugThread.log('Telemetry is not disabled!');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Otherwise run the game
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Windows.current.hide();
|
|
||||||
|
|
||||||
launcher.updateDiscordRPC('in-game');
|
|
||||||
|
|
||||||
launcher.tray.update([
|
|
||||||
{ text: 'Starting the game...', disabled: true }
|
|
||||||
]);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Selecting wine executable
|
|
||||||
*/
|
|
||||||
let wineExeutable = 'wine';
|
|
||||||
|
|
||||||
const runner = await Runners.current();
|
|
||||||
|
|
||||||
if (runner !== null)
|
|
||||||
wineExeutable = `${await constants.paths.runnersDir}/${runner.name}/${runner.files.wine}`;
|
|
||||||
|
|
||||||
debugThread.log(`Wine executable path: ${wineExeutable}`);
|
|
||||||
|
|
||||||
// Some special variables
|
|
||||||
let env: any = {};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* HUD
|
|
||||||
*/
|
|
||||||
switch (await Configs.get('hud'))
|
|
||||||
{
|
|
||||||
case 'dxvk':
|
|
||||||
env['DXVK_HUD'] = 'fps,frametimes,version,gpuload';
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'mangohud':
|
|
||||||
env['MANGOHUD'] = 1;
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Wine synchronizations
|
|
||||||
*
|
|
||||||
* @link https://github.com/AdelKS/LinuxGamingGuide#wine-tkg
|
|
||||||
*/
|
|
||||||
switch (await Configs.get('winesync'))
|
|
||||||
{
|
|
||||||
case 'esync':
|
|
||||||
env['WINEESYNC'] = 1;
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'fsync':
|
|
||||||
env['WINEESYNC'] = 1;
|
|
||||||
env['WINEFSYNC'] = 1;
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* AMD FSR
|
|
||||||
*/
|
|
||||||
if (await Configs.get('fsr'))
|
|
||||||
{
|
|
||||||
env['WINE_FULLSCREEN_FSR'] = 1;
|
|
||||||
env['WINE_FULLSCREEN_FSR_STRENGTH'] = 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Shaders
|
|
||||||
*/
|
|
||||||
const shaders = await Configs.get('shaders');
|
|
||||||
|
|
||||||
if (shaders !== 'none' && await Launcher.isPackageAvailable('reshade'))
|
|
||||||
{
|
|
||||||
const launcherShadersFile = `${await constants.paths.launcherDir}/vkBasalt.conf`;
|
|
||||||
|
|
||||||
env['ENABLE_VKBASALT'] = 1;
|
|
||||||
env['VKBASALT_CONFIG_FILE'] = launcherShadersFile;
|
|
||||||
|
|
||||||
if (shaders !== 'custom')
|
|
||||||
{
|
{
|
||||||
const userShadersFile = `${constants.paths.shadersDir}/${shaders}/vkBasalt.conf`;
|
Notification.show({
|
||||||
|
...(Locales.translate('notifications.telemetry_not_disabled') as { title: string, body: string }),
|
||||||
await Neutralino.filesystem.writeFile(launcherShadersFile, await Neutralino.filesystem.readFile(userShadersFile));
|
icon: `${constants.paths.appDir}/public/images/baal64-transparent.png`,
|
||||||
}
|
importance: 'critical'
|
||||||
}
|
});
|
||||||
|
|
||||||
/**
|
debugThread.log('Telemetry is not disabled!');
|
||||||
* GPU selection
|
|
||||||
*/
|
|
||||||
// TODO
|
|
||||||
/*if (LauncherLib.getConfig('gpu') != 'default')
|
|
||||||
{
|
|
||||||
const gpu = await SwitcherooControl.getGpuByName(LauncherLib.getConfig('gpu'));
|
|
||||||
|
|
||||||
if (gpu)
|
|
||||||
{
|
|
||||||
env = {
|
|
||||||
...env,
|
|
||||||
...SwitcherooControl.getEnvAsObject(gpu)
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
else console.warn(`GPU ${LauncherLib.getConfig('gpu')} not found. Launching on the default GPU`);
|
// Otherwise run the game
|
||||||
}*/
|
else
|
||||||
|
{
|
||||||
|
Windows.current.hide();
|
||||||
|
|
||||||
let command = `"${path.addSlashes(wineExeutable)}" ${await Configs.get('fps_unlocker') ? 'unlockfps.bat' : 'launcher.bat'}`;
|
launcher.updateDiscordRPC('in-game');
|
||||||
|
|
||||||
/**
|
launcher.tray.update([
|
||||||
* Gamemode integration
|
{ text: 'Starting the game...', disabled: true }
|
||||||
*/
|
]);
|
||||||
if (await Configs.get('gamemode') && await Launcher.isPackageAvailable('gamemoderun'))
|
|
||||||
command = `gamemoderun ${command}`;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Starting the game
|
* Selecting wine executable
|
||||||
*/
|
*/
|
||||||
const startTime = Date.now();
|
let wineExeutable = 'wine';
|
||||||
|
|
||||||
const process = await Process.run(command, {
|
const runner = await Runners.current();
|
||||||
env: {
|
|
||||||
WINEPREFIX: await constants.paths.prefix.current,
|
|
||||||
...env,
|
|
||||||
...((await Configs.get('env') as object|null) ?? {})
|
|
||||||
},
|
|
||||||
cwd: await constants.paths.gameDir
|
|
||||||
});
|
|
||||||
|
|
||||||
// Game was started by the launcher.bat file
|
if (runner !== null)
|
||||||
// so we just need to wait until AnimeGame.e process
|
wineExeutable = `${await constants.paths.runnersDir}/${runner.name}/${runner.files.wine}`;
|
||||||
// will be closed
|
|
||||||
process.finish(() => {
|
|
||||||
const processName = `${constants.placeholders.uppercase.first + constants.placeholders.uppercase.second}.e`;
|
|
||||||
|
|
||||||
let closeGameCounter = 0;
|
debugThread.log(`Wine executable path: ${wineExeutable}`);
|
||||||
|
|
||||||
const waiter = async () => {
|
// Some special variables
|
||||||
const processes: string = (await Neutralino.os.execCommand('ps -A')).stdOut;
|
let env: any = {};
|
||||||
|
|
||||||
// Game is still running
|
/**
|
||||||
if (processes.includes(processName))
|
* HUD
|
||||||
|
*/
|
||||||
|
switch (await Configs.get('hud'))
|
||||||
{
|
{
|
||||||
const playtime = Math.round((Date.now() - startTime) / 1000);
|
case 'dxvk':
|
||||||
|
env['DXVK_HUD'] = 'fps,frametimes,version,gpuload';
|
||||||
|
|
||||||
let hours: string|number = Math.floor(playtime / 3600);
|
break;
|
||||||
let minutes: string|number = Math.floor((playtime - hours * 3600) / 60);
|
|
||||||
let seconds: string|number = playtime - hours * 3600 - minutes * 60;
|
|
||||||
|
|
||||||
if (hours < 10)
|
case 'mangohud':
|
||||||
hours = `0${hours}`;
|
env['MANGOHUD'] = 1;
|
||||||
|
|
||||||
if (minutes < 10)
|
break;
|
||||||
minutes = `0${minutes}`;
|
|
||||||
|
|
||||||
if (seconds < 10)
|
|
||||||
seconds = `0${seconds}`;
|
|
||||||
|
|
||||||
// FIXME: tray doesn't work in AppImage
|
|
||||||
launcher.tray.update([
|
|
||||||
{ text: `Playing for ${hours}:${minutes}:${seconds}`, disabled: true },
|
|
||||||
{
|
|
||||||
text: `Close game${closeGameCounter > 0 ? ` (${closeGameCounter})` : ''}`,
|
|
||||||
|
|
||||||
click: () => Neutralino.os.execCommand(`kill ${++closeGameCounter < 3 ? '-15' : '-9'} $(pidof ${processName})`)
|
|
||||||
}
|
|
||||||
]);
|
|
||||||
|
|
||||||
setTimeout(waiter, 3000);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Game was closed
|
/**
|
||||||
else
|
* Wine synchronizations
|
||||||
|
*
|
||||||
|
* @link https://github.com/AdelKS/LinuxGamingGuide#wine-tkg
|
||||||
|
*/
|
||||||
|
switch (await Configs.get('winesync'))
|
||||||
{
|
{
|
||||||
const stopTime = Date.now();
|
case 'esync':
|
||||||
|
env['WINEESYNC'] = 1;
|
||||||
|
|
||||||
Windows.current.show();
|
break;
|
||||||
// FIXME: Windows.current.center(1280, 700);
|
|
||||||
|
|
||||||
launcher.updateDiscordRPC('in-launcher');
|
case 'fsync':
|
||||||
launcher.tray.hide();
|
env['WINEESYNC'] = 1;
|
||||||
|
env['WINEFSYNC'] = 1;
|
||||||
|
|
||||||
// Purge game logs
|
break;
|
||||||
Configs.get('purge_logs.game').then(async (purge_logs) => {
|
|
||||||
if (purge_logs)
|
|
||||||
{
|
|
||||||
const gameDir = path.addSlashes(await constants.paths.gameDir);
|
|
||||||
|
|
||||||
// Delete .log files (e.g. "ZFGameBrowser_xxxx.log")
|
|
||||||
Neutralino.os.execCommand(`find "${gameDir}" -maxdepth 1 -type f -name "*.log" -delete`);
|
|
||||||
|
|
||||||
// Delete .dmp files (e.g. "DumpFile-zfbrowser-xxxxxx.dmp")
|
|
||||||
Neutralino.os.execCommand(`find "${gameDir}" -maxdepth 1 -type f -name "*.dmp" -delete`);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// TODO
|
|
||||||
|
|
||||||
resolve();
|
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
setTimeout(waiter, 5000);
|
/**
|
||||||
|
* AMD FSR
|
||||||
|
*/
|
||||||
|
if (await Configs.get('fsr'))
|
||||||
|
{
|
||||||
|
env['WINE_FULLSCREEN_FSR'] = 1;
|
||||||
|
env['WINE_FULLSCREEN_FSR_STRENGTH'] = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shaders
|
||||||
|
*/
|
||||||
|
const shaders = await Configs.get('shaders');
|
||||||
|
|
||||||
|
if (shaders !== 'none' && await Package.exists('reshade'))
|
||||||
|
{
|
||||||
|
const launcherShadersFile = `${await constants.paths.launcherDir}/vkBasalt.conf`;
|
||||||
|
|
||||||
|
env['ENABLE_VKBASALT'] = 1;
|
||||||
|
env['VKBASALT_CONFIG_FILE'] = launcherShadersFile;
|
||||||
|
|
||||||
|
if (shaders !== 'custom')
|
||||||
|
{
|
||||||
|
const userShadersFile = `${constants.paths.shadersDir}/${shaders}/vkBasalt.conf`;
|
||||||
|
|
||||||
|
await Neutralino.filesystem.writeFile(launcherShadersFile, await Neutralino.filesystem.readFile(userShadersFile));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GPU selection
|
||||||
|
*/
|
||||||
|
// TODO
|
||||||
|
/*if (LauncherLib.getConfig('gpu') != 'default')
|
||||||
|
{
|
||||||
|
const gpu = await SwitcherooControl.getGpuByName(LauncherLib.getConfig('gpu'));
|
||||||
|
|
||||||
|
if (gpu)
|
||||||
|
{
|
||||||
|
env = {
|
||||||
|
...env,
|
||||||
|
...SwitcherooControl.getEnvAsObject(gpu)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
else console.warn(`GPU ${LauncherLib.getConfig('gpu')} not found. Launching on the default GPU`);
|
||||||
|
}*/
|
||||||
|
|
||||||
|
let command = `"${path.addSlashes(wineExeutable)}" ${await Configs.get('fps_unlocker') ? 'unlockfps.bat' : 'launcher.bat'}`;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gamemode integration
|
||||||
|
*/
|
||||||
|
if (await Configs.get('gamemode') && await Package.exists('gamemoderun'))
|
||||||
|
command = `gamemoderun ${command}`;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starting the game
|
||||||
|
*/
|
||||||
|
const startTime = Date.now();
|
||||||
|
|
||||||
|
const process = await Process.run(command, {
|
||||||
|
env: {
|
||||||
|
WINEPREFIX: await constants.paths.prefix.current,
|
||||||
|
...env,
|
||||||
|
...((await Configs.get('env') as object|null) ?? {})
|
||||||
|
},
|
||||||
|
cwd: await constants.paths.gameDir
|
||||||
|
});
|
||||||
|
|
||||||
|
// Game was started by the launcher.bat file
|
||||||
|
// so we just need to wait until AnimeGame.e process
|
||||||
|
// will be closed
|
||||||
|
process.finish(() => {
|
||||||
|
const processName = `${constants.placeholders.uppercase.first + constants.placeholders.uppercase.second}.e`;
|
||||||
|
|
||||||
|
let closeGameCounter = 0;
|
||||||
|
|
||||||
|
const waiter = async () => {
|
||||||
|
const processes: string = (await Neutralino.os.execCommand('ps -A')).stdOut;
|
||||||
|
|
||||||
|
// Game is still running
|
||||||
|
if (processes.includes(processName))
|
||||||
|
{
|
||||||
|
const playtime = Math.round((Date.now() - startTime) / 1000);
|
||||||
|
|
||||||
|
let hours: string|number = Math.floor(playtime / 3600);
|
||||||
|
let minutes: string|number = Math.floor((playtime - hours * 3600) / 60);
|
||||||
|
let seconds: string|number = playtime - hours * 3600 - minutes * 60;
|
||||||
|
|
||||||
|
if (hours < 10)
|
||||||
|
hours = `0${hours}`;
|
||||||
|
|
||||||
|
if (minutes < 10)
|
||||||
|
minutes = `0${minutes}`;
|
||||||
|
|
||||||
|
if (seconds < 10)
|
||||||
|
seconds = `0${seconds}`;
|
||||||
|
|
||||||
|
// FIXME: tray doesn't work in AppImage
|
||||||
|
launcher.tray.update([
|
||||||
|
{ text: `Playing for ${hours}:${minutes}:${seconds}`, disabled: true },
|
||||||
|
{
|
||||||
|
text: `Close game${closeGameCounter > 0 ? ` (${closeGameCounter})` : ''}`,
|
||||||
|
|
||||||
|
click: () => Neutralino.os.execCommand(`kill ${++closeGameCounter < 3 ? '-15' : '-9'} $(pidof ${processName})`)
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
|
||||||
|
setTimeout(waiter, 3000);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Game was closed
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const stopTime = Date.now();
|
||||||
|
|
||||||
|
Windows.current.show();
|
||||||
|
// FIXME: Windows.current.center(1280, 700);
|
||||||
|
|
||||||
|
launcher.updateDiscordRPC('in-launcher');
|
||||||
|
launcher.tray.hide();
|
||||||
|
|
||||||
|
// Purge game logs
|
||||||
|
Configs.get('purge_logs.game').then(async (purge_logs) => {
|
||||||
|
if (purge_logs)
|
||||||
|
{
|
||||||
|
const gameDir = path.addSlashes(await constants.paths.gameDir);
|
||||||
|
|
||||||
|
// Delete .log files (e.g. "ZFGameBrowser_xxxx.log")
|
||||||
|
Neutralino.os.execCommand(`find "${gameDir}" -maxdepth 1 -type f -name "*.log" -delete`);
|
||||||
|
|
||||||
|
// Delete .dmp files (e.g. "DumpFile-zfbrowser-xxxxxx.dmp")
|
||||||
|
Neutralino.os.execCommand(`find "${gameDir}" -maxdepth 1 -type f -name "*.dmp" -delete`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
setTimeout(waiter, 5000);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
Notification.show({
|
||||||
|
...(Locales.translate('notifications.iputils_package_required') as { title: string, body: string }),
|
||||||
|
icon: `${constants.paths.appDir}/public/images/baal64-transparent.png`,
|
||||||
|
importance: 'critical'
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in a new issue