Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
13a56f4200 |
No files matched your search
@@ -324,12 +324,9 @@ async fn initialize_state(app: tauri::AppHandle) -> api::Result<()> {
|
|||||||
/// external game-directory root for the one-click StarLight install flow.
|
/// external game-directory root for the one-click StarLight install flow.
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
fn get_launcher_root_dir() -> api::Result<String> {
|
fn get_launcher_root_dir() -> api::Result<String> {
|
||||||
let exe = std::env::current_exe().map_err(|error| {
|
let dir = theseus::default_game_dir_root().ok_or_else(|| {
|
||||||
theseus::Error::from(theseus::ErrorKind::FSError(error.to_string()))
|
|
||||||
})?;
|
|
||||||
let dir = exe.parent().ok_or_else(|| {
|
|
||||||
theseus::Error::from(theseus::ErrorKind::FSError(
|
theseus::Error::from(theseus::ErrorKind::FSError(
|
||||||
"Launcher executable has no parent directory".to_string(),
|
"Could not determine a default game directory root".to_string(),
|
||||||
))
|
))
|
||||||
})?;
|
})?;
|
||||||
Ok(dir.to_string_lossy().into_owned())
|
Ok(dir.to_string_lossy().into_owned())
|
||||||
|
|||||||
@@ -109,7 +109,6 @@ pub struct CrashAnalysisMod {
|
|||||||
struct SourceCandidate {
|
struct SourceCandidate {
|
||||||
path: PathBuf,
|
path: PathBuf,
|
||||||
filename: String,
|
filename: String,
|
||||||
upload_name: String,
|
|
||||||
source_type: &'static str,
|
source_type: &'static str,
|
||||||
modified: SystemTime,
|
modified: SystemTime,
|
||||||
}
|
}
|
||||||
@@ -117,25 +116,10 @@ struct SourceCandidate {
|
|||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct SourceText {
|
struct SourceText {
|
||||||
filename: String,
|
filename: String,
|
||||||
upload_name: String,
|
|
||||||
source_type: &'static str,
|
source_type: &'static str,
|
||||||
modified: SystemTime,
|
modified: SystemTime,
|
||||||
content: String,
|
content: String,
|
||||||
}
|
}
|
||||||
pub(super) struct CollectedCrashLog {
|
|
||||||
pub name: String,
|
|
||||||
pub content: CensoredString,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(super) struct CollectedCrashLogs {
|
|
||||||
pub combined_log: CensoredString,
|
|
||||||
pub sources: Vec<CollectedCrashLog>,
|
|
||||||
}
|
|
||||||
|
|
||||||
struct CrashContext {
|
|
||||||
sources: Vec<SourceText>,
|
|
||||||
credentials: Vec<Credentials>,
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Rule {
|
struct Rule {
|
||||||
id: &'static str,
|
id: &'static str,
|
||||||
@@ -1201,25 +1185,20 @@ async fn collect_candidates(
|
|||||||
) -> crate::Result<Vec<SourceCandidate>> {
|
) -> crate::Result<Vec<SourceCandidate>> {
|
||||||
let mut candidates = Vec::new();
|
let mut candidates = Vec::new();
|
||||||
let mut locations = vec![
|
let mut locations = vec![
|
||||||
(
|
(directories.game_logs_dir(instance_root), "minecraft_log"),
|
||||||
directories.game_logs_dir(instance_root),
|
|
||||||
"minecraft_log",
|
|
||||||
None,
|
|
||||||
),
|
|
||||||
(
|
(
|
||||||
directories.game_crash_reports_dir(instance_root),
|
directories.game_crash_reports_dir(instance_root),
|
||||||
"crash_report",
|
"crash_report",
|
||||||
Some("crash-reports"),
|
|
||||||
),
|
),
|
||||||
(instance_root.to_path_buf(), "instance_log", None),
|
(instance_root.to_path_buf(), "instance_log"),
|
||||||
];
|
];
|
||||||
if let Some(dot_minecraft) = isolated_minecraft_root(instance_root) {
|
if let Some(dot_minecraft) = isolated_minecraft_root(instance_root) {
|
||||||
// PCL-CE also searches the shared `.minecraft` root for JVM crash
|
// PCL-CE also searches the shared `.minecraft` root for JVM crash
|
||||||
// logs when a version-isolated instance is active.
|
// logs when a version-isolated instance is active.
|
||||||
locations.push((dot_minecraft, "minecraft_root_log", None));
|
locations.push((dot_minecraft, "minecraft_root_log"));
|
||||||
}
|
}
|
||||||
|
|
||||||
for (directory, default_type, upload_prefix) in locations {
|
for (directory, default_type) in locations {
|
||||||
if !tokio::fs::try_exists(&directory).await? {
|
if !tokio::fs::try_exists(&directory).await? {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -1269,9 +1248,6 @@ async fn collect_candidates(
|
|||||||
};
|
};
|
||||||
candidates.push(SourceCandidate {
|
candidates.push(SourceCandidate {
|
||||||
path: entry.path(),
|
path: entry.path(),
|
||||||
upload_name: upload_prefix
|
|
||||||
.map(|prefix| format!("{prefix}/{filename}"))
|
|
||||||
.unwrap_or_else(|| filename.clone()),
|
|
||||||
filename,
|
filename,
|
||||||
source_type,
|
source_type,
|
||||||
modified: metadata.modified().unwrap_or(SystemTime::UNIX_EPOCH),
|
modified: metadata.modified().unwrap_or(SystemTime::UNIX_EPOCH),
|
||||||
@@ -1281,53 +1257,6 @@ async fn collect_candidates(
|
|||||||
Ok(candidates)
|
Ok(candidates)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn collect_crash_context(
|
|
||||||
instance_id: &str,
|
|
||||||
) -> crate::Result<CrashContext> {
|
|
||||||
let state = State::get().await?;
|
|
||||||
let (instance_path, game_dir_override) =
|
|
||||||
resolve_instance_path(instance_id, &state).await?;
|
|
||||||
let instance_root = state
|
|
||||||
.directories
|
|
||||||
.resolve_game_dir(&instance_path, game_dir_override.as_deref());
|
|
||||||
let candidates =
|
|
||||||
collect_candidates(&instance_root, &state.directories).await?;
|
|
||||||
let selected = select_run_candidates(candidates);
|
|
||||||
let sources = read_sources(selected).await;
|
|
||||||
let credentials = Credentials::get_all(&state.pool)
|
|
||||||
.await?
|
|
||||||
.into_iter()
|
|
||||||
.map(|credential| credential.1)
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
Ok(CrashContext {
|
|
||||||
sources,
|
|
||||||
credentials,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(super) async fn collect_crash_logs(
|
|
||||||
instance_id: &str,
|
|
||||||
) -> crate::Result<CollectedCrashLogs> {
|
|
||||||
let CrashContext {
|
|
||||||
sources,
|
|
||||||
credentials,
|
|
||||||
..
|
|
||||||
} = collect_crash_context(instance_id).await?;
|
|
||||||
let combined_log =
|
|
||||||
CensoredString::censor(build_combined_log(&sources), &credentials);
|
|
||||||
let sources = sources
|
|
||||||
.into_iter()
|
|
||||||
.map(|source| CollectedCrashLog {
|
|
||||||
name: source.upload_name,
|
|
||||||
content: CensoredString::censor(source.content, &credentials),
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
Ok(CollectedCrashLogs {
|
|
||||||
combined_log,
|
|
||||||
sources,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn isolated_minecraft_root(instance_root: &Path) -> Option<PathBuf> {
|
fn isolated_minecraft_root(instance_root: &Path) -> Option<PathBuf> {
|
||||||
let versions_dir = instance_root.parent()?;
|
let versions_dir = instance_root.parent()?;
|
||||||
if versions_dir
|
if versions_dir
|
||||||
@@ -1391,7 +1320,6 @@ async fn read_sources(candidates: Vec<SourceCandidate>) -> Vec<SourceText> {
|
|||||||
};
|
};
|
||||||
sources.push(SourceText {
|
sources.push(SourceText {
|
||||||
filename: candidate.filename,
|
filename: candidate.filename,
|
||||||
upload_name: candidate.upload_name,
|
|
||||||
source_type: candidate.source_type,
|
source_type: candidate.source_type,
|
||||||
modified: candidate.modified,
|
modified: candidate.modified,
|
||||||
content: String::from_utf8_lossy(bytes).into_owned(),
|
content: String::from_utf8_lossy(bytes).into_owned(),
|
||||||
@@ -1851,7 +1779,6 @@ mod tests {
|
|||||||
fn source(filename: &str, content: &str) -> SourceText {
|
fn source(filename: &str, content: &str) -> SourceText {
|
||||||
SourceText {
|
SourceText {
|
||||||
filename: filename.to_string(),
|
filename: filename.to_string(),
|
||||||
upload_name: filename.to_string(),
|
|
||||||
source_type: "minecraft_log",
|
source_type: "minecraft_log",
|
||||||
modified: SystemTime::now(),
|
modified: SystemTime::now(),
|
||||||
content: content.to_string(),
|
content: content.to_string(),
|
||||||
@@ -1875,7 +1802,6 @@ mod tests {
|
|||||||
let candidates = vec![SourceCandidate {
|
let candidates = vec![SourceCandidate {
|
||||||
path: PathBuf::from("launcher_log.txt"),
|
path: PathBuf::from("launcher_log.txt"),
|
||||||
filename: "launcher_log.txt".to_string(),
|
filename: "launcher_log.txt".to_string(),
|
||||||
upload_name: "launcher_log.txt".to_string(),
|
|
||||||
source_type: "minecraft_log",
|
source_type: "minecraft_log",
|
||||||
modified: SystemTime::now() - Duration::from_secs(181),
|
modified: SystemTime::now() - Duration::from_secs(181),
|
||||||
}];
|
}];
|
||||||
|
|||||||
@@ -4,7 +4,8 @@ use futures::StreamExt;
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_json::{Value, json};
|
use serde_json::{Value, json};
|
||||||
|
|
||||||
use super::crash_analysis::collect_crash_logs;
|
use super::CensoredString;
|
||||||
|
use super::crash_analysis::analyze_crash;
|
||||||
use crate::State;
|
use crate::State;
|
||||||
use crate::emit_logshare_ai_event;
|
use crate::emit_logshare_ai_event;
|
||||||
|
|
||||||
@@ -199,6 +200,32 @@ fn source_identifier() -> String {
|
|||||||
format!("{LOGSHARE_SOURCE_PREFIX}/{}", env!("CARGO_PKG_VERSION"))
|
format!("{LOGSHARE_SOURCE_PREFIX}/{}", env!("CARGO_PKG_VERSION"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct CollectedCrashLog {
|
||||||
|
name: String,
|
||||||
|
content: CensoredString,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct CollectedCrashLogs {
|
||||||
|
combined_log: CensoredString,
|
||||||
|
sources: Vec<CollectedCrashLog>,
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn collect_crash_logs(
|
||||||
|
instance_id: &str,
|
||||||
|
) -> crate::Result<CollectedCrashLogs> {
|
||||||
|
let analysis = analyze_crash(instance_id).await?;
|
||||||
|
Ok(CollectedCrashLogs {
|
||||||
|
combined_log: analysis.combined_log,
|
||||||
|
sources: analysis
|
||||||
|
.sources
|
||||||
|
.into_iter()
|
||||||
|
.map(|source| CollectedCrashLog {
|
||||||
|
name: source.filename,
|
||||||
|
content: source.content,
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
})
|
||||||
|
}
|
||||||
async fn prepare_upload(instance_id: &str) -> crate::Result<UploadBody> {
|
async fn prepare_upload(instance_id: &str) -> crate::Result<UploadBody> {
|
||||||
let settings = get_log_share_settings().await?;
|
let settings = get_log_share_settings().await?;
|
||||||
let logs = collect_crash_logs(instance_id).await?;
|
let logs = collect_crash_logs(instance_id).await?;
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ pub use state::{DirectoryInfo, State};
|
|||||||
pub use storage::*;
|
pub use storage::*;
|
||||||
pub use util::fetch::build_proxied_client;
|
pub use util::fetch::build_proxied_client;
|
||||||
pub use util::file_lock::{LockingProcess, get_locking_processes};
|
pub use util::file_lock::{LockingProcess, get_locking_processes};
|
||||||
pub use util::platform::is_process_elevated;
|
pub use util::platform::{default_game_dir_root, is_process_elevated};
|
||||||
pub use util::proxy::{ProxyConfig, ProxyMode, ProxyTestResult};
|
pub use util::proxy::{ProxyConfig, ProxyMode, ProxyTestResult};
|
||||||
pub use util::symlink::SymlinkCapability;
|
pub use util::symlink::SymlinkCapability;
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
//! Platform-related code
|
//! Platform-related code
|
||||||
use daedalus::minecraft::{Os, OsRule};
|
use daedalus::minecraft::{Os, OsRule};
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
/// (e.g. "Run as administrator" on Windows).
|
/// (e.g. "Run as administrator" on Windows).
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
@@ -143,3 +144,31 @@ pub fn classpath_separator(java_arch: &str) -> &'static str {
|
|||||||
Os::Windows | Os::WindowsArm64 => ";",
|
Os::Windows | Os::WindowsArm64 => ";",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Root directory used as the default for one-click StarLight instance
|
||||||
|
/// installs. Portable builds live next to their executable, which is a fine
|
||||||
|
/// place for game data. System installs (e.g. a Linux `.deb` under `/usr/bin`)
|
||||||
|
/// do not, so fall back to the user's home directory there.
|
||||||
|
pub fn default_game_dir_root() -> Option<PathBuf> {
|
||||||
|
let exe_dir = std::env::current_exe().ok()?.parent()?.to_path_buf();
|
||||||
|
if cfg!(target_os = "linux") && is_system_install_dir(&exe_dir) {
|
||||||
|
return dirs::home_dir();
|
||||||
|
}
|
||||||
|
Some(exe_dir)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
fn is_system_install_dir(path: &std::path::Path) -> bool {
|
||||||
|
const SYSTEM_PREFIXES: &[&str] = &[
|
||||||
|
"/usr/", "/opt/", "/snap/", "/bin/", "/sbin/", "/lib/", "/lib64/",
|
||||||
|
];
|
||||||
|
let display = path.to_string_lossy();
|
||||||
|
SYSTEM_PREFIXES
|
||||||
|
.iter()
|
||||||
|
.any(|prefix| display.starts_with(prefix))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(target_os = "linux"))]
|
||||||
|
fn is_system_install_dir(_path: &std::path::Path) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
Reference in new issue
Block a user