feat: Pass stdout + stderr + pid callbacks to frontend
This commit is contained in:
parent
b77f48ed90
commit
fbab841bbd
5 changed files with 97 additions and 23 deletions
|
|
@ -1,10 +1,10 @@
|
|||
use std::{io::{stderr, stdout}, path::PathBuf};
|
||||
use std::{path::PathBuf, process::Stdio};
|
||||
|
||||
use anyhow::anyhow;
|
||||
use async_trait::async_trait;
|
||||
use chrono::{DateTime, Local};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tokio::process;
|
||||
use tokio::{io::{AsyncBufReadExt, BufReader}, process};
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::auth::Account;
|
||||
|
|
@ -46,11 +46,17 @@ pub struct SetupInfo {
|
|||
pub natives_dir: PathBuf,
|
||||
}
|
||||
|
||||
pub struct LaunchCallbacks {
|
||||
pub on_launch: Box<dyn FnMut(u32) + Send>,
|
||||
pub on_stdout: Box<dyn FnMut(String) + Send>,
|
||||
pub on_stderr: Box<dyn FnMut(String) + Send>,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
pub trait ClientTrait<'a>: Send + Sync {
|
||||
fn new(cluster: &'a Cluster, manifest: &'a Manifest) -> Self where Self: Sized;
|
||||
|
||||
async fn launch(&self, info: LaunchInfo) -> crate::Result<()> {
|
||||
async fn launch(&self, info: LaunchInfo, mut callbacks: LaunchCallbacks) -> crate::Result<i32> {
|
||||
let manifest = &self.get_manifest().minecraft_manifest;
|
||||
|
||||
let args = get_arguments(manifest)?
|
||||
|
|
@ -63,9 +69,10 @@ pub trait ClientTrait<'a>: Send + Sync {
|
|||
.replace("${auth_access_token}", &info.account.access_token)
|
||||
.replace("${auth_session}", "0") // TODO: Figure out how to get this session ID.
|
||||
.replace("${user_properties}", "{}") // TODO: Figure out these properties.
|
||||
.replace("${user_type}", "1");
|
||||
.replace("${user_type}", "1")
|
||||
.replace("${version_type}", "OneLauncher");
|
||||
|
||||
process::Command::new(&info.java).arg("-XX:-UseAdaptiveSizePolicy")
|
||||
let mut process = process::Command::new(&info.java).arg("-XX:-UseAdaptiveSizePolicy")
|
||||
.arg("-XX:-OmitStackTraceInFastThrow")
|
||||
.arg("-Dminecraft.launcher.brand=onelauncher")
|
||||
.arg("-Dminecraft.launcher.version=1")
|
||||
|
|
@ -76,13 +83,36 @@ pub trait ClientTrait<'a>: Send + Sync {
|
|||
.arg(info.setup.libraries)
|
||||
.arg(manifest.main_class.clone())
|
||||
.args(args.split_whitespace())
|
||||
.stdout(stdout()) // TODO: Implement better logging
|
||||
.stderr(stderr())
|
||||
.spawn()?
|
||||
.wait()
|
||||
.await?;
|
||||
.stdout(Stdio::piped())
|
||||
.stderr(Stdio::piped())
|
||||
.spawn()?;
|
||||
|
||||
let pid = process.id().unwrap_or(0);
|
||||
(callbacks.on_launch)(pid);
|
||||
|
||||
let stdout = process.stdout.take().ok_or(anyhow!("Couldn't take stdout"))?;
|
||||
let stderr = process.stderr.take().ok_or(anyhow!("Couldn't take stderr"))?;
|
||||
|
||||
Ok(())
|
||||
let mut stdout_lines = BufReader::new(stdout).lines();
|
||||
let mut stderr_lines = BufReader::new(stderr).lines();
|
||||
|
||||
let stdout_task = tokio::spawn(async move {
|
||||
while let Some(line) = stdout_lines.next_line().await.expect("Couldn't read line") {
|
||||
(callbacks.on_stdout)(line);
|
||||
}
|
||||
});
|
||||
|
||||
let stderr_task = tokio::spawn(async move {
|
||||
while let Some(line) = stderr_lines.next_line().await.expect("Couldn't read line") {
|
||||
(callbacks.on_stderr)(line);
|
||||
}
|
||||
});
|
||||
|
||||
let status = process.wait().await?;
|
||||
tokio::try_join!(stdout_task, stderr_task)?;
|
||||
|
||||
let exit_code = status.code().unwrap_or(0);
|
||||
Ok(exit_code)
|
||||
}
|
||||
|
||||
async fn setup(&self) -> crate::Result<SetupInfo>;
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ use uuid::Uuid;
|
|||
|
||||
use crate::{auth::Account, utils::dirs};
|
||||
|
||||
use super::{client::{ClientTrait, Cluster, LaunchInfo, Manifest}, clients::{self, vanilla, ClientType}, java};
|
||||
use super::{client::{ClientTrait, Cluster, LaunchCallbacks, LaunchInfo, Manifest}, clients::{self, vanilla, ClientType}, java};
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum ClientManagerError {
|
||||
|
|
@ -75,7 +75,8 @@ impl ClientManager {
|
|||
pub async fn launch_cluster(
|
||||
&mut self,
|
||||
uuid: Uuid,
|
||||
) -> crate::Result<()> {
|
||||
callbacks: LaunchCallbacks,
|
||||
) -> crate::Result<i32> {
|
||||
let client = self.get_impl_uuid(uuid)?;
|
||||
|
||||
let java = java::download_java(&dirs::java_dir()?, client.get_manifest().minecraft_manifest.java_version.major_version).await?;
|
||||
|
|
@ -95,8 +96,8 @@ impl ClientManager {
|
|||
mem_max: 4096,
|
||||
};
|
||||
|
||||
client.launch(info).await?;
|
||||
Ok(())
|
||||
let exit_code = client.launch(info, callbacks).await?;
|
||||
Ok(exit_code)
|
||||
}
|
||||
|
||||
pub async fn create_cluster(
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
use onelauncher::game::{client::{Cluster, Manifest}, clients::ClientType};
|
||||
use tauri::State;
|
||||
use onelauncher::game::{client::{Cluster, LaunchCallbacks, Manifest}, clients::ClientType};
|
||||
use tauri::{Manager, State};
|
||||
use tokio::sync::Mutex;
|
||||
use uuid::Uuid;
|
||||
|
||||
|
|
@ -41,12 +41,32 @@ pub async fn get_cluster(
|
|||
|
||||
#[tauri::command]
|
||||
pub async fn launch_cluster(
|
||||
app: tauri::AppHandle,
|
||||
state: State<'_, Mutex<GameManagerState>>,
|
||||
uuid: Uuid,
|
||||
) -> Result<(), String> {
|
||||
) -> Result<i32, String> {
|
||||
let manager = &mut state.lock().await.client_manager;
|
||||
manager.launch_cluster(uuid).await?;
|
||||
Ok(())
|
||||
|
||||
let on_launch_app = app.clone();
|
||||
let on_stdout_app = app.clone();
|
||||
let on_stderr_app = app.clone();
|
||||
|
||||
let callbacks = LaunchCallbacks {
|
||||
on_launch: Box::new(move |pid| {
|
||||
on_launch_app.emit("game:launch", pid).expect("Failed to emit game:launched");
|
||||
}),
|
||||
|
||||
on_stdout: Box::new(move |line| {
|
||||
on_stdout_app.emit("game:stdout", line).expect("Failed to emit game:stdout");
|
||||
}),
|
||||
|
||||
on_stderr: Box::new(move |line| {
|
||||
on_stderr_app.emit("game:stderr", line).expect("Failed to emit game:stderr");
|
||||
})
|
||||
};
|
||||
|
||||
let exit_code = manager.launch_cluster(uuid, callbacks).await?;
|
||||
Ok(exit_code)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { invoke } from '@tauri-apps/api/core';
|
||||
import { listen } from '@tauri-apps/api/event';
|
||||
|
||||
export async function getCluster(uuid: string): Promise<Core.Cluster> {
|
||||
return await invoke<Core.Cluster>(
|
||||
|
|
@ -61,9 +62,25 @@ export async function refreshClientManager(): Promise<void> {
|
|||
return await invoke('plugin:onelauncher|refresh_client_manager');
|
||||
}
|
||||
|
||||
export async function launchCluster(uuid: string): Promise<number> {
|
||||
return await invoke<number>(
|
||||
interface LaunchCallbacks {
|
||||
on_launch: (pid: number) => any;
|
||||
on_stdout: (line: string) => any;
|
||||
on_stderr: (line: string) => any;
|
||||
};
|
||||
|
||||
export async function launchCluster(uuid: string, callbacks: LaunchCallbacks): Promise<number> {
|
||||
const unlisten_launch = await listen<number>('game:launch', e => callbacks.on_launch(e.payload));
|
||||
const unlisten_stdout = await listen<string>('game:stdout', e => callbacks.on_stdout(e.payload));
|
||||
const unlisten_stderr = await listen<string>('game:stderr', e => callbacks.on_stderr(e.payload));
|
||||
|
||||
const exit_code = await invoke<number>(
|
||||
'plugin:onelauncher|launch_cluster',
|
||||
{ uuid },
|
||||
);
|
||||
|
||||
unlisten_launch();
|
||||
unlisten_stdout();
|
||||
unlisten_stderr();
|
||||
|
||||
return exit_code;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -76,7 +76,13 @@ function ClusterCardContextMenu(props: ClusterCardContextMenuProps) {
|
|||
}
|
||||
|
||||
function launch() {
|
||||
launchCluster(props.id);
|
||||
const exit_code = launchCluster(props.id, {
|
||||
on_launch: pid => console.log('Launched with PID:', pid),
|
||||
on_stdout: line => console.log('STDOUT:', line),
|
||||
on_stderr: line => console.log('STDERR:', line),
|
||||
});
|
||||
|
||||
console.log('Exit with code: ', exit_code);
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue