Implement a more robust IPC system between the launcher and client (#4159)

* Implement a more robust IPC system between the launcher and client

* Clippy fix and cargo fmt

* Switch to cached JsonReader with LENIENT parsing to avoid race conditions

* Make RPC send messages in lines

* Try to bind to either IPv4 or IPv6 and communicate version

* Move message handling into a separate function to avoid too much code in a macro
This commit is contained in:
Josiah Glosson
2025-08-13 16:28:44 -07:00
committed by GitHub
parent b81e727204
commit 5ffcc48d75
15 changed files with 568 additions and 70 deletions

View File

@@ -16,6 +16,7 @@ use daedalus::{
use dunce::canonicalize;
use hashlink::LinkedHashSet;
use std::io::{BufRead, BufReader};
use std::net::SocketAddr;
use std::{collections::HashMap, path::Path};
use uuid::Uuid;
@@ -124,6 +125,7 @@ pub fn get_jvm_arguments(
quick_play_type: &QuickPlayType,
quick_play_version: QuickPlayVersion,
log_config: Option<&LoggingConfiguration>,
ipc_addr: SocketAddr,
) -> crate::Result<Vec<String>> {
let mut parsed_arguments = Vec::new();
@@ -181,6 +183,11 @@ pub fn get_jvm_arguments(
.to_string_lossy()
));
parsed_arguments
.push(format!("-Dmodrinth.internal.ipc.host={}", ipc_addr.ip()));
parsed_arguments
.push(format!("-Dmodrinth.internal.ipc.port={}", ipc_addr.port()));
parsed_arguments.push(format!(
"-Dmodrinth.internal.quickPlay.serverVersion={}",
serde_json::to_value(quick_play_version.server)?

View File

@@ -12,6 +12,7 @@ use crate::state::{
Credentials, JavaVersion, ProcessMetadata, ProfileInstallStage,
};
use crate::util::io;
use crate::util::rpc::RpcServerBuilder;
use crate::{State, get_resource_file, process, state as st};
use chrono::Utc;
use daedalus as d;
@@ -22,7 +23,6 @@ use serde::Deserialize;
use st::Profile;
use std::fmt::Write;
use std::path::PathBuf;
use tokio::io::AsyncWriteExt;
use tokio::process::Command;
mod args;
@@ -608,6 +608,8 @@ pub async fn launch_minecraft(
let (main_class_keep_alive, main_class_path) =
get_resource_file!(env "JAVA_JARS_DIR" / "theseus.jar")?;
let rpc_server = RpcServerBuilder::new().launch().await?;
command.args(
args::get_jvm_arguments(
args.get(&d::minecraft::ArgumentType::Jvm)
@@ -633,6 +635,7 @@ pub async fn launch_minecraft(
.logging
.as_ref()
.and_then(|x| x.get(&LoggingSide::Client)),
rpc_server.address(),
)?
.into_iter(),
);
@@ -767,7 +770,8 @@ pub async fn launch_minecraft(
state.directories.profile_logs_dir(&profile.path),
version_info.logging.is_some(),
main_class_keep_alive,
async |process: &ProcessMetadata, stdin| {
rpc_server,
async |process: &ProcessMetadata, rpc_server| {
let process_start_time = process.start_time.to_rfc3339();
let profile_created_time = profile.created.to_rfc3339();
let profile_modified_time = profile.modified.to_rfc3339();
@@ -790,14 +794,11 @@ pub async fn launch_minecraft(
let Some(value) = value else {
continue;
};
stdin.write_all(b"property\t").await?;
stdin.write_all(key.as_bytes()).await?;
stdin.write_u8(b'\t').await?;
stdin.write_all(value.as_bytes()).await?;
stdin.write_u8(b'\n').await?;
rpc_server
.call_method_2::<()>("set_system_property", key, value)
.await?;
}
stdin.write_all(b"launch\n").await?;
stdin.flush().await?;
rpc_server.call_method::<()>("launch").await?;
Ok(())
},
)