From a0e029e047add66cddef26709354549c4c4d310c Mon Sep 17 00:00:00 2001 From: Julian Chang Date: Thu, 27 Aug 2026 07:01:27 +0900 Subject: [PATCH 1/2] theoretically stop terminal from randomly opening --- packages/oneclient_common/src/lib.rs | 1 + packages/oneclient_common/src/process.rs | 12 ++++++ .../oneclient_core/src/clusters/prepare.rs | 10 +++-- packages/oneclient_core/src/game/launch.rs | 1 + packages/oneclient_java/src/checker.rs | 1 + packages/oneclient_java/src/resolve.rs | 43 ++++++++++++++++++- packages/oneclient_java/src/service.rs | 16 +++++-- 7 files changed, 76 insertions(+), 8 deletions(-) create mode 100644 packages/oneclient_common/src/process.rs diff --git a/packages/oneclient_common/src/lib.rs b/packages/oneclient_common/src/lib.rs index bd0372b77..71a82dd14 100644 --- a/packages/oneclient_common/src/lib.rs +++ b/packages/oneclient_common/src/lib.rs @@ -6,6 +6,7 @@ pub mod domain; pub mod os_ext; pub mod paths; pub mod patch; +pub mod process; pub mod search; pub mod version; diff --git a/packages/oneclient_common/src/process.rs b/packages/oneclient_common/src/process.rs new file mode 100644 index 000000000..f53567501 --- /dev/null +++ b/packages/oneclient_common/src/process.rs @@ -0,0 +1,12 @@ +pub fn no_window(command: &mut std::process::Command) { + #[cfg(windows)] + { + use std::os::windows::process::CommandExt; + + const CREATE_NO_WINDOW: u32 = 0x0800_0000; + command.creation_flags(CREATE_NO_WINDOW); + } + + #[cfg(not(windows))] + let _ = command; +} \ No newline at end of file diff --git a/packages/oneclient_core/src/clusters/prepare.rs b/packages/oneclient_core/src/clusters/prepare.rs index e3f929ea5..5f4e73f05 100644 --- a/packages/oneclient_core/src/clusters/prepare.rs +++ b/packages/oneclient_core/src/clusters/prepare.rs @@ -333,7 +333,8 @@ async fn run_forge_processors( .await? .ok_or_else(|| GameError::ProcessorMainClass(processor.jar.clone()))?; - let output = Command::new(&java.absolute_path) + let mut command = Command::new(&java.absolute_path); + command .arg("-cp") .arg(game::get_classpath_library(&libraries, &cp)?) .arg(&main) @@ -341,9 +342,10 @@ async fn run_forge_processors( &libraries, &processor.args, data, - )?) - .output() - .await?; + )?); + oneclient_common::process::no_window(command.as_std_mut()); + + let output = command.output().await?; if !output.status.success() { return Err(GameError::ProcessorFailed( diff --git a/packages/oneclient_core/src/game/launch.rs b/packages/oneclient_core/src/game/launch.rs index 0d42e1fd5..fea190a2e 100644 --- a/packages/oneclient_core/src/game/launch.rs +++ b/packages/oneclient_core/src/game/launch.rs @@ -655,6 +655,7 @@ async fn run_hook(hook: Option<&str>, cwd: &Path) { }; command.current_dir(cwd); + oneclient_common::process::no_window(command.as_std_mut()); if let Err(err) = command.status().await { tracing::warn!("hook '{hook}' failed: {err}"); } diff --git a/packages/oneclient_java/src/checker.rs b/packages/oneclient_java/src/checker.rs index f6fd7dc00..3f2829d75 100644 --- a/packages/oneclient_java/src/checker.rs +++ b/packages/oneclient_java/src/checker.rs @@ -29,6 +29,7 @@ pub async fn check_java_runtime(absolute_path: String) -> JavaResult = command diff --git a/packages/oneclient_java/src/resolve.rs b/packages/oneclient_java/src/resolve.rs index a5e671eb6..4b1f5714f 100644 --- a/packages/oneclient_java/src/resolve.rs +++ b/packages/oneclient_java/src/resolve.rs @@ -63,5 +63,46 @@ fn validate_executable(path: &Path) -> JavaResult { }); } - Ok(path.to_path_buf()) + Ok(prefer_javaw(path)) } + +#[must_use] +pub fn prefer_javaw(path: impl AsRef) -> PathBuf { + let path = path.as_ref(); + + #[cfg(windows)] + if path.file_name().is_some_and(|name| name == "java.exe") { + let javaw = path.with_file_name("javaw.exe"); + if javaw.is_file() { + return javaw; + } + } + + path.to_path_buf() +} + +#[cfg(test)] +mod tests { + use super::prefer_javaw; + + #[test] + fn prefers_the_windowless_twin() { + let dir = std::env::temp_dir().join("oneclient-prefer-javaw"); + std::fs::create_dir_all(&dir).unwrap(); + std::fs::write(dir.join("java.exe"), b"").unwrap(); + std::fs::write(dir.join("javaw.exe"), b"").unwrap(); + + let picked = prefer_javaw(dir.join("java.exe")); + let expected = if cfg!(windows) { "javaw.exe" } else { "java.exe" }; + assert_eq!(picked.file_name().unwrap(), expected); + + // A lone `java.exe` has nothing to swap to + std::fs::remove_file(dir.join("javaw.exe")).unwrap(); + assert_eq!( + prefer_javaw(dir.join("java.exe")).file_name().unwrap(), + "java.exe" + ); + + std::fs::remove_dir_all(&dir).unwrap(); + } +} \ No newline at end of file diff --git a/packages/oneclient_java/src/service.rs b/packages/oneclient_java/src/service.rs index 6a70b6f27..37a5c06ab 100644 --- a/packages/oneclient_java/src/service.rs +++ b/packages/oneclient_java/src/service.rs @@ -72,14 +72,22 @@ impl JavaService { let Some(path) = java_path else { return Ok(None); }; - let Some(runtime) = self.store.get_by_path(path).await? else { + let path = crate::resolve::prefer_javaw(path); + let Some(runtime) = self.store.get_by_path(&path.to_string_lossy()).await? else { return Ok(None); }; self.revalidate(runtime).await } #[tracing::instrument(level = "debug", skip(self))] - async fn revalidate(&self, runtime: JavaRuntime) -> JavaResult> { + async fn revalidate(&self, mut runtime: JavaRuntime) -> JavaResult> { + let preferred = crate::resolve::prefer_javaw(&runtime.absolute_path); + if preferred.as_path() != Path::new(&runtime.absolute_path) { + self.store.delete_by_path(&runtime.absolute_path).await?; + runtime.absolute_path = preferred.to_string_lossy().into_owned(); + runtime.probe_version = 0; + } + if runtime.probe_version == PROBE_VERSION { if Path::new(&runtime.absolute_path).is_file() { return Ok(Some(runtime)); @@ -374,7 +382,9 @@ impl JavaService { async fn persist(&self, executable: &Path, info: &JavaCheckInfo) -> JavaResult { let runtime = JavaRuntime { - absolute_path: executable.to_string_lossy().into_owned(), + absolute_path: crate::resolve::prefer_javaw(executable) + .to_string_lossy() + .into_owned(), major: parse_major_version(&info.version)?, version: info.version.clone(), vendor: JavaVendor::from_str(&info.vendor) From 70c743317a5cca93c145ddc0161e2fbcf66b5b58 Mon Sep 17 00:00:00 2001 From: Julian Chang Date: Thu, 3 Sep 2026 03:32:06 -0400 Subject: [PATCH 2/2] slime out javaw part --- packages/oneclient_common/src/process.rs | 2 +- packages/oneclient_java/src/resolve.rs | 43 +----------------------- packages/oneclient_java/src/service.rs | 16 ++------- 3 files changed, 5 insertions(+), 56 deletions(-) diff --git a/packages/oneclient_common/src/process.rs b/packages/oneclient_common/src/process.rs index f53567501..6ccc56611 100644 --- a/packages/oneclient_common/src/process.rs +++ b/packages/oneclient_common/src/process.rs @@ -9,4 +9,4 @@ pub fn no_window(command: &mut std::process::Command) { #[cfg(not(windows))] let _ = command; -} \ No newline at end of file +} diff --git a/packages/oneclient_java/src/resolve.rs b/packages/oneclient_java/src/resolve.rs index 4b1f5714f..a5e671eb6 100644 --- a/packages/oneclient_java/src/resolve.rs +++ b/packages/oneclient_java/src/resolve.rs @@ -63,46 +63,5 @@ fn validate_executable(path: &Path) -> JavaResult { }); } - Ok(prefer_javaw(path)) + Ok(path.to_path_buf()) } - -#[must_use] -pub fn prefer_javaw(path: impl AsRef) -> PathBuf { - let path = path.as_ref(); - - #[cfg(windows)] - if path.file_name().is_some_and(|name| name == "java.exe") { - let javaw = path.with_file_name("javaw.exe"); - if javaw.is_file() { - return javaw; - } - } - - path.to_path_buf() -} - -#[cfg(test)] -mod tests { - use super::prefer_javaw; - - #[test] - fn prefers_the_windowless_twin() { - let dir = std::env::temp_dir().join("oneclient-prefer-javaw"); - std::fs::create_dir_all(&dir).unwrap(); - std::fs::write(dir.join("java.exe"), b"").unwrap(); - std::fs::write(dir.join("javaw.exe"), b"").unwrap(); - - let picked = prefer_javaw(dir.join("java.exe")); - let expected = if cfg!(windows) { "javaw.exe" } else { "java.exe" }; - assert_eq!(picked.file_name().unwrap(), expected); - - // A lone `java.exe` has nothing to swap to - std::fs::remove_file(dir.join("javaw.exe")).unwrap(); - assert_eq!( - prefer_javaw(dir.join("java.exe")).file_name().unwrap(), - "java.exe" - ); - - std::fs::remove_dir_all(&dir).unwrap(); - } -} \ No newline at end of file diff --git a/packages/oneclient_java/src/service.rs b/packages/oneclient_java/src/service.rs index 37a5c06ab..6a70b6f27 100644 --- a/packages/oneclient_java/src/service.rs +++ b/packages/oneclient_java/src/service.rs @@ -72,22 +72,14 @@ impl JavaService { let Some(path) = java_path else { return Ok(None); }; - let path = crate::resolve::prefer_javaw(path); - let Some(runtime) = self.store.get_by_path(&path.to_string_lossy()).await? else { + let Some(runtime) = self.store.get_by_path(path).await? else { return Ok(None); }; self.revalidate(runtime).await } #[tracing::instrument(level = "debug", skip(self))] - async fn revalidate(&self, mut runtime: JavaRuntime) -> JavaResult> { - let preferred = crate::resolve::prefer_javaw(&runtime.absolute_path); - if preferred.as_path() != Path::new(&runtime.absolute_path) { - self.store.delete_by_path(&runtime.absolute_path).await?; - runtime.absolute_path = preferred.to_string_lossy().into_owned(); - runtime.probe_version = 0; - } - + async fn revalidate(&self, runtime: JavaRuntime) -> JavaResult> { if runtime.probe_version == PROBE_VERSION { if Path::new(&runtime.absolute_path).is_file() { return Ok(Some(runtime)); @@ -382,9 +374,7 @@ impl JavaService { async fn persist(&self, executable: &Path, info: &JavaCheckInfo) -> JavaResult { let runtime = JavaRuntime { - absolute_path: crate::resolve::prefer_javaw(executable) - .to_string_lossy() - .into_owned(), + absolute_path: executable.to_string_lossy().into_owned(), major: parse_major_version(&info.version)?, version: info.version.clone(), vendor: JavaVendor::from_str(&info.vendor)