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

Plugins detection and displaying #294

Closed
wants to merge 24 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
e36d6df
use pass by reference where possible
catornot Apr 15, 2023
c0ff70e
installing plugins
catornot Apr 16, 2023
6719997
Merge branch 'R2NorthstarTools:main' into plugins-download
catornot Apr 17, 2023
c132668
Merge branch 'R2NorthstarTools:main' into plugins-download
catornot Apr 19, 2023
3ef92f7
move plugins install state to frontend
catornot Apr 19, 2023
9af7a8e
Merge branch 'plugins-download' of https://github.com/catornot/Flight…
catornot Apr 19, 2023
a707a38
fix silly mistake
catornot Apr 19, 2023
5812a55
huh
catornot Apr 19, 2023
73ccb92
no more mistakes in frontend
catornot Apr 19, 2023
822202f
add a popup warning to plugin installing
catornot Apr 20, 2023
190a390
fix issues with frontend
catornot Apr 20, 2023
5fb2107
formating
catornot Apr 20, 2023
3ba35f4
localization lol
catornot Apr 22, 2023
87f5dbb
Merge branch 'main' into plugins-download
catornot Apr 22, 2023
7ecc570
use subdirs for installed plugins
catornot Apr 25, 2023
b02913c
move plugin download its own file
catornot Apr 28, 2023
a62c5f4
display downloaded plugins
catornot Apr 28, 2023
da7d809
fmt and refactor thingy
catornot Apr 28, 2023
942a5e8
use mod string for folder name
catornot Apr 28, 2023
4bb7a1f
nuke previous folders when installing
catornot Apr 29, 2023
605fe04
Merge branch 'plugins-download' into plugins-detection
catornot Apr 29, 2023
2712b63
also list plugins in plugins folder
catornot Apr 29, 2023
c14b369
oh
catornot Apr 29, 2023
2418d4d
Merge branch 'main' into plugins-detection
catornot May 27, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions src-tauri/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions src-tauri/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ log = "0.4.17"
zip-extract = "0.1.2"
# open urls
open = "3.2.0"
# for statics
once_cell = "1.17.1"

[features]
# by default Tauri runs in production mode
Expand Down
19 changes: 17 additions & 2 deletions src-tauri/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
windows_subsystem = "windows"
)]

use once_cell::sync::OnceCell;

use std::{
env,
sync::{Arc, Mutex},
Expand Down Expand Up @@ -34,18 +36,23 @@ use mod_management::{
get_installed_mods_and_properties, set_mod_enabled_status,
};

mod plugin_management;
use crate::plugin_management::download::{receive_install_status, InstallStatusSender};

mod northstar;
use northstar::get_northstar_version_number;

mod thunderstore;
use thunderstore::query_thunderstore_packages_api;

use tauri::{Manager, Runtime};
use tauri::{AppHandle, Manager, Runtime};
use tokio::time::sleep;

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

pub static APP_HANDLE: OnceCell<AppHandle> = OnceCell::new();

fn main() {
// Setup logger
let mut log_builder = pretty_env_logger::formatted_builder();
Expand Down Expand Up @@ -107,9 +114,14 @@ fn main() {
}
});

APP_HANDLE
.set(app.app_handle())
.expect("failed to set a app handle");

Ok(())
})
.manage(Counter(Default::default()))
.manage(InstallStatusSender::new())
.invoke_handler(tauri::generate_handler![
force_panic,
find_game_install_location_caller,
Expand Down Expand Up @@ -146,6 +158,7 @@ fn main() {
apply_mods_pr,
get_launcher_download_link,
close_application,
receive_install_status,
])
.run(tauri::generate_context!())
.expect("error while running tauri application");
Expand Down Expand Up @@ -340,8 +353,10 @@ async fn launch_northstar_steam_caller(
async fn install_mod_caller(
game_install: GameInstall,
thunderstore_mod_string: String,
can_install_plugins: bool,
) -> Result<(), String> {
fc_download_mod_and_install(&game_install, &thunderstore_mod_string).await?;
fc_download_mod_and_install(&game_install, &thunderstore_mod_string, can_install_plugins)
.await?;
match clean_up_download_folder(&game_install, false) {
Ok(()) => Ok(()),
Err(err) => {
Expand Down
58 changes: 47 additions & 11 deletions src-tauri/src/mod_management/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,18 @@ use app::NorthstarMod;
use serde::{Deserialize, Serialize};
use std::io::Read;
use std::path::PathBuf;
use thermite::prelude::ThermiteError;

use app::get_enabled_mods;
use app::GameInstall;

use crate::plugin_management::{download::install_plugin,detection::{find_installed_plugins,installed_plugins_to_mod}};

#[derive(Debug, Clone)]
struct ParsedThunderstoreModString {
author_name: String,
mod_name: String,
version: Option<String>,
pub struct ParsedThunderstoreModString {
pub author_name: String,
pub mod_name: String,
pub version: Option<String>,
}

impl std::str::FromStr for ParsedThunderstoreModString {
Expand All @@ -39,8 +42,8 @@ impl std::str::FromStr for ParsedThunderstoreModString {

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct ThunderstoreManifest {
name: String,
version_number: String,
pub name: String,
pub version_number: String,
}

#[derive(Serialize, Deserialize, Debug, Clone)]
Expand Down Expand Up @@ -250,6 +253,12 @@ pub fn get_installed_mods_and_properties(
installed_mods.push(current_mod);
}

// push plugins into this list
// plugins should probably have there own tab but I hate frontend
installed_mods.extend(installed_plugins_to_mod(
&find_installed_plugins(&game_install).map_err(|err| err.to_string())?,
));

Ok(installed_mods)
}

Expand Down Expand Up @@ -314,6 +323,7 @@ async fn get_mod_dependencies(thunderstore_mod_string: &str) -> Result<Vec<Strin
pub async fn fc_download_mod_and_install(
game_install: &GameInstall,
thunderstore_mod_string: &str,
can_install_plugins: bool,
) -> Result<(), String> {
// Get mods and download directories
let download_directory = format!(
Expand All @@ -335,7 +345,7 @@ pub async fn fc_download_mod_and_install(

// Recursively install dependencies
for dep in deps {
match fc_download_mod_and_install(game_install, &dep).await {
match fc_download_mod_and_install(game_install, &dep, can_install_plugins).await {
Ok(()) => (),
Err(err) => {
if err == "Cannot install Northstar as a mod!" {
Expand Down Expand Up @@ -379,15 +389,41 @@ pub async fn fc_download_mod_and_install(
let author = thunderstore_mod_string.split('-').next().unwrap();

// Extract the mod to the mods directory
match thermite::core::manage::install_mod(author, &f, std::path::Path::new(&mods_directory)) {
Ok(()) => (),
Err(err) => return Err(err.to_string()),
let result_mod = match thermite::core::manage::install_mod(
author,
&f,
std::path::Path::new(&mods_directory),
) {
Ok(()) => Ok(()),
err if matches!(err, Err(ThermiteError::PrefixError(_))) => err, // probably happens when there is not mod folder found
Err(err) => Err(err.to_string())?,
};

// Injected plugin install

let result_plugin = match install_plugin(
game_install,
&f,
thunderstore_mod_string,
can_install_plugins,
)
.await
{
err if matches!(err, Err(ThermiteError::MissingFile(_))) => err,
Err(err) => Err(err.to_string())?,
r => r,
};

// Delete downloaded zip file
std::fs::remove_file(path).unwrap();

Ok(())
// Because of the match expression only errors that can indicate missing mod/plugins folder
// we can say that it worked if the plugin install worked
if result_plugin.is_ok() {
Ok(())
} else {
result_mod.map_err(|e| e.to_string())
}
}

/// Deletes a given Northstar mod folder
Expand Down
68 changes: 68 additions & 0 deletions src-tauri/src/plugin_management/detection.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
use crate::{mod_management::ThunderstoreManifest, GameInstall, NorthstarMod};
use std::{ffi::OsStr, path::{PathBuf, Path}};
use thermite::prelude::ThermiteError;

pub fn installed_plugins_to_mod(
manifests: &[(ThunderstoreManifest, PathBuf)],
) -> Vec<NorthstarMod> {
manifests
.iter()
.map(|(m, path)| NorthstarMod {
name: m.name.clone(),
version: None, // assume None
thunderstore_mod_string: Some(m.name.clone()),
enabled: true, // assume it is enabled
directory: path.display().to_string(),
})
.collect()
}

pub fn find_installed_plugins(
game_install: &GameInstall,
) -> Result<Vec<(ThunderstoreManifest, PathBuf)>, ThermiteError> {
let plugins_directory = PathBuf::new()
.join(&game_install.game_path)
.join("R2Northstar")
.join("plugins");

Ok(plugins_directory
.read_dir()
.map_err(|_| ThermiteError::MissingFile(Box::new(plugins_directory)))?
.filter_map(|f| f.ok())
.map(|e| e.path())
.filter_map(|p| find_manifest(p.as_path()).or_else(|| find_plugin_in_root(p.as_path())))
.collect())
}

fn find_plugin_in_root(file: &Path) -> Option<(ThunderstoreManifest, PathBuf)> {
if file.extension()? == "dll" {
Some((
ThunderstoreManifest {
name: file.file_name()?.to_str()?.to_string(),
version_number: "0.0.0".to_string(), // TODO: peak the dll to find it's version
},
file.to_owned(),
))
} else {
None
}
}

// this can't be async :(
fn find_manifest(dir: &Path) -> Option<(ThunderstoreManifest, PathBuf)> {
pasre_manifest_path(
dir.read_dir()
.ok()?
.filter_map(|e| e.ok())
.map(|e| e.path())
.filter(|path| path.file_name() == Some(OsStr::new("manifest.json")))
.last()?,
)
}

fn pasre_manifest_path(path: PathBuf) -> Option<(ThunderstoreManifest, PathBuf)> {
Some((
json5::from_str(&std::fs::read_to_string(&path).ok()?).ok()?,
path,
))
}
Loading