You've already forked AstralRinth
forked from didirus/AstralRinth
String settings hooks (#82)
* added theme; env change * began refactoring * added process hook * now singular string for each hook * fixed splitting by comma to by space * profile_create function updated * prettier * added jre validator * restructured so that it doesnt look like a vec * fixed merge issue * snake case * resolved merge issues + added process events * clippy, fmt * removed unnecssary func
This commit is contained in:
@@ -1,4 +1,6 @@
|
||||
//! Authentication flow interface
|
||||
use std::path::PathBuf;
|
||||
|
||||
use crate::{
|
||||
launcher::download,
|
||||
prelude::Profile,
|
||||
@@ -139,3 +141,8 @@ pub async fn validate_globals() -> crate::Result<bool> {
|
||||
let settings = state.settings.read().await;
|
||||
Ok(settings.java_globals.is_all_valid())
|
||||
}
|
||||
|
||||
// Validates JRE at a given at a given path
|
||||
pub async fn check_jre(path: PathBuf) -> crate::Result<Option<JavaVersion>> {
|
||||
Ok(jre::check_java_at_filepath(&path))
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ pub mod tags;
|
||||
pub mod data {
|
||||
pub use crate::state::{
|
||||
DirectoryInfo, Hooks, JavaSettings, MemorySettings, ModLoader,
|
||||
ProfileMetadata, Settings, WindowSize,
|
||||
ProfileMetadata, Settings, Theme, WindowSize,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -229,8 +229,8 @@ async fn install_pack(
|
||||
.await?;
|
||||
|
||||
let loading_bar = init_loading(
|
||||
LoadingBarType::PackDownload {
|
||||
pack_name ,
|
||||
LoadingBarType::PackDownload {
|
||||
pack_name,
|
||||
pack_id: project_id,
|
||||
pack_version: version_id,
|
||||
},
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
//! Theseus process management interface
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::state::MinecraftChild;
|
||||
pub use crate::{
|
||||
state::{
|
||||
@@ -9,31 +11,33 @@ pub use crate::{
|
||||
State,
|
||||
};
|
||||
|
||||
// Gets whether a child process stored in the state by PID has finished
|
||||
// Gets whether a child process stored in the state by UUID has finished
|
||||
#[tracing::instrument]
|
||||
pub async fn has_finished_by_pid(pid: u32) -> crate::Result<bool> {
|
||||
Ok(get_exit_status_by_pid(pid).await?.is_some())
|
||||
pub async fn has_finished_by_uuid(uuid: &Uuid) -> crate::Result<bool> {
|
||||
Ok(get_exit_status_by_uuid(uuid).await?.is_some())
|
||||
}
|
||||
|
||||
// Gets the exit status of a child process stored in the state by PID
|
||||
// Gets the exit status of a child process stored in the state by UUID
|
||||
#[tracing::instrument]
|
||||
pub async fn get_exit_status_by_pid(pid: u32) -> crate::Result<Option<i32>> {
|
||||
pub async fn get_exit_status_by_uuid(
|
||||
uuid: &Uuid,
|
||||
) -> crate::Result<Option<i32>> {
|
||||
let state = State::get().await?;
|
||||
let children = state.children.read().await;
|
||||
Ok(children.exit_status(&pid).await?.and_then(|f| f.code()))
|
||||
Ok(children.exit_status(uuid).await?.and_then(|f| f.code()))
|
||||
}
|
||||
|
||||
// Gets the PID of each stored process in the state
|
||||
// Gets the UUID of each stored process in the state
|
||||
#[tracing::instrument]
|
||||
pub async fn get_all_pids() -> crate::Result<Vec<u32>> {
|
||||
pub async fn get_all_uuids() -> crate::Result<Vec<Uuid>> {
|
||||
let state = State::get().await?;
|
||||
let children = state.children.read().await;
|
||||
Ok(children.keys())
|
||||
}
|
||||
|
||||
// Gets the PID of each *running* stored process in the state
|
||||
// Gets the UUID of each *running* stored process in the state
|
||||
#[tracing::instrument]
|
||||
pub async fn get_all_running_pids() -> crate::Result<Vec<u32>> {
|
||||
pub async fn get_all_running_uuids() -> crate::Result<Vec<Uuid>> {
|
||||
let state = State::get().await?;
|
||||
let children = state.children.read().await;
|
||||
children.running_keys().await
|
||||
@@ -55,62 +59,62 @@ pub async fn get_all_running_profiles() -> crate::Result<Vec<Profile>> {
|
||||
children.running_profiles().await
|
||||
}
|
||||
|
||||
// Gets the PID of each stored process in the state by profile path
|
||||
// Gets the UUID of each stored process in the state by profile path
|
||||
#[tracing::instrument]
|
||||
pub async fn get_pids_by_profile_path(
|
||||
pub async fn get_uuids_by_profile_path(
|
||||
profile_path: &Path,
|
||||
) -> crate::Result<Vec<u32>> {
|
||||
) -> crate::Result<Vec<Uuid>> {
|
||||
let state = State::get().await?;
|
||||
let children = state.children.read().await;
|
||||
children.running_keys_with_profile(profile_path).await
|
||||
}
|
||||
|
||||
// Gets stdout of a child process stored in the state by PID, as a string
|
||||
// Gets stdout of a child process stored in the state by UUID, as a string
|
||||
#[tracing::instrument]
|
||||
pub async fn get_stdout_by_pid(pid: u32) -> crate::Result<String> {
|
||||
pub async fn get_stdout_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(&pid) {
|
||||
if let Some(child) = children.get(uuid) {
|
||||
let child = child.read().await;
|
||||
Ok(child.stdout.get_output().await?)
|
||||
} else {
|
||||
Err(crate::ErrorKind::LauncherError(format!(
|
||||
"No child process with PID {}",
|
||||
pid
|
||||
"No child process by UUID {}",
|
||||
uuid
|
||||
))
|
||||
.as_error())
|
||||
}
|
||||
}
|
||||
|
||||
// Gets stderr of a child process stored in the state by PID, as a string
|
||||
// Gets stderr of a child process stored in the state by UUID, as a string
|
||||
#[tracing::instrument]
|
||||
pub async fn get_stderr_by_pid(pid: u32) -> crate::Result<String> {
|
||||
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(&pid) {
|
||||
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 PID {}",
|
||||
pid
|
||||
"No child process with UUID {}",
|
||||
uuid
|
||||
))
|
||||
.as_error())
|
||||
}
|
||||
}
|
||||
|
||||
// Kill a child process stored in the state by PID, as a string
|
||||
// Kill a child process stored in the state by UUID, as a string
|
||||
#[tracing::instrument]
|
||||
pub async fn kill_by_pid(pid: u32) -> crate::Result<()> {
|
||||
pub async fn kill_by_uuid(uuid: &Uuid) -> crate::Result<()> {
|
||||
let state = State::get().await?;
|
||||
let children = state.children.read().await;
|
||||
if let Some(mchild) = children.get(&pid) {
|
||||
if let Some(mchild) = children.get(uuid) {
|
||||
let mut mchild = mchild.write().await;
|
||||
kill(&mut mchild).await
|
||||
} else {
|
||||
@@ -119,13 +123,13 @@ pub async fn kill_by_pid(pid: u32) -> crate::Result<()> {
|
||||
}
|
||||
}
|
||||
|
||||
// Wait for a child process stored in the state by PID
|
||||
// Wait for a child process stored in the state by UUID
|
||||
#[tracing::instrument]
|
||||
pub async fn wait_for_by_pid(pid: u32) -> crate::Result<()> {
|
||||
pub async fn wait_for_by_uuid(uuid: &Uuid) -> crate::Result<()> {
|
||||
let state = State::get().await?;
|
||||
let children = state.children.read().await;
|
||||
// No error returned for already killed process
|
||||
if let Some(mchild) = children.get(&pid) {
|
||||
if let Some(mchild) = children.get(uuid) {
|
||||
let mut mchild = mchild.write().await;
|
||||
wait_for(&mut mchild).await
|
||||
} else {
|
||||
@@ -137,18 +141,30 @@ pub async fn wait_for_by_pid(pid: u32) -> crate::Result<()> {
|
||||
// Kill a running child process directly, and wait for it to be killed
|
||||
#[tracing::instrument]
|
||||
pub async fn kill(running: &mut MinecraftChild) -> crate::Result<()> {
|
||||
running.child.kill().await?;
|
||||
running.current_child.write().await.kill().await?;
|
||||
wait_for(running).await
|
||||
}
|
||||
|
||||
// Await on the completion of a child process directly
|
||||
#[tracing::instrument]
|
||||
pub async fn wait_for(running: &mut MinecraftChild) -> crate::Result<()> {
|
||||
let result = running.child.wait().await.map_err(|err| {
|
||||
crate::ErrorKind::LauncherError(format!(
|
||||
"Error running minecraft: {err}"
|
||||
))
|
||||
})?;
|
||||
// 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
|
||||
.manager
|
||||
.take()
|
||||
.ok_or_else(|| {
|
||||
crate::ErrorKind::LauncherError(format!(
|
||||
"Process manager already completed or missing for process {}",
|
||||
running.uuid
|
||||
))
|
||||
})?
|
||||
.await?
|
||||
.map_err(|err| {
|
||||
crate::ErrorKind::LauncherError(format!(
|
||||
"Error running minecraft: {err}"
|
||||
))
|
||||
})?;
|
||||
|
||||
match result.success() {
|
||||
false => Err(crate::ErrorKind::LauncherError(format!(
|
||||
|
||||
@@ -256,7 +256,7 @@ pub async fn run_credentials(
|
||||
.await?;
|
||||
let pre_launch_hooks =
|
||||
&profile.hooks.as_ref().unwrap_or(&settings.hooks).pre_launch;
|
||||
for hook in pre_launch_hooks.iter() {
|
||||
if let Some(hook) = pre_launch_hooks {
|
||||
// TODO: hook parameters
|
||||
let mut cmd = hook.split(' ');
|
||||
if let Some(command) = cmd.next() {
|
||||
@@ -336,6 +336,23 @@ pub async fn run_credentials(
|
||||
|
||||
let env_args = &settings.custom_env_args;
|
||||
|
||||
// Post post exit hooks
|
||||
let post_exit_hook =
|
||||
&profile.hooks.as_ref().unwrap_or(&settings.hooks).post_exit;
|
||||
|
||||
let post_exit_hook = if let Some(hook) = post_exit_hook {
|
||||
let mut cmd = hook.split(' ');
|
||||
if let Some(command) = cmd.next() {
|
||||
let mut command = Command::new(command);
|
||||
command.args(&cmd.collect::<Vec<&str>>()).current_dir(path);
|
||||
Some(command)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let mc_process = crate::launcher::launch_minecraft(
|
||||
&profile.metadata.game_version,
|
||||
&profile.metadata.loader_version,
|
||||
@@ -347,20 +364,10 @@ pub async fn run_credentials(
|
||||
&memory,
|
||||
&resolution,
|
||||
credentials,
|
||||
post_exit_hook,
|
||||
&profile,
|
||||
)
|
||||
.await?;
|
||||
|
||||
// Insert child into state
|
||||
let mut state_children = state.children.write().await;
|
||||
let pid = mc_process.id().ok_or_else(|| {
|
||||
crate::ErrorKind::LauncherError(
|
||||
"Process failed to stay open.".to_string(),
|
||||
)
|
||||
})?;
|
||||
let mchild_arc = state_children
|
||||
.insert_process(pid, path.to_path_buf(), mc_process)
|
||||
.await?;
|
||||
|
||||
Ok(mchild_arc)
|
||||
Ok(mc_process)
|
||||
}
|
||||
|
||||
@@ -129,7 +129,7 @@ pub async fn emit_warning(message: &str) -> crate::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// emit_process(pid, event, message)
|
||||
// emit_process(uuid, pid, event, message)
|
||||
#[allow(unused_variables)]
|
||||
pub async fn emit_process(
|
||||
uuid: uuid::Uuid,
|
||||
|
||||
@@ -93,7 +93,7 @@ pub enum LoadingBarType {
|
||||
pack_name: String,
|
||||
pack_id: Option<String>,
|
||||
pack_version: Option<String>,
|
||||
},
|
||||
},
|
||||
MinecraftDownload {
|
||||
profile_uuid: Uuid,
|
||||
profile_name: String,
|
||||
@@ -121,11 +121,11 @@ pub struct ProcessPayload {
|
||||
pub event: ProcessPayloadType,
|
||||
pub message: String,
|
||||
}
|
||||
#[derive(Serialize, Clone)]
|
||||
#[derive(Serialize, Clone, Debug)]
|
||||
pub enum ProcessPayloadType {
|
||||
Launched,
|
||||
// Finishing, // TODO: process restructing incoming, currently this is never emitted
|
||||
// Finished, // TODO: process restructing incoming, currently this is never emitted
|
||||
Updated, // eg: if the MinecraftChild changes to its post-command process instead of the Minecraft process
|
||||
Finished,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Clone)]
|
||||
|
||||
@@ -33,7 +33,7 @@ pub async fn download_minecraft(
|
||||
LoadingBarType::MinecraftDownload {
|
||||
// If we are downloading minecraft for a profile, provide its name and uuid
|
||||
profile_name: profile.metadata.name.clone(),
|
||||
profile_uuid: profile.uuid,
|
||||
profile_uuid: profile.uuid,
|
||||
},
|
||||
100.0,
|
||||
"Downloading Minecraft...",
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
//! Logic for launching Minecraft
|
||||
use crate::{process, state as st};
|
||||
use crate::{
|
||||
process,
|
||||
state::{self as st, MinecraftChild},
|
||||
};
|
||||
use daedalus as d;
|
||||
use dunce::canonicalize;
|
||||
use st::Profile;
|
||||
use std::{path::Path, process::Stdio};
|
||||
use tokio::process::{Child, Command};
|
||||
use std::{path::Path, process::Stdio, sync::Arc};
|
||||
use tokio::process::Command;
|
||||
|
||||
mod args;
|
||||
|
||||
@@ -58,8 +61,9 @@ pub async fn launch_minecraft(
|
||||
memory: &st::MemorySettings,
|
||||
resolution: &st::WindowSize,
|
||||
credentials: &auth::Credentials,
|
||||
post_exit_hook: Option<Command>,
|
||||
profile: &Profile, // optional ref to Profile for event tracking
|
||||
) -> crate::Result<Child> {
|
||||
) -> crate::Result<Arc<tokio::sync::RwLock<MinecraftChild>>> {
|
||||
let state = st::State::get().await?;
|
||||
let instance_path = &canonicalize(instance_path)?;
|
||||
|
||||
@@ -182,10 +186,10 @@ pub async fn launch_minecraft(
|
||||
// Check if profile has a running profile, and reject running the command if it does
|
||||
// Done late so a quick double call doesn't launch two instances
|
||||
let existing_processes =
|
||||
process::get_pids_by_profile_path(instance_path).await?;
|
||||
if let Some(pid) = existing_processes.first() {
|
||||
process::get_uuids_by_profile_path(instance_path).await?;
|
||||
if let Some(uuid) = existing_processes.first() {
|
||||
return Err(crate::ErrorKind::LauncherError(format!(
|
||||
"Profile {} is already running at PID: {pid}",
|
||||
"Profile {} is already running at UUID: {uuid}",
|
||||
instance_path.display()
|
||||
))
|
||||
.as_error());
|
||||
@@ -233,12 +237,15 @@ pub async fn launch_minecraft(
|
||||
.stdout(Stdio::piped())
|
||||
.stderr(Stdio::piped());
|
||||
|
||||
command.spawn().map_err(|err| {
|
||||
crate::ErrorKind::LauncherError(format!(
|
||||
"Error running Minecraft (minecraft-{} @ {}): {err}",
|
||||
&version.id,
|
||||
instance_path.display()
|
||||
))
|
||||
.as_error()
|
||||
})
|
||||
// Create Minecraft child by inserting it into the state
|
||||
// This also spawns the process and prepares the subsequent processes
|
||||
let mut state_children = state.children.write().await;
|
||||
state_children
|
||||
.insert_process(
|
||||
uuid::Uuid::new_v4(),
|
||||
instance_path.to_path_buf(),
|
||||
command,
|
||||
post_exit_hook,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
@@ -1,25 +1,29 @@
|
||||
use super::Profile;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process::ExitStatus;
|
||||
use std::{collections::HashMap, sync::Arc};
|
||||
use tokio::io::{AsyncBufReadExt, BufReader};
|
||||
use tokio::process::Child;
|
||||
use tokio::process::Command;
|
||||
use tokio::process::{ChildStderr, ChildStdout};
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
use crate::event::emit::emit_process;
|
||||
use crate::event::ProcessPayloadType;
|
||||
|
||||
use super::Profile;
|
||||
use tokio::task::JoinHandle;
|
||||
use uuid::Uuid;
|
||||
|
||||
// Child processes (instances of Minecraft)
|
||||
// A wrapper over a Hashmap connecting PID -> MinecraftChild
|
||||
pub struct Children(HashMap<u32, Arc<RwLock<MinecraftChild>>>);
|
||||
pub struct Children(HashMap<Uuid, Arc<RwLock<MinecraftChild>>>);
|
||||
|
||||
// Minecraft Child, bundles together the PID, the actual Child, and the easily queryable stdout and stderr streams
|
||||
#[derive(Debug)]
|
||||
pub struct MinecraftChild {
|
||||
pub uuid: uuid::Uuid,
|
||||
pub pid: u32,
|
||||
pub uuid: Uuid,
|
||||
pub profile_path: PathBuf, //todo: make UUID when profiles are recognized by UUID
|
||||
pub child: tokio::process::Child,
|
||||
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,
|
||||
}
|
||||
@@ -29,16 +33,18 @@ impl Children {
|
||||
Children(HashMap::new())
|
||||
}
|
||||
|
||||
// Inserts a child process to keep track of, and returns a reference to the container struct MinecraftChild
|
||||
// Runs the command in process, inserts a child process to keep track of, and returns a reference to the container struct MinecraftChild
|
||||
// The threads for stdout and stderr are spawned here
|
||||
// Unlike a Hashmap's 'insert', this directly returns the reference to the Child rather than any previously stored Child that may exist
|
||||
// Unlike a Hashmap's 'insert', this directly returns the reference to the MinecraftChild rather than any previously stored MinecraftChild that may exist
|
||||
pub async fn insert_process(
|
||||
&mut self,
|
||||
pid: u32,
|
||||
uuid: Uuid,
|
||||
profile_path: PathBuf,
|
||||
mut child: tokio::process::Child,
|
||||
mut mc_command: Command,
|
||||
post_command: Option<Command>, // Command to run after minecraft.
|
||||
) -> crate::Result<Arc<RwLock<MinecraftChild>>> {
|
||||
let uuid = uuid::Uuid::new_v4();
|
||||
// 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::new();
|
||||
@@ -55,11 +61,25 @@ impl Children {
|
||||
let stderr_clone = stderr.clone();
|
||||
tokio::spawn(async move {
|
||||
if let Err(e) = stderr_clone.read_stderr(child_stderr).await {
|
||||
eprintln!("Stderr thread died with error: {}", e);
|
||||
eprintln!("Stderr process died with error: {}", e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Slots child into manager
|
||||
let pid = child.id().ok_or_else(|| {
|
||||
crate::ErrorKind::LauncherError(
|
||||
"Process immediately failed, could not get PID".to_string(),
|
||||
)
|
||||
})?;
|
||||
let current_child = Arc::new(RwLock::new(child));
|
||||
let manager = Some(tokio::spawn(Self::sequential_process_manager(
|
||||
uuid,
|
||||
post_command,
|
||||
pid,
|
||||
current_child.clone(),
|
||||
)));
|
||||
|
||||
emit_process(
|
||||
uuid,
|
||||
pid,
|
||||
@@ -71,24 +91,88 @@ impl Children {
|
||||
// Create MinecraftChild
|
||||
let mchild = MinecraftChild {
|
||||
uuid,
|
||||
pid,
|
||||
profile_path,
|
||||
child,
|
||||
current_child,
|
||||
stdout,
|
||||
stderr,
|
||||
manager,
|
||||
};
|
||||
|
||||
let mchild = Arc::new(RwLock::new(mchild));
|
||||
self.0.insert(pid, mchild.clone());
|
||||
self.0.insert(uuid, mchild.clone());
|
||||
Ok(mchild)
|
||||
}
|
||||
|
||||
// Spawns a new child process and inserts it into the hashmap
|
||||
// Also, as the process ends, it spawns the follow-up process if it exists
|
||||
// By convention, ExitStatus is last command's exit status, and we exit on the first non-zero exit status
|
||||
async fn sequential_process_manager(
|
||||
uuid: Uuid,
|
||||
post_command: Option<Command>,
|
||||
mut current_pid: u32,
|
||||
current_child: Arc<RwLock<Child>>,
|
||||
) -> crate::Result<ExitStatus> {
|
||||
let current_child = current_child.clone();
|
||||
|
||||
// Wait on current Minecraft Child
|
||||
let mut mc_exit_status;
|
||||
loop {
|
||||
if let Some(t) = current_child.write().await.try_wait()? {
|
||||
mc_exit_status = t;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if !mc_exit_status.success() {
|
||||
return Ok(mc_exit_status); // Err for a non-zero exit is handled in helper
|
||||
}
|
||||
|
||||
// If a post-command exist, switch to it and wait on it
|
||||
if let Some(mut m_command) = post_command {
|
||||
{
|
||||
let mut current_child = current_child.write().await;
|
||||
let new_child = m_command.spawn()?;
|
||||
current_pid = new_child.id().ok_or_else(|| {
|
||||
crate::ErrorKind::LauncherError(
|
||||
"Process immediately failed, could not get PID"
|
||||
.to_string(),
|
||||
)
|
||||
})?;
|
||||
*current_child = new_child;
|
||||
}
|
||||
emit_process(
|
||||
uuid,
|
||||
current_pid,
|
||||
ProcessPayloadType::Updated,
|
||||
"Completed Minecraft, switching to post-commands",
|
||||
)
|
||||
.await?;
|
||||
|
||||
loop {
|
||||
if let Some(t) = current_child.write().await.try_wait()? {
|
||||
mc_exit_status = t;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
emit_process(
|
||||
uuid,
|
||||
current_pid,
|
||||
ProcessPayloadType::Finished,
|
||||
"Exited process",
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(mc_exit_status)
|
||||
}
|
||||
|
||||
// Returns a ref to the child
|
||||
pub fn get(&self, pid: &u32) -> Option<Arc<RwLock<MinecraftChild>>> {
|
||||
self.0.get(pid).cloned()
|
||||
pub fn get(&self, uuid: &Uuid) -> Option<Arc<RwLock<MinecraftChild>>> {
|
||||
self.0.get(uuid).cloned()
|
||||
}
|
||||
|
||||
// Gets all PID keys
|
||||
pub fn keys(&self) -> Vec<u32> {
|
||||
pub fn keys(&self) -> Vec<Uuid> {
|
||||
self.0.keys().cloned().collect()
|
||||
}
|
||||
|
||||
@@ -96,25 +180,25 @@ impl Children {
|
||||
// Returns None if the child is still running
|
||||
pub async fn exit_status(
|
||||
&self,
|
||||
pid: &u32,
|
||||
uuid: &Uuid,
|
||||
) -> crate::Result<Option<std::process::ExitStatus>> {
|
||||
if let Some(child) = self.get(pid) {
|
||||
let child = child.clone();
|
||||
let mut child = child.write().await;
|
||||
Ok(child.child.try_wait()?)
|
||||
if let Some(child) = self.get(uuid) {
|
||||
let child = child.write().await;
|
||||
let status = child.current_child.write().await.try_wait()?;
|
||||
Ok(status)
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
// Gets all PID keys of running children
|
||||
pub async fn running_keys(&self) -> crate::Result<Vec<u32>> {
|
||||
pub async fn running_keys(&self) -> crate::Result<Vec<Uuid>> {
|
||||
let mut keys = Vec::new();
|
||||
for key in self.keys() {
|
||||
if let Some(child) = self.get(&key) {
|
||||
let child = child.clone();
|
||||
let mut child = child.write().await;
|
||||
if child.child.try_wait()?.is_none() {
|
||||
let child = child.write().await;
|
||||
if child.current_child.write().await.try_wait()?.is_none() {
|
||||
keys.push(key);
|
||||
}
|
||||
}
|
||||
@@ -126,7 +210,7 @@ impl Children {
|
||||
pub async fn running_keys_with_profile(
|
||||
&self,
|
||||
profile_path: &Path,
|
||||
) -> crate::Result<Vec<u32>> {
|
||||
) -> crate::Result<Vec<Uuid>> {
|
||||
let running_keys = self.running_keys().await?;
|
||||
let mut keys = Vec::new();
|
||||
for key in running_keys {
|
||||
@@ -147,8 +231,8 @@ impl Children {
|
||||
for key in self.keys() {
|
||||
if let Some(child) = self.get(&key) {
|
||||
let child = child.clone();
|
||||
let mut child = child.write().await;
|
||||
if child.child.try_wait()?.is_none() {
|
||||
let child = child.write().await;
|
||||
if child.current_child.write().await.try_wait()?.is_none() {
|
||||
profiles.push(child.profile_path.clone());
|
||||
}
|
||||
}
|
||||
@@ -163,8 +247,8 @@ impl Children {
|
||||
for key in self.keys() {
|
||||
if let Some(child) = self.get(&key) {
|
||||
let child = child.clone();
|
||||
let mut child = child.write().await;
|
||||
if child.child.try_wait()?.is_none() {
|
||||
let child = child.write().await;
|
||||
if child.current_child.write().await.try_wait()?.is_none() {
|
||||
if let Some(prof) =
|
||||
crate::api::profile::get(&child.profile_path.clone())
|
||||
.await?
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
//! Theseus settings file
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{collections::HashSet, path::Path};
|
||||
use std::path::Path;
|
||||
use tokio::fs;
|
||||
|
||||
use super::JavaGlobals;
|
||||
@@ -13,6 +13,7 @@ const CURRENT_FORMAT_VERSION: u32 = 1;
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
#[serde(default)]
|
||||
pub struct Settings {
|
||||
pub theme: Theme,
|
||||
pub memory: MemorySettings,
|
||||
pub game_resolution: WindowSize,
|
||||
pub custom_java_args: Vec<String>,
|
||||
@@ -27,6 +28,7 @@ pub struct Settings {
|
||||
impl Default for Settings {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
theme: Theme::Dark,
|
||||
memory: MemorySettings::default(),
|
||||
game_resolution: WindowSize::default(),
|
||||
custom_java_args: Vec::new(),
|
||||
@@ -74,6 +76,15 @@ impl Settings {
|
||||
}
|
||||
}
|
||||
|
||||
/// Theseus theme
|
||||
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum Theme {
|
||||
Dark,
|
||||
Light,
|
||||
Oled,
|
||||
}
|
||||
|
||||
/// Minecraft memory settings
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, Copy)]
|
||||
pub struct MemorySettings {
|
||||
@@ -105,10 +116,10 @@ impl Default for WindowSize {
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
|
||||
#[serde(default)]
|
||||
pub struct Hooks {
|
||||
#[serde(skip_serializing_if = "HashSet::is_empty")]
|
||||
pub pre_launch: HashSet<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub pre_launch: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub wrapper: Option<String>,
|
||||
#[serde(skip_serializing_if = "HashSet::is_empty")]
|
||||
pub post_exit: HashSet<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub post_exit: Option<String>,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user