Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add ability to get installed mods from logs #156

Draft
wants to merge 10 commits into
base: main
Choose a base branch
from
4 changes: 4 additions & 0 deletions src-tauri/bindings/ParsedLogResults.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
import type { ParsedModFromLog } from "./ParsedModFromLog";

export interface ParsedLogResults { northstar_launcher_version: string, installed_mods: Array<ParsedModFromLog>, has_northstar_crashed: boolean, }
3 changes: 3 additions & 0 deletions src-tauri/bindings/ParsedModFromLog.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.

export interface ParsedModFromLog { mod_name: string, enabled: boolean, }
3 changes: 3 additions & 0 deletions src-tauri/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ use tauri::Manager;
use tauri_plugin_store::PluginBuilder;
use tokio::time::sleep;

use crate::repair_and_verify::log_handling::parse_given_log_text;

#[derive(Default)]
struct Counter(Arc<Mutex<i32>>);

Expand Down Expand Up @@ -115,6 +117,7 @@ fn main() {
delete_northstar_mod,
get_server_player_count,
delete_thunderstore_mod,
parse_given_log_text,
query_thunderstore_packages_api,
get_pull_requests_wrapper,
apply_launcher_pr,
Expand Down
117 changes: 117 additions & 0 deletions src-tauri/src/repair_and_verify/log_handling.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
use regex::Regex;
use serde::{Deserialize, Serialize};
use ts_rs::TS;

#[derive(Serialize, Deserialize, Debug, Clone, TS)]
#[ts(export)]
pub struct ParsedModFromLog {
mod_name: String,
enabled: bool,
}

#[derive(Serialize, Deserialize, Debug, Clone, TS)]
#[ts(export)]
pub struct ParsedLogResults {
northstar_launcher_version: String,
installed_mods: Vec<ParsedModFromLog>,
has_northstar_crashed: bool,
}

/// Parse logs for installed mods
fn parse_given_log_text_for_installed_mods(
log_text: String,
) -> Result<Vec<ParsedModFromLog>, String> {
// Regex to capture mod loading and whether enabled/disabled
let regex = Regex::new(r"(?m)Loaded mod (.*) successfully\n.*\[NORTHSTAR\] \[info\] Mod (.*) is (enabled|disabled)\n").unwrap();

// Run regex, result will be an iterator over tuples containing the start and end indices for each match in the string
let result = regex.captures_iter(&log_text);

let mut mods = Vec::new();
for mat in result {
// Get the captured string, which is the first and only capturing group in the regex
let mod_name = match mat.get(1) {
Some(mod_name) => mod_name.as_str().to_string(),
None => {
println!("Failed parsing {:?}", mat); // log on failure
continue;
}
};
let mod_name_copy = match mat.get(2) {
Some(mod_name) => mod_name.as_str().to_string(),
None => {
println!("Failed parsing {:?}", mat); // log on failure
continue;
}
};
let enabled_disabled = match mat.get(3) {
Some(mod_name) => mod_name.as_str().to_string(),
None => {
println!("Failed parsing {:?}", mat); // log on failure
continue;
}
};
println!("{}, {}, {}", mod_name, mod_name_copy, enabled_disabled);
if mod_name != mod_name_copy {
return Err("Mod names don't match up".to_string());
}

// TODO improve checking
let mod_enabled;
if enabled_disabled == "enabled" {
mod_enabled = true;
} else {
mod_enabled = false;
}

let parsed_mod_from_log = ParsedModFromLog {
mod_name,
enabled: mod_enabled,
};

// Add mod to list
mods.push(parsed_mod_from_log);
}

// Return the captured mod names
return Ok(mods);
}

/// Parse logs for Northstar launcher version
fn parse_for_northstar_launcher_version(log_text: String) -> Result<String, String> {
let regex = Regex::new(r"(?m)NorthstarLauncher version: (.*)\n").unwrap();

// result will be an iterator over tuples containing the start and end indices for each match in the string
let mut result = regex.captures_iter(&log_text);

// Return found Northstar launcher version number
match result.next() {
None => Err("Couldn't parse Northstar launcher version".to_string()),
Some(mat) => match mat.get(1) {
None => Err("Couldn't parse Northstar launcher version".to_string()),
Some(mod_name) => Ok(mod_name.as_str().to_string()),
},
}
}

fn parse_log_for_crash(log_text: String) -> bool {
let pattern = Regex::new(r"(?m)Northstar has crashed!").unwrap();
pattern.is_match(&log_text)
}

/// Parse logs for installed mods
#[tauri::command]
pub async fn parse_given_log_text(log_text: String) -> Result<ParsedLogResults, String> {
let installed_mods = parse_given_log_text_for_installed_mods(log_text.clone())?;
let northstar_launcher_version = parse_for_northstar_launcher_version(log_text.clone())?;
let has_northstar_crashed = parse_log_for_crash(log_text);

let parsed_log_results = ParsedLogResults {
northstar_launcher_version,
installed_mods,
has_northstar_crashed,
};

// Return the parsed results
return Ok(parsed_log_results);
}
2 changes: 2 additions & 0 deletions src-tauri/src/repair_and_verify/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
pub mod log_handling;

use crate::mod_management::{rebuild_enabled_mods_json, set_mod_enabled_status};
use anyhow::anyhow;
/// Contains various functions to repair common issues and verifying installation
Expand Down
63 changes: 63 additions & 0 deletions src-vue/src/views/DeveloperView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,42 @@
<el-button type="primary" @click="clearFlightCorePersistentStore">
Delete FlightCore persistent store
</el-button>
<h3>Tech support</h3>

<el-button type="primary" @click="parseGivenLogTextForMods">
Parse logs
</el-button>
<el-input
v-model="log_content"
type="textarea"
:rows="5"
placeholder="Paste log content here"

/>
<div>
<el-table :data="logResults">
<el-table-column prop="northstar_launcher_version"
label="Northstar Launcher Version"></el-table-column>
<el-table-column prop="installed_mods" label="Installed and enabled/disabled Mods">
<template v-slot="{ row }">
<ul>
<li v-for="mod in row.installed_mods">
<el-icon class="no-inherit">
<Select v-if="mod.enabled" />
<Close v-else />
</el-icon>
{{ mod.mod_name }}
</li>
</ul>
</template>
</el-table-column>
<el-table-column prop="has_northstar_crashed" label="Northstar Crashed">
<template v-slot="{ row }">
{{ row.has_northstar_crashed }}
</template>
</el-table-column>
</el-table>
</div>

<h3>Testing</h3>
<pull-requests-selector />
Expand All @@ -70,6 +106,8 @@ import { GameInstall } from "../utils/GameInstall";
import { Store } from 'tauri-plugin-store-api';
import { ReleaseCanal } from "../utils/ReleaseCanal";
import PullRequestsSelector from "../components/PullRequestsSelector.vue";
import { ParsedLogResults } from "../../../src-tauri/bindings/ParsedLogResults";
import { ParsedModFromLog } from "../../../src-tauri/bindings/ParsedModFromLog";
const persistentStore = new Store('flight-core-settings.json');

export default defineComponent({
Expand All @@ -80,6 +118,8 @@ export default defineComponent({
data() {
return {
mod_to_install_field_string : "",
log_content : "",
logResults: [] as ParsedLogResults[]
}
},
methods: {
Expand Down Expand Up @@ -261,6 +301,29 @@ export default defineComponent({
notification.close();
});
},
async parseGivenLogTextForMods() {
let current_log_content = this.log_content;
await invoke<[ParsedLogResults]>("parse_given_log_text", { logText: current_log_content })
.then((message) => {
console.log(message); // TODO present better here
this.logResults.push(message);
// Show user notification if task completed.
ElNotification({
title: `Done`,
message: `${message}`,
type: 'success',
position: 'bottom-right'
});
})
.catch((error) => {
ElNotification({
title: 'Error',
message: error,
type: 'error',
position: 'bottom-right'
});
});
},
}
});
</script>
Expand Down