aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src-tauri/Cargo.lock5
-rw-r--r--src-tauri/Cargo.toml8
-rw-r--r--src-tauri/src/main.rs68
-rw-r--r--src-tauri/src/mod_management/mod.rs191
-rw-r--r--src-tauri/src/platform_specific/linux.rs75
-rw-r--r--src-tauri/tauri.conf.json2
-rw-r--r--src-vue/src/components/LanguageSelector.vue4
-rw-r--r--src-vue/src/i18n/lang/zh_Hans.json153
-rw-r--r--src-vue/src/main.ts3
-rw-r--r--src-vue/src/views/DeveloperView.vue28
10 files changed, 504 insertions, 33 deletions
diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock
index a1699c16..d57c1752 100644
--- a/src-tauri/Cargo.lock
+++ b/src-tauri/Cargo.lock
@@ -85,13 +85,14 @@ checksum = "224afbd727c3d6e4b90103ece64b8d1b67fbb1973b1046c2281eed3f3803f800"
[[package]]
name = "app"
-version = "1.23.0"
+version = "1.24.0"
dependencies = [
"anyhow",
"async-recursion",
"chrono",
"const_format",
"dirs",
+ "glob",
"json5",
"libthermite",
"log",
@@ -1996,10 +1997,12 @@ version = "0.7.0-alpha"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b3927ebe9945316ba6a70f4384045fba7da9261e23b44faa7fddf9238f8916fa"
dependencies = [
+ "flate2",
"json5",
"regex",
"serde",
"serde_json",
+ "tar",
"thiserror",
"tracing",
"ureq",
diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml
index 1ce47b8a..1c758eb3 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 = ""
@@ -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"
@@ -33,7 +33,7 @@ steamlocate = "1.2"
# Error messages
anyhow = "1.0"
# libthermite for Northstar/mod install handling
-libthermite = "0.7.0-alpha"
+libthermite = { version = ""0.7.0-alpha", 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..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;
@@ -144,6 +143,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,
@@ -172,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::<Vec<_>>();
- let message = "FlightCore requires WebView2 to run.\n\nClick OK to open installation instructions.".encode_utf16().chain(Some(0)).collect::<Vec<_>>();
- 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();
}
}
}
@@ -525,3 +520,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<String, String> {
+ #[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/mod_management/mod.rs b/src-tauri/src/mod_management/mod.rs
index adc46661..b11181c2 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<String>,
+}
+
+/// Parse `mods` folder for installed mods.
+pub fn parse_mods_in_package(
+ package_mods_path: PathBuf,
+ thunderstore_mod_string: ParsedThunderstoreModString,
+) -> Result<Vec<NorthstarMod>, 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<PathBuf> = Vec::new();
+ let mut mods: Vec<NorthstarMod> = 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<Vec<NorthstarMod>, anyhow::Error> {
+ let mut collected_mods: Vec<NorthstarMod> = 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<Vec<NorthstarMod>, 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) {
@@ -413,11 +549,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)
}
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<String> {
+ 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<String, String> {
+ 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-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": {
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/i18n/lang/zh_Hans.json b/src-vue/src/i18n/lang/zh_Hans.json
index 0967ef42..6447a6cf 100644
--- a/src-vue/src/i18n/lang/zh_Hans.json
+++ b/src-vue/src/i18n/lang/zh_Hans.json
@@ -1 +1,152 @@
-{}
+{
+ "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页面时的速度有影响。",
+ "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"
+ }
+ }
+}
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);
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
</el-button>
+ <el-button type="primary" @click="installNSProton">
+ Install NSProton
+ </el-button>
+
+ <el-button type="primary" @click="uninstallNSProton">
+ Remove NSProton
+ </el-button>
+
+ <el-button type="primary" @click="getLocalNSProtonVersion">
+ Get local NSProton Version
+ </el-button>
+
<h3>Testing:</h3>
<el-button type="primary" @click="launchGameWithoutChecks">
@@ -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"); })
+ },
}
});
</script>