You've already forked AstralRinth
forked from didirus/AstralRinth
Analytics + more bug fixes (#144)
* Analytics + more bug fixes * debug deadlock * Fix mostly everything * merge fixes * fix rest * final fixeS
This commit is contained in:
@@ -5,8 +5,7 @@ use tokio::fs::read_to_string;
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct Logs {
|
||||
pub datetime_string: String,
|
||||
pub stdout: Option<String>,
|
||||
pub stderr: Option<String>,
|
||||
pub output: Option<String>,
|
||||
}
|
||||
impl Logs {
|
||||
async fn build(
|
||||
@@ -15,19 +14,11 @@ impl Logs {
|
||||
clear_contents: Option<bool>,
|
||||
) -> crate::Result<Self> {
|
||||
Ok(Self {
|
||||
stdout: if clear_contents.unwrap_or(false) {
|
||||
output: if clear_contents.unwrap_or(false) {
|
||||
None
|
||||
} else {
|
||||
Some(
|
||||
get_stdout_by_datetime(profile_uuid, &datetime_string)
|
||||
.await?,
|
||||
)
|
||||
},
|
||||
stderr: if clear_contents.unwrap_or(false) {
|
||||
None
|
||||
} else {
|
||||
Some(
|
||||
get_stderr_by_datetime(profile_uuid, &datetime_string)
|
||||
get_output_by_datetime(profile_uuid, &datetime_string)
|
||||
.await?,
|
||||
)
|
||||
},
|
||||
@@ -74,18 +65,15 @@ pub async fn get_logs_by_datetime(
|
||||
datetime_string: String,
|
||||
) -> crate::Result<Logs> {
|
||||
Ok(Logs {
|
||||
stdout: Some(
|
||||
get_stdout_by_datetime(profile_uuid, &datetime_string).await?,
|
||||
),
|
||||
stderr: Some(
|
||||
get_stderr_by_datetime(profile_uuid, &datetime_string).await?,
|
||||
output: Some(
|
||||
get_output_by_datetime(profile_uuid, &datetime_string).await?,
|
||||
),
|
||||
datetime_string,
|
||||
})
|
||||
}
|
||||
|
||||
#[tracing::instrument]
|
||||
pub async fn get_stdout_by_datetime(
|
||||
pub async fn get_output_by_datetime(
|
||||
profile_uuid: uuid::Uuid,
|
||||
datetime_string: &str,
|
||||
) -> crate::Result<String> {
|
||||
@@ -97,19 +85,6 @@ pub async fn get_stdout_by_datetime(
|
||||
)
|
||||
}
|
||||
|
||||
#[tracing::instrument]
|
||||
pub async fn get_stderr_by_datetime(
|
||||
profile_uuid: uuid::Uuid,
|
||||
datetime_string: &str,
|
||||
) -> crate::Result<String> {
|
||||
let state = State::get().await?;
|
||||
let logs_folder = state.directories.profile_logs_dir(profile_uuid);
|
||||
Ok(
|
||||
read_to_string(logs_folder.join(datetime_string).join("stderr.log"))
|
||||
.await?,
|
||||
)
|
||||
}
|
||||
|
||||
#[tracing::instrument]
|
||||
pub async fn delete_logs(profile_uuid: uuid::Uuid) -> crate::Result<()> {
|
||||
let state = State::get().await?;
|
||||
|
||||
@@ -335,7 +335,6 @@ async fn install_pack(
|
||||
async { Ok(()) }
|
||||
})
|
||||
.await?;
|
||||
State::sync().await?;
|
||||
|
||||
let profile = profile.clone();
|
||||
let result = async {
|
||||
@@ -487,6 +486,8 @@ async fn install_pack(
|
||||
Some(loading_bar),
|
||||
)
|
||||
.await?;
|
||||
|
||||
State::sync().await?;
|
||||
}
|
||||
|
||||
Ok::<PathBuf, crate::Error>(profile.clone())
|
||||
|
||||
@@ -69,9 +69,9 @@ pub async fn get_uuids_by_profile_path(
|
||||
children.running_keys_with_profile(profile_path).await
|
||||
}
|
||||
|
||||
// Gets stdout of a child process stored in the state by UUID, as a string
|
||||
// Gets output of a child process stored in the state by UUID, as a string
|
||||
#[tracing::instrument]
|
||||
pub async fn get_stdout_by_uuid(uuid: &Uuid) -> crate::Result<String> {
|
||||
pub async fn get_output_by_uuid(uuid: &Uuid) -> crate::Result<String> {
|
||||
let state = State::get().await?;
|
||||
// Get stdout from child
|
||||
let children = state.children.read().await;
|
||||
@@ -79,7 +79,7 @@ pub async fn get_stdout_by_uuid(uuid: &Uuid) -> crate::Result<String> {
|
||||
// Extract child or return crate::Error
|
||||
if let Some(child) = children.get(uuid) {
|
||||
let child = child.read().await;
|
||||
Ok(child.stdout.get_output().await?)
|
||||
Ok(child.output.get_output().await?)
|
||||
} else {
|
||||
Err(crate::ErrorKind::LauncherError(format!(
|
||||
"No child process by UUID {}",
|
||||
@@ -89,26 +89,6 @@ pub async fn get_stdout_by_uuid(uuid: &Uuid) -> crate::Result<String> {
|
||||
}
|
||||
}
|
||||
|
||||
// Gets stderr of a child process stored in the state by UUID, as a string
|
||||
#[tracing::instrument]
|
||||
pub async fn get_stderr_by_uuid(uuid: &Uuid) -> crate::Result<String> {
|
||||
let state = State::get().await?;
|
||||
// Get stdout from child
|
||||
let children = state.children.read().await;
|
||||
|
||||
// Extract child or return crate::Error
|
||||
if let Some(child) = children.get(uuid) {
|
||||
let child = child.read().await;
|
||||
Ok(child.stderr.get_output().await?)
|
||||
} else {
|
||||
Err(crate::ErrorKind::LauncherError(format!(
|
||||
"No child process with UUID {}",
|
||||
uuid
|
||||
))
|
||||
.as_error())
|
||||
}
|
||||
}
|
||||
|
||||
// Kill a child process stored in the state by UUID, as a string
|
||||
#[tracing::instrument]
|
||||
pub async fn kill_by_uuid(uuid: &Uuid) -> crate::Result<()> {
|
||||
@@ -150,7 +130,7 @@ pub async fn kill(running: &mut MinecraftChild) -> crate::Result<()> {
|
||||
pub async fn wait_for(running: &mut MinecraftChild) -> crate::Result<()> {
|
||||
// We do not wait on the Child directly, but wait on the thread manager.
|
||||
// This way we can still run all cleanup hook functions that happen after.
|
||||
let result = running
|
||||
running
|
||||
.manager
|
||||
.take()
|
||||
.ok_or_else(|| {
|
||||
@@ -166,12 +146,5 @@ pub async fn wait_for(running: &mut MinecraftChild) -> crate::Result<()> {
|
||||
))
|
||||
})?;
|
||||
|
||||
match result.success() {
|
||||
false => Err(crate::ErrorKind::LauncherError(format!(
|
||||
"Minecraft exited with non-zero code {}",
|
||||
result.code().unwrap_or(-1)
|
||||
))
|
||||
.as_error()),
|
||||
true => Ok(()),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -120,6 +120,7 @@ pub async fn edit_icon(
|
||||
ProfilePayloadType::Edited,
|
||||
)
|
||||
.await?;
|
||||
State::sync().await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -133,7 +134,10 @@ pub async fn edit_icon(
|
||||
profile.metadata.icon = None;
|
||||
async { Ok(()) }
|
||||
})
|
||||
.await
|
||||
.await?;
|
||||
State::sync().await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -288,6 +292,7 @@ pub async fn update_all(
|
||||
ProfilePayloadType::Edited,
|
||||
)
|
||||
.await?;
|
||||
State::sync().await?;
|
||||
|
||||
Ok(Arc::try_unwrap(map).unwrap().into_inner())
|
||||
} else {
|
||||
@@ -344,6 +349,7 @@ pub async fn update_project(
|
||||
ProfilePayloadType::Edited,
|
||||
)
|
||||
.await?;
|
||||
State::sync().await?;
|
||||
}
|
||||
|
||||
return Ok(path);
|
||||
@@ -378,6 +384,7 @@ pub async fn add_project_from_version(
|
||||
ProfilePayloadType::Edited,
|
||||
)
|
||||
.await?;
|
||||
State::sync().await?;
|
||||
|
||||
Ok(path)
|
||||
} else {
|
||||
@@ -418,6 +425,7 @@ pub async fn add_project_from_path(
|
||||
ProfilePayloadType::Edited,
|
||||
)
|
||||
.await?;
|
||||
State::sync().await?;
|
||||
|
||||
Ok(path)
|
||||
} else {
|
||||
@@ -444,6 +452,7 @@ pub async fn toggle_disable_project(
|
||||
ProfilePayloadType::Edited,
|
||||
)
|
||||
.await?;
|
||||
State::sync().await?;
|
||||
|
||||
Ok(res)
|
||||
} else {
|
||||
@@ -470,6 +479,7 @@ pub async fn remove_project(
|
||||
ProfilePayloadType::Edited,
|
||||
)
|
||||
.await?;
|
||||
State::sync().await?;
|
||||
|
||||
Ok(())
|
||||
} else {
|
||||
|
||||
@@ -164,7 +164,11 @@ pub(crate) async fn get_loader_version_from_loader(
|
||||
let filter = |it: &LoaderVersion| match version.as_str() {
|
||||
"latest" => true,
|
||||
"stable" => it.stable,
|
||||
id => it.id == *id || format!("{}-{}", game_version, id) == it.id,
|
||||
id => {
|
||||
it.id == *id
|
||||
|| format!("{}-{}", game_version, id) == it.id
|
||||
|| format!("{}-{}-{}", game_version, id, game_version) == it.id
|
||||
}
|
||||
};
|
||||
|
||||
let loader_data = match loader {
|
||||
|
||||
@@ -124,7 +124,7 @@ impl Drop for LoadingBarId {
|
||||
// Emit event to indicatif progress bar arc
|
||||
#[cfg(feature = "cli")]
|
||||
{
|
||||
let cli_progress_bar = bar.cli_progress_bar.clone();
|
||||
let cli_progress_bar = bar.cli_progress_bar;
|
||||
cli_progress_bar.finish();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -262,12 +262,17 @@ fn parse_minecraft_argument(
|
||||
resolution: WindowSize,
|
||||
) -> crate::Result<String> {
|
||||
Ok(argument
|
||||
.replace("${accessToken}", access_token)
|
||||
.replace("${auth_access_token}", access_token)
|
||||
.replace("${auth_session}", access_token)
|
||||
.replace("${auth_player_name}", username)
|
||||
// TODO: add auth xuid eventually
|
||||
.replace("${auth_xuid}", "0")
|
||||
.replace("${auth_uuid}", &uuid.hyphenated().to_string())
|
||||
.replace("${uuid}", &uuid.hyphenated().to_string())
|
||||
.replace("${clientid}", "c4502edb-87c6-40cb-b595-64a280cf8906")
|
||||
.replace("${user_properties}", "{}")
|
||||
.replace("${user_type}", "mojang")
|
||||
.replace("${user_type}", "msa")
|
||||
.replace("${version_name}", version)
|
||||
.replace("${assets_index_name}", asset_index_name)
|
||||
.replace(
|
||||
|
||||
@@ -14,6 +14,7 @@ use daedalus as d;
|
||||
use daedalus::minecraft::VersionInfo;
|
||||
use dunce::canonicalize;
|
||||
use st::Profile;
|
||||
use std::collections::HashMap;
|
||||
use std::fs;
|
||||
use std::{process::Stdio, sync::Arc};
|
||||
use tokio::process::Command;
|
||||
@@ -36,9 +37,12 @@ pub fn parse_rule(rule: &d::minecraft::Rule, java_version: &str) -> bool {
|
||||
features: Some(ref features),
|
||||
..
|
||||
} => {
|
||||
features.has_demo_resolution.unwrap_or(false)
|
||||
|| (features.has_demo_resolution.is_none()
|
||||
&& features.is_demo_user.is_none())
|
||||
!features.is_demo_user.unwrap_or(true)
|
||||
|| features.has_custom_resolution.unwrap_or(false)
|
||||
|| !features.has_quick_plays_support.unwrap_or(true)
|
||||
|| !features.is_quick_play_multiplayer.unwrap_or(true)
|
||||
|| !features.is_quick_play_realms.unwrap_or(true)
|
||||
|| !features.is_quick_play_singleplayer.unwrap_or(true)
|
||||
}
|
||||
_ => false,
|
||||
};
|
||||
@@ -431,7 +435,6 @@ pub async fn launch_minecraft(
|
||||
fs::create_dir_all(&logs_dir)?;
|
||||
|
||||
let stdout_log_path = logs_dir.join("stdout.log");
|
||||
let stderr_log_path = logs_dir.join("stderr.log");
|
||||
|
||||
crate::api::profile::edit(&profile.path, |prof| {
|
||||
prof.metadata.last_played = Some(Utc::now());
|
||||
@@ -439,6 +442,34 @@ pub async fn launch_minecraft(
|
||||
async { Ok(()) }
|
||||
})
|
||||
.await?;
|
||||
State::sync().await?;
|
||||
|
||||
let mut censor_strings = HashMap::new();
|
||||
let username = whoami::username();
|
||||
censor_strings.insert(
|
||||
format!("/{}/", username),
|
||||
"/{COMPUTER_USERNAME}/".to_string(),
|
||||
);
|
||||
censor_strings.insert(
|
||||
format!("\\{}\\", username),
|
||||
"\\{COMPUTER_USERNAME}\\".to_string(),
|
||||
);
|
||||
censor_strings.insert(
|
||||
credentials.access_token.clone(),
|
||||
"{MINECRAFT_ACCESS_TOKEN}".to_string(),
|
||||
);
|
||||
censor_strings.insert(
|
||||
credentials.username.clone(),
|
||||
"{MINECRAFT_USERNAME}".to_string(),
|
||||
);
|
||||
censor_strings.insert(
|
||||
credentials.id.as_simple().to_string(),
|
||||
"{MINECRAFT_UUID}".to_string(),
|
||||
);
|
||||
censor_strings.insert(
|
||||
credentials.id.as_hyphenated().to_string(),
|
||||
"{MINECRAFT_UUID}".to_string(),
|
||||
);
|
||||
|
||||
// Create Minecraft child by inserting it into the state
|
||||
// This also spawns the process and prepares the subsequent processes
|
||||
@@ -448,9 +479,9 @@ pub async fn launch_minecraft(
|
||||
Uuid::new_v4(),
|
||||
instance_path.to_path_buf(),
|
||||
stdout_log_path,
|
||||
stderr_log_path,
|
||||
command,
|
||||
post_exit_hook,
|
||||
censor_strings,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
@@ -26,8 +26,7 @@ pub struct MinecraftChild {
|
||||
pub profile_path: PathBuf, //todo: make UUID when profiles are recognized by UUID
|
||||
pub manager: Option<JoinHandle<crate::Result<ExitStatus>>>, // None when future has completed and been handled
|
||||
pub current_child: Arc<RwLock<Child>>,
|
||||
pub stdout: SharedOutput,
|
||||
pub stderr: SharedOutput,
|
||||
pub output: SharedOutput,
|
||||
}
|
||||
|
||||
impl Children {
|
||||
@@ -45,27 +44,27 @@ impl Children {
|
||||
&mut self,
|
||||
uuid: Uuid,
|
||||
profile_path: PathBuf,
|
||||
stdout_log_path: PathBuf,
|
||||
stderr_log_path: PathBuf,
|
||||
log_path: PathBuf,
|
||||
mut mc_command: Command,
|
||||
post_command: Option<Command>, // Command to run after minecraft.
|
||||
censor_strings: HashMap<String, String>,
|
||||
) -> crate::Result<Arc<RwLock<MinecraftChild>>> {
|
||||
// Takes the first element of the commands vector and spawns it
|
||||
let mut child = mc_command.spawn()?;
|
||||
|
||||
// Create std watcher threads for stdout and stderr
|
||||
let stdout = SharedOutput::build(&stdout_log_path).await?;
|
||||
let shared_output =
|
||||
SharedOutput::build(&log_path, censor_strings).await?;
|
||||
if let Some(child_stdout) = child.stdout.take() {
|
||||
let stdout_clone = stdout.clone();
|
||||
let stdout_clone = shared_output.clone();
|
||||
tokio::spawn(async move {
|
||||
if let Err(e) = stdout_clone.read_stdout(child_stdout).await {
|
||||
error!("Stdout process died with error: {}", e);
|
||||
}
|
||||
});
|
||||
}
|
||||
let stderr = SharedOutput::build(&stderr_log_path).await?;
|
||||
if let Some(child_stderr) = child.stderr.take() {
|
||||
let stderr_clone = stderr.clone();
|
||||
let stderr_clone = shared_output.clone();
|
||||
tokio::spawn(async move {
|
||||
if let Err(e) = stderr_clone.read_stderr(child_stderr).await {
|
||||
error!("Stderr process died with error: {}", e);
|
||||
@@ -100,8 +99,7 @@ impl Children {
|
||||
uuid,
|
||||
profile_path,
|
||||
current_child,
|
||||
stdout,
|
||||
stderr,
|
||||
output: shared_output,
|
||||
manager,
|
||||
};
|
||||
|
||||
@@ -293,13 +291,18 @@ impl Default for Children {
|
||||
pub struct SharedOutput {
|
||||
output: Arc<RwLock<String>>,
|
||||
log_file: Arc<RwLock<File>>,
|
||||
censor_strings: HashMap<String, String>,
|
||||
}
|
||||
|
||||
impl SharedOutput {
|
||||
async fn build(log_file_path: &Path) -> crate::Result<Self> {
|
||||
async fn build(
|
||||
log_file_path: &Path,
|
||||
censor_strings: HashMap<String, String>,
|
||||
) -> crate::Result<Self> {
|
||||
Ok(SharedOutput {
|
||||
output: Arc::new(RwLock::new(String::new())),
|
||||
log_file: Arc::new(RwLock::new(File::create(log_file_path).await?)),
|
||||
censor_strings,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -317,14 +320,17 @@ impl SharedOutput {
|
||||
let mut line = String::new();
|
||||
|
||||
while buf_reader.read_line(&mut line).await? > 0 {
|
||||
let val_line = self.censor_log(line.clone());
|
||||
|
||||
{
|
||||
let mut output = self.output.write().await;
|
||||
output.push_str(&line);
|
||||
output.push_str(&val_line);
|
||||
}
|
||||
{
|
||||
let mut log_file = self.log_file.write().await;
|
||||
log_file.write_all(line.as_bytes()).await?;
|
||||
log_file.write_all(val_line.as_bytes()).await?;
|
||||
}
|
||||
|
||||
line.clear();
|
||||
}
|
||||
Ok(())
|
||||
@@ -338,12 +344,27 @@ impl SharedOutput {
|
||||
let mut line = String::new();
|
||||
|
||||
while buf_reader.read_line(&mut line).await? > 0 {
|
||||
let val_line = self.censor_log(line.clone());
|
||||
|
||||
{
|
||||
let mut output = self.output.write().await;
|
||||
output.push_str(&line);
|
||||
output.push_str(&val_line);
|
||||
}
|
||||
{
|
||||
let mut log_file = self.log_file.write().await;
|
||||
log_file.write_all(val_line.as_bytes()).await?;
|
||||
}
|
||||
|
||||
line.clear();
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn censor_log(&self, mut val: String) -> String {
|
||||
for (find, replace) in &self.censor_strings {
|
||||
val = val.replace(find, replace);
|
||||
}
|
||||
|
||||
val
|
||||
}
|
||||
}
|
||||
|
||||
@@ -266,7 +266,16 @@ async fn init_watcher() -> crate::Result<Debouncer<RecommendedWatcher>> {
|
||||
}
|
||||
}
|
||||
|
||||
if !visited_paths.contains(&new_path) {
|
||||
if e.path
|
||||
.components()
|
||||
.any(|x| x.as_os_str() == "crash-reports")
|
||||
&& e.path
|
||||
.extension()
|
||||
.map(|x| x == "txt")
|
||||
.unwrap_or(false)
|
||||
{
|
||||
Profile::crash_task(new_path);
|
||||
} else if !visited_paths.contains(&new_path) {
|
||||
Profile::sync_projects_task(new_path.clone());
|
||||
visited_paths.push(new_path);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use super::settings::{Hooks, MemorySettings, WindowSize};
|
||||
use crate::config::MODRINTH_API_URL;
|
||||
use crate::data::DirectoryInfo;
|
||||
use crate::event::emit::emit_profile;
|
||||
use crate::event::emit::{emit_profile, emit_warning};
|
||||
use crate::event::ProfilePayloadType;
|
||||
use crate::prelude::JavaVersion;
|
||||
use crate::state::projects::Project;
|
||||
@@ -198,6 +198,30 @@ impl Profile {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn crash_task(path: PathBuf) {
|
||||
tokio::task::spawn(async move {
|
||||
let res = async {
|
||||
let profile = crate::api::profile::get(&path, None).await?;
|
||||
|
||||
if let Some(profile) = profile {
|
||||
emit_warning(&format!("Profile {} has crashed! Visit the logs page to see a crash report.", profile.metadata.name)).await?;
|
||||
}
|
||||
|
||||
Ok::<(), crate::Error>(())
|
||||
}
|
||||
.await;
|
||||
|
||||
match res {
|
||||
Ok(()) => {}
|
||||
Err(err) => {
|
||||
tracing::warn!(
|
||||
"Unable to send crash report to frontend: {err}"
|
||||
)
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
pub fn sync_projects_task(path: PathBuf) {
|
||||
tokio::task::spawn(async move {
|
||||
let res = async {
|
||||
@@ -306,6 +330,7 @@ impl Profile {
|
||||
.await?;
|
||||
watch_path(profile_path, watcher, ProjectType::DataPack.get_folder())
|
||||
.await?;
|
||||
watch_path(profile_path, watcher, "crash-reports").await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -575,6 +600,11 @@ impl Profiles {
|
||||
))
|
||||
.await?;
|
||||
|
||||
{
|
||||
let profiles = state.profiles.read().await;
|
||||
profiles.sync().await?;
|
||||
}
|
||||
|
||||
Ok::<(), crate::Error>(())
|
||||
}
|
||||
.await;
|
||||
|
||||
@@ -28,6 +28,10 @@ pub struct Settings {
|
||||
pub collapsed_navigation: bool,
|
||||
#[serde(default)]
|
||||
pub developer_mode: bool,
|
||||
#[serde(default)]
|
||||
pub opt_out_analytics: bool,
|
||||
#[serde(default)]
|
||||
pub advanced_rendering: bool,
|
||||
}
|
||||
|
||||
impl Default for Settings {
|
||||
@@ -46,6 +50,8 @@ impl Default for Settings {
|
||||
version: CURRENT_FORMAT_VERSION,
|
||||
collapsed_navigation: false,
|
||||
developer_mode: false,
|
||||
opt_out_analytics: false,
|
||||
advanced_rendering: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user