2.0.0 beta 4
- made preparations to make discord rpc settings - fixed game updating mechanism - fixed `Patch.getPatchInfo()` method work - added proper design for patch-related launcher states - fixed progress bar displaying issues during pre-downloaded game unpacking
BIN
public/icons/discord/artgame.jpg
Normal file
After Width: | Height: | Size: 519 KiB |
BIN
public/icons/discord/artgame2.jpg
Normal file
After Width: | Height: | Size: 468 KiB |
BIN
public/icons/discord/artgame3.jpg
Normal file
After Width: | Height: | Size: 511 KiB |
BIN
public/icons/discord/beidougame.jpg
Normal file
After Width: | Height: | Size: 121 KiB |
BIN
public/icons/discord/game.png
Normal file
After Width: | Height: | Size: 41 KiB |
BIN
public/icons/discord/gi-icon.png
Normal file
After Width: | Height: | Size: 627 KiB |
BIN
public/icons/discord/kleegame.png
Normal file
After Width: | Height: | Size: 983 KiB |
BIN
public/icons/discord/kleegame2.jpg
Normal file
After Width: | Height: | Size: 510 KiB |
BIN
public/icons/discord/liyuegame.jpg
Normal file
After Width: | Height: | Size: 179 KiB |
|
@ -44,8 +44,15 @@ settings:
|
|||
light: Hell
|
||||
dark: Dunkel
|
||||
|
||||
discord: Discord RPC
|
||||
dxvks: DXVK
|
||||
# Discord RPC
|
||||
discord:
|
||||
title: Discord RPC
|
||||
settings:
|
||||
title: Discord RPC settings
|
||||
items:
|
||||
timer: Display spent time
|
||||
in-launcher: In-launcher text
|
||||
in-game: In-game text
|
||||
|
||||
# Verberssungen
|
||||
enhancements:
|
||||
|
|
|
@ -44,8 +44,15 @@ settings:
|
|||
light: Light
|
||||
dark: Dark
|
||||
|
||||
discord: Discord RPC
|
||||
dxvks: DXVK
|
||||
# Discord RPC
|
||||
discord:
|
||||
title: Discord RPC
|
||||
settings:
|
||||
title: Discord RPC settings
|
||||
items:
|
||||
timer: Display spent time
|
||||
in-launcher: In-launcher text
|
||||
in-game: In-game text
|
||||
|
||||
# Enhancements
|
||||
enhancements:
|
||||
|
|
|
@ -44,8 +44,15 @@ settings:
|
|||
light: Светлая
|
||||
dark: Тёмная
|
||||
|
||||
discord: Discord RPC
|
||||
dxvks: DXVK
|
||||
# Discord RPC
|
||||
discord:
|
||||
title: Discord RPC
|
||||
settings:
|
||||
title: Настройки Discord RPC
|
||||
items:
|
||||
timer: Отображать потраченное время
|
||||
in-launcher: Текст в лаунчере
|
||||
in-game: Текст в игре
|
||||
|
||||
# Улучшения
|
||||
enhancements:
|
||||
|
|
35
src/components/DiscordSettings.svelte
Normal file
|
@ -0,0 +1,35 @@
|
|||
<script lang="ts">
|
||||
import { _ } from 'svelte-i18n';
|
||||
|
||||
export let visible: boolean = false;
|
||||
export let valuesChanged: (values: { game: string, launcher: string }) => void = () => {};
|
||||
|
||||
import Checkbox from './Checkbox.svelte';
|
||||
</script>
|
||||
|
||||
<div style="display: {visible ? 'block' : 'none'}">
|
||||
<h3>{$_('settings.general.items.discord.settings.title')}</h3>
|
||||
|
||||
<Checkbox lang="settings.general.items.discord.settings.items.timer" prop="discord.fields.timer" />
|
||||
|
||||
<table class="table" style="margin-top: 16px">
|
||||
<tr>
|
||||
<td>
|
||||
<span>{$_('settings.general.items.discord.settings.items.in-launcher')}</span>
|
||||
</td>
|
||||
|
||||
<td>
|
||||
<input value="Preparing to launch" on:input={(value) => console.log(value)}>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<span>{$_('settings.general.items.discord.settings.items.in-game')}</span>
|
||||
</td>
|
||||
|
||||
<td>
|
||||
<input value="Playing the game">
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
|
@ -81,24 +81,19 @@ promisify(async () => {
|
|||
* RPC settings
|
||||
*/
|
||||
fields: {
|
||||
/**
|
||||
* Launcher title
|
||||
*/
|
||||
title: 'An Anime Game Launcher',
|
||||
|
||||
/**
|
||||
* Small messages after title
|
||||
*/
|
||||
state: {
|
||||
/**
|
||||
* Message showed when you're in game
|
||||
*/
|
||||
'in-launcher': 'Playing the game',
|
||||
|
||||
states: {
|
||||
/**
|
||||
* Message showed when you're in launcher
|
||||
*/
|
||||
'in-game': 'Preparing to launch'
|
||||
'in-launcher': 'Preparing to launch',
|
||||
|
||||
/**
|
||||
* Message showed when you're in game
|
||||
*/
|
||||
'in-game': 'Playing the game'
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
@import "components/selectionBox"
|
||||
@import "components/dropdownCheckboxes"
|
||||
@import "components/selectionList"
|
||||
@import "components/table"
|
||||
|
||||
@mixin themable($theme-name, $theme-map)
|
||||
body[data-theme=#{$theme-name}]
|
||||
|
|
65
src/sass/components/table.sass
Normal file
|
@ -0,0 +1,65 @@
|
|||
@use "sass:map"
|
||||
|
||||
@mixin themable($theme-name, $theme-map)
|
||||
body[data-theme=#{$theme-name}]
|
||||
table.table
|
||||
width: calc(100% - 24px)
|
||||
|
||||
margin-left: 12px
|
||||
|
||||
border-spacing: 0
|
||||
|
||||
$cell-height: 40px
|
||||
$padding-v: 4px
|
||||
$padding-h: 8px
|
||||
|
||||
tr
|
||||
height: $cell-height
|
||||
|
||||
cursor: pointer
|
||||
|
||||
&:hover
|
||||
background-color: map.get($theme-map, "background1")
|
||||
|
||||
td input
|
||||
background-color: map.get($theme-map, "background1")
|
||||
|
||||
td:nth-of-type(1),
|
||||
th:nth-of-type(1)
|
||||
width: 40%
|
||||
|
||||
th,
|
||||
td
|
||||
border-bottom: 1px solid map.get($theme-map, "background1")
|
||||
|
||||
&:not(:last-child)
|
||||
border-right: 1px solid map.get($theme-map, "background1")
|
||||
|
||||
td
|
||||
span
|
||||
display: block
|
||||
|
||||
width: calc(100% - 2 * $padding-h)
|
||||
height: calc($cell-height - 2 * $padding-h)
|
||||
|
||||
margin: auto
|
||||
|
||||
input
|
||||
display: block
|
||||
|
||||
width: calc(100% - 2 * $padding-h)
|
||||
height: calc($cell-height - 2 * $padding-h)
|
||||
|
||||
color: map.get($theme-map, "text")
|
||||
|
||||
font-size: 15px
|
||||
|
||||
margin: auto
|
||||
border: unset
|
||||
outline: none
|
||||
|
||||
@import "../themes/light"
|
||||
@import "../themes/dark"
|
||||
|
||||
@include themable(light, $light)
|
||||
@include themable(dark, $dark)
|
|
@ -15,6 +15,7 @@
|
|||
import Checkbox from './components/Checkbox.svelte';
|
||||
import SelectionBox from './components/SelectionBox.svelte';
|
||||
import DropdownCheckboxes from './components/DropdownCheckboxes.svelte';
|
||||
import DiscordSettings from './components/DiscordSettings.svelte';
|
||||
import DXVKSelectionList from './components/DXVKSelectionList.svelte';
|
||||
import RunnerSelectionList from './components/RunnerSelectionList.svelte';
|
||||
import ShadersSelection from './components/ShadersSelection.svelte';
|
||||
|
@ -110,9 +111,12 @@
|
|||
|
||||
let dxvkRecommendable = true,
|
||||
runnersRecommendable = true,
|
||||
discordRpcSettings = false,
|
||||
fpsUnlockerAvailable = true,
|
||||
voiceUpdateRequired = false;
|
||||
|
||||
Configs.get('discord.enabled').then((enabled) => discordRpcSettings = enabled as boolean);
|
||||
|
||||
// Auto theme switcher
|
||||
Configs.get('theme').then((theme) => switchTheme(theme as string));
|
||||
|
||||
|
@ -169,7 +173,13 @@
|
|||
valueChanged={switchTheme}
|
||||
/>
|
||||
|
||||
<Checkbox lang="settings.general.items.discord" prop="discord.enabled" />
|
||||
<Checkbox
|
||||
lang="settings.general.items.discord.title"
|
||||
prop="discord.enabled"
|
||||
valueChanged={(value) => discordRpcSettings = value}
|
||||
/>
|
||||
|
||||
<DiscordSettings visible={discordRpcSettings} />
|
||||
</div>
|
||||
|
||||
<div class="settings-item" id="enhancements">
|
||||
|
|
|
@ -34,12 +34,12 @@ export default class Game
|
|||
public static get current(): Promise<string|null>
|
||||
{
|
||||
return new Promise(async (resolve) => {
|
||||
const persistentPath = `${await constants.paths.gameDataDir}/Persistent/ScriptVersion`;
|
||||
// const persistentPath = `${await constants.paths.gameDataDir}/Persistent/ScriptVersion`;
|
||||
const globalGameManagersPath = `${await constants.paths.gameDataDir}/globalgamemanagers`;
|
||||
|
||||
Neutralino.filesystem.readFile(persistentPath)
|
||||
/*Neutralino.filesystem.readFile(persistentPath)
|
||||
.then((version) => resolve(version))
|
||||
.catch(() => {
|
||||
.catch(() => {*/
|
||||
Neutralino.filesystem.readBinaryFile(globalGameManagersPath)
|
||||
.then((config: ArrayBuffer) => {
|
||||
const buffer = new TextDecoder('ascii').decode(new Uint8Array(config));
|
||||
|
@ -53,7 +53,7 @@ export default class Game
|
|||
resolve(version !== null ? version[1] : null);
|
||||
})
|
||||
.catch(() => resolve(null));
|
||||
});
|
||||
// });
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -105,10 +105,7 @@ export default class Launcher
|
|||
id: '901534333360304168',
|
||||
|
||||
// @ts-expect-error
|
||||
details: discord.fields.title,
|
||||
|
||||
// @ts-expect-error
|
||||
state: discord.fields.state[state],
|
||||
details: discord.fields.states[state],
|
||||
|
||||
icon: {
|
||||
// @ts-expect-error
|
||||
|
@ -117,7 +114,7 @@ export default class Launcher
|
|||
|
||||
time: {
|
||||
// @ts-expect-error
|
||||
start: discord.fields.time ? Math.round(Date.now() / 1000) : 0
|
||||
start: discord.fields.timer ? Math.round(Date.now() / 1000) : 0
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -192,21 +192,20 @@ export default class Patch
|
|||
return new Promise(async (resolve, reject) => {
|
||||
const patchUri = constants.uri.patch[source];
|
||||
|
||||
fetch(`${patchUri}/raw/master/${version.replaceAll('.', '')}/patch.sh`, this.fetchTimeout)
|
||||
.then((patcherResponse) => {
|
||||
fetch(`${patchUri}/raw/master/${version.replaceAll('.', '')}/README.txt`, this.fetchTimeout)
|
||||
.then((readmeResponse) => {
|
||||
// Return an error if patch's server is unavailable
|
||||
if (patcherResponse.status === null)
|
||||
if (readmeResponse.status === null)
|
||||
reject(new Error(`${source} patch repository is unreachable`));
|
||||
|
||||
// If [version]/patch.sh file doesn't exist - it means
|
||||
// If [version]/README.txt file doesn't exist - it means
|
||||
// that patch repo has no [version]
|
||||
else if (patcherResponse.status === 404)
|
||||
else if (readmeResponse.status === 404)
|
||||
resolve(null);
|
||||
|
||||
// Otherwise it should be [preparation], [testing] or [stable]
|
||||
else
|
||||
{
|
||||
|
||||
fetch(`${patchUri}/raw/master/${version.replaceAll('.', '')}/patch_files/unityplayer_patch.vcdiff`, this.fetchTimeout)
|
||||
.then((response) => {
|
||||
// Return an error if patch's server is unavailable
|
||||
|
@ -228,46 +227,53 @@ export default class Patch
|
|||
// Otherwise it's [testing] or [stable]
|
||||
else
|
||||
{
|
||||
patcherResponse.body(this.fetchTimeout)
|
||||
.then((response) => {
|
||||
fetch(`${patchUri}/raw/master/${version.replaceAll('.', '')}/patch.sh`, this.fetchTimeout)
|
||||
.then((patcherResponse) => {
|
||||
// Return an error if patch's server is unavailable
|
||||
if (response === '')
|
||||
if (patcherResponse.status === null)
|
||||
reject(new Error(`${source} patch repository is unreachable`));
|
||||
|
||||
// Otherwise - let's prepare [testing] or [stable] output
|
||||
else
|
||||
{
|
||||
// If this line is commented - then it's [stable] version
|
||||
// Otherwise it's [testing]
|
||||
const stableMark = '#echo "If you would like to test this patch, modify this script and remove the line below this one."';
|
||||
else patcherResponse.body(this.fetchTimeout)
|
||||
.then((response) => {
|
||||
// Return an error if patch's server is unavailable
|
||||
if (response === '')
|
||||
reject(new Error(`${source} patch repository is unreachable`));
|
||||
|
||||
let patchInfo: PatchInfo = {
|
||||
version: version,
|
||||
state: response.includes(stableMark) ? 'stable' : 'testing',
|
||||
applied: false,
|
||||
source: source
|
||||
};
|
||||
// Otherwise - let's prepare [testing] or [stable] output
|
||||
else
|
||||
{
|
||||
// If this line is commented - then it's [stable] version
|
||||
// Otherwise it's [testing]
|
||||
const stableMark = '#echo "If you would like to test this patch, modify this script and remove the line below this one."';
|
||||
|
||||
const originalPlayer = /if \[ "\${sum}" != "([a-z0-9]{32})" \]; then/mg.exec(response);
|
||||
let patchInfo: PatchInfo = {
|
||||
version: version,
|
||||
state: response.includes(stableMark) ? 'stable' : 'testing',
|
||||
applied: false,
|
||||
source: source
|
||||
};
|
||||
|
||||
// If we could get original UnityPlayer.dll hash - then we can
|
||||
// compare it with actual UnityPlayer.dll hash and say whether the patch
|
||||
// was applied or not
|
||||
if (originalPlayer !== null)
|
||||
{
|
||||
constants.paths.gameDir.then((gameDir) => {
|
||||
Neutralino.filesystem.readBinaryFile(`${gameDir}/UnityPlayer.dll`)
|
||||
.then((currPlayer: ArrayBuffer) => {
|
||||
patchInfo.applied = md5(currPlayer) != originalPlayer[1];
|
||||
const originalPlayer = /if \[ "\${sum}" != "([a-z0-9]{32})" \]; then/mg.exec(response);
|
||||
|
||||
resolve(patchInfo);
|
||||
})
|
||||
.catch(() => resolve(patchInfo));
|
||||
});
|
||||
}
|
||||
// If we could get original UnityPlayer.dll hash - then we can
|
||||
// compare it with actual UnityPlayer.dll hash and say whether the patch
|
||||
// was applied or not
|
||||
if (originalPlayer !== null)
|
||||
{
|
||||
constants.paths.gameDir.then((gameDir) => {
|
||||
Neutralino.filesystem.readBinaryFile(`${gameDir}/UnityPlayer.dll`)
|
||||
.then((currPlayer: ArrayBuffer) => {
|
||||
patchInfo.applied = md5(currPlayer) != originalPlayer[1];
|
||||
|
||||
else resolve(patchInfo);
|
||||
}
|
||||
resolve(patchInfo);
|
||||
})
|
||||
.catch(() => resolve(patchInfo));
|
||||
});
|
||||
}
|
||||
|
||||
else resolve(patchInfo);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
|
|
@ -152,14 +152,20 @@ export default class State
|
|||
case 'test-patch-available':
|
||||
this.launchButton.textContent = 'Apply test patch';
|
||||
|
||||
this.launchButton.classList.add('button-blue');
|
||||
|
||||
this.launchButton.setAttribute('aria-label', 'This game version has an anti-cheat patch, but it is in the testing phase. You can wait a few days until it is stable or apply it at your own risk');
|
||||
|
||||
break;
|
||||
|
||||
case 'patch-unavailable':
|
||||
// TODO: some warning message
|
||||
this.launchButton.textContent = 'Patch unavailable';
|
||||
|
||||
this.launchButton.classList.add('button-blue');
|
||||
this.launchButton.setAttribute('disabled', '');
|
||||
|
||||
this.launchButton.setAttribute('aria-label', 'This game version has no anti-cheat patch. Please, wait a few days before there will be a test or stable version');
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -293,13 +299,19 @@ export default class State
|
|||
{
|
||||
const patch = await Patch.latest;
|
||||
|
||||
if (!patch.applied)
|
||||
// If the latest game version is, for example, 2.3.0
|
||||
// and the patch is 2.4.0 preparation, it means that
|
||||
// 2.4.0 will be released soon, but since it's still not released
|
||||
// we shouldn't show something about it to user and just let him play the game
|
||||
if (gameLatest.game.latest.version === patch.version && !patch.applied)
|
||||
{
|
||||
state = patch.state == 'preparation' ?
|
||||
'patch-unavailable' : (patch.state == 'testing' ?
|
||||
'test-patch-available' : 'patch-available');
|
||||
}
|
||||
|
||||
// Patch is more important than game pre-downloading
|
||||
// because otherwise we will not be able to play the game
|
||||
else if (gameLatest.pre_download_game && !await Game.isUpdatePredownloaded())
|
||||
state = 'game-pre-installation-available';
|
||||
|
||||
|
|
|
@ -45,6 +45,8 @@ export default (launcher: Launcher): Promise<void> => {
|
|||
showPercents: true,
|
||||
showTotals: true
|
||||
});
|
||||
|
||||
launcher.progressBar?.show();
|
||||
});
|
||||
|
||||
stream?.unpackProgress((current: number, total: number, difference: number) => {
|
||||
|
|
|
@ -71,6 +71,8 @@ export default (launcher: Launcher): Promise<void> => {
|
|||
showPercents: true,
|
||||
showTotals: true
|
||||
});
|
||||
|
||||
launcher.progressBar?.show();
|
||||
});
|
||||
|
||||
stream?.unpackProgress((current: number, total: number, difference: number) => {
|
||||
|
|