aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src-tauri/src/main.rs2
-rw-r--r--src-tauri/src/northstar/mod.rs1
-rw-r--r--src-tauri/src/northstar/profile.rs76
-rw-r--r--src-vue/src/i18n/lang/en.json11
-rw-r--r--src-vue/src/plugins/store.ts20
-rw-r--r--src-vue/src/views/SettingsView.vue60
6 files changed, 170 insertions, 0 deletions
diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs
index a7827a44..66bb98d2 100644
--- a/src-tauri/src/main.rs
+++ b/src-tauri/src/main.rs
@@ -158,6 +158,8 @@ fn main() {
close_application,
development::install_git_main,
get_available_northstar_versions,
+ northstar::profile::fetch_profiles,
+ northstar::profile::validate_profile,
])
.run(tauri::generate_context!())
{
diff --git a/src-tauri/src/northstar/mod.rs b/src-tauri/src/northstar/mod.rs
index 173495c6..4fecfe51 100644
--- a/src-tauri/src/northstar/mod.rs
+++ b/src-tauri/src/northstar/mod.rs
@@ -1,6 +1,7 @@
//! This module deals with handling things around Northstar such as
//! - getting version number
pub mod install;
+pub mod profile;
use crate::util::check_ea_app_or_origin_running;
use crate::{
diff --git a/src-tauri/src/northstar/profile.rs b/src-tauri/src/northstar/profile.rs
new file mode 100644
index 00000000..78e734d0
--- /dev/null
+++ b/src-tauri/src/northstar/profile.rs
@@ -0,0 +1,76 @@
+use crate::GameInstall;
+
+// These folders are part of Titanfall 2 and
+// should NEVER be used as a Profile
+const SKIP_PATHS: [&str; 8] = [
+ "___flightcore-temp",
+ "__overlay",
+ "bin",
+ "Core",
+ "r2",
+ "vpk",
+ "platform",
+ "Support",
+];
+
+// A profile may have one of these to be detected
+const MAY_CONTAIN: [&str; 10] = [
+ "mods/",
+ "plugins/",
+ "packages/",
+ "logs/",
+ "runtime/",
+ "save_data/",
+ "Northstar.dll",
+ "enabledmods.json",
+ "placeholder.playerdata.pdata",
+ "LEGAL.txt",
+];
+
+/// Returns a list of Profile names
+/// All the returned Profiles can be found relative to the game path
+#[tauri::command]
+pub fn fetch_profiles(game_install: GameInstall) -> Result<Vec<String>, String> {
+ let mut profiles: Vec<String> = Vec::new();
+
+ for content in MAY_CONTAIN {
+ let pattern = format!("{}/*/{}", game_install.game_path, content);
+ for e in glob::glob(&pattern).expect("Failed to read glob pattern") {
+ let path = e.unwrap();
+ let mut ancestors = path.ancestors();
+
+ ancestors.next();
+
+ let profile_path = std::path::Path::new(ancestors.next().unwrap());
+ let profile_name = profile_path
+ .file_name()
+ .unwrap()
+ .to_os_string()
+ .into_string()
+ .unwrap();
+
+ if !profiles.contains(&profile_name) {
+ profiles.push(profile_name);
+ }
+ }
+ }
+
+ Ok(profiles)
+}
+
+/// Validates if a given profile is actually a valid profile
+#[tauri::command]
+pub fn validate_profile(game_install: GameInstall, profile: String) -> bool {
+ // Game files are never a valid profile
+ // Prevent users with messed up installs from making it even worse
+ if SKIP_PATHS.contains(&profile.as_str()) {
+ return false;
+ }
+
+ log::info!("Validating Profile {}", profile);
+
+ let profile_path = format!("{}/{}", game_install.game_path, profile);
+ let profile_dir = std::path::Path::new(profile_path.as_str());
+
+ profile_dir.is_dir()
+}
diff --git a/src-vue/src/i18n/lang/en.json b/src-vue/src/i18n/lang/en.json
index 238c6c41..1fa4e7ee 100644
--- a/src-vue/src/i18n/lang/en.json
+++ b/src-vue/src/i18n/lang/en.json
@@ -110,6 +110,10 @@
"show_deprecated_mods_desc1": "This allows you to see deprecated mods in the online mods collection.",
"show_deprecated_mods_desc2": "Watch out, such mods are usually deprecated for a good reason.",
+ "profile": {
+ "active": "Active Profile"
+ },
+
"repair": {
"title": "Repair",
"open_window": "Open repair window",
@@ -150,6 +154,13 @@
}
},
+ "profile": {
+ "invalid": {
+ "title": "Invalid Profile",
+ "text": "The profile you tried to switch to is no longer valid."
+ }
+ },
+
"flightcore_outdated": {
"title": "FlightCore outdated!",
"text": "Please update FlightCore.\nRunning outdated version {oldVersion}.\nNewest is {newVersion}!"
diff --git a/src-vue/src/plugins/store.ts b/src-vue/src/plugins/store.ts
index e18498a6..8f0c3fee 100644
--- a/src-vue/src/plugins/store.ts
+++ b/src-vue/src/plugins/store.ts
@@ -38,6 +38,7 @@ export interface FlightCoreStore {
thunderstoreMods: ThunderstoreMod[],
thunderstoreModsCategories: string[],
installed_mods: NorthstarMod[],
+ available_profiles: string[],
northstar_is_running: boolean,
origin_is_running: boolean,
@@ -62,6 +63,8 @@ export const store = createStore<FlightCoreStore>({
developer_mode: false,
game_install: {} as unknown as GameInstall,
+ available_profiles: [],
+
flightcore_version: "",
installed_northstar_version: "",
@@ -284,6 +287,9 @@ export const store = createStore<FlightCoreStore>({
return;
}
+ // Clear installed mod list first so we don't end up with leftovers
+ state.installed_mods = [];
+
// Call back-end for installed mods
await invoke("get_installed_mods_and_properties", { gameInstall: state.game_install })
.then((message) => {
@@ -312,6 +318,16 @@ export const store = createStore<FlightCoreStore>({
i18n.global.tc(`channels.names.${state.northstar_release_canal}`),
i18n.global.tc('channels.release.switch.text', {canal: state.northstar_release_canal}),
);
+ },
+ async fetchProfiles(state: FlightCoreStore) {
+ await invoke("fetch_profiles", { gameInstall: state.game_install })
+ .then((message) => {
+ state.available_profiles = message as string[];
+ })
+ .catch((error) => {
+ console.error(error);
+ showErrorNotification(error);
+ });
}
}
});
@@ -415,6 +431,8 @@ async function _initializeApp(state: any) {
await _get_northstar_version_number(state);
}
+ store.commit('fetchProfiles');
+
await invoke<[number, number]>("get_server_player_count")
.then((message) => {
state.player_count = message[0];
@@ -465,6 +483,8 @@ function _initializeListeners(state: any) {
* state, for it to be displayed in UI.
*/
async function _get_northstar_version_number(state: any) {
+ state.installed_northstar_version = "";
+
await invoke("get_northstar_version_number", { gameInstall: state.game_install })
.then((message) => {
let northstar_version_number: string = message as string;
diff --git a/src-vue/src/views/SettingsView.vue b/src-vue/src/views/SettingsView.vue
index c7ca2ded..c209da31 100644
--- a/src-vue/src/views/SettingsView.vue
+++ b/src-vue/src/views/SettingsView.vue
@@ -21,6 +21,21 @@
</el-input>
</div>
+ <!-- Northstar Active Profile -->
+ <div class="fc_parameter__panel" v-if="$store.state.developer_mode">
+ <h3>{{ $t('settings.profile.active') }}</h3>
+ <el-dropdown trigger="click">
+ <el-button>
+ {{ $store.state.game_install.profile }} <el-icon class="el-icon--right"><arrow-down /></el-icon>
+ </el-button>
+ <template #dropdown>
+ <el-dropdown-menu>
+ <el-dropdown-item v-for="profile in $store.state.available_profiles" @click="switchProfile(profile)">{{ profile }}</el-dropdown-item>
+ </el-dropdown-menu>
+ </template>
+ </el-dropdown>
+ </div>
+
<!-- Thunderstore mods per page configuration -->
<div class="fc_parameter__panel">
<h3>{{ $t('settings.nb_ts_mods_per_page') }}</h3>
@@ -96,6 +111,7 @@ import { showErrorNotification, showNotification } from "../utils/ui";
import LanguageSelector from "../components/LanguageSelector.vue";
const persistentStore = new Store('flight-core-settings.json');
import { open } from '@tauri-apps/api/shell';
+import { i18n } from '../main';
export default defineComponent({
name: "SettingsView",
@@ -144,6 +160,17 @@ export default defineComponent({
persistentStore.set('thunderstore-mods-per-page', { value });
await persistentStore.save(); // explicit save to disk
}
+ },
+ availableProfiles(): Object {
+ let profiles = this.$store.state.available_profiles
+
+ // convert string array to object array so we can fill a table
+ let data = profiles.reduce(
+ (a: Object[], v: string) => [...a, {"name": v}],
+ []
+ );
+
+ return data;
}
},
methods: {
@@ -172,6 +199,39 @@ export default defineComponent({
async openGameInstallFolder() {
// Opens the folder in default file explorer application
await open(`${this.$store.state.game_install.game_path}`);
+ },
+ async switchProfile(value: string) {
+ let store = this.$store;
+ let state = store.state;
+
+ await invoke("validate_profile", { gameInstall: state.game_install, profile: value })
+ .then(async (message) => {
+ if (!message)
+ {
+ // Profile is no longer valid, inform the user...
+ showErrorNotification(
+ i18n.global.tc('notification.profile.invalid.text'),
+ i18n.global.tc('notification.profile.invalid.title')
+ );
+
+ // ...and refresh
+ store.commit('fetchProfiles');
+ return;
+ }
+
+ state.game_install.profile = value;
+
+ // Check for Northstar updates
+ store.commit('checkNorthstarUpdates');
+
+ // Save change in persistent store
+ await persistentStore.set('game-install', { value: state.game_install });
+ await persistentStore.save(); // explicit save to disk
+ })
+ .catch((error) => {
+ console.error(error);
+ showErrorNotification(error);
+ });
}
},
mounted() {