diff --git a/assets/jsons/translations/de.json b/assets/jsons/translations/de.json index 67881067..2d785e4b 100644 --- a/assets/jsons/translations/de.json +++ b/assets/jsons/translations/de.json @@ -28,6 +28,11 @@ "pin": "Anheften", "unpin": "Lösen" }, + "generic": { + "env": { + "parse": "Konnte den Umgebungsvariablen-String nicht richtig parsen." + } + }, "title-bar": { "outdated": "veraltet" }, @@ -51,7 +56,7 @@ "outdated-tippy": "Diese Version ist veraltet, und einige Mods oder Funktionen funktionieren möglicherweise nicht wie erwartet. Es wird empfohlen, die empfohlene Version ({recommendedVersion}) von Beat Saber zu verwenden, um die neuesten Funktionen und Fehlerbehebungen zu genießen.", "advanced-launch": { "button": "Startoptionen", - "placeholder": "Weitere Argumente, bspw: --revert; --nowait" + "placeholder": "Startoptionen, bspw: KEY=VALUE %command% fpfc" }, "skipsteam": "Steam überspringen", "skipsteam-description": "Verhindert, dass Steam automatisch mit Beat Saber geöffnet wird. Aktivieren Sie dies, wenn Sie eine andere VR-Laufzeit wie WiVRn oder Monado verwenden, mit der SteamVR interferieren könnte.", diff --git a/assets/jsons/translations/en.json b/assets/jsons/translations/en.json index 439d65c4..c21dc3ba 100644 --- a/assets/jsons/translations/en.json +++ b/assets/jsons/translations/en.json @@ -28,6 +28,11 @@ "pin": "Pin", "unpin": "Unpin" }, + "generic": { + "env": { + "parse": "Could not properly parse the env var string." + } + }, "title-bar": { "outdated": "outdated" }, @@ -51,7 +56,7 @@ "outdated-tippy": "This version is outdated, and some mods or features may no longer work as expected. Prefer using the recommended version ({recommendedVersion}) of Beat Saber to enjoy the latest features and bugfixes.", "advanced-launch": { "button": "Launch options", - "placeholder": "Additional arguments ex: --revert; --nowait" + "placeholder": "Launch options ex: KEY=VALUE %command% fpfc" }, "skipsteam": "Skip Steam", "skipsteam-description": "Stops Steam from opening automatically with Beat Saber, enable if you are using a different VR runtime like WiVRn or Monado that SteamVR may interfere with.", diff --git a/assets/jsons/translations/es.json b/assets/jsons/translations/es.json index aca42f3a..71fd2b9d 100644 --- a/assets/jsons/translations/es.json +++ b/assets/jsons/translations/es.json @@ -28,6 +28,11 @@ "pin": "Fijar", "unpin": "Desfijar" }, + "generic": { + "env": { + "parse": "No se pudo analizar correctamente la cadena de la variable de entorno." + } + }, "title-bar": { "outdated": "obsoleto" }, @@ -51,7 +56,7 @@ "outdated-tippy": "Esta versión está desactualizada, y algunos mods o funciones pueden no funcionar como se espera. Es mejor usar la versión recomendada ({recommendedVersion}) de Beat Saber para disfrutar de las últimas características y correcciones.", "advanced-launch": { "button": "Opciones de lanzamiento", - "placeholder": "Argumentos adicionales ej: --revert; --no-wait" + "placeholder": "Opciones de lanzamiento ej: KEY=VALUE %command% fpfc" }, "skipsteam": "Saltar Steam", "skipsteam-description": "Evita que Steam se abra automáticamente con Beat Saber, habilita esto si estás usando un runtime de VR diferente como WiVRn o Monado que SteamVR podría interferir.", diff --git a/assets/jsons/translations/fr.json b/assets/jsons/translations/fr.json index b652643a..6549e2b2 100644 --- a/assets/jsons/translations/fr.json +++ b/assets/jsons/translations/fr.json @@ -28,6 +28,11 @@ "pin": "Épingler", "unpin": "Désépingler" }, + "generic": { + "env": { + "parse": "Impossible d'analyser correctement la chaîne de variable d'environnement." + } + }, "title-bar": { "outdated": "obsolète" }, @@ -51,7 +56,7 @@ "outdated-tippy": "Cette version est obsolète et certains mods ou fonctionnalités peuvent ne plus fonctionner comme prévu. Préférez utiliser la version recommandée ({recommendedVersion}) de Beat Saber pour profiter des dernières fonctionnalités et correctifs.", "advanced-launch": { "button": "Options de lancement", - "placeholder": "Arguments supplémentaires ex: --revert; --nowait" + "placeholder": "Options de lancement ex: KEY=VALUE %command% fpfc" }, "skipsteam": "Ignorer Steam", "skipsteam-description": "Empêche Steam de s'ouvrir automatiquement avec Beat Saber, activez-le si vous utilisez un autre runtime VR comme WiVRn ou Monado avec lequel SteamVR pourrait interférer.", diff --git a/assets/jsons/translations/it.json b/assets/jsons/translations/it.json index 80b2ceba..92b28c2f 100644 --- a/assets/jsons/translations/it.json +++ b/assets/jsons/translations/it.json @@ -28,6 +28,11 @@ "pin": "Fissa", "unpin": "Rimuovi Fissaggio" }, + "generic": { + "env": { + "parse": "Impossibile analizzare correttamente la stringa della variabile d'ambiente." + } + }, "title-bar": { "outdated": "obsoleta" }, @@ -51,7 +56,7 @@ "outdated-tippy": "Questa versione è obsoleta, e alcune mod o funzioni non potrebbero più funzionare correttamente. Consigliamo di usare la versione raccomandata ({recommendedVersion}) di Beat Saber per godere delle ultime funzioni e bugfix.", "advanced-launch": { "button": "Opzioni di Lancio", - "placeholder": "Argomenti aggiuntivi es: --revert; --nowait" + "placeholder": "Opzioni di Lancio es: KEY=VALUE %command% fpfc" }, "skipsteam": "Salta Steam", "skipsteam-description": "Ferma Steam da aprirsi automaticamente con Beat Saber, abilitalo se stai usando un VR runtime differente come WiVRn o Monado con cui SteamVR potrebbe interferire.", diff --git a/assets/jsons/translations/ja.json b/assets/jsons/translations/ja.json index b3ef17ba..51d25188 100644 --- a/assets/jsons/translations/ja.json +++ b/assets/jsons/translations/ja.json @@ -28,6 +28,11 @@ "pin": "固定", "unpin": "固定解除" }, + "generic": { + "env": { + "parse": "環境変数の文字列を正しく解析できませんでした。" + } + }, "title-bar": { "outdated": "時代遅れ" }, @@ -51,7 +56,7 @@ "outdated-tippy": "このバージョンは古いため、一部のMODや機能が期待通りに動作しない可能性があります。最新の機能やバグ修正を楽しむには、推奨バージョン ({recommendedVersion}) のBeat Saberを使用することをお勧めします。", "advanced-launch": { "button": "起動オプション", - "placeholder": "追加引数 例:--revert; --nowait" + "placeholder": "起動オプション 例:KEY=VALUE %command% fpfc" }, "skipsteam": "Steamをスキップ", "skipsteam-description": "Beat Saberと一緒にSteamが自動的に開くのを防ぎます。SteamVRが干渉する可能性のあるWiVRnやMonadoなど、別のVRランタイムを使用している場合は有効にしてください。", diff --git a/assets/jsons/translations/ko.json b/assets/jsons/translations/ko.json index 5b84c9df..f8b79e2e 100644 --- a/assets/jsons/translations/ko.json +++ b/assets/jsons/translations/ko.json @@ -28,6 +28,11 @@ "pin": "고정", "unpin": "고정 해제" }, + "generic": { + "env": { + "parse": "환경 변수 문자열을 제대로 파싱할 수 없습니다." + } + }, "title-bar": { "outdated": "구식" }, @@ -51,7 +56,7 @@ "outdated-tippy": "이 버전은 오래되어 일부 모드나 기능이 예상대로 작동하지 않을 수 있습니다. 최신 기능과 버그 수정을 사용하려면 권장 버전({recommendedVersion})의 Beat Saber를 사용하는 것이 좋습니다.", "advanced-launch": { "button": "실행 옵션", - "placeholder": "추가 인수 예: --revert; --nowait" + "placeholder": "실행 옵션 예: KEY=VALUE %command% fpfc" }, "skipsteam": "Steam 실행 건너뛰기", "skipsteam-description": "Beat Saber와 함께 Steam이 자동으로 열리는 것을 방지합니다. SteamVR이 간섭할 수 있는 WiVRn 또는 Monado와 같은 다른 VR 런타임을 사용하는 경우 활성화하세요.", diff --git a/assets/jsons/translations/ru.json b/assets/jsons/translations/ru.json index 0823c3a7..cbc5381c 100644 --- a/assets/jsons/translations/ru.json +++ b/assets/jsons/translations/ru.json @@ -28,6 +28,11 @@ "pin": "Закрепить", "unpin": "Открепить" }, + "generic": { + "env": { + "parse": "Не удалось правильно разобрать строку переменной окружения." + } + }, "title-bar": { "outdated": "устаревший" }, @@ -51,7 +56,7 @@ "outdated-tippy": "Эта версия устарела, и некоторые моды или функции могут работать не так, как ожидалось. Рекомендуется использовать рекомендуемую версию ({recommendedVersion}) Beat Saber, чтобы воспользоваться последними функциями и исправлениями ошибок.", "advanced-launch": { "button": "Параметры запуска", - "placeholder": "Параметры запуска, например: --revert; --nowait" + "placeholder": "Параметры запуска, например: KEY=VALUE %command% fpfc" }, "skipsteam": "Пропустить Steam", "skipsteam-description": "Предотвращает автоматическое открытие Steam с Beat Saber, включите, если вы используете другую VR-среду, такую как WiVRn или Monado, с которой SteamVR может мешать.", diff --git a/assets/jsons/translations/zh-tw.json b/assets/jsons/translations/zh-tw.json index adbc7502..3907ea16 100644 --- a/assets/jsons/translations/zh-tw.json +++ b/assets/jsons/translations/zh-tw.json @@ -28,6 +28,11 @@ "pin": "固定", "unpin": "取消固定" }, + "generic": { + "env": { + "parse": "無法正確解析環境變數字串。" + } + }, "title-bar": { "outdated": "過時" }, @@ -51,7 +56,7 @@ "outdated-tippy": "此版本已過時,某些模組或功能可能無法按預期運作。建議使用推薦版本的 Beat Saber ({recommendedVersion}),以享受最新功能和修復。", "advanced-launch": { "button": "啟動選項", - "placeholder": "額外啟動參數,例如: --revert; --nowait" + "placeholder": "啟動選項,例如: KEY=VALUE %command% fpfc" }, "skipsteam": "跳過 Steam", "skipsteam-description": "防止 Steam 與 Beat Saber 自動打開,如果您使用的是其他 VR 執行時,如 WiVRn 或 Monado,SteamVR 可能會干擾,請啟用此選項。", diff --git a/assets/jsons/translations/zh.json b/assets/jsons/translations/zh.json index 0dc0f347..c6c9adaa 100644 --- a/assets/jsons/translations/zh.json +++ b/assets/jsons/translations/zh.json @@ -28,6 +28,11 @@ "pin": "固定", "unpin": "取消固定" }, + "generic": { + "env": { + "parse": "无法正确解析环境变量字符串。" + } + }, "title-bar": { "outdated": "过时" }, @@ -51,7 +56,7 @@ "outdated-tippy": "此版本已过时,某些模组或功能可能无法按预期运行。建议使用推荐版本的 Beat Saber ({recommendedVersion}),以享受最新功能和修复。", "advanced-launch": { "button": "启动选项", - "placeholder": "额外启动参数,例如: --revert; --nowait" + "placeholder": "启动选项,例如: KEY=VALUE %command% fpfc" }, "skipsteam": "跳过 Steam", "skipsteam-description": "防止 Steam 与 Beat Saber 自动打开,如果您使用的是其他 VR 运行时,如 WiVRn 或 Monado,SteamVR 可能会干扰,请启用此选项。", diff --git a/src/__tests__/unit/env.test.ts b/src/__tests__/unit/env.test.ts new file mode 100644 index 00000000..fc6c80d2 --- /dev/null +++ b/src/__tests__/unit/env.test.ts @@ -0,0 +1,61 @@ +import { parseEnvString } from "main/helpers/env.helpers"; + +describe("Test parseEnvString", () => { + + it("Empty", () => { + const envVars = parseEnvString(""); + expect(envVars).toEqual({}); + }); + + it("Single test; no quotes", () => { + const envString = "HELLO=World!"; + const envVars = parseEnvString(envString); + expect(envVars).toEqual({ + HELLO: "World!", + }); + }); + + it("Single test; single quotes", () => { + const envString = "SINGLE_QOUTE='Single quote with spaces'"; + const envVars = parseEnvString(envString); + expect(envVars).toEqual({ + SINGLE_QOUTE: "Single quote with spaces", + }); + }); + + it("Single test; double quotes", () => { + const envString = 'DOUBLE_QOUTE="Some random quote."'; + const envVars = parseEnvString(envString); + expect(envVars).toEqual({ + DOUBLE_QOUTE: "Some random quote.", + }); + }); + + it("Single test; empty value", () => { + const envString = "EMPTY="; + const envVars = parseEnvString(envString); + expect(envVars).toEqual({ + EMPTY: "", + }); + }); + + it("Multiple test; combined", () => { + const envString = `HELLO=World! DOUBLE_QUOTE="Two Words" SINGLE_QUOTE='' EMPTY=` + const envVars = parseEnvString(envString); + expect(envVars).toEqual(expect.objectContaining({ + HELLO: "World!", + DOUBLE_QUOTE: "Two Words", + SINGLE_QUOTE: "", + EMPTY: "" + })); + }); + + it("Key with numbers and lower case", () => { + const envString = "H3ll0=world"; + const envVars = parseEnvString(envString); + expect(envVars).toEqual({ + H3ll0: "world", + }); + }); + +}); diff --git a/src/main/helpers/env.helpers.ts b/src/main/helpers/env.helpers.ts index 8eaffea7..bb4dd8c6 100644 --- a/src/main/helpers/env.helpers.ts +++ b/src/main/helpers/env.helpers.ts @@ -1,3 +1,4 @@ +import { CustomError } from "shared/models/exceptions/custom-error.class"; import { ProviderPlatform } from "shared/models/provider-platform.enum"; export function execOnOs(executions: { [key in ProviderPlatform]?: () => T }, noError = false): T { @@ -10,4 +11,118 @@ export function execOnOs(executions: { [key in ProviderPlatform]?: () => T }, } return undefined; -} \ No newline at end of file +} + +enum EnvParserState { + NAME_START, + NAME, + VALUE_START, + VALUE, + QUOTE_VALUE, + DQUOTE_VALUE, + SPACE, + ERROR, +}; + +const isAlphaCharacter = (c: string) => + (c >= "a" && c <= "z") || (c >= "A" && c <= "Z"); +const isNumber = (c: string) => c >= "0" && c <= "9"; + +export function parseEnvString(envString: string): Record { + const envVars: Record = {}; + + let state: EnvParserState = EnvParserState.NAME_START; + let index = 0; + let newName = ""; + for (let pos = 0; pos < envString.length; ++pos) { + const c = envString[pos]; + + switch (state) { + case EnvParserState.NAME_START: + if (isAlphaCharacter(c) || c === "_") { + state = EnvParserState.NAME; + index = pos; + } else if (c !== " ") { + state = EnvParserState.ERROR; + } + break; + + case EnvParserState.NAME: + if (c === "=") { + state = EnvParserState.VALUE_START; + newName = envString.substring(index, pos); + index = pos + 1; + } else if (!isAlphaCharacter(c) && !isNumber(c) && c !== "_") { + state = EnvParserState.ERROR; + } + break; + + case EnvParserState.VALUE_START: + if (c === "'") { + ++index; + state = EnvParserState.QUOTE_VALUE; + } else if (c === '"') { + ++index; + state = EnvParserState.DQUOTE_VALUE; + } else if (c === " ") { + state = EnvParserState.NAME_START; + envVars[newName] = ""; + } else { + state = EnvParserState.VALUE; + } + break; + + case EnvParserState.VALUE: + if (c === " ") { + state = EnvParserState.NAME_START; + envVars[newName] = envString.substring(index, pos); + } + break; + + case EnvParserState.QUOTE_VALUE: + if (c === "'") { + state = EnvParserState.SPACE; + envVars[newName] = envString.substring(index, pos); + } + break; + + case EnvParserState.DQUOTE_VALUE: + if (c === '"') { + state = EnvParserState.SPACE; + envVars[newName] = envString.substring(index, pos); + } + break; + + case EnvParserState.SPACE: + if (c === " ") { + state = EnvParserState.NAME_START; + } else { + state = EnvParserState.ERROR; + } + break; + + default: + } + + if (state === EnvParserState.ERROR) { + throw new CustomError( + `parseEnvString failed: invalid character at position ${pos}`, + "generic.env.parse" + ); + } + } + + if (state === EnvParserState.VALUE_START || state === EnvParserState.VALUE) { + envVars[newName] = envString.substring(index); + return envVars; + } + + if (state === EnvParserState.NAME_START || state === EnvParserState.SPACE) { + return envVars; + } + + throw new CustomError( + "parseEnvString failed: invalid ending state", + "generic.env.parse" + ); +} diff --git a/src/main/services/bs-launcher/abstract-launcher.service.ts b/src/main/services/bs-launcher/abstract-launcher.service.ts index b61d23b9..0399e3f1 100644 --- a/src/main/services/bs-launcher/abstract-launcher.service.ts +++ b/src/main/services/bs-launcher/abstract-launcher.service.ts @@ -8,6 +8,7 @@ import { LinuxService } from "../linux.service"; import { BsmShellLog, bsmSpawn } from "main/helpers/os.helpers"; import { IS_FLATPAK } from "main/constants"; import { LaunchMods } from "shared/models/bs-launch/launch-option.interface"; +import { parseEnvString } from "main/helpers/env.helpers"; export function buildBsLaunchArgs(launchOptions: LaunchOption): string[] { const launchArgs = []; @@ -29,8 +30,8 @@ export function buildBsLaunchArgs(launchOptions: LaunchOption): string[] { launchArgs.push("editor"); } - if (launchOptions.additionalArgs) { - launchArgs.push(...launchOptions.additionalArgs); + if (launchOptions.command) { + launchArgs.push(launchOptions.command); } return Array.from(new Set(launchArgs).values()); @@ -46,6 +47,8 @@ export abstract class AbstractLauncherService { this.localVersions = BSLocalVersionService.getInstance(); } + private readonly COMMAND_FORMAT = "%command%"; + protected launchBSProcess(bsExePath: string, args: string[], options?: SpawnBsProcessOptions): ChildProcessWithoutNullStreams { const spawnOptions: SpawnOptionsWithoutStdio = { detached: true, cwd: path.dirname(bsExePath), ...(options || {}) }; @@ -116,6 +119,34 @@ export abstract class AbstractLauncherService { return { process, exit }; } + + protected injectAdditionalArgsEnvs( + launchOptions: LaunchOption, + env: Record + ) { + if (!launchOptions.command) { + return; + } + + const { command } = launchOptions; + const index = command.indexOf(this.COMMAND_FORMAT); + if (index === -1) { + return; + } + + const envString = command.substring(0, index); + log.info("Parsing env string ", `"${envString}"`) + for (const [ key, value ] of Object.entries(parseEnvString(envString))) { + if (key in env) { + log.warn("Ignoring", `${key}=${value}`, "already set env launch command"); + } else { + log.info("Injecting", `${key}="${value}"`, "to the env launch command"); + } + } + + launchOptions.command = command.substring(index + this.COMMAND_FORMAT.length); + } + } export type SpawnBsProcessOptions = { diff --git a/src/main/services/bs-launcher/bs-launcher.service.ts b/src/main/services/bs-launcher/bs-launcher.service.ts index b03dd0ef..66e4e9e5 100644 --- a/src/main/services/bs-launcher/bs-launcher.service.ts +++ b/src/main/services/bs-launcher/bs-launcher.service.ts @@ -95,10 +95,6 @@ export class BSLauncherService { const params = objectFromEntries(shortcutLink.searchParams.entries()) as ShortcutParams; - if(typeof params.additionalArgs === "string"){ - params.additionalArgs = [params.additionalArgs]; - } - return params; } @@ -123,7 +119,7 @@ export class BSLauncherService { oculus: params.versionOculus === "true", ino: +params.versionIno }, - additionalArgs: params.additionalArgs, + command: params.command, launchMods, }; @@ -141,7 +137,7 @@ export class BSLauncherService { if(launchOptions.launchMods?.includes(LaunchMods.OCULUS)){ res.oculusMode = "true"; } if(launchOptions.launchMods?.includes(LaunchMods.FPFC)){ res.desktopMode = "true"; } if(launchOptions.launchMods?.includes(LaunchMods.DEBUG)){ res.debug = "true"; } - if(launchOptions.additionalArgs){ res.additionalArgs = launchOptions.additionalArgs; } + if(launchOptions.command){ res.command = launchOptions.command; } if(launchOptions.launchMods?.includes(LaunchMods.SKIP_STEAM)){ res.skipSteam = "true"; } if(launchOptions.launchMods?.includes(LaunchMods.PROTON_LOGS)){ res.protonLogs = "true"; } @@ -287,7 +283,7 @@ type ShortcutParams = { oculusMode?: string; desktopMode?: string; debug?: string; - additionalArgs?: string[]; + command?: string; skipSteam?: string; protonLogs?: string; version: string; diff --git a/src/main/services/bs-launcher/oculus-launcher.service.ts b/src/main/services/bs-launcher/oculus-launcher.service.ts index d92ccef9..6a02c652 100644 --- a/src/main/services/bs-launcher/oculus-launcher.service.ts +++ b/src/main/services/bs-launcher/oculus-launcher.service.ts @@ -1,4 +1,4 @@ -import { Observable, ReplaySubject } from "rxjs"; +import { Observable } from "rxjs"; import { StoreLauncherInterface } from "./store-launcher.interface"; import { BSLaunchError, BSLaunchEvent, BSLaunchEventData, LaunchOption } from "../../../shared/models/bs-launch"; import { OculusService } from "../oculus.service"; @@ -9,7 +9,6 @@ import { pathExists } from "fs-extra"; import { AbstractLauncherService, buildBsLaunchArgs } from "./abstract-launcher.service"; import { isProcessRunning } from "../../helpers/os.helpers"; import { CustomError } from "../../../shared/models/exceptions/custom-error.class"; -import { UtilsService } from "../utils.service"; export class OculusLauncherService extends AbstractLauncherService implements StoreLauncherInterface { @@ -23,14 +22,10 @@ export class OculusLauncherService extends AbstractLauncherService implements St } private readonly oculus: OculusService; - private readonly util: UtilsService; - - private readonly oculusLib$ = new ReplaySubject(); private constructor() { super(); this.oculus = OculusService.getInstance(); - this.util = UtilsService.getInstance(); } public launch(launchOptions: LaunchOption): Observable { @@ -54,10 +49,17 @@ export class OculusLauncherService extends AbstractLauncherService implements St // Make sure Oculus is running await this.oculus.startOculus().catch(err => log.error("Error while starting Oculus", err)); + const env: Record = {}; + this.injectAdditionalArgsEnvs(launchOptions, env); + obs.next({type: BSLaunchEvent.BS_LAUNCHING}); // Launch Beat Saber - const process = this.launchBs(exePath, buildBsLaunchArgs(launchOptions)); + const process = this.launchBs( + exePath, + buildBsLaunchArgs(launchOptions), + { env } + ); return process.exit.catch(err => { throw CustomError.fromError(err, BSLaunchError.BS_EXIT_ERROR); diff --git a/src/main/services/bs-launcher/steam-launcher.service.ts b/src/main/services/bs-launcher/steam-launcher.service.ts index ab8277d6..8b46ff42 100644 --- a/src/main/services/bs-launcher/steam-launcher.service.ts +++ b/src/main/services/bs-launcher/steam-launcher.service.ts @@ -102,7 +102,6 @@ export class SteamLauncherService extends AbstractLauncherService implements Sto await this.restoreSteamVR().catch(log.error); } - const launchArgs = buildBsLaunchArgs(launchOptions); const steamPath = await this.steam.getSteamPath(); const env = { @@ -122,6 +121,9 @@ export class SteamLauncherService extends AbstractLauncherService implements Sto Object.assign(env, linuxSetup.env); } + this.injectAdditionalArgsEnvs(launchOptions, env); + const launchArgs = buildBsLaunchArgs(launchOptions); + obs.next({type: BSLaunchEvent.BS_LAUNCHING}); const spawnOpts = { env, cwd: bsFolderPath }; diff --git a/src/renderer/components/modal/modal-types/create-launch-shortcut-modal.component.tsx b/src/renderer/components/modal/modal-types/create-launch-shortcut-modal.component.tsx index 0ec7966e..5c44584d 100644 --- a/src/renderer/components/modal/modal-types/create-launch-shortcut-modal.component.tsx +++ b/src/renderer/components/modal/modal-types/create-launch-shortcut-modal.component.tsx @@ -22,8 +22,8 @@ export const CreateLaunchShortcutModal: ModalComponent<{ steamShortcut: boolean, const color = useThemeColor("second-color"); const [launchOption, setLaunchOptions] = useState(bsLauncher.getLaunchOptions(data)); - const [advanced, setAdvanced] = useState(!!launchOption.additionalArgs?.length); - const [additionalArgsString, setAdditionalArgsString] = useState(launchOption.additionalArgs?.join("; ") ?? ""); + const [advanced, setAdvanced] = useState(!!launchOption.command?.length); + const [command, setCommand] = useState(launchOption.command || ""); const [steamShortcut, setSteamShortcut] = useState(false); const isSteamVersion = useMemo(() => { @@ -31,13 +31,7 @@ export const CreateLaunchShortcutModal: ModalComponent<{ steamShortcut: boolean, }, [data]); const completeModal = () => { - - if(advanced) { - launchOption.additionalArgs = additionalArgsString.split(";").map(arg => arg.trim()).filter(arg => arg.length); - } else { - launchOption.additionalArgs = undefined; - } - + launchOption.command = command.trim(); resolver({exitCode: ModalExitCode.COMPLETED, data: { launchOption, steamShortcut }}); } @@ -95,8 +89,8 @@ export const CreateLaunchShortcutModal: ModalComponent<{ steamShortcut: boolean, type="text" className="w-full rounded-md text-center outline-none bg-light-main-color-3 dark:bg-main-color-3" placeholder={t("pages.version-viewer.launch-mods.advanced-launch.placeholder")} - value={additionalArgsString} - onChange={e => setAdditionalArgsString(e.target.value)} + value={command} + onChange={e => setCommand(e.target.value)} /> diff --git a/src/renderer/components/version-viewer/slides/launch/launch-slide.component.tsx b/src/renderer/components/version-viewer/slides/launch/launch-slide.component.tsx index 3f933c89..472e2086 100644 --- a/src/renderer/components/version-viewer/slides/launch/launch-slide.component.tsx +++ b/src/renderer/components/version-viewer/slides/launch/launch-slide.component.tsx @@ -38,7 +38,7 @@ export function LaunchSlide({ version }: Props) { const versions = useService(BSVersionManagerService); const [advancedLaunch, setAdvancedLaunch] = useState(false); - const [additionalArgsString, setAdditionalArgsString] = useState(configService.get("additionnal-args") || ""); + const [command, setCommand] = useState(configService.get("launch-command") || ""); const versionDownloading = useObservable(() => bsDownloader.downloadingVersion$); const [activeLaunchMods, setActiveLaunchMods] = useState(configService.get("launch-mods") ?? []); const [pinnedLaunchMods, setPinnedLaunchMods] = useState(configService.get("pinned-launch-mods" as DefaultConfigKey) ?? []); @@ -46,8 +46,8 @@ export function LaunchSlide({ version }: Props) { const versionRunning = useObservable(() => bsLauncherService.versionRunning$); useEffect(() => { - configService.set("additionnal-args", additionalArgsString); - }, [additionalArgsString]); + configService.set("launch-command", command); + }, [command]); useEffect(() => { configService.set("pinned-launch-mods", pinnedLaunchMods); @@ -146,12 +146,10 @@ export function LaunchSlide({ version }: Props) { }, [activeLaunchMods, pinnedLaunchMods, version]); const launch = async () => { - const additionalArgs = additionalArgsString?.split(";").map(arg => arg.trim()).filter(arg => arg.length > 0); - const launch$ = bsLauncherService.launch({ version, launchMods: activeLaunchMods, - additionalArgs: advancedLaunch ? additionalArgs : [], + command, }); return lastValueFrom(launch$).catch(() => {}); @@ -195,9 +193,9 @@ export function LaunchSlide({ version }: Props) {
- + { @@ -207,7 +205,7 @@ export function LaunchSlide({ version }: Props) { />
- +
("not-rewind-backup-oculus"); - } - - private setNotRewindBackupOculus(value: boolean): void{ - this.config.set("not-rewind-backup-oculus", value); - } - - public getLaunchOptions(version: BSVersion): LaunchOption{ + public getLaunchOptions(version: BSVersion): LaunchOption { return { version, launchMods: this.config.get("launch-mods") ?? [], - additionalArgs: (this.config.get("additionnal-args") || "").split(";").map(arg => arg.trim()).filter(arg => arg.length > 0), + command: this.config.get("launch-command") || "", } } @@ -61,7 +53,12 @@ export class BSLauncherService { this.notificationService.notifySuccess({title: `notifications.bs-launch.success.titles.${event.type}`, desc: `notifications.bs-launch.success.msg.${event.type}`}); }, error: (err: CustomError) => { - if(!err?.code || !Object.values(BSLaunchError).includes(err.code as BSLaunchError)){ + if (err?.code?.startsWith("generic.")) { + this.notificationService.notifyError({ + title: "notifications.bs-launch.errors.titles.UNKNOWN_ERROR", + desc: err.code, + }); + } else if(!err?.code || !Object.values(BSLaunchError).includes(err.code as BSLaunchError)){ this.notificationService.notifyError({title: "notifications.bs-launch.errors.titles.UNKNOWN_ERROR", desc: "notifications.bs-launch.errors.msg.UNKNOWN_ERROR"}); } else { this.notificationService.notifyError({title: `notifications.bs-launch.errors.titles.${err.code}`, desc: `notifications.bs-launch.errors.msg.${err.code}`, duration: sToMs(9)}) diff --git a/src/shared/models/bs-launch/launch-option.interface.ts b/src/shared/models/bs-launch/launch-option.interface.ts index 6199fdde..d255e67a 100644 --- a/src/shared/models/bs-launch/launch-option.interface.ts +++ b/src/shared/models/bs-launch/launch-option.interface.ts @@ -14,6 +14,6 @@ export type LaunchMod = typeof LaunchMods[keyof typeof LaunchMods]; export interface LaunchOption { version: BSVersion, launchMods?: LaunchMod[], - additionalArgs?: string[], + command?: string, admin?: boolean }