mirror of
https://github.com/open-goal/launcher.git
synced 2024-10-20 04:57:38 -04:00
config: fix issue related to loading the settings on initial launch (#109)
This commit is contained in:
parent
00f6218015
commit
2cee538376
|
@ -30,7 +30,6 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@tauri-apps/api": "^1.0.2",
|
"@tauri-apps/api": "^1.0.2",
|
||||||
"svelte-navigator": "^3.1.6",
|
"svelte-navigator": "^3.1.6"
|
||||||
"tauri-plugin-store-api": "github:tauri-apps/tauri-plugin-store"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
13
src-tauri/Cargo.lock
generated
13
src-tauri/Cargo.lock
generated
|
@ -61,7 +61,6 @@ dependencies = [
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"tauri",
|
"tauri",
|
||||||
"tauri-build",
|
"tauri-build",
|
||||||
"tauri-plugin-store",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -2846,18 +2845,6 @@ dependencies = [
|
||||||
"tauri-utils",
|
"tauri-utils",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "tauri-plugin-store"
|
|
||||||
version = "0.0.0"
|
|
||||||
source = "git+https://github.com/tauri-apps/tauri-plugin-store#17cf4d781c82a6c6578c472068c11136c0652d50"
|
|
||||||
dependencies = [
|
|
||||||
"log",
|
|
||||||
"serde",
|
|
||||||
"serde_json",
|
|
||||||
"tauri",
|
|
||||||
"thiserror",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tauri-runtime"
|
name = "tauri-runtime"
|
||||||
version = "0.10.2"
|
version = "0.10.2"
|
||||||
|
|
|
@ -19,10 +19,6 @@ serde_json = "1.0"
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
tauri = { version = "1.0.3", features = ["api-all", "devtools"] }
|
tauri = { version = "1.0.3", features = ["api-all", "devtools"] }
|
||||||
|
|
||||||
[dependencies.tauri-plugin-store]
|
|
||||||
git = "https://github.com/tauri-apps/tauri-plugin-store"
|
|
||||||
#branch = "main"
|
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
# by default Tauri runs in production mode
|
# by default Tauri runs in production mode
|
||||||
# when `tauri dev` runs it is executed with `cargo run --no-default-features` if `devPath` is an URL
|
# when `tauri dev` runs it is executed with `cargo run --no-default-features` if `devPath` is an URL
|
||||||
|
|
|
@ -4,7 +4,6 @@
|
||||||
)]
|
)]
|
||||||
|
|
||||||
use tauri::RunEvent;
|
use tauri::RunEvent;
|
||||||
use tauri_plugin_store::PluginBuilder;
|
|
||||||
|
|
||||||
mod commands;
|
mod commands;
|
||||||
use commands::close_splashscreen;
|
use commands::close_splashscreen;
|
||||||
|
@ -14,7 +13,6 @@ use commands::copy_dir;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
tauri::Builder::default()
|
tauri::Builder::default()
|
||||||
.plugin(PluginBuilder::default().build())
|
|
||||||
.invoke_handler(tauri::generate_handler![
|
.invoke_handler(tauri::generate_handler![
|
||||||
get_highest_simd,
|
get_highest_simd,
|
||||||
open_dir,
|
open_dir,
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<script type="ts">
|
<script type="ts">
|
||||||
import { getGameInstallVersion, setInstallStatus } from "$lib/config";
|
import { launcherConfig } from "$lib/config";
|
||||||
import { getInternalName, SupportedGame } from "$lib/constants";
|
import { getInternalName, SupportedGame } from "$lib/constants";
|
||||||
import { launchGame } from "$lib/launch";
|
import { launchGame } from "$lib/launch";
|
||||||
import { openDir } from "$lib/rpc/commands";
|
import { openDir } from "$lib/rpc/commands";
|
||||||
|
@ -15,7 +15,7 @@ import { createEventDispatcher, onMount } from "svelte";
|
||||||
let gameVersion = undefined;
|
let gameVersion = undefined;
|
||||||
|
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
gameVersion = await getGameInstallVersion(activeGame);
|
gameVersion = await launcherConfig.getGameInstallVersion(activeGame);
|
||||||
configPath = await join(await configDir(), "OpenGOAL", getInternalName(activeGame));
|
configPath = await join(await configDir(), "OpenGOAL", getInternalName(activeGame));
|
||||||
componentLoaded = true;
|
componentLoaded = true;
|
||||||
});
|
});
|
||||||
|
@ -25,7 +25,7 @@ import { createEventDispatcher, onMount } from "svelte";
|
||||||
}
|
}
|
||||||
|
|
||||||
async function onClickUninstall() {
|
async function onClickUninstall() {
|
||||||
await setInstallStatus(SupportedGame.Jak1, false);
|
await launcherConfig.setInstallStatus(SupportedGame.Jak1, false);
|
||||||
dispatch('change');
|
dispatch('change');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,7 +45,7 @@ import { createEventDispatcher, onMount } from "svelte";
|
||||||
{#if componentLoaded}
|
{#if componentLoaded}
|
||||||
<div id="launcherControls">
|
<div id="launcherControls">
|
||||||
<button class="btn lg" on:click={onClickPlay}>Play</button>
|
<button class="btn lg" on:click={onClickPlay}>Play</button>
|
||||||
<p>Game Version: {gameVersion}</p>
|
<p class="text-shadow">Game Version: {gameVersion}</p>
|
||||||
<!-- TODO - when clicking decompile/compile -- show logs -->
|
<!-- TODO - when clicking decompile/compile -- show logs -->
|
||||||
<div class="mt-1">
|
<div class="mt-1">
|
||||||
<button class="btn md" on:click={() => openDir(configPath)}>Settings and Saves</button>
|
<button class="btn md" on:click={() => openDir(configPath)}>Settings and Saves</button>
|
||||||
|
@ -55,3 +55,9 @@ import { createEventDispatcher, onMount } from "svelte";
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.text-shadow {
|
||||||
|
text-shadow: -1px -1px 0 #000, 1px -1px 0 #000, -1px 1px 0 #000, 1px 1px 0 #000;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<script type="ts">
|
<script type="ts">
|
||||||
import { areRequirementsMet } from "$lib/config";
|
import { launcherConfig } from "$lib/config";
|
||||||
import { gameNeedsReinstall, isInstalling, ProcessLogs } from "$lib/stores/AppStore";
|
import { gameNeedsReinstall, isInstalling, ProcessLogs } from "$lib/stores/AppStore";
|
||||||
import { fullInstallation, recompileGame } from "$lib/setup/setup";
|
import { checkRequirements, fullInstallation, recompileGame } from "$lib/setup/setup";
|
||||||
// components
|
// components
|
||||||
import Progress from "./Progress.svelte";
|
import Progress from "./Progress.svelte";
|
||||||
// constants
|
// constants
|
||||||
|
@ -18,7 +18,11 @@
|
||||||
let requirementsMet = false;
|
let requirementsMet = false;
|
||||||
|
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
requirementsMet = await areRequirementsMet();
|
// NOTE - potentially has problems if the user changes hardware
|
||||||
|
if (!(await launcherConfig.areRequirementsMet())) {
|
||||||
|
await checkRequirements();
|
||||||
|
}
|
||||||
|
requirementsMet = await launcherConfig.areRequirementsMet();
|
||||||
componentLoaded = true;
|
componentLoaded = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<script>
|
<script>
|
||||||
import { InstallStatus } from "../../../lib/stores/AppStore";
|
import { InstallStatus } from "$lib/stores/AppStore";
|
||||||
import { tweened } from "svelte/motion";
|
import { tweened } from "svelte/motion";
|
||||||
import { cubicOut } from "svelte/easing";
|
import { cubicOut } from "svelte/easing";
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<script type="ts">
|
<script type="ts">
|
||||||
import { isAVXRequirementMet, isOpenGLRequirementMet } from "$lib/config";
|
import { launcherConfig } from "$lib/config";
|
||||||
|
|
||||||
import { onMount } from "svelte";
|
import { onMount } from "svelte";
|
||||||
|
|
||||||
|
@ -9,8 +9,8 @@ import { onMount } from "svelte";
|
||||||
let isOpenGLMet = false;
|
let isOpenGLMet = false;
|
||||||
|
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
isAVXMet = await isAVXRequirementMet();
|
isAVXMet = await launcherConfig.isAVXRequirementMet();
|
||||||
isOpenGLMet = await isOpenGLRequirementMet();
|
isOpenGLMet = await launcherConfig.isOpenGLRequirementMet();
|
||||||
componentLoaded = true;
|
componentLoaded = true;
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -1,212 +1,255 @@
|
||||||
import { createDir, readTextFile, writeFile } from "@tauri-apps/api/fs";
|
import { createDir, readTextFile, writeFile } from "@tauri-apps/api/fs";
|
||||||
import { appDir, join } from "@tauri-apps/api/path";
|
import { appDir, join } from "@tauri-apps/api/path";
|
||||||
import { Store } from "tauri-plugin-store-api";
|
|
||||||
import { SupportedGame } from "./constants";
|
import { SupportedGame } from "./constants";
|
||||||
import { fileExists } from "./utils/file";
|
import { fileExists } from "./utils/file";
|
||||||
import { log } from "./utils/log";
|
import { log } from "./utils/log";
|
||||||
|
|
||||||
class GameConfig {
|
interface Serializable<T> {
|
||||||
isInstalled: boolean = false;
|
deserialize(input: Object): T;
|
||||||
version: string = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: LINK REQUIREMENTS TO CHECK REQUIREMENTS FUNCTION TO AVOID RUNNING FUNCTION IF REQUIREMENTS ARE MET
|
class GameConfig implements Serializable<GameConfig> {
|
||||||
class LauncherConfig {
|
isInstalled: boolean = false;
|
||||||
version = "1.0";
|
version: string = null;
|
||||||
requirements: { avx: boolean | null; openGL: boolean | null } = {
|
|
||||||
avx: null,
|
deserialize(json: JSON): GameConfig {
|
||||||
openGL: null,
|
this.isInstalled = json["isInstalled"];
|
||||||
};
|
this.version = json["version"];
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class GameRequirements implements Serializable<GameRequirements> {
|
||||||
|
avx: boolean | null = null;
|
||||||
|
openGL: boolean | null = null;
|
||||||
|
|
||||||
|
deserialize(json: JSON): GameRequirements {
|
||||||
|
this.avx = json["avx"];
|
||||||
|
this.openGL = json["openGL"];
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class LauncherConfig implements Serializable<LauncherConfig> {
|
||||||
|
version: string | null = "1.0";
|
||||||
|
requirements: GameRequirements = new GameRequirements();
|
||||||
games = {
|
games = {
|
||||||
[SupportedGame.Jak1]: new GameConfig(),
|
[SupportedGame.Jak1]: new GameConfig(),
|
||||||
[SupportedGame.Jak2]: new GameConfig(),
|
[SupportedGame.Jak2]: new GameConfig(),
|
||||||
[SupportedGame.Jak3]: new GameConfig(),
|
[SupportedGame.Jak3]: new GameConfig(),
|
||||||
[SupportedGame.JakX]: new GameConfig(),
|
[SupportedGame.JakX]: new GameConfig(),
|
||||||
};
|
};
|
||||||
lastActiveGame: SupportedGame;
|
lastActiveGame: SupportedGame | null;
|
||||||
}
|
|
||||||
|
|
||||||
const store = new Store("settings.json");
|
private _loaded: boolean = false;
|
||||||
|
|
||||||
/**
|
deserialize(json: JSON): LauncherConfig {
|
||||||
* Checks the version to enable safe config operations
|
this.version = json["version"];
|
||||||
* @param {*} version "<major>.<minor>"
|
this.requirements = new GameRequirements().deserialize(
|
||||||
* @returns True if majors match, and expected minor greater than or equal to stored. False otherwise, or if no version can be found
|
json["requirements"]
|
||||||
*/
|
);
|
||||||
async function validVersion(version: string): Promise<boolean> {
|
this.games[SupportedGame.Jak1] = new GameConfig().deserialize(
|
||||||
let [major, minor] = version.split(".");
|
json["games"][SupportedGame.Jak1]
|
||||||
await store.load();
|
);
|
||||||
if (!(await store.has("version"))) {
|
this.games[SupportedGame.Jak2] = new GameConfig().deserialize(
|
||||||
return false;
|
json["games"][SupportedGame.Jak2]
|
||||||
|
);
|
||||||
|
this.games[SupportedGame.Jak3] = new GameConfig().deserialize(
|
||||||
|
json["games"][SupportedGame.Jak3]
|
||||||
|
);
|
||||||
|
this.games[SupportedGame.JakX] = new GameConfig().deserialize(
|
||||||
|
json["games"][SupportedGame.JakX]
|
||||||
|
);
|
||||||
|
this.lastActiveGame = json["lastActiveGame"];
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
let [storedMajor, storedMinor]: string[] = (await store.get("version")).split(
|
|
||||||
"."
|
|
||||||
);
|
|
||||||
if (major != storedMajor) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (parseInt(minor) < parseInt(storedMinor)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function initConfig() {
|
private async loadConfigFromFile() {
|
||||||
const path = await join(await appDir(), "settings.json");
|
if (this._loaded) {
|
||||||
let configExists = await fileExists(path);
|
return;
|
||||||
if (!configExists) {
|
}
|
||||||
log.info("settings file not found or could not be loaded!");
|
const configPath = await join(await appDir(), "settings.json");
|
||||||
await createDir(await appDir(), { recursive: true });
|
const configExists = await fileExists(configPath);
|
||||||
|
if (!configExists) {
|
||||||
|
console.log(
|
||||||
|
`[Launcher]: Settings file not found at '${configPath}, initializing with defaults!`
|
||||||
|
);
|
||||||
|
await createDir(await appDir(), { recursive: true });
|
||||||
|
await writeFile({
|
||||||
|
contents: JSON.stringify(this, null, 2),
|
||||||
|
path: configPath,
|
||||||
|
});
|
||||||
|
console.log("[Launcher]: Settings file initialized");
|
||||||
|
} else {
|
||||||
|
const data = await readTextFile(configPath);
|
||||||
|
this.deserialize(JSON.parse(data));
|
||||||
|
}
|
||||||
|
this._loaded = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async saveConfigToFile() {
|
||||||
|
if (!this._loaded) {
|
||||||
|
log.info("config not loaded when trying to save, initializing it first!");
|
||||||
|
await this.loadConfigFromFile();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const path = await join(await appDir(), "settings.json");
|
||||||
await writeFile({
|
await writeFile({
|
||||||
contents: JSON.stringify(new LauncherConfig(), null, 2),
|
contents: JSON.stringify(this, null, 2),
|
||||||
path: path,
|
path: path,
|
||||||
});
|
});
|
||||||
log.info("settings file initialized");
|
log.info("settings file initialized");
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If a game is installed or not
|
* Checks the version to enable safe config operations
|
||||||
* @param {string} supportedGame
|
*
|
||||||
* @returns {Promise<boolean>}
|
* Assumes the config has been loaded before calling to reduce boilerplate
|
||||||
*/
|
*
|
||||||
export async function getInstallStatus(
|
* @param {*} version "<major>.<minor>"
|
||||||
supportedGame: SupportedGame
|
* @returns True if majors match, and expected minor greater than or equal to stored. False otherwise, or if no version can be found
|
||||||
): Promise<boolean> {
|
*/
|
||||||
await store.load();
|
private validVersion(requiredVersion: string): boolean {
|
||||||
if (!(await validVersion("1.0"))) {
|
const [requiredMajor, requiredMinor] = requiredVersion.split(".");
|
||||||
return false;
|
if (this.version === null) {
|
||||||
}
|
return false;
|
||||||
// TODO: create a proper type for gameConfigs - exists with 'LauncherConfig'
|
}
|
||||||
const gameConfigs: object = await store.get("games");
|
const [storedMajor, storedMinor] = this.version.split(".");
|
||||||
if (gameConfigs == null || !(supportedGame in gameConfigs)) {
|
if (requiredMajor != storedMajor) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return gameConfigs[supportedGame].isInstalled;
|
if (parseInt(requiredMinor) < parseInt(storedMinor)) {
|
||||||
}
|
return false;
|
||||||
|
}
|
||||||
/**
|
|
||||||
* The last game that was considered active in the launcher
|
|
||||||
* @returns {Promise<SupportedGame | null>}
|
|
||||||
*/
|
|
||||||
export async function getLastActiveGame(): Promise<SupportedGame> {
|
|
||||||
await store.load();
|
|
||||||
if (!(await validVersion("1.0"))) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const lastActiveGame: SupportedGame = await store.get("lastActiveGame");
|
|
||||||
return lastActiveGame;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {string} supportedGame
|
|
||||||
* @param {boolean} installed
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
export async function setInstallStatus(
|
|
||||||
supportedGame: SupportedGame,
|
|
||||||
installed: boolean
|
|
||||||
): Promise<void> {
|
|
||||||
await store.load();
|
|
||||||
if (!(await validVersion("1.0"))) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// TODO: create a proper type for gameConfigs - 'LauncherConfig'
|
|
||||||
let gameConfigs: object = await store.get("games");
|
|
||||||
// NOTE: Do we need this conditional? Considering we generate the store file this condition should never happen.
|
|
||||||
if (gameConfigs == null || !(supportedGame in gameConfigs)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
gameConfigs[supportedGame].isInstalled = installed;
|
|
||||||
await store.set("games", gameConfigs);
|
|
||||||
await store.save();
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function setRequirementsMet(
|
|
||||||
avx: boolean = null,
|
|
||||||
openGL: boolean = null
|
|
||||||
) {
|
|
||||||
await store.load();
|
|
||||||
await store.set("requirements", { avx, openGL });
|
|
||||||
await store.save();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks the user config file to see if avx and openGL requirements are met.
|
|
||||||
*/
|
|
||||||
export async function areRequirementsMet(): Promise<boolean> {
|
|
||||||
if ((await isAVXRequirementMet()) && (await isOpenGLRequirementMet())) {
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function isAVXRequirementMet(): Promise<boolean> {
|
// GETTERS
|
||||||
await store.load();
|
|
||||||
let requirements = await store.get("requirements");
|
|
||||||
if (!requirements["avx"]) {
|
|
||||||
log.error("requirement false - AVX unsupported");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function isOpenGLRequirementMet(): Promise<boolean> {
|
/**
|
||||||
await store.load();
|
* If a game is installed or not
|
||||||
let requirements = await store.get("requirements");
|
* @param {string} supportedGame
|
||||||
if (!requirements["openGL"]) {
|
* @returns {Promise<boolean>}
|
||||||
log.error("requirement false - OpenGL unsupported");
|
*/
|
||||||
return false;
|
async getInstallStatus(supportedGame: SupportedGame): Promise<boolean> {
|
||||||
}
|
await this.loadConfigFromFile();
|
||||||
return true;
|
if (!this.validVersion("1.0")) {
|
||||||
}
|
return false;
|
||||||
|
}
|
||||||
export async function getGameInstallVersion(
|
const gameConfigs = this.games;
|
||||||
game: SupportedGame
|
if (gameConfigs === null || !(supportedGame in gameConfigs)) {
|
||||||
): Promise<String> {
|
return false;
|
||||||
// TODO - this can fail on first time startup from splash (where the config is init)
|
}
|
||||||
// no idea why yet
|
return gameConfigs[supportedGame].isInstalled;
|
||||||
await store.load();
|
|
||||||
let games: GameConfig = await store.get("games");
|
|
||||||
const { version } = games[game];
|
|
||||||
return version;
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function setGameInstallVersion(game: SupportedGame) {
|
|
||||||
const version = await getLatestToolsVersion();
|
|
||||||
await store.load();
|
|
||||||
let games: GameConfig = await store.get("games");
|
|
||||||
games[game].version = version;
|
|
||||||
await store.set("games", games);
|
|
||||||
return await store.save();
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function getLatestToolsVersion(): Promise<String> {
|
|
||||||
const appDirPath = await appDir();
|
|
||||||
const userMetaPath = await join(appDirPath, "data", "metadata.json");
|
|
||||||
const data = await readTextFile(userMetaPath);
|
|
||||||
const { version } = JSON.parse(data);
|
|
||||||
return version;
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function shouldUpdateGameInstall(
|
|
||||||
game: SupportedGame
|
|
||||||
): Promise<Boolean> {
|
|
||||||
const installVersion = await getGameInstallVersion(game);
|
|
||||||
if (installVersion === null || installVersion === undefined) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const toolsVersion = await getLatestToolsVersion();
|
|
||||||
|
|
||||||
if (installVersion === toolsVersion) {
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
log.warn("Tools version is different than install verison", {
|
/**
|
||||||
tools: toolsVersion,
|
* The last game that was considered active in the launcher
|
||||||
installed: installVersion,
|
* @returns {Promise<SupportedGame | null>}
|
||||||
});
|
*/
|
||||||
return true;
|
async getLastActiveGame(): Promise<SupportedGame | null> {
|
||||||
|
await this.loadConfigFromFile();
|
||||||
|
if (!this.validVersion("1.0")) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return this.lastActiveGame;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks the user config file to see if avx and openGL requirements are met.
|
||||||
|
*/
|
||||||
|
async areRequirementsMet(): Promise<boolean> {
|
||||||
|
await this.loadConfigFromFile();
|
||||||
|
if (!this.validVersion("1.0")) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return this.requirements.avx && this.requirements.openGL;
|
||||||
|
}
|
||||||
|
|
||||||
|
async isAVXRequirementMet(): Promise<boolean> {
|
||||||
|
await this.loadConfigFromFile();
|
||||||
|
if (!this.validVersion("1.0")) {
|
||||||
|
log.error("requirement false - AVX unsupported");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return this.requirements.avx;
|
||||||
|
}
|
||||||
|
|
||||||
|
async isOpenGLRequirementMet(): Promise<boolean> {
|
||||||
|
await this.loadConfigFromFile();
|
||||||
|
if (!this.validVersion("1.0")) {
|
||||||
|
log.error("requirement false - OpenGL 4.3 unsupported");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return this.requirements.openGL;
|
||||||
|
}
|
||||||
|
|
||||||
|
async getGameInstallVersion(game: SupportedGame): Promise<String> {
|
||||||
|
await this.loadConfigFromFile();
|
||||||
|
if (!this.validVersion("1.0")) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return this.games[game].version;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async getLatestProjectBinaryVersion(): Promise<string | null> {
|
||||||
|
// TODO - make a LauncherMetadata class similar to this
|
||||||
|
const appDirPath = await appDir();
|
||||||
|
const userMetaPath = await join(appDirPath, "data", "metadata.json");
|
||||||
|
// TODO - ensure it exists!
|
||||||
|
const data = await readTextFile(userMetaPath);
|
||||||
|
const { version } = JSON.parse(data);
|
||||||
|
return version;
|
||||||
|
}
|
||||||
|
|
||||||
|
async shouldUpdateGameInstall(game: SupportedGame): Promise<Boolean> {
|
||||||
|
const installVersion = await this.getGameInstallVersion(game);
|
||||||
|
if (installVersion === null || installVersion === undefined) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const toolsVersion = await this.getLatestProjectBinaryVersion();
|
||||||
|
|
||||||
|
if (installVersion === toolsVersion) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
log.warn("Tools version is different than install verison", {
|
||||||
|
tools: toolsVersion,
|
||||||
|
installed: installVersion,
|
||||||
|
});
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// SETTERS
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} supportedGame
|
||||||
|
* @param {boolean} installed
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
async setInstallStatus(
|
||||||
|
supportedGame: SupportedGame,
|
||||||
|
installed: boolean
|
||||||
|
): Promise<void> {
|
||||||
|
this.games[supportedGame].isInstalled = installed;
|
||||||
|
await this.saveConfigToFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
async setRequirementsMet(
|
||||||
|
avx: boolean = null,
|
||||||
|
openGL: boolean = null
|
||||||
|
): Promise<void> {
|
||||||
|
this.requirements.avx = avx;
|
||||||
|
this.requirements.openGL = openGL;
|
||||||
|
await this.saveConfigToFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
async setGameInstallVersion(game: SupportedGame) {
|
||||||
|
const version = await this.getLatestProjectBinaryVersion();
|
||||||
|
this.games[game].version = version;
|
||||||
|
await this.saveConfigToFile();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Initialize with defaults
|
||||||
|
export let launcherConfig: LauncherConfig = new LauncherConfig();
|
||||||
|
|
|
@ -5,13 +5,10 @@ import { getHighestSimd } from "$lib/rpc/commands";
|
||||||
import { InstallStatus, isInstalling } from "../stores/AppStore";
|
import { InstallStatus, isInstalling } from "../stores/AppStore";
|
||||||
import { SETUP_ERROR, SETUP_SUCCESS, SupportedGame } from "$lib/constants";
|
import { SETUP_ERROR, SETUP_SUCCESS, SupportedGame } from "$lib/constants";
|
||||||
import { filePrompt } from "$lib/utils/file";
|
import { filePrompt } from "$lib/utils/file";
|
||||||
import {
|
import { launcherConfig } from "$lib/config";
|
||||||
setGameInstallVersion,
|
|
||||||
setInstallStatus,
|
|
||||||
setRequirementsMet,
|
|
||||||
} from "../config";
|
|
||||||
import { resolveErrorCode } from "./setup_errors";
|
import { resolveErrorCode } from "./setup_errors";
|
||||||
import { installLog, log } from "$lib/utils/log";
|
import { installLog, log } from "$lib/utils/log";
|
||||||
|
import { ProcessLogs } from "$lib/stores/AppStore";
|
||||||
|
|
||||||
let sidecarOptions = {};
|
let sidecarOptions = {};
|
||||||
|
|
||||||
|
@ -27,7 +24,7 @@ export async function isAVXSupported() {
|
||||||
if (highestSIMD.toLowerCase().startsWith("avx")) {
|
if (highestSIMD.toLowerCase().startsWith("avx")) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
throw new Error("UNSUPPORTED AVX");
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -38,7 +35,8 @@ export async function isOpenGLVersionSupported(
|
||||||
version: string
|
version: string
|
||||||
): Promise<boolean> {
|
): Promise<boolean> {
|
||||||
if ((await os.platform()) === "darwin") {
|
if ((await os.platform()) === "darwin") {
|
||||||
throw new Error("Unsupported OS!");
|
// TODO - log!
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
// Otherwise, query for the version
|
// Otherwise, query for the version
|
||||||
let command = Command.sidecar("bin/glewinfo", ["-version", version]);
|
let command = Command.sidecar("bin/glewinfo", ["-version", version]);
|
||||||
|
@ -47,21 +45,22 @@ export async function isOpenGLVersionSupported(
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
log.error("opengl requirement check failed", {
|
log.error("opengl requirement check failed", {
|
||||||
|
version: version,
|
||||||
statusCode: output.code,
|
statusCode: output.code,
|
||||||
stdout: output.stdout,
|
stdout: output.stdout,
|
||||||
stderr: output.stderr,
|
stderr: output.stderr,
|
||||||
});
|
});
|
||||||
throw new Error("UNSUPPORTED OPENGL VERSION");
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function checkRequirements(): Promise<Boolean> {
|
export async function checkRequirements(): Promise<void> {
|
||||||
try {
|
try {
|
||||||
await isAVXSupported();
|
const isAVX = await isAVXSupported();
|
||||||
await isOpenGLVersionSupported("4.3");
|
const isOpenGL = await isOpenGLVersionSupported("4.3");
|
||||||
await setRequirementsMet(true, true);
|
console.log(`avx - ${isAVX} opengl - ${isOpenGL}`);
|
||||||
return true;
|
await launcherConfig.setRequirementsMet(isAVX, isOpenGL);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
return false;
|
await launcherConfig.setRequirementsMet(false, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -109,11 +108,13 @@ export async function extractAndValidateISO(
|
||||||
installLog.info(output.stdout, {
|
installLog.info(output.stdout, {
|
||||||
game: SupportedGame.Jak1,
|
game: SupportedGame.Jak1,
|
||||||
});
|
});
|
||||||
|
ProcessLogs.update((currLogs) => currLogs + output.stdout);
|
||||||
}
|
}
|
||||||
if (output.stderr) {
|
if (output.stderr) {
|
||||||
installLog.error(output.stderr, {
|
installLog.error(output.stderr, {
|
||||||
game: SupportedGame.Jak1,
|
game: SupportedGame.Jak1,
|
||||||
});
|
});
|
||||||
|
ProcessLogs.update((currLogs) => currLogs + output.stderr);
|
||||||
}
|
}
|
||||||
if (output.code === 0) {
|
if (output.code === 0) {
|
||||||
return true;
|
return true;
|
||||||
|
@ -143,11 +144,13 @@ export async function decompileGameData(filePath: string): Promise<boolean> {
|
||||||
installLog.info(output.stdout, {
|
installLog.info(output.stdout, {
|
||||||
game: SupportedGame.Jak1,
|
game: SupportedGame.Jak1,
|
||||||
});
|
});
|
||||||
|
ProcessLogs.update((currLogs) => currLogs + output.stdout);
|
||||||
}
|
}
|
||||||
if (output.stderr) {
|
if (output.stderr) {
|
||||||
installLog.error(output.stderr, {
|
installLog.error(output.stderr, {
|
||||||
game: SupportedGame.Jak1,
|
game: SupportedGame.Jak1,
|
||||||
});
|
});
|
||||||
|
ProcessLogs.update((currLogs) => currLogs + output.stderr);
|
||||||
}
|
}
|
||||||
if (output.code === 0) {
|
if (output.code === 0) {
|
||||||
return true;
|
return true;
|
||||||
|
@ -175,11 +178,13 @@ export async function compileGame(filePath: string): Promise<Boolean> {
|
||||||
installLog.info(output.stdout, {
|
installLog.info(output.stdout, {
|
||||||
game: SupportedGame.Jak1,
|
game: SupportedGame.Jak1,
|
||||||
});
|
});
|
||||||
|
ProcessLogs.update((currLogs) => currLogs + output.stdout);
|
||||||
}
|
}
|
||||||
if (output.stderr) {
|
if (output.stderr) {
|
||||||
installLog.error(output.stderr, {
|
installLog.error(output.stderr, {
|
||||||
game: SupportedGame.Jak1,
|
game: SupportedGame.Jak1,
|
||||||
});
|
});
|
||||||
|
ProcessLogs.update((currLogs) => currLogs + output.stderr);
|
||||||
}
|
}
|
||||||
if (output.code === 0) {
|
if (output.code === 0) {
|
||||||
InstallStatus.update(() => SETUP_SUCCESS.ready);
|
InstallStatus.update(() => SETUP_SUCCESS.ready);
|
||||||
|
@ -193,12 +198,13 @@ export async function fullInstallation(game: SupportedGame): Promise<boolean> {
|
||||||
isInstalling.update(() => true);
|
isInstalling.update(() => true);
|
||||||
try {
|
try {
|
||||||
isoPath = await isoPrompt();
|
isoPath = await isoPrompt();
|
||||||
|
ProcessLogs.update(() => "");
|
||||||
await extractAndValidateISO(isoPath);
|
await extractAndValidateISO(isoPath);
|
||||||
await decompileGameData(isoPath);
|
await decompileGameData(isoPath);
|
||||||
await compileGame(isoPath);
|
await compileGame(isoPath);
|
||||||
await setInstallStatus(game, true);
|
await launcherConfig.setInstallStatus(game, true);
|
||||||
isInstalling.update(() => false);
|
isInstalling.update(() => false);
|
||||||
await setGameInstallVersion(game);
|
await launcherConfig.setGameInstallVersion(game);
|
||||||
return true;
|
return true;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
installLog.error("unexpected error encountered", {
|
installLog.error("unexpected error encountered", {
|
||||||
|
@ -226,7 +232,7 @@ export async function recompileGame(game: SupportedGame) {
|
||||||
await decompileGameData(isoPath);
|
await decompileGameData(isoPath);
|
||||||
await compileGame(isoPath);
|
await compileGame(isoPath);
|
||||||
// update settings.json with latest tools version from metadata.json
|
// update settings.json with latest tools version from metadata.json
|
||||||
await setGameInstallVersion(game);
|
await launcherConfig.setGameInstallVersion(game);
|
||||||
isInstalling.update(() => false);
|
isInstalling.update(() => false);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
installLog.error("unexpected error encountered", {
|
installLog.error("unexpected error encountered", {
|
||||||
|
|
|
@ -6,4 +6,4 @@ export const InstallStatus = writable({
|
||||||
});
|
});
|
||||||
export const isInstalling = writable(false);
|
export const isInstalling = writable(false);
|
||||||
export const gameNeedsReinstall = writable(false);
|
export const gameNeedsReinstall = writable(false);
|
||||||
export const ProcessLogs = writable();
|
export const ProcessLogs = writable("");
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<script>
|
<script>
|
||||||
import { fade } from "svelte/transition";
|
import { fade } from "svelte/transition";
|
||||||
import { getInstallStatus, shouldUpdateGameInstall } from "$lib/config";
|
import { launcherConfig } from "$lib/config";
|
||||||
import { fromRoute, getGameTitle, SupportedGame } from "$lib/constants";
|
import { fromRoute, getGameTitle, SupportedGame } from "$lib/constants";
|
||||||
import { useParams } from "svelte-navigator";
|
import { useParams } from "svelte-navigator";
|
||||||
import GameContent from "../components/games/GameControls.svelte";
|
import GameContent from "../components/games/GameControls.svelte";
|
||||||
|
@ -31,7 +31,7 @@
|
||||||
activeGame = fromRoute($params["game_name"]);
|
activeGame = fromRoute($params["game_name"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
isGameInstalled = await getInstallStatus(activeGame);
|
isGameInstalled = await launcherConfig.getInstallStatus(activeGame);
|
||||||
|
|
||||||
// Do some checks before the user can play the game
|
// Do some checks before the user can play the game
|
||||||
// First, let's see if their data directory needs updating
|
// First, let's see if their data directory needs updating
|
||||||
|
@ -39,7 +39,7 @@
|
||||||
dataDirUpToDate = await isDataDirectoryUpToDate();
|
dataDirUpToDate = await isDataDirectoryUpToDate();
|
||||||
// If it's up to date we'll do the second check now, does their game need to be re-compiled?
|
// If it's up to date we'll do the second check now, does their game need to be re-compiled?
|
||||||
if (dataDirUpToDate) {
|
if (dataDirUpToDate) {
|
||||||
if (await shouldUpdateGameInstall(activeGame)) {
|
if (await launcherConfig.shouldUpdateGameInstall(activeGame)) {
|
||||||
// await recompileGame(activeGame);
|
// await recompileGame(activeGame);
|
||||||
gameNeedsReinstall.update(() => true);
|
gameNeedsReinstall.update(() => true);
|
||||||
}
|
}
|
||||||
|
@ -53,7 +53,7 @@
|
||||||
try {
|
try {
|
||||||
await copyDataDirectory();
|
await copyDataDirectory();
|
||||||
// Now that the directory is up to date, let's see if they need to reinstall the game
|
// Now that the directory is up to date, let's see if they need to reinstall the game
|
||||||
if (await shouldUpdateGameInstall(activeGame)) {
|
if (await launcherConfig.shouldUpdateGameInstall(activeGame)) {
|
||||||
gameNeedsReinstall.update(() => true);
|
gameNeedsReinstall.update(() => true);
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
@ -63,13 +63,13 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
async function updateGameState(evt) {
|
async function updateGameState(evt) {
|
||||||
isGameInstalled = await getInstallStatus(activeGame);
|
isGameInstalled = await launcherConfig.getInstallStatus(activeGame);
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if componentLoaded}
|
{#if componentLoaded}
|
||||||
<div class="flex-center" in:fade>
|
<div class="flex-center" in:fade>
|
||||||
<h1>
|
<h1 class="text-shadow">
|
||||||
{getGameTitle(activeGame)}
|
{getGameTitle(activeGame)}
|
||||||
</h1>
|
</h1>
|
||||||
{#if isGameInstalled && !$gameNeedsReinstall}
|
{#if isGameInstalled && !$gameNeedsReinstall}
|
||||||
|
@ -102,3 +102,9 @@
|
||||||
{:else}
|
{:else}
|
||||||
<!-- TODO - component library - spinner -->
|
<!-- TODO - component library - spinner -->
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.text-shadow {
|
||||||
|
text-shadow: -1px -1px 0 #000, 1px -1px 0 #000, -1px 1px 0 #000, 1px 1px 0 #000;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
<script>
|
<script>
|
||||||
import { closeSplashScreen } from "$lib/rpc/commands";
|
import { closeSplashScreen } from "$lib/rpc/commands";
|
||||||
import { areRequirementsMet, initConfig } from "$lib/config";
|
|
||||||
import { checkRequirements } from "$lib/setup/setup";
|
|
||||||
import { onMount } from "svelte";
|
import { onMount } from "svelte";
|
||||||
import logo from "$assets/images/icon.webp";
|
import logo from "$assets/images/icon.webp";
|
||||||
import { copyDataDirectory, dataDirectoryExists } from "$lib/utils/data-files";
|
import { copyDataDirectory, dataDirectoryExists } from "$lib/utils/data-files";
|
||||||
|
@ -12,13 +10,6 @@ import { log } from "$lib/utils/log";
|
||||||
|
|
||||||
// Events
|
// Events
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
await initConfig();
|
|
||||||
currentStatusText = "Checking Requirements";
|
|
||||||
currentProgress = 10;
|
|
||||||
// NOTE - potentially has problems if the user changes hardware
|
|
||||||
if (!(await areRequirementsMet())) {
|
|
||||||
await checkRequirements();
|
|
||||||
}
|
|
||||||
currentStatusText = "Checking Data Files";
|
currentStatusText = "Checking Data Files";
|
||||||
currentProgress = 25;
|
currentProgress = 25;
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue