Skip to content

Commit 964749b

Browse files
committed
feat: 添加 Java 搜索
1 parent d6685dd commit 964749b

7 files changed

Lines changed: 311 additions & 12 deletions

File tree

src/OverrideLauncher.Core.Example/Program.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
using OverrideLauncher.Core.Classes.Launch.Runner;
1212
using OverrideLauncher.Core.Classes.Parameter;
1313
using OverrideLauncher.Core.Classes.Reader;
14+
using OverrideLauncher.Core.Classes.Utilities;
1415

1516
var installname = "1.20.1";
1617
var installroot = "D:\\.minecraft";
@@ -19,7 +20,7 @@
1920

2021
// 自动使用 官方 镜像源
2122
DictionaryDownloadHost.SwitchMirror("official"); // 官方: official
22-
// BMCL API: bmclapi
23+
// BMCL API: bmclapi
2324
Console.WriteLine($"使用镜像源: {DictionaryDownloadHost.GetCurrentMirror().Name}");
2425

2526
// 自定义下载版本
@@ -187,10 +188,9 @@ await fori.Install(new ClientRootInfo()
187188
},
188189
Account = new AccountOffline("Ove").Authenticate(),
189190
LauncherVersion = "2.0",
190-
JvmInfo = new JavaInfo()
191-
{
192-
Path = "C:\\Program Files\\Common Files\\Oracle\\Java\\javapath\\java.exe"
193-
}
191+
LauncherInfo = "RMCL 4.0 (By Override.Core)",
192+
JvmInfo = JavaUtil.GetJavaListAsync().Result[0],
193+
WindowInfo = new ClientWindowInfo().GetWindowSize(ClientWindowInfo.WindowInfo.w1920h1080)
194194
});
195195
info.OutputDataReceived += (sender, e) =>
196196
{

src/OverrideLauncher.Core/Base/Entry/Info/ClientRunnerInfo.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,5 @@ public class ClientRunnerInfo
1111
public string LauncherVersion { get; set; }
1212
public bool IsDemo { get; set; } = false;
1313
public JavaInfo JvmInfo { get; set; }
14+
public ClientWindowInfo WindowInfo { get; set; } = new();
1415
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
namespace OverrideLauncher.Core.Base.Entry.Info;
2+
3+
public class ClientWindowInfo
4+
{
5+
public int Width { get; set; } = 800;
6+
public int Height { get; set; } = 480;
7+
8+
public ClientWindowInfo GetWindowSize(WindowInfo info)
9+
{
10+
switch (info)
11+
{
12+
case WindowInfo.w800h480:
13+
Width = 800;
14+
Height = 480;
15+
break;
16+
case WindowInfo.w1920h1080:
17+
Width = 1920;
18+
Height = 1080;
19+
break;
20+
}
21+
22+
return this;
23+
}
24+
25+
public enum WindowInfo
26+
{
27+
w800h480,
28+
w1920h1080
29+
}
30+
}
Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,16 @@
11
namespace OverrideLauncher.Core.Base.Entry.Info.Java;
22

33
public class JavaInfo
4-
{
5-
public string Path { get; set; }
6-
public bool IsGC { get; set; } = true;
7-
public int MemorySize { get; set; } = 1024;
4+
{
5+
public bool Is64bit { get; init; }
6+
public string JavaPath { get; init; }
7+
public string JavaType { get; init; }
8+
public string JavaVersion { get; init; }
9+
public int MajorVersion { get; init; }
10+
11+
public string JavaFolder => Path.GetDirectoryName(JavaPath);
12+
13+
public override string ToString() {
14+
return $"{JavaVersion} - {JavaType} - {JavaPath}";
15+
}
816
}

src/OverrideLauncher.Core/Classes/Launch/Runner/RunnerClient.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ public RunnerClient(ClientRunnerInfo info)
2727

2828
this.StartInfo = new ProcessStartInfo()
2929
{
30-
FileName = _info.JvmInfo.Path,
30+
FileName = _info.JvmInfo.JavaPath,
3131
Arguments = maker.Make()
3232
};
3333
}

src/OverrideLauncher.Core/Classes/Parameter/ParameterClientLaunchMaker.cs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,8 +67,8 @@ public string Make()
6767
"${classpath}",
6868
"-cp",
6969
"-Djna.tmpdir=${natives_directory}",
70-
$"{(_info.JvmInfo.IsGC ? "-XX:+UseG1GC" : "")} -XX:-UseAdaptiveSizePolicy -XX:-OmitStackTraceInFastThrow",
71-
$"-Xmx{_info.JvmInfo.MemorySize}m",
70+
/*$"{(_info.JvmInfo.IsGC ? "-XX:+UseG1GC" : "")} -XX:-UseAdaptiveSizePolicy -XX:-OmitStackTraceInFastThrow",
71+
$"-Xmx{_info.JvmInfo.MemorySize}m",*/
7272
"-Djava.library.path=${natives_directory}",
7373
"-Dorg.lwjgl.librarypath=${natives_directory}",
7474
"-Dorg.lwjgl.system.SharedLibraryExtractPath=${natives_directory}",
@@ -96,6 +96,10 @@ public string Make()
9696
});
9797

9898
if(_info.IsDemo) ResultArgs.Add("--demo");
99+
ResultArgs.Add("-width");
100+
ResultArgs.Add(_info.WindowInfo.Width.ToString());
101+
ResultArgs.Add("-height");
102+
ResultArgs.Add(_info.WindowInfo.Height.ToString());
99103

100104
return string.Join(' ', ResultArgs);
101105
}
Lines changed: 256 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,256 @@
1+
// 本段部分实现逻辑参考自 ProjBobcat 的 DeepJavaSearcher.cs
2+
// 仓库地址:https://github.com/Corona-Studio/ProjBobcat
3+
4+
using Microsoft.Win32;
5+
using System.Diagnostics;
6+
using System.Runtime.CompilerServices;
7+
using System.Runtime.Versioning;
8+
using System.Text.RegularExpressions;
9+
using OverrideLauncher.Core.Base.Entry.Info.Java;
10+
11+
namespace OverrideLauncher.Core.Classes.Utilities;
12+
13+
public static partial class JavaUtil {
14+
public static async Task<JavaInfo> GetJavaInfoAsync(string javaPath, CancellationToken cancellationToken = default) {
15+
if (string.IsNullOrEmpty(javaPath) || !File.Exists(javaPath)) {
16+
return null;
17+
}
18+
19+
using var process = Process.Start(new ProcessStartInfo(javaPath) {
20+
Arguments = "-version",
21+
CreateNoWindow = true,
22+
UseShellExecute = false,
23+
RedirectStandardError = true,
24+
RedirectStandardOutput = true
25+
});
26+
27+
string text = process.StandardError.ReadToEnd();
28+
if (string.IsNullOrEmpty(text))
29+
ArgumentException.ThrowIfNullOrWhiteSpace(text);
30+
31+
bool is64bit = text.Contains("64-bit", StringComparison.OrdinalIgnoreCase);
32+
string javaVersion = JavaVersionRegex().Match(text).Groups["version"].Value;
33+
34+
string javaType = text.Contains("java(tm)", StringComparison.OrdinalIgnoreCase)
35+
? "Java"
36+
: text.Contains("zulu")
37+
? "ZuluJDK"
38+
: "OpenJDK";
39+
40+
await process.WaitForExitAsync(cancellationToken);
41+
42+
var versionParts = javaVersion.Split(".");
43+
return new JavaInfo {
44+
Is64bit = is64bit,
45+
JavaPath = javaPath,
46+
JavaType = javaType,
47+
JavaVersion = javaVersion,
48+
MajorVersion = (int.Parse(versionParts[0]) == 1) ? int.Parse(versionParts[1]) : int.Parse(versionParts[0]),
49+
};
50+
}
51+
52+
public static async Task<List<JavaInfo>> GetJavaListAsync(CancellationToken cancellationToken = default) {
53+
var javaList = new List<JavaInfo>();
54+
55+
if (OperatingSystem.IsWindows()) {
56+
foreach (var java in GetJavasForWindows()) {
57+
if (File.Exists(java)) {
58+
var javaInfo = await GetJavaInfoAsync(java, cancellationToken);
59+
if (javaInfo != null) {
60+
javaList.Add(javaInfo);
61+
}
62+
}
63+
}
64+
return javaList;
65+
}
66+
67+
using var process = Process.Start(new ProcessStartInfo("whereis") {
68+
CreateNoWindow = true,
69+
UseShellExecute = false,
70+
RedirectStandardError = true,
71+
RedirectStandardOutput = true,
72+
ArgumentList = {
73+
"/b",
74+
"java"
75+
},
76+
});
77+
78+
if (process == null)
79+
return javaList;
80+
81+
do {
82+
cancellationToken.ThrowIfCancellationRequested();
83+
84+
var line = process.StandardOutput.ReadLine();
85+
if (string.IsNullOrEmpty(line) || !File.Exists(line))
86+
continue;
87+
88+
var javaInfo = await GetJavaInfoAsync(line, cancellationToken);
89+
if (javaInfo != null) {
90+
javaList.Add(javaInfo);
91+
}
92+
} while (!process.HasExited);
93+
94+
await process.WaitForExitAsync(cancellationToken);
95+
var lastLine = await process.StandardOutput.ReadLineAsync(cancellationToken);
96+
97+
if (!string.IsNullOrEmpty(lastLine) && File.Exists(lastLine)) {
98+
var javaInfo = await GetJavaInfoAsync(lastLine, cancellationToken);
99+
if (javaInfo != null) {
100+
javaList.Add(javaInfo);
101+
}
102+
}
103+
104+
return javaList;
105+
}
106+
107+
#region Privates
108+
private static Regex JavaVersionRegex() => new Regex("(java|openjdk) version \"\\s*(?<version>\\S+)\\s*\"");
109+
110+
[SupportedOSPlatform("Windows")]
111+
private static IEnumerable<string> GetJavasForWindows() {
112+
//Use by:https://github.com/Xcube-Studio/Natsurainko.FluentCore/blob/main/Natsurainko.FluentCore/Environment/JavaUtils.cs
113+
List<string> result = [];
114+
115+
#region Cmd: Find Java by running "where java" command in cmd.exe
116+
117+
using var process = new Process() {
118+
StartInfo = new ProcessStartInfo() {
119+
FileName = "cmd",
120+
UseShellExecute = false,
121+
RedirectStandardOutput = true,
122+
RedirectStandardError = true,
123+
RedirectStandardInput = true,
124+
CreateNoWindow = true
125+
},
126+
EnableRaisingEvents = true,
127+
};
128+
129+
process.Start();
130+
process.BeginErrorReadLine();
131+
process.BeginOutputReadLine();
132+
133+
var output = new List<string>();
134+
135+
process.OutputDataReceived += (sender, e) => {
136+
if (!string.IsNullOrEmpty(e.Data))
137+
output.Add(e.Data);
138+
};
139+
process.ErrorDataReceived += (sender, e) => {
140+
if (!string.IsNullOrEmpty(e.Data))
141+
output.Add(e.Data);
142+
};
143+
144+
process.StandardInput.WriteLine("where java");
145+
process.StandardInput.WriteLine("exit");
146+
process.WaitForExit();
147+
148+
IEnumerable<string> javaPaths = output.Where(
149+
x => !string.IsNullOrEmpty(x) && x.EndsWith("java.exe") && File.Exists(x)
150+
)!; // null checked in the where clause
151+
result.AddRange(javaPaths);
152+
153+
#endregion
154+
155+
#region Registry: Find Java by searching the registry
156+
157+
var javaHomePaths = new List<string>();
158+
159+
// Local function: recursively search for the keyName in the registry
160+
static List<string> ForRegistryKey(RegistryKey registryKey, string keyName) {
161+
var result = new List<string>();
162+
163+
foreach (string valueName in registryKey.GetValueNames()) {
164+
if (valueName == keyName) // Check that the valueName exists
165+
result.Add((string)registryKey.GetValue(valueName)!);
166+
}
167+
168+
foreach (string registrySubKey in registryKey.GetSubKeyNames()) {
169+
using var subKey = registryKey.OpenSubKey(registrySubKey);
170+
if (subKey is not null) // Check that the registrySubKey exists
171+
result.AddRange(ForRegistryKey(subKey, keyName));
172+
}
173+
174+
return result;
175+
}
176+
;
177+
178+
using var reg = Registry.LocalMachine.OpenSubKey("SOFTWARE");
179+
180+
if (reg is not null && reg.GetSubKeyNames().Contains("JavaSoft")) {
181+
using var registryKey = reg.OpenSubKey("JavaSoft");
182+
if (registryKey is not null)
183+
javaHomePaths.AddRange(ForRegistryKey(registryKey, "JavaHome"));
184+
}
185+
186+
if (reg is not null && reg.GetSubKeyNames().Contains("WOW6432Node")) {
187+
using var registryKey = reg.OpenSubKey("WOW6432Node");
188+
if (registryKey is not null && registryKey.GetSubKeyNames().Contains("JavaSoft")) {
189+
using var registrySubKey = reg.OpenSubKey("JavaSoft");
190+
if (registrySubKey is not null)
191+
ForRegistryKey(registrySubKey, "JavaHome").ForEach(x => javaHomePaths.Add(x));
192+
}
193+
}
194+
195+
foreach (var item in javaHomePaths)
196+
if (Directory.Exists(item))
197+
result.AddRange(GetFilesInDirectory(item, "java.exe"));
198+
199+
#endregion
200+
201+
#region Special Folders
202+
203+
List<string> folders = [];
204+
205+
// %APPDATA%\.minecraft\cache\java
206+
string appDataPath = Environment.GetEnvironmentVariable("APPDATA");
207+
208+
if (!string.IsNullOrEmpty(appDataPath))
209+
folders.Add(Path.Combine(appDataPath, ".minecraft\\cache\\java"));
210+
211+
// %APPDATA%\.minecraft\runtime\
212+
if (!string.IsNullOrEmpty(appDataPath))
213+
folders.Add(Path.Combine(appDataPath, ".minecraft\\runtime\\"));
214+
215+
// %JAVA_HOME%
216+
string javaHomePath = Environment.GetEnvironmentVariable("JAVA_HOME");
217+
if (javaHomePath is not null)
218+
folders.Add(javaHomePath);
219+
220+
// Program Files\Java
221+
folders.Add(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), "Java"));
222+
folders.Add(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86), "Java"));
223+
224+
// Program Files\Zulu
225+
folders.Add(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), "Zulu"));
226+
folders.Add(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86), "Zulu"));
227+
228+
// Check Java for each folder
229+
foreach (var folder in folders)
230+
if (Directory.Exists(folder))
231+
result.AddRange(GetFilesInDirectory(folder, "java.exe"));
232+
233+
#endregion
234+
235+
return result.Distinct();
236+
}
237+
238+
private static IEnumerable<string> GetFilesInDirectory(string directoryPath, string searchPattern) {
239+
var files = new List<string>();
240+
241+
try {
242+
// 使用 Directory.GetFiles 递归搜索文件
243+
files.AddRange(Directory.GetFiles(directoryPath, searchPattern, SearchOption.AllDirectories));
244+
}
245+
catch (UnauthorizedAccessException) {
246+
// 忽略无权限访问的目录
247+
}
248+
catch (Exception) {
249+
// 忽略其他异常(如路径过长等)
250+
}
251+
252+
return files;
253+
}
254+
255+
#endregion
256+
}

0 commit comments

Comments
 (0)