From 9dccdb0b0335f82aa9f2e63642f16ff0f2699528 Mon Sep 17 00:00:00 2001 From: Jan Date: Tue, 18 Jul 2023 14:26:20 +0200 Subject: feat: Add basic Proton management (#383) Adds logic to install, remove, and check existing install of NorthstarProton. --- src-tauri/Cargo.lock | 3 ++ src-tauri/Cargo.toml | 4 +- src-tauri/src/main.rs | 32 ++++++++++++++ src-tauri/src/platform_specific/linux.rs | 75 ++++++++++++++++++++++++++++++++ src-vue/src/views/DeveloperView.vue | 28 ++++++++++++ 5 files changed, 141 insertions(+), 1 deletion(-) diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 77064841..968d523b 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -83,6 +83,7 @@ dependencies = [ "chrono", "const_format", "dirs", + "glob", "json5", "libthermite", "log", @@ -1987,9 +1988,11 @@ version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "27cd844bbc25676cd14fa9ad04cc40e0f3c4d5c66107ef3a99896db1f81324c0" dependencies = [ + "flate2", "json5", "serde", "serde_json", + "tar", "thiserror", "tracing", "ureq", diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index ed523a4b..63ff51f7 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -33,7 +33,7 @@ steamlocate = "1.2" # Error messages anyhow = "1.0" # libthermite for Northstar/mod install handling -libthermite = "0.6.5" +libthermite = { version = "0.6.5", features = ["proton"] } # zip stuff zip = "0.6.2" # Regex @@ -62,6 +62,8 @@ zip-extract = "0.1.2" # open urls open = "3.2.0" semver = "1.0" +# simplified filesystem access +glob = "0.3.1" dirs = "5" [target.'cfg(windows)'.dependencies] diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index a584eea9..a295f322 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -144,6 +144,9 @@ fn main() { mod_management::delete_northstar_mod, util::get_server_player_count, mod_management::delete_thunderstore_mod, + install_northstar_proton_wrapper, + uninstall_northstar_proton_wrapper, + get_local_northstar_proton_wrapper_version, open_repair_window, thunderstore::query_thunderstore_packages_api, github::get_list_of_tags, @@ -525,3 +528,32 @@ pub fn check_is_valid_game_path(game_install_path: &str) -> Result<(), String> { fn get_host_os() -> String { env::consts::OS.to_string() } + +/// On Linux attempts to install NorthstarProton +/// On Windows simply returns an error message +#[tauri::command] +async fn install_northstar_proton_wrapper() -> Result<(), String> { + #[cfg(target_os = "linux")] + return linux::install_ns_proton().map_err(|err| err.to_string()); + + #[cfg(target_os = "windows")] + Err("Not supported on Windows".to_string()) +} + +#[tauri::command] +async fn uninstall_northstar_proton_wrapper() -> Result<(), String> { + #[cfg(target_os = "linux")] + return linux::uninstall_ns_proton(); + + #[cfg(target_os = "windows")] + Err("Not supported on Windows".to_string()) +} + +#[tauri::command] +async fn get_local_northstar_proton_wrapper_version() -> Result { + #[cfg(target_os = "linux")] + return linux::get_local_ns_proton_version(); + + #[cfg(target_os = "windows")] + Err("Not supported on Windows".to_string()) +} diff --git a/src-tauri/src/platform_specific/linux.rs b/src-tauri/src/platform_specific/linux.rs index 4b9964e9..674f384b 100644 --- a/src-tauri/src/platform_specific/linux.rs +++ b/src-tauri/src/platform_specific/linux.rs @@ -3,6 +3,81 @@ use regex::Regex; use std::process::Command; +fn get_proton_dir() -> Option { + let steam_dir = steamlocate::SteamDir::locate()?; + let compat_dir = format!("{}/compatibilitytools.d/", steam_dir.path.display()); + + Some(compat_dir) +} + +/// Downloads and installs NS proton +/// Assumes Steam install +pub fn install_ns_proton() -> Result<(), thermite::prelude::ThermiteError> { + // Get latest NorthstarProton release + let latest = thermite::core::latest_release()?; + + let temp_dir = std::env::temp_dir(); + let path = format!("{}/nsproton-{}.tar.gz", temp_dir.display(), latest); + let archive = std::fs::File::create(path.clone())?; + + // Download the latest Proton release + log::info!("Downloading NorthstarProton to {}", path); + thermite::core::download_ns_proton(latest, archive)?; + log::info!("Finished Download"); + + let compat_dir = get_proton_dir().unwrap(); + std::fs::create_dir_all(compat_dir.clone())?; + + let finished = std::fs::File::open(path.clone())?; + + // Extract to Proton dir + log::info!("Installing NorthstarProton to {}", compat_dir); + thermite::core::install_ns_proton(&finished, compat_dir)?; + log::info!("Finished Installation"); + drop(finished); + + std::fs::remove_file(path)?; + + Ok(()) +} + +/// Remove NS Proton +pub fn uninstall_ns_proton() -> Result<(), String> { + let compat_dir = get_proton_dir().unwrap(); + let pattern = format!("{}/NorthstarProton-*", compat_dir); + for e in glob::glob(&pattern).expect("Failed to read glob pattern") { + std::fs::remove_dir_all(e.unwrap()).unwrap(); + } + + Ok(()) +} + +/// Get the latest installed NS Proton version +pub fn get_local_ns_proton_version() -> Result { + let compat_dir = get_proton_dir().unwrap(); + let ns_prefix = "NorthstarProton-"; + let pattern = format!("{}/{}*/version", compat_dir, ns_prefix); + + let mut version: String = "".to_string(); + + for e in glob::glob(&pattern).expect("Failed to read glob pattern") { + let version_content = std::fs::read_to_string(e.unwrap()).unwrap(); + let version_string = version_content.split(' ').nth(1).unwrap(); + + if version_string.starts_with(ns_prefix) { + version = version_string[ns_prefix.len()..version_string.len() - 1] + .to_string() + .clone(); + } + } + + if version.is_empty() { + return Err("Northstar Proton is not installed".to_string()); + } + + Ok(version) +} + pub fn check_glibc_v() -> f32 { let out = Command::new("/bin/ldd") .arg("--version") diff --git a/src-vue/src/views/DeveloperView.vue b/src-vue/src/views/DeveloperView.vue index 500c9c35..28ab3892 100644 --- a/src-vue/src/views/DeveloperView.vue +++ b/src-vue/src/views/DeveloperView.vue @@ -21,6 +21,18 @@ Check NSProton Compatibility + + Install NSProton + + + + Remove NSProton + + + + Get local NSProton Version + +

Testing:

@@ -308,6 +320,22 @@ export default defineComponent({ notification.close(); }); }, + async installNSProton() { + showNotification(`Started NSProton install`); + await invoke("install_northstar_proton_wrapper") + .then((message) => { showNotification(`Done`); }) + .catch((error) => { showNotification(`Error`, error, "error"); }) + }, + async uninstallNSProton() { + await invoke("uninstall_northstar_proton_wrapper") + .then((message) => { showNotification(`Done`); }) + .catch((error) => { showNotification(`Error`, error, "error"); }) + }, + async getLocalNSProtonVersion() { + await invoke("get_local_northstar_proton_wrapper_version") + .then((message) => { showNotification(`NSProton Version`, message as string); }) + .catch((error) => { showNotification(`Error`, error, "error"); }) + }, } }); -- cgit v1.2.3 From 2153cb606e3c811c9ab36c4a10132f7166a51054 Mon Sep 17 00:00:00 2001 From: Harmony Weblate <96563367+harmony-weblate@users.noreply.github.com> Date: Tue, 18 Jul 2023 23:49:26 +0200 Subject: i18n: Translations update from Weblate (#430) Translated using Weblate (Chinese (Simplified)) Currently translated at 70.5% (72 of 102 strings) Translation: Northstar/FlightCore Translate-URL: https://translate.harmony.tf/projects/northstar/flightcore/zh_Hans/ Co-authored-by: zxcPandora --- src-vue/src/i18n/lang/zh_Hans.json | 99 +++++++++++++++++++++++++++++++++++++- 1 file changed, 98 insertions(+), 1 deletion(-) diff --git a/src-vue/src/i18n/lang/zh_Hans.json b/src-vue/src/i18n/lang/zh_Hans.json index 0967ef42..17db60ec 100644 --- a/src-vue/src/i18n/lang/zh_Hans.json +++ b/src-vue/src/i18n/lang/zh_Hans.json @@ -1 +1,98 @@ -{} +{ + "menu": { + "mods": "模组", + "settings": "设置", + "dev": "开发者模式", + "play": "开始游玩", + "changelog": "更新日志" + }, + "generic": { + "error": "错误", + "cancel": "取消", + "yes": "是", + "no": "否", + "informationShort": "信息", + "downloading": "下载中", + "extracting": "解压中", + "done": "完成", + "success": "成功" + }, + "play": { + "button": { + "northstar_is_running": "游戏正在运行", + "select_game_dir": "选择Titanfall 2游戏目录", + "install": "安装", + "installing": "安装中...", + "update": "升级", + "ready_to_play": "启动游戏", + "updating": "升级中..." + }, + "unknown_version": "未知版本", + "see_patch_notes": "参阅相关补丁说明", + "servers": "服务器", + "players": "玩家", + "unable_to_load_playercount": "加载玩家数量失败", + "ea_app_running": "EA App正在运行:", + "northstar_running": "Northstar正在运行:" + }, + "mods": { + "local": { + "no_mods": "未找到模组。", + "delete_confirm": "你确定要删除该模组吗?", + "delete": "删除", + "part_of_ts_mod": "该Northstar模组来源于Thunderstore", + "success_deleting": "成功删除 {modName}" + }, + "online": { + "try_another_search": "尝试其他搜索方式!", + "no_match": "未找到相匹配的模组。" + }, + "menu": { + "local": "本地", + "search": "搜索", + "sort_mods": "模组排序", + "select_categories": "标签选择", + "sort": { + "date_desc": "日期降序", + "date_asc": "日期升序", + "name_desc": "按名称(Z到A)", + "most_downloaded": "最多下载", + "top_rated": "最高评分", + "name_asc": "按名称(A到Z)" + }, + "online": "线上", + "filter": "筛选" + }, + "card": { + "button": { + "being_installed": "安装中...", + "being_updated": "升级中...", + "installed": "已安装", + "install": "安装", + "outdated": "升级" + }, + "by": "作者:", + "remove": "移除模组", + "remove_dialog_title": "警告", + "remove_success": "已移除{modName}", + "install_success": "已安装 {modName}", + "more_info": "更多信息", + "remove_dialog_text": "删除该来自Thunderstore的模组?" + } + }, + "settings": { + "manage_install": "安装管理", + "choose_folder": "选择安装目录", + "open_game_folder": "打开文件夹", + "nb_ts_mods_per_page": "Thunderstore每页显示多少个模组", + "nb_ts_mods_per_page_desc2": "该值设为0时将不再显示页码。", + "nb_ts_mods_reset": "重置为默认值", + "language": "语言", + "language_select": "请选择你需要的语言", + "about": "关于:", + "flightcore_version": "FlightCore 版本:", + "testing": "测试选项:", + "enable_test_channels": "开启测试版本选项", + "nb_ts_mods_per_page_desc1": "该数值对加载Thunderstore页面时的速度有影响。" + } +} -- cgit v1.2.3 From 8e733e5127463073aa90c566afdb5c242af64aa0 Mon Sep 17 00:00:00 2001 From: Jan Date: Wed, 19 Jul 2023 02:06:38 +0200 Subject: feat: Replace MessageBoxW invocation with Tauri dialog (#418) --- src-tauri/Cargo.toml | 2 +- src-tauri/src/main.rs | 36 ++++++++++++++---------------------- 2 files changed, 15 insertions(+), 23 deletions(-) diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 63ff51f7..443a997b 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -23,7 +23,7 @@ tauri-build = { version = "1.4", features = [] } [dependencies] serde_json = "1.0" serde = { version = "1.0", features = ["derive"] } -tauri = { version = "1.4", features = ["api-all", "updater"] } +tauri = { version = "1.4", features = ["api-all", "dialog", "updater"] } tokio = { version = "1", features = ["full"] } # Sentry (crash) logging sentry = "0.30" diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index a295f322..1067f5d3 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -9,11 +9,6 @@ use std::{ time::Duration, }; -#[cfg(target_os = "windows")] -use std::ptr::null_mut; -#[cfg(target_os = "windows")] -use winapi::um::winuser::{MessageBoxW, MB_ICONERROR, MB_OK, MB_USERICON}; - use crate::constants::REFRESH_DELAY; mod development; @@ -26,6 +21,10 @@ mod util; use semver::Version; use serde::{Deserialize, Serialize}; +#[cfg(target_os = "windows")] +use tauri::api::dialog::blocking::MessageDialogBuilder; +#[cfg(target_os = "windows")] +use tauri::api::dialog::{MessageDialogButtons, MessageDialogKind}; use tauri::{Manager, Runtime}; use tokio::time::sleep; use ts_rs::TS; @@ -175,23 +174,16 @@ fn main() { #[cfg(target_os = "windows")] { log::error!("WebView2 not installed: {err}"); - // Display a message box to the user with a button to open the installation instructions - let title = "WebView2 not found" - .encode_utf16() - .chain(Some(0)) - .collect::>(); - let message = "FlightCore requires WebView2 to run.\n\nClick OK to open installation instructions.".encode_utf16().chain(Some(0)).collect::>(); - unsafe { - let result = MessageBoxW( - null_mut(), - message.as_ptr(), - title.as_ptr(), - MB_OK | MB_ICONERROR | MB_USERICON, - ); - if result == 1 { - // Open the installation instructions URL in the user's default web browser - open::that("https://github.com/R2NorthstarTools/FlightCore/blob/main/docs/TROUBLESHOOTING.md#flightcore-wont-launch").unwrap(); - } + let dialog = MessageDialogBuilder::new( + "WebView2 not found", + "FlightCore requires WebView2 to run.\n\nClick OK to open installation instructions." + ) + .kind(MessageDialogKind::Error) + .buttons(MessageDialogButtons::Ok); + + if dialog.show() { + // Open the installation instructions URL in the user's default web browser + open::that("https://github.com/R2NorthstarTools/FlightCore/blob/main/docs/TROUBLESHOOTING.md#flightcore-wont-launch").unwrap(); } } } -- cgit v1.2.3 From 68c765738331deb207621d25c87b1b6eda4ebf3c Mon Sep 17 00:00:00 2001 From: Harmony Weblate <96563367+harmony-weblate@users.noreply.github.com> Date: Wed, 19 Jul 2023 11:05:39 +0200 Subject: i18n: Translations update from Weblate (#431) Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (102 of 102 strings) Translation: Northstar/FlightCore Translate-URL: https://translate.harmony.tf/projects/northstar/flightcore/zh_Hans/ Co-authored-by: zxcPandora --- src-vue/src/i18n/lang/zh_Hans.json | 56 +++++++++++++++++++++++++++++++++++++- 1 file changed, 55 insertions(+), 1 deletion(-) diff --git a/src-vue/src/i18n/lang/zh_Hans.json b/src-vue/src/i18n/lang/zh_Hans.json index 17db60ec..dbc53104 100644 --- a/src-vue/src/i18n/lang/zh_Hans.json +++ b/src-vue/src/i18n/lang/zh_Hans.json @@ -93,6 +93,60 @@ "flightcore_version": "FlightCore 版本:", "testing": "测试选项:", "enable_test_channels": "开启测试版本选项", - "nb_ts_mods_per_page_desc1": "该数值对加载Thunderstore页面时的速度有影响。" + "nb_ts_mods_per_page_desc1": "该数值对加载Thunderstore页面时的速度有影响。", + "dev_mode_enabled_title": "看上面!", + "dev_mode_enabled_text": "开发者模式已启用。", + "show_deprecated_mods": "显示已弃用的Thunderstore模组", + "show_deprecated_mods_desc1": "该选项会使您可以在线上模组合集中看到已弃用的模组。", + "show_deprecated_mods_desc2": "请注意,这类模组被弃用一般是有原因的。", + "repair": { + "title": "修复", + "window": { + "title": "FlightCore 修复工具", + "warning": "此工具包含修复Northstar和FlightCore各种常见问题的功能。", + "disable_all_but_core": "除了核心模组以外禁用其他模组", + "disable_all_but_core_success": "已禁用除核心模组以外的所有模组", + "disable_modsettings": "禁用ModSettings模组", + "disable_modsettings_success": "已禁用ModSettings模组", + "force_delete_temp_dl": "强制删除临时下载目录", + "delete_persistent_store": "删除FlightCore永久存储文件", + "reinstall_text": "请耐心等待", + "reinstall_success": "成功重装Northstar", + "force_reinstall_ns": "强制重装Northstar", + "reinstall_title": "正在强制重装Northstar" + }, + "open_window": "打开修复工具" + } + }, + "notification": { + "game_folder": { + "new": { + "title": "新的游戏目录", + "text": "已成功更新游戏目录。" + }, + "wrong": { + "title": "错误的文件夹", + "text": "所选文件夹不是有效的Titanfall2安装目录。" + }, + "not_found": { + "title": "未找到Titanfall2!", + "text": "请手动选择安装目录" + } + }, + "flightcore_outdated": { + "title": "FlightCore需要更新!", + "text": "请更新FlightCore.\n正在运行旧版本 {oldVersion}.\n最新版本为 {newVersion}!" + } + }, + "channels": { + "release": { + "switch": { + "text": "将资源版本切换至 \"{canal}\"." + } + }, + "names": { + "NorthstarReleaseCandidate": "Northstar测试版本", + "Northstar": "Northstar" + } } } -- cgit v1.2.3 From 54b775d60450928365f8267d1c871f7d57042b5e Mon Sep 17 00:00:00 2001 From: GeckoEidechse <40122905+GeckoEidechse@users.noreply.github.com> Date: Wed, 19 Jul 2023 15:22:44 +0200 Subject: i18n: Enable Simplified Chinese translations (#432) --- src-vue/src/components/LanguageSelector.vue | 4 ++++ src-vue/src/main.ts | 3 ++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src-vue/src/components/LanguageSelector.vue b/src-vue/src/components/LanguageSelector.vue index afa2549a..4f275369 100644 --- a/src-vue/src/components/LanguageSelector.vue +++ b/src-vue/src/components/LanguageSelector.vue @@ -46,6 +46,10 @@ export default defineComponent({ value: 'it', label: 'Italiano' }, + { + value: 'zh_Hans', + label: '简体中文' + }, ] }), mounted: async function () { diff --git a/src-vue/src/main.ts b/src-vue/src/main.ts index e07c14f2..b595f21c 100644 --- a/src-vue/src/main.ts +++ b/src-vue/src/main.ts @@ -17,6 +17,7 @@ import de from "./i18n/lang/de.json"; import pl from "./i18n/lang/pl.json"; import ru from "./i18n/lang/ru.json"; import it from "./i18n/lang/it.json"; +import zh_Hans from "./i18n/lang/zh_Hans.json"; const app = createApp(App); @@ -26,7 +27,7 @@ export const i18n = createI18n({ locale: 'en', fallbackLocale: 'en', messages: { - en, fr, de, pl, ru, it + en, fr, de, pl, ru, it, zh_Hans } }); app.use(i18n); -- cgit v1.2.3 From 085ed68999db7ceb16b2d38a258f752e621e4981 Mon Sep 17 00:00:00 2001 From: GeckoEidechse Date: Wed, 19 Jul 2023 15:24:12 +0200 Subject: chore: Bump FlightCore version to 1.24.0 --- src-tauri/Cargo.lock | 2 +- src-tauri/Cargo.toml | 2 +- src-tauri/tauri.conf.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 968d523b..cd380a27 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -76,7 +76,7 @@ checksum = "224afbd727c3d6e4b90103ece64b8d1b67fbb1973b1046c2281eed3f3803f800" [[package]] name = "app" -version = "1.23.0" +version = "1.24.0" dependencies = [ "anyhow", "async-recursion", diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 443a997b..03eb8e9e 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "app" -version = "1.23.0" +version = "1.24.0" description = "A Tauri App" authors = ["you"] license = "" diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index 820b6d9b..b801b9a6 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -8,7 +8,7 @@ }, "package": { "productName": "FlightCore", - "version": "1.23.0" + "version": "1.24.0" }, "tauri": { "allowlist": { -- cgit v1.2.3 From 2ad7807a0232c09e238c5847844fa21e01e6e4b4 Mon Sep 17 00:00:00 2001 From: GeckoEidechse <40122905+GeckoEidechse@users.noreply.github.com> Date: Wed, 19 Jul 2023 16:58:43 +0200 Subject: feat: Support reading mods from Northstar `packages` directory (#417) --- src-tauri/src/mod_management/mod.rs | 144 +++++++++++++++++++++++++++++++++++- 1 file changed, 140 insertions(+), 4 deletions(-) diff --git a/src-tauri/src/mod_management/mod.rs b/src-tauri/src/mod_management/mod.rs index 825cba37..2ac26ede 100644 --- a/src-tauri/src/mod_management/mod.rs +++ b/src-tauri/src/mod_management/mod.rs @@ -4,15 +4,17 @@ use crate::constants::{BLACKLISTED_MODS, CORE_MODS}; use async_recursion::async_recursion; use crate::NorthstarMod; -use anyhow::Result; +use anyhow::{anyhow, Result}; use serde::{Deserialize, Serialize}; +use std::str::FromStr; +use std::string::ToString; use std::{fs, path::PathBuf}; mod legacy; use crate::GameInstall; #[derive(Debug, Clone)] -struct ParsedThunderstoreModString { +pub struct ParsedThunderstoreModString { author_name: String, mod_name: String, version: String, @@ -182,6 +184,132 @@ pub fn set_mod_enabled_status( Ok(()) } +/// Resembles the bare minimum keys in Northstar `mods.json` +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct ModJson { + #[serde(rename = "Name")] + name: String, + #[serde(rename = "Version")] + version: Option, +} + +/// Parse `mods` folder for installed mods. +pub fn parse_mods_in_package( + package_mods_path: PathBuf, + thunderstore_mod_string: ParsedThunderstoreModString, +) -> Result, anyhow::Error> { + let paths = match std::fs::read_dir(package_mods_path) { + Ok(paths) => paths, + Err(_err) => return Err(anyhow!("No mods folder found")), + }; + + let mut directories: Vec = Vec::new(); + let mut mods: Vec = Vec::new(); + + // Get list of folders in `mods` directory + for path in paths { + let my_path = path.unwrap().path(); + let md = std::fs::metadata(my_path.clone()).unwrap(); + if md.is_dir() { + directories.push(my_path); + } + } + + // Iterate over folders and check if they are Northstar mods + for directory in directories { + let directory_str = directory.to_str().unwrap().to_string(); + // Check if mod.json exists + let mod_json_path = format!("{}/mod.json", directory_str); + if !std::path::Path::new(&mod_json_path).exists() { + continue; + } + + // Read file into string and parse it + let data = std::fs::read_to_string(mod_json_path.clone())?; + let parsed_mod_json: ModJson = match json5::from_str(&data) { + Ok(parsed_json) => parsed_json, + Err(err) => { + log::warn!("Failed parsing {} with {}", mod_json_path, err.to_string()); + continue; + } + }; + + // Get directory path + let mod_directory = directory.to_str().unwrap().to_string(); + + let ns_mod = NorthstarMod { + name: parsed_mod_json.name, + version: parsed_mod_json.version, + thunderstore_mod_string: Some(thunderstore_mod_string.to_string()), + enabled: false, // Placeholder + directory: mod_directory, + }; + + mods.push(ns_mod); + } + + // Return found mod names + Ok(mods) +} + +/// Parse `packages` folder for installed mods. +pub fn parse_installed_package_mods( + game_install: &GameInstall, +) -> Result, anyhow::Error> { + let mut collected_mods: Vec = Vec::new(); + + let packages_folder = format!("{}/R2Northstar/packages/", game_install.game_path); + + let packages_dir = match fs::read_dir(packages_folder) { + Ok(res) => res, + Err(err) => { + // We couldn't read directory, probably cause it doesn't exist yet. + // In that case we just say no package mods installed. + log::warn!("{err}"); + return Ok(vec![]); + } + }; + + // Iteratore over folders in `packages` dir + for entry in packages_dir { + let entry_path = entry?.path(); + let entry_str = entry_path.file_name().unwrap().to_str().unwrap(); + + // Use the struct's from_str function to verify format + if entry_path.is_dir() { + let package_thunderstore_string = match ParsedThunderstoreModString::from_str(entry_str) + { + Ok(res) => res, + Err(err) => { + log::warn!( + "Not a Thunderstore mod string \"{}\" cause: {}", + entry_path.display(), + err + ); + continue; + } + }; + let manifest_path = entry_path.join("manifest.json"); + let mods_path = entry_path.join("mods"); + + // Ensure `manifest.json` and `mods/` dir exist + if manifest_path.exists() && mods_path.is_dir() { + let mods = + match parse_mods_in_package(mods_path, package_thunderstore_string.clone()) { + Ok(res) => res, + Err(err) => { + log::warn!("Failed parsing cause: {err}"); + continue; + } + }; + collected_mods.extend(mods); + } + } + } + + Ok(collected_mods) +} + /// Gets list of installed mods and their properties /// - name /// - is enabled? @@ -189,11 +317,19 @@ pub fn set_mod_enabled_status( pub fn get_installed_mods_and_properties( game_install: GameInstall, ) -> Result, String> { - // Get actually installed mods - let found_installed_mods = match legacy::parse_installed_mods(&game_install) { + // Get installed mods from packages + let mut found_installed_mods = match parse_installed_package_mods(&game_install) { Ok(res) => res, Err(err) => return Err(err.to_string()), }; + // Get installed legacy mods + let found_installed_legacy_mods = match legacy::parse_installed_mods(&game_install) { + Ok(res) => res, + Err(err) => return Err(err.to_string()), + }; + + // Combine list of package and legacy mods + found_installed_mods.extend(found_installed_legacy_mods); // Get enabled mods as JSON let enabled_mods: serde_json::Value = match get_enabled_mods(&game_install) { -- cgit v1.2.3 From c834328029846ce0e5785d33e2e854d3cd8557b8 Mon Sep 17 00:00:00 2001 From: Harmony Weblate <96563367+harmony-weblate@users.noreply.github.com> Date: Wed, 19 Jul 2023 17:51:27 +0200 Subject: i18n: Translations update from Weblate (#433) Translated using Weblate (Chinese (Simplified)) Currently translated at 100.0% (102 of 102 strings) Translation: Northstar/FlightCore Translate-URL: https://translate.harmony.tf/projects/northstar/flightcore/zh_Hans/ Co-authored-by: zxcPandora --- src-vue/src/i18n/lang/zh_Hans.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src-vue/src/i18n/lang/zh_Hans.json b/src-vue/src/i18n/lang/zh_Hans.json index dbc53104..6447a6cf 100644 --- a/src-vue/src/i18n/lang/zh_Hans.json +++ b/src-vue/src/i18n/lang/zh_Hans.json @@ -32,8 +32,8 @@ "servers": "服务器", "players": "玩家", "unable_to_load_playercount": "加载玩家数量失败", - "ea_app_running": "EA App正在运行:", - "northstar_running": "Northstar正在运行:" + "ea_app_running": "EA App运行状态:", + "northstar_running": "Northstar运行状态:" }, "mods": { "local": { -- cgit v1.2.3 From 29837bfe678dad64f01d6f6be69d43d659f402ae Mon Sep 17 00:00:00 2001 From: GeckoEidechse <40122905+GeckoEidechse@users.noreply.github.com> Date: Wed, 19 Jul 2023 23:37:22 +0200 Subject: feat: Add ability to delete package (#425) Adds ability to delete mods from `packages` directory. --- src-tauri/src/mod_management/mod.rs | 47 +++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/src-tauri/src/mod_management/mod.rs b/src-tauri/src/mod_management/mod.rs index 2ac26ede..b28671c8 100644 --- a/src-tauri/src/mod_management/mod.rs +++ b/src-tauri/src/mod_management/mod.rs @@ -548,11 +548,58 @@ pub fn delete_northstar_mod(game_install: GameInstall, nsmod_name: String) -> Re Err(format!("Mod {nsmod_name} not found to be installed")) } +/// Deletes a given Thunderstore package +fn delete_package_folder(ts_package_directory: &str) -> Result<(), String> { + let ns_mod_dir_path = std::path::Path::new(&ts_package_directory); + + // Safety check: Check whether `manifest.json` exists and exit early if not + // If it does not exist, we might not be dealing with a Thunderstore package + let mod_json_path = ns_mod_dir_path.join("manifest.json"); + if !mod_json_path.exists() { + // If it doesn't exist, return an error + return Err(format!( + "manifest.json does not exist in {}", + ts_package_directory + )); + } + + match std::fs::remove_dir_all(ts_package_directory) { + Ok(()) => Ok(()), + Err(err) => Err(format!("Failed deleting package: {err}")), + } +} + /// Deletes all NorthstarMods related to a Thunderstore mod #[tauri::command] pub fn delete_thunderstore_mod( game_install: GameInstall, thunderstore_mod_string: String, ) -> Result<(), String> { + // Check packages + let packages_folder = format!("{}/R2Northstar/packages", game_install.game_path); + if std::path::Path::new(&packages_folder).exists() { + for entry in fs::read_dir(packages_folder).unwrap() { + let entry = entry.unwrap(); + + // Check if it's a folder and skip if otherwise + if !entry.file_type().unwrap().is_dir() { + log::warn!("Skipping \"{}\", not a file", entry.path().display()); + continue; + } + + let entry_path = entry.path(); + let package_folder_ts_string = entry_path.file_name().unwrap().to_string_lossy(); + + if package_folder_ts_string != thunderstore_mod_string { + // Not the mod folder we are looking for, try the next one\ + continue; + } + + // All checks passed, this is the matching mod + return delete_package_folder(&entry.path().display().to_string()); + } + } + + // Try legacy mod installs as fallback legacy::delete_thunderstore_mod(game_install, thunderstore_mod_string) } -- cgit v1.2.3