From 97be082dbbe66684f3e8a558e309329cad024e27 Mon Sep 17 00:00:00 2001 From: 0neGal Date: Mon, 29 Apr 2024 18:59:28 +0200 Subject: actual Linux launch support The "Steam (Auto)" launch method should ideally work in all scenarios, ideally! Obviously, I can't and haven't tested in every environment, but I've attempted to make sure it functions. Launching Vanilla and Northstar works just fine, custom launch arguments also work just fine, it works with normal Steam, Flatpak Steam, and as a fallback with the Steam Browser Protocol (`steam://`) There's also the option to set your own/custom launch command for both the Vanilla and Northstar launch options. How well they work will of course depend on what the user set them to. "Steam (Auto)" attempts to pick the right Steam launch method depending on what's available, if the Steam executable can be found, it'll use "Steam (Executable)", if it cant and Flatpak is found on top of an install of Steam through Flatpak, then "Steam (Flatpak)" is used, if all of that fails, then we attempt to use "Steam (Protocol)" Some toasts will be shown if you attempt to run the game with either "Steam (Executable)" or "Steam (Flatpak)" and they cant find the game/Steam. This isn't an issue with "Steam (Auto)" --- src/app/js/toast.js | 4 + src/lang/en.json | 35 ++++++++- src/modules/in_path.js | 24 ++++++ src/modules/launch.js | 197 +++++++++++++++++++++++++++++++++++++++++++------ src/win.js | 19 +++-- 5 files changed, 248 insertions(+), 31 deletions(-) create mode 100644 src/modules/in_path.js diff --git a/src/app/js/toast.js b/src/app/js/toast.js index e0aba6b..501bf42 100644 --- a/src/app/js/toast.js +++ b/src/app/js/toast.js @@ -69,3 +69,7 @@ function dismissToast(id) { }, 500) } } + +ipcRenderer.on("toast", (_, properties) => { + Toast(properties); +}) diff --git a/src/lang/en.json b/src/lang/en.json index eca640d..c705517 100644 --- a/src/lang/en.json +++ b/src/lang/en.json @@ -134,6 +134,7 @@ "title": { "ns": "Northstar", + "linux": "Linux", "language": "Language", "updates": "Updates", "misc": "Miscellaneous" @@ -198,6 +199,26 @@ "open_gamepath_alert": "No valid gamepath is selected, so there's no gamepath to open, please select a valid gamepath first!", "reset_config_alert": "Please confirm that you want to reset the config file, after confirming Viper will delete the config file, and then restart." + }, + + "linux_launch_method": { + "title": "Linux launch method", + "desc": "The method to use when launching the game on Linux", + "methods": { + "steam_auto": "Steam (Auto)", + "steam_executable": "Steam (Executable)", + "steam_flatpak": "Steam (Flatpak)", + "steam_protocol": "Steam (Protocol)", + "command": "Custom command" + } + }, + "linux_launch_cmd_ns": { + "title": "Northstar launch command", + "desc": "This is the command that will be used when you use \"Custom command\" as the launch method and you're launching Northstar, launch options are appended at the end of the command and in an environment variable named $TF_ARGS" + }, + "linux_launch_cmd_vanilla": { + "title": "Vanilla launch command", + "desc": "This is the command that will be used when you use \"Custom command\" as the launch method and you're launching the vanilla game, launch options are appended at the end of the command and in an environment variable named $TF_ARGS" } }, @@ -234,7 +255,12 @@ "failed": "Failed to install", "malformed": "Incorrect folder structure!", "unknown_error": "Unknown Error!", - "no_internet": "No Internet" + "no_internet": "No Internet", + "failed_launch_command": "Failed running launch command", + "missing_launch_command": "Missing launch command", + "missing_steam": "Missing Steam", + "missing_flatpak": "Missing Flatpak", + "missing_flatpak_steam": "Missing Flatpak Steam" }, "desc": { @@ -243,7 +269,12 @@ "failed": "An unknown error occurred while trying to install the mod. This may be the author's fault, and it may also be Viper's fault.", "duped": "has multiple mod folders in it, with the same name, causing duplicate folders, if you're the developer, you should fix this.", "unknown_error": "An unknown error occurred, click for more details. You may want to take a screenshot of the detailed error when filing a bug report.", - "no_internet": "Viper may not work properly." + "no_internet": "Viper may not work properly.", + "failed_launch_command": "Something went wrong whilst running the custom launch command", + "missing_launch_command": "There's currently no custom launch command set, one has to be configured to launch", + "missing_steam": "Can't launch with Steam directly, as it doesn't seem to be installed", + "missing_flatpak": "Can't launch with Flatpak, as it doesn't seem to be installed", + "missing_flatpak_steam": "Can't launch with the Flatpak version of Steam, as it doesn't seem to be installed" } } }, diff --git a/src/modules/in_path.js b/src/modules/in_path.js new file mode 100644 index 0000000..49288c1 --- /dev/null +++ b/src/modules/in_path.js @@ -0,0 +1,24 @@ +const fs = require("fs"); +const join = require("path").join; + +// checks whether `executable_to_check` is in `$PATH` +module.exports = (executable_to_check) => { + // get folders in `$PATH` + let path_dirs = process.env["PATH"].split(":"); + + // run through folders + for (let i = 0; i < path_dirs.length; i++) { + // path to executable this iteration + let executable = join(path_dirs[i], executable_to_check); + + // if `executable` exists and is a file, then we found it + if (fs.existsSync(executable) + && fs.statSync(executable).isFile()) { + + return true; + } + } + + // we didn't find `executable_to_check` + return false; +} diff --git a/src/modules/launch.js b/src/modules/launch.js index 73ce408..544e155 100644 --- a/src/modules/launch.js +++ b/src/modules/launch.js @@ -1,15 +1,16 @@ -const exec = require("child_process").exec; -const ipcMain = require("electron").ipcMain; +const { ipcMain, shell } = require("electron"); +const { exec, execSync } = require("child_process"); const cli = require("../cli"); const win = require("../win"); const lang = require("../lang"); +const in_path = require("./in_path"); const settings = require("./settings"); console = require("./console"); -ipcMain.on("launch-ns", launch); +ipcMain.on("launch-ns", () => {launch()}); ipcMain.on("launch-vanilla", () => { launch("vanilla"); }) @@ -18,37 +19,187 @@ ipcMain.on("launch-vanilla", () => { // // either Northstar or Vanilla. Linux support is not currently a thing, // however it'll be added at some point. -function launch(game_version) { - // return early, and show error message if on Linux - if (process.platform == "linux") { - win().alert(lang("cli.launch.linux_error")); - console.error(lang("cli.launch.linux_error")); - cli.exit(1); - return; - } +function launch(game_version, method = settings().linux_launch_method) { + console.info( + lang("general.launching"), + game_version || "Northstar" + "..." + ) // change current directory to gamepath process.chdir(settings().gamepath); let launch_args = settings().nsargs || ""; - // launch the requested game version - switch(game_version) { - case "vanilla": - console.info(lang("general.launching"), "Vanilla..."); - exec("Titanfall2.exe " + launch_args, { - cwd: settings().gamepath + // add `-vanilla` or `-northstar` depending on `game_version` + if (game_version == "vanilla") { + launch_args += " -vanilla" + } else { + launch_args += " -northstar" + } + + + // Linux launch support + if (process.platform == "linux") { + let flatpak_id = "com.valvesoftware.Steam"; + let steam_run_str = `steam://run/1237970//${launch_args}/`; + console.log(steam_run_str) + + // returns whether the Flatpak version of Steam is installed + let flatpak_steam_installed = () => { + // this will throw an error if the command fails, + // either because of an error with the command, or + // because it's not installed, either way, + // indicating it's not installed + try { + execSync( + `flatpak info ${flatpak_id}` + ) + + return true; + }catch(err) {} + + return false; + } + + switch(method) { + case "steam_auto": + // if a Steam executable is found, use that + if (in_path("steam")) { + return launch(game_version, "steam_executable"); + + // is Flatpak (likely) installed? + } else if (in_path("flatpak")) { + + if (flatpak_steam_installed()) { + return launch(game_version, "steam_flatpak"); + } + } + + // fallback to Steam protocol + return launch(game_version, "steam_protocol"); + + case "steam_flatpak": + // make sure Flatpak is installed, and show an error + // toast if not + if (! in_path("flatpak")) { + win().toast({ + scheme: "error", + title: lang("gui.toast.title.missing_flatpak"), + description: lang( + "gui.toast.desc.missing_flatpak" + ) + }) + return; + } + + // make sure the Flatpak version of Steam is installed, + // and show an error toast if not + if (! flatpak_steam_installed()) { + win().toast({ + scheme: "error", + title: lang( + "gui.toast.title.missing_flatpak_steam" + ), + description: lang( + "gui.toast.desc.missing_flatpak_steam" + ) + }) + return; + } + + // attempt to launch the game with Flatpak Steam + exec(`flatpak run ${flatpak_id} "${steam_run_str}"`, { + + cwd: settings().gamepath + }) + + return; + + case "steam_executable": + // make sure Steam is installed, and show an error toast + // if not + if (! in_path("steam")) { + win().toast({ + scheme: "error", + title: lang("gui.toast.title.missing_steam"), + description: lang( + "gui.toast.desc.missing_steam" + ) + }) + return; + } + + // attempt to launch the game with the Steam executable + exec(`steam "${steam_run_str}"`, { + cwd: settings().gamepath + }) + + return; + + case "steam_protocol": + // attempt to launch the game with the Steam protocol + shell.openExternal(steam_run_str) + + return; + } + + // launch Vanilla with custom command + let command = settings().linux_launch_cmd_ns; + if (game_version == "vanilla") { + command = settings().linux_launch_cmd_vanilla; + } + + // make sure the custom command is not just whitespace, and show + // an error toast if it is + if (! command.trim()) { + win().toast({ + scheme: "error", + title: lang("gui.toast.title.missing_launch_command"), + description: lang( + "gui.toast.desc.missing_launch_command" + ) }) - break; - default: - console.info(lang("general.launching"), "Northstar..."); - exec("NorthstarLauncher.exe " + launch_args, { - cwd: settings().gamepath + return; + } + + // launch Northstar with custom command + try { + exec(command, { + cwd: settings().gamepath, + env: { + ...process.env, + TF_ARGS: launch_args + } }) + }catch(err) { + // show error if custom commands fails + // this should basically never trigger, it should only + // trigger if `command` isn't set to something valid + win().toast({ + scheme: "error", + title: lang("gui.toast.title.failed_launch_command"), + description: lang( + "gui.toast.desc.failed_launch_command" + ) + }) + } + + return; + } - break; + // default launches with Northstar + let executable = "NorthstarLauncher.exe" + + // change over to using vanilla executable + if (game_version == "vanilla") { + executable = "Titanfall2.exe" } + + // launch executable/game + exec(executable + " " + launch_args, { + cwd: settings().gamepath + }) } module.exports = launch; diff --git a/src/win.js b/src/win.js index 9bec3c9..91ef93a 100644 --- a/src/win.js +++ b/src/win.js @@ -25,6 +25,11 @@ alert = async (msg) => { }) } +// sends an toast to the renderer +toast = (properties) => { + win.send("toast", properties); +} + // this increments for every confirm alert that's created, the ID is // used to keep track of popups being opened or closed. let confirm_id = 0; @@ -48,9 +53,10 @@ confirm = async (msg) => { let win = { send: () => {}, - log: log, - alert: alert, - confirm: confirm + log: log, + toast: toast, + alert: alert, + confirm: confirm } let func = () => { @@ -60,9 +66,10 @@ let func = () => { func.set = (main_window) => { win = main_window; - win.log = log; - win.alert = alert; - win.confirm = confirm; + win.log = log; + win.toast = toast; + win.alert = alert; + win.confirm = confirm; } module.exports = func; -- cgit v1.2.3