mirror of
https://github.com/an-anime-team/an-anime-game-launcher.git
synced 2025-03-07 16:47:58 +03:00
Added telemetry checking
- updated vite version - made `Game.isTelemetryDisabled()` method to check whether game's telemetry is disabled - made `Domain` class and `Domain.getInfo()` method to get information about some domain - made `Notifications` class to manage system notifications - added telemetry checking before game launching
This commit is contained in:
parent
10b910c2ae
commit
0c1a9e68ca
10 changed files with 254 additions and 104 deletions
|
@ -55,25 +55,26 @@ To work this launcher requires
|
|||
| curl | To download archives with game, voice data, runners and so on |
|
||||
| xdelta3 | To apply the patch to the game |
|
||||
| cabextract | To install fonts to the wine prefix |
|
||||
| notify-send | To send system notifications |
|
||||
|
||||
## Install
|
||||
|
||||
### apt-get
|
||||
|
||||
```sh
|
||||
sudo apt-get install webkit2gtk unzip tar git curl xdelta3 cabextract
|
||||
sudo apt-get install webkit2gtk unzip tar git curl xdelta3 cabextract notify-send
|
||||
```
|
||||
|
||||
### pacman
|
||||
|
||||
```sh
|
||||
sudo pacman -Syu webkit2gtk unzip tar git curl xdelta3 cabextract
|
||||
sudo pacman -Syu webkit2gtk unzip tar git curl xdelta3 cabextract notify-send
|
||||
```
|
||||
|
||||
### dnf
|
||||
|
||||
```sh
|
||||
sudo dnf install webkit2gtk unzip tar git curl xdelta cabextract
|
||||
sudo dnf install webkit2gtk unzip tar git curl xdelta cabextract notify-send
|
||||
```
|
||||
|
||||
# Additional requirements
|
||||
|
@ -162,8 +163,8 @@ This is our current roadmap goals. You can find older ones [here](ROADMAP.md)
|
|||
* PropertiesEditor
|
||||
* Rewrite sass code, provide more flexible theming ability
|
||||
* <s>Add `svelte-i18n`</s>
|
||||
* <s>Telemetry checking</s>
|
||||
* Game pre-installation
|
||||
* Telemetry checking
|
||||
* Launcher auto-updates
|
||||
* Statistics window
|
||||
* Debugger
|
||||
|
|
|
@ -25,6 +25,6 @@
|
|||
"svelte-preprocess": "^4.10.1",
|
||||
"tslib": "^2.3.1",
|
||||
"typescript": "^4.5.4",
|
||||
"vite": "^2.7.7"
|
||||
"vite": "^2.7.8"
|
||||
}
|
||||
}
|
||||
|
|
BIN
public/icons/baal64-transparent.png
Normal file
BIN
public/icons/baal64-transparent.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 24 KiB |
|
@ -1,7 +1,7 @@
|
|||
import Configs from './Configs';
|
||||
|
||||
declare const Neutralino;
|
||||
declare const NL_PATH;
|
||||
declare const NL_CWD;
|
||||
|
||||
class Prefix
|
||||
{
|
||||
|
@ -41,7 +41,7 @@ class Paths
|
|||
/**
|
||||
* Directory where the launcher's executable stored
|
||||
*/
|
||||
public static readonly appDir: string = NL_PATH;
|
||||
public static readonly appDir: string = NL_CWD;
|
||||
|
||||
/**
|
||||
* Shaders directory
|
||||
|
|
|
@ -8,6 +8,8 @@ import type {
|
|||
import constants from './Constants';
|
||||
import fetch from './core/Fetch';
|
||||
import AbstractInstaller from './core/AbstractInstaller';
|
||||
import Domain from './core/Domain';
|
||||
import promisify from './core/promisify';
|
||||
|
||||
declare const Neutralino;
|
||||
|
||||
|
@ -144,6 +146,32 @@ export default class Game
|
|||
.catch((error) => reject(error));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the telemetry servers are disabled
|
||||
*/
|
||||
public static isTelemetryDisabled(): Promise<boolean>
|
||||
{
|
||||
return new Promise(async (resolve) => {
|
||||
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
|
||||
});
|
||||
|
||||
pipeline.then((result) => {
|
||||
let disabled = false;
|
||||
|
||||
Object.values(result).forEach((value) => disabled ||= value as boolean);
|
||||
|
||||
resolve(disabled === false);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export { Stream };
|
||||
|
|
36
src/ts/core/Domain.ts
Normal file
36
src/ts/core/Domain.ts
Normal file
|
@ -0,0 +1,36 @@
|
|||
import type { DomainInfo } from '../types/Domain';
|
||||
|
||||
import Process from '../neutralino/Process';
|
||||
|
||||
export default class Domain
|
||||
{
|
||||
public static getInfo(uri: string): Promise<DomainInfo>
|
||||
{
|
||||
return new Promise(async (resolve) => {
|
||||
const process = await Process.run(`ping -n 1 -w 1 -B ${uri}`);
|
||||
|
||||
// If something will be wrong - at least we'll have
|
||||
// to wait 1.5 seconds instread of 2
|
||||
process.runningInterval = 500;
|
||||
process.outputInterval = 500;
|
||||
|
||||
let output = '';
|
||||
|
||||
process.output((outputPart) => {
|
||||
output += outputPart;
|
||||
|
||||
const regex = /PING (.*) \(([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})\) from ([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}) : [\d]+\([\d]+\) bytes of data/gm.exec(output);
|
||||
|
||||
if (regex !== null)
|
||||
{
|
||||
resolve({
|
||||
uri: regex[1],
|
||||
remoteIp: regex[2],
|
||||
localIp: regex[3],
|
||||
available: regex[2] !== regex[3]
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
32
src/ts/core/Notifications.ts
Normal file
32
src/ts/core/Notifications.ts
Normal file
|
@ -0,0 +1,32 @@
|
|||
import type { NotificationsOptions } from '../types/Notifications';
|
||||
|
||||
import Process from '../neutralino/Process';
|
||||
|
||||
declare const Neutralino;
|
||||
|
||||
export default class Notifications
|
||||
{
|
||||
/**
|
||||
* Show notification
|
||||
*/
|
||||
public static show(options: NotificationsOptions)
|
||||
{
|
||||
let command = `notify-send '${Process.addSlashes(options.title)}' '${Process.addSlashes(options.body)}'`;
|
||||
|
||||
// Specify notification icon
|
||||
if (options.icon)
|
||||
command += ` -i '${Process.addSlashes(options.icon)}'`;
|
||||
|
||||
// Specify notification duration
|
||||
if (options.duration)
|
||||
command += ` -d ${options.duration}`;
|
||||
|
||||
// Specify notification importance
|
||||
if (options.importance)
|
||||
command += ` -u ${options.importance}`;
|
||||
|
||||
Neutralino.os.execCommand(command, {
|
||||
background: true
|
||||
});
|
||||
}
|
||||
};
|
|
@ -1,124 +1,145 @@
|
|||
import Configs from '../../Configs';
|
||||
import constants from '../../Constants';
|
||||
import Notifications from '../../core/Notifications';
|
||||
import Runners from '../../core/Runners';
|
||||
import Game from '../../Game';
|
||||
import Process from '../../neutralino/Process';
|
||||
|
||||
declare const Neutralino;
|
||||
|
||||
export default (): Promise<void> => {
|
||||
return new Promise(async (resolve) => {
|
||||
/**
|
||||
* Selecting wine executable
|
||||
*/
|
||||
let wineExeutable = 'wine';
|
||||
const telemetry = await Game.isTelemetryDisabled();
|
||||
|
||||
const runner = await Runners.current();
|
||||
|
||||
if (runner !== null)
|
||||
// If telemetry servers are not disabled
|
||||
if (telemetry)
|
||||
{
|
||||
wineExeutable = `${await constants.paths.runnersDir}/${runner.name}/${runner.files.wine}`;
|
||||
const icon = `${constants.paths.appDir}/public/icons/baal64-transparent.png`;
|
||||
|
||||
try
|
||||
Notifications.show({
|
||||
title: 'An Anime Game Launcher',
|
||||
body: 'Telemetry servers are not disabled',
|
||||
icon: icon,
|
||||
importance: 'critical'
|
||||
});
|
||||
}
|
||||
|
||||
// Otherwise run the game
|
||||
else
|
||||
{
|
||||
/**
|
||||
* Selecting wine executable
|
||||
*/
|
||||
let wineExeutable = 'wine';
|
||||
|
||||
const runner = await Runners.current();
|
||||
|
||||
if (runner !== null)
|
||||
{
|
||||
Neutralino.filesystem.getStats(wineExeutable);
|
||||
wineExeutable = `${await constants.paths.runnersDir}/${runner.name}/${runner.files.wine}`;
|
||||
|
||||
try
|
||||
{
|
||||
Neutralino.filesystem.getStats(wineExeutable);
|
||||
}
|
||||
|
||||
catch
|
||||
{
|
||||
wineExeutable = 'wine';
|
||||
|
||||
await Configs.set('runner', null);
|
||||
}
|
||||
}
|
||||
|
||||
catch
|
||||
{
|
||||
wineExeutable = 'wine';
|
||||
console.log(`Wine executable: ${wineExeutable}`);
|
||||
|
||||
await Configs.set('runner', null);
|
||||
// Some special variables
|
||||
let env: any = {};
|
||||
|
||||
/**
|
||||
* HUD
|
||||
*/
|
||||
switch (await Configs.get('hud'))
|
||||
{
|
||||
case 'dxvk':
|
||||
env['DXVK_HUD'] = 'fps,frametimes';
|
||||
|
||||
break;
|
||||
|
||||
case 'mangohud':
|
||||
env['MANGOHUD'] = 1;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`Wine executable: ${wineExeutable}`);
|
||||
/**
|
||||
* Shaders
|
||||
*/
|
||||
const shaders = await Configs.get('shaders');
|
||||
|
||||
// Some special variables
|
||||
let env: any = {};
|
||||
|
||||
/**
|
||||
* HUD
|
||||
*/
|
||||
switch (await Configs.get('hud'))
|
||||
{
|
||||
case 'dxvk':
|
||||
env['DXVK_HUD'] = 'fps,frametimes';
|
||||
|
||||
break;
|
||||
|
||||
case 'mangohud':
|
||||
env['MANGOHUD'] = 1;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shaders
|
||||
*/
|
||||
const shaders = await Configs.get('shaders');
|
||||
|
||||
if (shaders !== null)
|
||||
{
|
||||
const userShadersFile = `${constants.paths.shadersDir}/${shaders}/vkBasalt.conf`;
|
||||
const launcherShadersFile = `${await constants.paths.launcherDir}/vkBasalt.conf`;
|
||||
|
||||
env['ENABLE_VKBASALT'] = 1;
|
||||
env['VKBASALT_CONFIG_FILE'] = launcherShadersFile;
|
||||
|
||||
await Neutralino.filesystem.writeFile(launcherShadersFile, await Neutralino.filesystem.readFile(userShadersFile));
|
||||
}
|
||||
|
||||
/**
|
||||
* GPU selection
|
||||
*/
|
||||
/*if (LauncherLib.getConfig('gpu') != 'default')
|
||||
{
|
||||
const gpu = await SwitcherooControl.getGpuByName(LauncherLib.getConfig('gpu'));
|
||||
|
||||
if (gpu)
|
||||
if (shaders !== null)
|
||||
{
|
||||
env = {
|
||||
const userShadersFile = `${constants.paths.shadersDir}/${shaders}/vkBasalt.conf`;
|
||||
const launcherShadersFile = `${await constants.paths.launcherDir}/vkBasalt.conf`;
|
||||
|
||||
env['ENABLE_VKBASALT'] = 1;
|
||||
env['VKBASALT_CONFIG_FILE'] = launcherShadersFile;
|
||||
|
||||
await Neutralino.filesystem.writeFile(launcherShadersFile, await Neutralino.filesystem.readFile(userShadersFile));
|
||||
}
|
||||
|
||||
/**
|
||||
* GPU selection
|
||||
*/
|
||||
/*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 = `${wineExeutable} ${LauncherLib.getConfig('fpsunlock') ? 'fpsunlock.bat' : 'launcher.bat'}`;
|
||||
|
||||
/**
|
||||
* Gamemode integration
|
||||
*/
|
||||
/*if (LauncherLib.getConfig('gamemode'))
|
||||
command = `gamemoderun ${command}`;*/
|
||||
|
||||
const command = `${wineExeutable} launcher.bat`;
|
||||
|
||||
console.log(`Execution command: ${command}`);
|
||||
|
||||
/**
|
||||
* Starting the game
|
||||
*/
|
||||
const startTime = Date.now();
|
||||
|
||||
const process = await Process.run(command, {
|
||||
env: {
|
||||
...env,
|
||||
...SwitcherooControl.getEnvAsObject(gpu)
|
||||
};
|
||||
}
|
||||
|
||||
else console.warn(`GPU ${LauncherLib.getConfig('gpu')} not found. Launching on the default GPU`);
|
||||
}*/
|
||||
|
||||
// let command = `${wineExeutable} ${LauncherLib.getConfig('fpsunlock') ? 'fpsunlock.bat' : 'launcher.bat'}`;
|
||||
WINEPREFIX: await constants.paths.prefix.current
|
||||
},
|
||||
cwd: await constants.paths.gameDir
|
||||
});
|
||||
|
||||
/**
|
||||
* Gamemode integration
|
||||
*/
|
||||
/*if (LauncherLib.getConfig('gamemode'))
|
||||
command = `gamemoderun ${command}`;*/
|
||||
// Game closed event
|
||||
process.finish(() => {
|
||||
const stopTime = Date.now();
|
||||
|
||||
const command = `${wineExeutable} launcher.bat`;
|
||||
// todo
|
||||
|
||||
console.log(`Execution command: ${command}`);
|
||||
|
||||
/**
|
||||
* Starting the game
|
||||
*/
|
||||
const startTime = Date.now();
|
||||
|
||||
const process = await Process.run(command, {
|
||||
env: {
|
||||
...env,
|
||||
|
||||
WINEPREFIX: await constants.paths.prefix.current
|
||||
},
|
||||
cwd: await constants.paths.gameDir
|
||||
});
|
||||
|
||||
// Game closed event
|
||||
process.finish(() => {
|
||||
const stopTime = Date.now();
|
||||
|
||||
// todo
|
||||
|
||||
resolve();
|
||||
});
|
||||
resolve();
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
|
8
src/ts/types/Domain.d.ts
vendored
Normal file
8
src/ts/types/Domain.d.ts
vendored
Normal file
|
@ -0,0 +1,8 @@
|
|||
type DomainInfo = {
|
||||
uri: string;
|
||||
remoteIp: string;
|
||||
localIp: string;
|
||||
available: boolean;
|
||||
};
|
||||
|
||||
export type { DomainInfo };
|
24
src/ts/types/Notifications.d.ts
vendored
Normal file
24
src/ts/types/Notifications.d.ts
vendored
Normal file
|
@ -0,0 +1,24 @@
|
|||
type NotificationsOptions = {
|
||||
title: string;
|
||||
body: string;
|
||||
|
||||
/**
|
||||
* Icon name or path
|
||||
*/
|
||||
icon?: string;
|
||||
|
||||
/**
|
||||
* Number of seconds this notification
|
||||
* will be visible
|
||||
*/
|
||||
duration?: number;
|
||||
|
||||
/**
|
||||
* Importance of the notification
|
||||
*
|
||||
* @default "normal"
|
||||
*/
|
||||
importance?: 'low' | 'normal' | 'critical';
|
||||
};
|
||||
|
||||
export type { NotificationsOptions };
|
Loading…
Add table
Reference in a new issue