Skip to content

Commit

Permalink
feat: Add basic Proton management (#383)
Browse files Browse the repository at this point in the history
Adds logic to install, remove, and check existing install of NorthstarProton.
  • Loading branch information
Jan200101 committed Jul 18, 2023
1 parent dfa5af1 commit 9dccdb0
Show file tree
Hide file tree
Showing 5 changed files with 141 additions and 1 deletion.
3 changes: 3 additions & 0 deletions src-tauri/Cargo.lock

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

4 changes: 3 additions & 1 deletion src-tauri/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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]
Expand Down
32 changes: 32 additions & 0 deletions src-tauri/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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<String, String> {
#[cfg(target_os = "linux")]
return linux::get_local_ns_proton_version();

#[cfg(target_os = "windows")]
Err("Not supported on Windows".to_string())
}
75 changes: 75 additions & 0 deletions src-tauri/src/platform_specific/linux.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down
28 changes: 28 additions & 0 deletions src-vue/src/views/DeveloperView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -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">
Expand Down Expand Up @@ -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>
Expand Down

0 comments on commit 9dccdb0

Please sign in to comment.