2.2.0-beta1

- changed versioning politicy ([game version]-[app state][app build])
- created patches system
  now launcher automatically applies patches to the game
- changed launcher config structure
- changed executable file on game launching to the launcher.bat
  to check the telemetry status and prevent game starting in positive result
- updated readme
This commit is contained in:
Observer KRypt0n_ 2021-10-14 22:35:29 +02:00
parent 00979b5e71
commit 3cc9409354
No known key found for this signature in database
GPG key ID: DC5D4EC1303465DA
15 changed files with 484 additions and 22 deletions

4
.gitignore vendored
View file

@ -1,4 +1,6 @@
node_modules
dist
package-lock.json
wineprefix-installation.log
wineprefix-installation.log
public/css/index.css
public/js

View file

@ -1,6 +1,11 @@
<img src="logo.png">
### Launcher is still in development and is not working properly. Please, wait for future updates
# Status
### Launcher is in the beta state; patch is in the testing phase
| Game version | Launcher version | Patch version | Download link |
| :---: | :---: | :---: | :---: |
| 2.2.0 | 2.2.0-beta1 | 2.2.0-testing | [Download](https://notabug.org/nobody/an-anime-game-launcher/releases/2.2.0-beta1) |
<br>

View file

@ -1,6 +1,6 @@
{
"name": "an-anime-game-linux-launcher",
"version": "0.1.0",
"version": "2.2.0-beta1",
"description": "An Anime Game Linux Launcher",
"author": "Nikita Podvirnyy",
"license": "GPL-3.0",

5
public/css/hint.min.css vendored Normal file

File diff suppressed because one or more lines are too long

View file

@ -7,6 +7,7 @@
<!-- CSS styles -->
<link rel="stylesheet" href="../css/index.css">
<link rel="stylesheet" href="../css/hint.min.css">
<!-- JS scripts -->
<script>require('../js/index.js');</script>

4
public/patch/patch.json Normal file
View file

@ -0,0 +1,4 @@
{
"version": "2.2.0",
"state": "testing"
}

129
public/patch/patch.sh Normal file
View file

@ -0,0 +1,129 @@
#!/usr/bin/env bash
# MacOS and *BSD do not have md5sum: use md5 instead
if [[ $(uname) == "Darwin" || $(uname) == *"BSD" ]]; then
md5sum() {
md5 -q $@
}
fi
DIR=$(dirname "${BASH_SOURCE[0]}")
FILE="UnityPlayer.dll"
CEXE="GenshinImpact_Data/upload_crash.exe"
sum=($(md5sum $FILE))
if [ "${sum}" != "38746fe5dbdce04311c84b2394f03686" ]; then
# The patch might corrupt invalid/outdated files if this check is skippd.
echo "Wrong file version or patch is already applied"
echo "md5sum: ${sum}" && exit 1
fi
# =========== DO NOT REMOVE START ===========
if [[ -e "$DIR/$FILE" ]]; then
# There is a good reason for this check. Do not pollute the game directory.
echo "Please move all patch files outside the game directory prior executing."
echo " -> See README.md for proper installation instructions" && exit 1
fi
# =========== DO NOT REMOVE END ===========
if ! command -v xdelta3 &>/dev/null; then
echo "xdelta3 application is required"
echo " -> Debian/Ubuntu: apt install xdelta3"
echo " -> Fedora: dnf install xdelta"
echo " -> Arch/Arch-based: pacman -S xdelta3"
echo " -> macOS: \"port install xdelta\" or \"brew install xdelta\""
exit 1
fi
# ===========================================================
echo
echo "--- Setting up blocked servers"
# START OF SUDO DANGER ZONE
etc_hosts="$(cat /etc/hosts)"
# See dev_tools/network.md (up-to-date as of 2.1.0)
servers=$(cat <<EOF
# Genshin logging servers (do not remove!)
0.0.0.0 log-upload-os.mihoyo.com
0.0.0.0 overseauspider.yuanshen.com
EOF
)
if [[ ! "$etc_hosts" == *"$servers"* ]]; then
echo "[MANDATORY] Adding following logging servers to /etc/hosts"
echo " If you really really want to skip this (Ctrl+C),"
echo " PLEASE add the entries manually. Otherwise they will receive"
echo " logs about The Wine project, hence UNCOVERING THIS PATCH!"
echo "$servers" | sudo -k tee -a /etc/hosts
if [ $? -ne 0 ]; then
echo "$servers"
read -p "Please append these lines to your /etc/hosts file now. Enter to continue."
fi
else
echo "--- Logging servers are already blocked. Skip."
fi
servers=$(cat <<EOF
# Optional Unity proxy/cdn servers
0.0.0.0 prd-lender.cdp.internal.unity3d.com
0.0.0.0 thind-prd-knob.data.ie.unity3d.com
0.0.0.0 thind-gke-usc.prd.data.corp.unity3d.com
0.0.0.0 cdp.cloud.unity3d.com
0.0.0.0 remote-config-proxy-prd.uca.cloud.unity3d.com
EOF
)
if [[ ! "$etc_hosts" == *"$servers"* ]]; then
echo "-- Adding proxy/cdn servers"
echo "$servers" | sudo tee -a /etc/hosts
if [ $? -ne 0 ]; then
read -p "--- FAILED to add the servers. Enter to continue."
fi
else
echo "--- Unity proxy/cdn servers are already blocked. Skip."
fi
etc_hosts=""
# END OF SUDO DANGER ZONE
echo ""
# No crashes shall be reported!
echo "--- Renaming the crash reporter"
if [[ -e "$CEXE" ]]; then
# Replace existing backups
mv -f "$CEXE" "$CEXE.bak"
fi
# Registry entry to add on startup
cp -f "$DIR/patch_files/mhyprot2_running.reg" .
# Add launcher & force update to ensure the checks are performed
echo "--- Adding launcher script"
cp -f "$DIR/patch_files/launcher.bat" .
# Do the patch now, replace existing backups (hash confirmed)
echo "--- Patching UnityPlayer"
xdelta_fail() {
mv -vf "$FILE.bak" "$FILE"
exit 1
}
mv -f "$FILE" "$FILE.bak"
# Perform patch or restore .bak on failure
xdelta3 -d -s "$FILE.bak" "$DIR/patch_files/unityplayer_patch.vcdiff" "$FILE" || xdelta_fail
# Done!
echo "==> Patch applied! Enjoy the game."
echo
echo "[NOTICE] Please refrain from sharing this project in public so"
echo " that there can be Linux patches in the future. Thank you."
exit 0

View file

@ -0,0 +1,66 @@
#!/usr/bin/env bash
echo "[NOTE] This patch is not required as of 2021-10-13. However, it might become"
echo " necessary afterwards (Friday?). If that's the case, comment the line below."
exit 0
# MacOS and *BSD do not have md5sum: use md5 instead
if [[ $(uname) == "Darwin" || $(uname) == *"BSD" ]]; then
md5sum() {
md5 -q $@
}
fi
DIR=$(dirname "${BASH_SOURCE[0]}")
FILE="GenshinImpact_Data/Plugins/xlua.dll"
sum=($(md5sum $FILE))
if [ "${sum}" != "526b36c2b8a070db61428b7fe69906a3" ]; then
# The patch might corrupt invalid/outdated files if this check is skippd.
echo "Wrong file version or patch is already applied"
echo "md5sum: ${sum}" && exit 1
fi
# =========== DO NOT REMOVE START ===========
if [[ -e "$DIR/$FILE" ]]; then
# There is a good reason for this check. Do not pollute the game directory.
echo "Please move all patch files outside the game directory prior executing."
echo " -> See README.md for proper installation instructions" && exit 1
fi
# =========== DO NOT REMOVE END ===========
if ! command -v xdelta3 &>/dev/null; then
echo "xdelta3 application is required"
exit 1
fi
echo "[INFO] Patch to fix a login and runtime crash"
echo ""
# ===========================================================
echo "[WARNING] Hereby you are violating the game's Terms of Service!"
echo " Do you accept the risk and possible consequences?"
read -p "Accept? [y/n] " choice
if [[ ! "$choice" == [JjSsYy]* ]]; then
exit 1
fi
echo
echo "--- Applying xLua patch"
xdelta_fail() {
mv -vf "$FILE.bak" "$FILE"
exit 1
}
mv -f "$FILE" "$FILE.bak"
# Perform patch or restore .bak on failure
xdelta3 -d -s "$FILE.bak" "$DIR/patch_files/xlua_patch.vcdiff" "$FILE" || xdelta_fail
# Done!
echo "==> Patch applied! Enjoy the game."
exit 0

View file

@ -0,0 +1,36 @@
@echo off
REM Notice: This file is overwritten for each patch for safety reasons.
REM Hence, any manual changes will be overwritten by the next patch.
REM ============ AVOID CHANGES HERE ============
SET perr=0
REM Verify that the important hosts are blocked
ping -n 1 -w 1 log-upload-os.mihoyo.com | find "[0.0.0.0]" >nul
IF %ERRORLEVEL% NEQ 0 SET perr=1
ping -n 1 -w 1 overseauspider.yuanshen.com | find "[0.0.0.0]" >nul
IF %ERRORLEVEL% NEQ 0 SET perr=1
IF %perr% NEQ 0 (
REM Show the message to the user
echo ERROR: Crucial domains are not blocked. Please re-run the patch script. >_error.txt
notepad _error.txt
del _error.txt
exit
)
REM Emulate the games behaviour
copy mhyprot2.sys "%TEMP%\"
regedit mhyprot2_running.reg
REM Disable crash reporting
IF EXIST GenshinImpact_Data\upload_crash.exe (
move "GenshinImpact_Data\upload_crash.exe" "GenshinImpact_Data\upload_crash.exe.bak"
)
REM ============= Launch the game =============
REM https://docs.unity3d.com/Manual/CommandLineArguments.html
REM Append the arguments to the command: launcher.bat arg1 arg2 ...
start GenshinImpact.exe %*

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -26,6 +26,15 @@ body
background-color: rgba(0, 0, 0, .35)
color: rgba(255, 255, 255, .7)
cursor: default
.button-blue
background-color: #8193c5
color: white
.button-blue:hover:not([disabled])
background-color: #7284b6
#launch
width: 238px
height: 64px

View file

@ -9,11 +9,19 @@ type Config = {
launcher: 'en-us' | 'ru-ru',
voice: 'en-us' | 'ru-ru'
},
version: string|null
version: string|null,
patch: {
version: string|null,
state: 'testing' | 'stable'
}
};
export class Genshinlib
{
public static readonly patchDir: string = path.join(path.dirname(__dirname), 'patch');
public static readonly patchJson: string = path.join(this.patchDir, 'patch.json');
public static readonly patchSh = path.join(this.patchDir, 'patch.sh');
public static readonly launcherDir: string = path.join(os.homedir(), 'genshin-impact-launcher');
public static readonly launcherJson: string = path.join(this.launcherDir, 'launcher.json');
@ -22,17 +30,17 @@ export class Genshinlib
protected static uri: string = 'https://sdk-os-static.mihoyo.com/hk4e_global/mdk/launcher/api/resource?key=gcStgarh&launcher_id=10';
public static get version(): string|null
public static get version(): Config['version']
{
return this.getLauncherInfo().version;
return this.getConfig().version;
}
public static get lang(): { launcher: string, voice: string }
public static get lang(): Config['lang']
{
return this.getLauncherInfo().lang;
return this.getConfig().lang;
}
public static getLauncherInfo (): Config
public static getConfig (): Config
{
if (!fs.existsSync(this.launcherJson))
fs.writeFileSync(this.launcherJson, JSON.stringify({
@ -40,15 +48,16 @@ export class Genshinlib
launcher: 'en-us',
voice: 'en-us'
},
version: null
}));
version: null,
patch: null
}, null, 4));
return JSON.parse(fs.readFileSync(this.launcherJson));
}
public static setLauncherInfo (info: Config): Genshinlib
public static setConfig (info: Config): Genshinlib
{
fs.writeFileSync(this.launcherJson, JSON.stringify(info));
fs.writeFileSync(this.launcherJson, JSON.stringify(info, null, 4));
return this;
}
@ -78,6 +87,11 @@ export class Genshinlib
return path.join(__dirname, '..', 'images', 'backgrounds', this.lang.launcher + '.png');
}
public static getPatchInfo (): { version: string, state: 'stable' | 'testing' }
{
return JSON.parse(fs.readFileSync(this.patchJson));
}
public static async downloadFile (uri: string, savePath: string, progress: (current: number, total: number, difference: number) => void): Promise<void|Error>
{
return new Promise((resolve, reject) => {

View file

@ -1,6 +1,6 @@
const path = require('path');
const fs = require('fs');
const { exec } = require('child_process');
const { exec, spawn } = require('child_process');
const { ipcRenderer } = require('electron');
import $ from 'cash-dom';
@ -16,14 +16,111 @@ $(() => {
$('body').css('background-image', `url(${ Genshinlib.getBackgroundUri() })`);
// TODO: create LauncherUI class and move a lot of code there
// because it becomes a fucking unfunny joke
Genshinlib.getData().then(data => {
// Update available
if (Genshinlib.version != data.game.latest.version)
$('#launch').text(Genshinlib.version === null ? 'Install' : 'Update');
// Patch version is incorrect
else if (Genshinlib.getConfig().patch.version != Genshinlib.getPatchInfo().version)
{
// Patch is not available
if (Genshinlib.getPatchInfo().version !== data.game.latest.version)
{
$('#launch').attr('disabled', 'disabled');
$('#launch').text('Patch required');
$('#launch').addClass('hint--top');
$('#launch').addClass('hint--medium');
$('#launch').attr('data-hint', 'This game version doesn\'t have the anti-cheat patch. Please, wait a few days before it will be created');
}
// Patch available
else if (Genshinlib.getPatchInfo().version === data.game.latest.version)
{
// Patch is stable
if (Genshinlib.getPatchInfo().state == 'stable')
{
console.log(`%c> Applying patch...`, 'font-size: 16px');
$('#launch').attr('disabled', 'disabled');
$('#launch').text('Applying patch...');
let patcherProcess = spawn('bash', [Genshinlib.patchSh], {
cwd: Genshinlib.gameDir,
env: {
...process.env,
WINEPREFIX: Genshinlib.prefixDir
}
});
patcherProcess.stdout.on('data', (data: string) => console.log(data.toString()));
patcherProcess.on('close', () => {
Genshinlib.setConfig({
...Genshinlib.getConfig(),
patch: Genshinlib.getPatchInfo()
});
$('#launch').removeAttr('disabled');
$('#launch').text('Launch');
});
}
// Patch is in testing phase
else
{
$('#launch').text('Apply test patch');
$('#launch').addClass('button-blue');
$('#launch').addClass('hint--top');
$('#launch').addClass('hint--large');
$('#launch').attr('data-hint', 'This game version has the anti-cheat patch, but it is in the test phase. You can wait a few days until it will become stable or apply it on your own risc');
}
}
}
// Current patch is in testing phase,
// but stable is available
else if (Genshinlib.getConfig().patch.version == Genshinlib.getPatchInfo().version && Genshinlib.getConfig().patch.state == 'testing' && Genshinlib.getPatchInfo().state == 'stable')
{
console.log(`%c> Applying patch...`, 'font-size: 16px');
$('#launch').attr('disabled', 'disabled');
$('#launch').text('Applying patch...');
let patcherProcess = spawn('bash', [Genshinlib.patchSh], {
cwd: Genshinlib.gameDir,
env: {
...process.env,
WINEPREFIX: Genshinlib.prefixDir
}
});
patcherProcess.stdout.on('data', (data: string) => console.log(data.toString()));
patcherProcess.on('close', () => {
Genshinlib.setConfig({
...Genshinlib.getConfig(),
patch: Genshinlib.getPatchInfo()
});
$('#launch').removeAttr('disabled');
$('#launch').text('Launch');
});
}
$('#launch').on('click', async () => {
// Creating wine prefix
if (!Genshinlib.isPrefixInstalled(Genshinlib.prefixDir))
{
console.log(`%c> Creating wineprefix...`, 'font-size: 16px');
$('#launch').css('display', 'none');
$('#downloader-panel').css('display', 'block');
@ -45,21 +142,67 @@ $(() => {
// Launching game
if ($('#launch').text() == 'Launch')
{
exec(`wine "${path.join(Genshinlib.gameDir, 'GenshinImpact.exe')}"`, {
console.log(`%c> Starting the game...`, 'font-size: 16px');
exec('wine launcher.bat', {
cwd: Genshinlib.gameDir,
env: {
...process.env,
WINEPREFIX: Genshinlib.prefixDir
}
}, () => {
}, (err: any, stdout: any, stderr: any) => {
console.log(`%c> Game closed`, 'font-size: 16px');
ipcRenderer.invoke('show-window');
console.log(err);
console.log(stdout);
console.log(stderr);
});
ipcRenderer.invoke('hide-window');
}
// Apply test patch
else if ($('#launch').text() == 'Apply test patch')
{
console.log(`%c> Applying patch...`, 'font-size: 16px');
$('#launch').attr('disabled', 'disabled');
$('#launch').text('Applying patch...');
let patcherProcess = spawn('bash', [Genshinlib.patchSh], {
cwd: Genshinlib.gameDir,
env: {
...process.env,
WINEPREFIX: Genshinlib.prefixDir
}
});
patcherProcess.stdout.on('data', (data: string) => console.log(data.toString()));
patcherProcess.on('close', () => {
Genshinlib.setConfig({
...Genshinlib.getConfig(),
patch: Genshinlib.getPatchInfo()
});
$('#launch').removeClass('button-blue');
$('#launch').removeClass('hint--top');
$('#launch').removeClass('hint--large');
$('#launch').removeAttr('disabled');
$('#launch').removeAttr('data-hint');
$('#launch').text('Launch');
});
}
// Installing game
else
{
console.log(`%c> Downloading game data...`, 'font-size: 16px');
$('#launch').css('display', 'none');
$('#downloader-panel').css('display', 'block');
@ -120,6 +263,8 @@ $(() => {
* Unpacking downloaded game
*/
console.log(`%c> Unpacking game data...`, 'font-size: 16px');
$('#speed').text('');
$('#eta').text('');
@ -158,6 +303,8 @@ $(() => {
unpacked = 0;
}
}).then(() => {
console.log(`%c> Downloading voice data...`, 'font-size: 16px');
fs.unlinkSync(path.join(Genshinlib.launcherDir, diff.name));
let voicePack = diff.voice_packs[1]; // en-us
@ -210,6 +357,8 @@ $(() => {
* Unpacking downloaded game
*/
console.log(`%c> Unpacking voice data...`, 'font-size: 16px');
$('#speed').text('');
$('#eta').text('');
@ -247,15 +396,57 @@ $(() => {
}).then(() => {
fs.unlinkSync(path.join(Genshinlib.launcherDir, voicePack.name));
Genshinlib.setLauncherInfo({
...Genshinlib.getLauncherInfo(),
Genshinlib.setConfig({
...Genshinlib.getConfig(),
version: data.game.latest.version
});
$('#launch').css('display', 'block');
$('#downloader-panel').css('display', 'none');
$('#launch').text('Launch');
// Patch available
if (Genshinlib.getPatchInfo().version === data.game.latest.version)
{
// TODO: check the patch state
console.log(`%c> Applying patch...`, 'font-size: 16px');
$('#downloaded').text('Applying patch...');
let patcherProcess = spawn('bash', [Genshinlib.patchSh], {
cwd: Genshinlib.gameDir,
env: {
...process.env,
WINEPREFIX: Genshinlib.prefixDir
}
});
patcherProcess.stdout.on('data', (data: string) => console.log(data.toString()));
patcherProcess.on('close', () => {
Genshinlib.setConfig({
...Genshinlib.getConfig(),
patch: Genshinlib.getPatchInfo()
});
$('#launch').css('display', 'block');
$('#downloader-panel').css('display', 'none');
$('#launch').text('Launch');
});
}
// Patch is not available
else
{
$('#launch').css('display', 'block');
$('#downloader-panel').css('display', 'none');
$('#launch').attr('disabled', 'disabled');
$('#launch').text('Patch required');
$('#launch').addClass('hint--top');
$('#launch').addClass('hint--medium');
$('#launch').attr('data-hint', 'This game version doesn\'t have the anti-cheat patch. Please, wait a few days before it will be created');
}
});
}).catch(err => console.log(err));
}).catch(err => console.log(err));