use std::path::{Path, PathBuf};
use inner_future::stream::StreamExt;
use crate::{
prelude::*,
utils::{locate_path, Arch},
};
#[derive(Debug, Clone)]
pub struct JavaRuntime {
java_path: String,
java_version: String,
java_main_version: u8,
java_64bit: bool,
java_arch: Arch,
}
impl JavaRuntime {
pub async fn from_java_path(java_path: impl AsRef<std::ffi::OsStr>) -> DynResult<Self> {
tracing::debug!(
"正在重新定位 Java 执行文件: {}",
java_path.as_ref().to_string_lossy()
);
let java_path = locate_path(java_path.as_ref());
tracing::debug!("Java 执行文件已重新定位: {}", java_path.display());
let output = query_java_version_output(&java_path).await?;
let version = query_java_version(&output);
let java_main_version = get_java_version(version);
let java_64bit = query_java_is_64bit(&output);
let java_arch = crate::utils::get_exec_arch(&java_path).await?;
Ok(Self {
java_path: java_path.to_string_lossy().to_string(),
java_64bit,
java_version: version.to_owned(),
java_main_version,
java_arch,
})
}
#[inline]
pub fn path(&self) -> &str {
&self.java_path
}
#[inline]
pub fn version(&self) -> &str {
&self.java_version
}
#[inline]
pub fn is_64bit(&self) -> bool {
self.java_64bit
}
#[inline]
pub fn main_version(&self) -> u8 {
self.java_main_version
}
#[inline]
pub fn arch(&self) -> Arch {
self.java_arch
}
}
async fn query_java_version_output(java_path: impl AsRef<std::ffi::OsStr>) -> DynResult<String> {
let c = {
#[cfg(windows)]
{
use inner_future::process::windows::CommandExt;
inner_future::process::Command::new(java_path)
.arg("-version")
.stdout(std::process::Stdio::piped())
.stderr(std::process::Stdio::piped())
.creation_flags(0x00000200 | 0x08000000)
.spawn()?
}
#[cfg(not(windows))]
{
inner_future::process::Command::new(java_path)
.arg("-version")
.stdout(std::process::Stdio::piped())
.stderr(std::process::Stdio::piped())
.spawn()?
}
};
let output = c.output().await?;
Ok(String::from_utf8(output.stderr)?)
}
fn query_java_is_64bit(java_output: &str) -> bool {
java_output.contains("64-Bit")
}
fn get_java_version(java_version_string: &str) -> u8 {
fn parser(input: &str) -> nom::IResult<&str, &str> {
nom::character::complete::digit1(input)
}
if let Ok((_, r)) = parser(java_version_string) {
let r = r.parse().unwrap(); if r > 1 {
r
} else if java_version_string.contains("1.8") {
8
} else if java_version_string.contains("1.7") {
7
} else {
0 }
} else if java_version_string.contains("1.8") {
8
} else if java_version_string.contains("1.7") {
7
} else {
0 }
}
pub async fn search_for_java() -> Vec<String> {
async fn check_bin_java_directory(path: impl AsRef<Path>, result: &mut Vec<String>) {
if let Ok(mut d) = inner_future::fs::read_dir(path).await {
while let Ok(Some(d)) = d.try_next().await {
let mut path = d.path();
#[cfg(target_os = "macos")]
path.push("Contents/Home");
path.push("bin");
#[cfg(target_os = "windows")]
{
path.push("java.exe");
if path.is_file() {
tracing::debug!("Added {}", path.display());
result.push(d.path().to_string_lossy().to_string());
}
{
path.pop();
path.push("javaw.exe");
if path.is_file() {
tracing::debug!("Added {}", path.display());
result.push(path.to_string_lossy().to_string());
}
}
}
#[cfg(not(windows))]
{
path.push("java");
if path.is_file() {
result.push(path.to_string_lossy().to_string());
}
}
}
}
}
fn check_java(path: impl Into<PathBuf>, result: &mut Vec<String>) {
let mut path: PathBuf = path.into();
#[cfg(windows)]
{
path.push("java.exe");
if path.is_file() {
result.push(path.to_string_lossy().to_string());
}
path.pop();
path.push("javaw.exe");
if path.is_file() {
result.push(path.to_string_lossy().to_string());
}
}
#[cfg(not(windows))]
{
path.push("java");
if path.is_file() {
result.push(path.to_string_lossy().to_string());
}
}
}
#[cfg(windows)]
{
let mut result = Vec::with_capacity(16);
use winreg::{enums::*, *};
fn search_local_machine_reg_value(key: &str, result: &mut Vec<String>) {
let hklm = RegKey::predef(HKEY_LOCAL_MACHINE);
if let Ok(subkey) = hklm
.open_subkey("SOFTWARE\\JavaSoft")
.and_then(|subkey| subkey.open_subkey(key))
{
for s in subkey.enum_keys().flatten() {
let subkey = subkey.open_subkey(s);
if let Ok(subkey) = subkey {
if let Ok(v) = subkey.get_value("JavaHome") {
result.push(v);
}
}
}
}
}
search_local_machine_reg_value("Java Runtime Environment", &mut result);
search_local_machine_reg_value("Java Development Kit", &mut result);
search_local_machine_reg_value("JRE", &mut result);
search_local_machine_reg_value("JDK", &mut result);
async fn list_java_directory(path: &str, result: &mut Vec<String>) {
let mut path = std::path::PathBuf::from(path);
path.push("Java");
check_bin_java_directory(&path, result).await;
path.pop();
path.push("BellSoft");
check_bin_java_directory(&path, result).await;
path.pop();
path.push("AdoptOpenJDK");
check_bin_java_directory(&path, result).await;
path.pop();
path.push("Zulu");
check_bin_java_directory(&path, result).await;
path.pop();
path.push("Microsoft");
check_bin_java_directory(&path, result).await;
path.pop();
path.push("Eclipse Foundation");
check_bin_java_directory(&path, result).await;
path.pop();
path.push("Semeru");
check_bin_java_directory(&path, result).await;
path.pop();
}
if let Ok(program_files) = std::env::var("ProgramFiles")
.or_else::<std::env::VarError, _>(|_| Ok("C:\\Program Files".into()))
{
list_java_directory(&program_files, &mut result).await;
}
if let Ok(program_files) = std::env::var("ProgramFiles(x86)")
.or_else::<std::env::VarError, _>(|_| Ok("C:\\Program Files (x86)".into()))
{
list_java_directory(&program_files, &mut result).await;
let mut minecraft_launcher_dir = PathBuf::from(program_files);
minecraft_launcher_dir
.push("Minecraft Launcher\\runtime\\jre-legacy\\windows-x64\\jre-legacy\\bin");
check_java(&minecraft_launcher_dir, &mut result);
minecraft_launcher_dir.pop();
minecraft_launcher_dir.pop();
minecraft_launcher_dir.pop();
minecraft_launcher_dir.push("windows-x86\\jre-legacy\\bin");
check_java(&minecraft_launcher_dir, &mut result);
minecraft_launcher_dir.pop();
minecraft_launcher_dir.pop();
minecraft_launcher_dir.pop();
minecraft_launcher_dir.pop();
minecraft_launcher_dir.push("java-runtime-alpha\\windows-x64\\java-runtime-alpha\\bin");
check_java(&minecraft_launcher_dir, &mut result);
minecraft_launcher_dir.pop();
minecraft_launcher_dir.pop();
minecraft_launcher_dir.pop();
minecraft_launcher_dir.push("windows-x86\\java-runtime-alpha\\bin");
check_java(&minecraft_launcher_dir, &mut result);
}
if let Ok(program_files) = std::env::var("ProgramFiles(ARM)")
.or_else::<std::env::VarError, _>(|_| Ok("C:\\Program Files (ARM)".into()))
{
list_java_directory(&program_files, &mut result).await;
}
if let Ok(paths) = std::env::var("PATH") {
for path in paths.split(';') {
check_java(path, &mut result);
}
}
if let Ok(path) = std::env::var("JABBA_HOME") {
let path = PathBuf::from(path).join("jdk");
check_bin_java_directory(path.as_path(), &mut result).await;
}
let mut result: Vec<_> = result
.into_iter()
.map(|mut a| {
#[cfg(windows)]
{
if !a.ends_with("java.exe")
&& !a.ends_with("javaw.exe")
&& !a.ends_with("java")
&& !a.ends_with("javaw")
{
if !a.ends_with('\\') && !a.ends_with('/') {
a.push('\\');
}
a.push_str("bin\\java.exe");
}
}
#[cfg(not(windows))]
{
if !a.ends_with("java") {
if !a.ends_with('/') {
a.push('/');
}
a.push_str("bin/java");
}
}
a
})
.collect();
result.sort();
result.dedup();
result
}
#[cfg(target_os = "linux")]
{
let mut result = Vec::with_capacity(16);
check_bin_java_directory("/usr/java/bin", &mut result).await;
check_bin_java_directory("/usr/lib/jvm/bin", &mut result).await;
check_bin_java_directory("/usr/lib32/jvm/bin", &mut result).await;
if let Ok(paths) = std::env::var("PATH") {
for path in paths.split(';') {
check_java(path, &mut result);
}
}
if let Ok(path) = std::env::var("JABBA_HOME") {
let path = PathBuf::from(path).join("jdk");
check_bin_java_directory(path.as_path(), &mut result).await;
}
if let Some(mut home_path) = dirs::home_dir() {
home_path.push(".minecraft/runtime/jre-legacy/linux/jre-legacy/bin");
check_java(&home_path, &mut result);
home_path.push("../../../../java-runtime-alpha/linux/java-runtime-alpha/bin");
check_java(&home_path, &mut result);
}
result
}
#[cfg(target_os = "macos")]
{
let mut result = Vec::with_capacity(16);
check_bin_java_directory("/Library/Java/JavaVirtualMachines", &mut result).await;
check_bin_java_directory("/System/Library/Java/JavaVirtualMachines", &mut result).await;
check_java(
"/Library/Internet Plug-Ins/JavaAppletPlugin.plugin/Contents/Home/bin/java",
&mut result,
);
check_java("/Applications/Xcode.app/Contents/Applications/Application Loader.app/Contents/MacOS/itms/java/bin/java", &mut result);
result
}
}
fn query_java_version(java_output: &str) -> &str {
fn parser(input: &str) -> nom::IResult<&str, &str> {
nom::bytes::complete::take_until("\"")(input)
}
let pat = "version \"";
if let Some(p) = java_output.find(pat) {
if let Ok((_, r)) = parser(&java_output[p + pat.len()..]) {
r
} else {
""
}
} else {
""
}
}