mirror of
https://github.com/HMCL-dev/HMCL.git
synced 2026-10-29 15:10:30 +08:00
Windows 平台自动使用 javaw.exe 启动游戏 (#7009)
This commit is contained in:
@@ -565,7 +565,13 @@ public class HMCLGameInstance extends DefaultGameInstance {
|
||||
return getLayout().getInstanceGameSettingsFile(id);
|
||||
}
|
||||
|
||||
public LaunchOptions.Builder getLaunchOptions(JavaRuntime javaVersion, Path gameDir, List<String> javaAgents, List<String> javaArguments, boolean makeLaunchScript) {
|
||||
public LaunchOptions.Builder getLaunchOptions(
|
||||
JavaRuntime javaRuntime,
|
||||
Path javaExecutable,
|
||||
Path gameDir,
|
||||
List<String> javaAgents,
|
||||
List<String> javaArguments,
|
||||
boolean makeLaunchScript) {
|
||||
GameSettings.Effective vs = getEffectiveSettings();
|
||||
boolean noJVMOptions = vs.getInheritable(GameSettings::noJVMOptionsProperty);
|
||||
boolean autoMemory = vs.getInheritable(GameSettings::autoMemoryProperty);
|
||||
@@ -577,7 +583,7 @@ public class HMCLGameInstance extends DefaultGameInstance {
|
||||
? null
|
||||
: Math.toIntExact(HMCLGameRepository.getAutoAllocatedMemory(
|
||||
SystemInfo.getPhysicalMemoryStatus().available(),
|
||||
javaVersion.getPlatform()) / 1024L / 1024L);
|
||||
javaRuntime.getPlatform()) / 1024L / 1024L);
|
||||
} else {
|
||||
maxMemory = vs.getMaxMemory();
|
||||
}
|
||||
@@ -585,7 +591,8 @@ public class HMCLGameInstance extends DefaultGameInstance {
|
||||
LaunchOptions.Builder builder = new LaunchOptions.Builder()
|
||||
.setInstanceId(getId())
|
||||
.setGameDir(gameDir)
|
||||
.setJava(javaVersion)
|
||||
.setJava(javaRuntime)
|
||||
.setJavaExecutable(javaExecutable)
|
||||
.setVersionType(Metadata.TITLE)
|
||||
.setVersionName(getId().id())
|
||||
.setProfileName(Metadata.TITLE)
|
||||
|
||||
@@ -57,11 +57,11 @@ import java.io.IOException;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.net.SocketTimeoutException;
|
||||
import java.nio.file.AccessDeniedException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.time.Instant;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@@ -148,29 +148,46 @@ public final class LauncherHelper {
|
||||
}
|
||||
|
||||
/// Builds and executes the launch pipeline for the captured game instance.
|
||||
@SuppressWarnings("NonAtomicOperationOnVolatileField")
|
||||
private void launch0() {
|
||||
// https://github.com/HMCL-dev/HMCL/pull/4121
|
||||
PROCESSES.removeIf(it -> it.get() == null);
|
||||
|
||||
final var context = new Object() {
|
||||
volatile GameInstanceManifest launchManifest;
|
||||
volatile JavaRuntime javaRuntime;
|
||||
volatile Path javaExecutable;
|
||||
};
|
||||
|
||||
HMCLGameRepository repository = repository();
|
||||
DefaultDependencyManager dependencyManager = repository.getDependency();
|
||||
// Resolve already deduplicated libraries; apply loader-specific argument repairs for this launch.
|
||||
var launchManifest = new AtomicReference<>(LaunchManifestNormalizer.repairForLaunch(gameInstance.getResolvedManifest()));
|
||||
boolean integrityCheck = gameInstance.unmarkLaunchedAbnormally();
|
||||
CountDownLatch launchingLatch = new CountDownLatch(1);
|
||||
List<String> javaAgents = new ArrayList<>(0);
|
||||
List<String> javaArguments = new ArrayList<>(0);
|
||||
|
||||
AtomicReference<JavaRuntime> javaVersionRef = new AtomicReference<>();
|
||||
// Resolve already deduplicated libraries; apply loader-specific argument repairs for this launch.
|
||||
context.launchManifest = LaunchManifestNormalizer.repairForLaunch(gameInstance.getResolvedManifest());
|
||||
|
||||
TaskExecutor executor = checkGameState(gameInstance, setting, launchManifest.get())
|
||||
TaskExecutor executor = checkGameState(gameInstance, setting, context.launchManifest)
|
||||
.thenComposeAsync(java -> {
|
||||
javaVersionRef.set(Objects.requireNonNull(java));
|
||||
launchManifest.set(NativePatcher.patchNative(gameInstance, launchManifest.get(), java, setting, javaArguments));
|
||||
context.javaRuntime = Objects.requireNonNull(java);
|
||||
context.javaExecutable = java.getBinary();
|
||||
if (java.getPlatform().os() == OperatingSystem.WINDOWS
|
||||
&& scriptFile == null
|
||||
&& !setting.getInheritable(GameSettings::notUseJavawProperty)
|
||||
&& FileUtils.getName(java.getBinary()).equalsIgnoreCase("java.exe")) {
|
||||
Path javawBinary = java.getBinary().resolveSibling("javaw.exe");
|
||||
if (Files.isRegularFile(javawBinary)) {
|
||||
context.javaExecutable = javawBinary;
|
||||
}
|
||||
}
|
||||
|
||||
context.launchManifest = NativePatcher.patchNative(gameInstance, context.launchManifest, java, setting, javaArguments);
|
||||
if (setting.getInheritable(GameSettings::notCheckGameProperty))
|
||||
return null;
|
||||
return Task.allOf(
|
||||
dependencyManager.checkGameCompletionAsync(gameInstance, launchManifest.get(), integrityCheck),
|
||||
dependencyManager.checkGameCompletionAsync(gameInstance, context.launchManifest, integrityCheck),
|
||||
Task.composeAsync(() -> {
|
||||
try {
|
||||
@Nullable ModpackConfiguration<?> configuration =
|
||||
@@ -203,7 +220,7 @@ public final class LauncherHelper {
|
||||
|
||||
String agent = FileUtils.getAbsolutePath(file) + "=" + renderer.mesaDriverName();
|
||||
|
||||
if (GameLibrariesTask.shouldDownloadLibrary(repository, launchManifest.get(), lib, integrityCheck)) {
|
||||
if (GameLibrariesTask.shouldDownloadLibrary(repository, context.launchManifest, lib, integrityCheck)) {
|
||||
return new LibraryDownloadTask(dependencyManager, file, lib)
|
||||
.thenRunAsync(() -> javaAgents.add(agent));
|
||||
} else {
|
||||
@@ -213,13 +230,13 @@ public final class LauncherHelper {
|
||||
})
|
||||
);
|
||||
}).withStage("launch.state.dependencies")
|
||||
.thenComposeAsync(() -> new GameVerificationFixTask(gameInstance, gameInstance.getVersion(), launchManifest.get()))
|
||||
.thenComposeAsync(() -> new GameVerificationFixTask(gameInstance, gameInstance.getVersion(), context.launchManifest))
|
||||
.thenComposeAsync(() -> {
|
||||
if (setting.getInheritable(GameSettings::allowAutoAgentProperty)
|
||||
|| setting.getInheritable(GameSettings::noJVMOptionsProperty)
|
||||
|| setting.getInheritable(GameSettings::noOptimizingJVMOptionsProperty)
|
||||
|| Boolean.TRUE.equals(state().getShownTips().get(LWJGL_3_4_1_TIP))
|
||||
|| !NativePatcher.needPatchMemoryUtil(launchManifest.get(), javaVersionRef.get().getParsedVersion())) {
|
||||
|| !NativePatcher.needPatchMemoryUtil(context.launchManifest, context.javaRuntime.getParsedVersion())) {
|
||||
return Task.completed(null);
|
||||
} else {
|
||||
CompletableFuture<Void> future = new CompletableFuture<>();
|
||||
@@ -239,7 +256,12 @@ public final class LauncherHelper {
|
||||
.thenComposeAsync(() -> logIn(account).withStage("launch.state.logging_in"))
|
||||
.thenComposeAsync(authInfo -> Task.supplyAsync(() -> {
|
||||
LaunchOptions.Builder launchOptionsBuilder = gameInstance.getLaunchOptions(
|
||||
javaVersionRef.get(), repository.getBaseDirectory(), javaAgents, javaArguments, scriptFile != null);
|
||||
context.javaRuntime,
|
||||
context.javaExecutable,
|
||||
repository.getBaseDirectory(),
|
||||
javaAgents,
|
||||
javaArguments,
|
||||
scriptFile != null);
|
||||
if (disableOfflineSkin) {
|
||||
launchOptionsBuilder.setDaemon(false);
|
||||
}
|
||||
@@ -248,7 +270,7 @@ public final class LauncherHelper {
|
||||
}
|
||||
|
||||
if (OperatingSystem.CURRENT_OS == OperatingSystem.WINDOWS && setting.getInheritable(GameSettings::highPerformanceProperty)) {
|
||||
@Nullable String javaPathGpuReg = FileUtils.getAbsolutePath(javaVersionRef.get().getBinary());
|
||||
@Nullable String javaPathGpuReg = FileUtils.getAbsolutePath(context.javaExecutable);
|
||||
try {
|
||||
WinReg reg = WinReg.INSTANCE;
|
||||
if (reg != null) {
|
||||
@@ -283,7 +305,7 @@ public final class LauncherHelper {
|
||||
|
||||
return new HMCLGameLauncher(
|
||||
gameInstance,
|
||||
launchManifest.get(),
|
||||
context.launchManifest,
|
||||
authInfo,
|
||||
launchOptions,
|
||||
launcherVisibility == LauncherVisibility.CLOSE
|
||||
|
||||
@@ -388,6 +388,19 @@ public sealed abstract class GameSettings extends ObservableSetting {
|
||||
return detectedJava;
|
||||
}
|
||||
|
||||
/// Property name for disabling automatic replacement with `javaw.exe` on Windows.
|
||||
public static final String PROPERTY_NOT_USE_JAVAW = "notUseJavaw";
|
||||
|
||||
/// Whether to keep the selected Java executable instead of replacing it with `javaw.exe`; defaults to `false`.
|
||||
@SerializedName(PROPERTY_NOT_USE_JAVAW)
|
||||
private final InheritableProperty<Boolean> notUseJavaw = newInheritableProperty(PROPERTY_NOT_USE_JAVAW, false);
|
||||
|
||||
/// Returns the property disabling automatic replacement of a Windows Java executable with a sibling `javaw.exe` file.
|
||||
/// If `true`, the selected executable is used unchanged. This property has no effect on other runtime platforms.
|
||||
public InheritableProperty<Boolean> notUseJavawProperty() {
|
||||
return notUseJavaw;
|
||||
}
|
||||
|
||||
/// Property name for customized JVM options.
|
||||
public static final String PROPERTY_JVM_OPTIONS = "jvmOptions";
|
||||
|
||||
|
||||
@@ -643,6 +643,11 @@ public final class GameSettingsPage<S extends GameSettings> extends StackPane
|
||||
jvmSettings
|
||||
);
|
||||
{
|
||||
var notUseJavawPane = createInheritableBooleanButton(GameSettings::notUseJavawProperty);
|
||||
jvmSettings.getContent().add(notUseJavawPane);
|
||||
notUseJavawPane.setTitle(i18n("settings.advanced.not_use_javaw"));
|
||||
notUseJavawPane.setSubtitle(i18n("settings.advanced.windows_only"));
|
||||
|
||||
var noJVMArgsPane = createInheritableBooleanButton(GameSettings::noJVMOptionsProperty);
|
||||
jvmSettings.getContent().add(noJVMArgsPane);
|
||||
noJVMArgsPane.setTitle(i18n("settings.advanced.no_jvm_args"));
|
||||
|
||||
@@ -1433,6 +1433,7 @@ settings.advanced.natives_directory=Native Library Path
|
||||
settings.advanced.natives_directory.custom.enabled=Use Custom Native Libraries
|
||||
settings.advanced.no_jvm_args=Do not add default JVM arguments
|
||||
settings.advanced.no_optimizing_jvm_args=Do not add default JVM optimization arguments
|
||||
settings.advanced.not_use_javaw=Do not use javaw.exe
|
||||
settings.advanced.precall_command=Pre-launch Command
|
||||
settings.advanced.precall_command.prompt=Commands to execute before the game launches
|
||||
settings.advanced.process_priority=Process Priority
|
||||
|
||||
@@ -176,6 +176,7 @@ addon.check_update.empty=جميع المودات محدّثة
|
||||
addon.check_update.failed_check=فشل التحقق من التحديثات.
|
||||
addon.check_update.failed_download=فشل تنزيل بعض الملفات.
|
||||
addon.check_update.file=الملف
|
||||
# addon.check_update.only_preview=
|
||||
# addon.check_update.preview=
|
||||
addon.check_update.source=المصدر
|
||||
addon.check_update.target_version=الإصدار المستهدف
|
||||
@@ -1432,6 +1433,7 @@ settings.advanced.natives_directory=مسار المكتبات الأصلية
|
||||
# settings.advanced.natives_directory.custom.enabled=
|
||||
settings.advanced.no_jvm_args=عدم إضافة وسيطات JVM الافتراضية
|
||||
settings.advanced.no_optimizing_jvm_args=عدم إضافة وسيطات تحسين JVM الافتراضية
|
||||
# settings.advanced.not_use_javaw=
|
||||
settings.advanced.precall_command=أمر ما قبل التشغيل
|
||||
settings.advanced.precall_command.prompt=أوامر تُنفَّذ قبل تشغيل اللعبة
|
||||
settings.advanced.process_priority=أولوية العملية
|
||||
|
||||
@@ -174,6 +174,7 @@ addon.check_update.empty=Alle Dateien sind auf dem neuesten Stand
|
||||
addon.check_update.failed_check=Fehler beim Suchen nach Aktualisierungen.
|
||||
addon.check_update.failed_download=Fehler beim Herunterladen einiger Dateien.
|
||||
addon.check_update.file=Datei
|
||||
# addon.check_update.only_preview=
|
||||
# addon.check_update.preview=
|
||||
addon.check_update.source=Quelle
|
||||
addon.check_update.target_version=Zielversion
|
||||
@@ -1431,6 +1432,7 @@ settings.advanced.natives_directory=Pfad der nativen Bibliotheken
|
||||
settings.advanced.natives_directory.custom.enabled=Benutzerdefinierte native Bibliotheken verwenden
|
||||
settings.advanced.no_jvm_args=Standard-JVM-Argumente nicht hinzufügen
|
||||
settings.advanced.no_optimizing_jvm_args=Standard-JVM-Optimierungsargumente nicht hinzufügen
|
||||
# settings.advanced.not_use_javaw=
|
||||
settings.advanced.precall_command=Pre-Launch-Befehl
|
||||
settings.advanced.precall_command.prompt=Befehle, die vor dem Spielstart ausgeführt werden sollen
|
||||
settings.advanced.process_priority=Prozesspriorität
|
||||
|
||||
@@ -174,6 +174,7 @@ addon.check_update.empty=Todos los mods están actualizados
|
||||
addon.check_update.failed_check=No se ha podido comprobar si hay actualizaciones.
|
||||
addon.check_update.failed_download=No se han podido descargar algunos de los archivos.
|
||||
addon.check_update.file=Archivo
|
||||
# addon.check_update.only_preview=
|
||||
# addon.check_update.preview=
|
||||
addon.check_update.source=Fuente
|
||||
addon.check_update.target_version=Versión de destino
|
||||
@@ -1424,6 +1425,7 @@ settings.advanced.natives_directory=Ruta de la biblioteca nativa
|
||||
# settings.advanced.natives_directory.custom.enabled=
|
||||
settings.advanced.no_jvm_args=No añadir argumentos JVM por defecto
|
||||
settings.advanced.no_optimizing_jvm_args=No añadir argumentos de optimización de JVM
|
||||
# settings.advanced.not_use_javaw=
|
||||
settings.advanced.precall_command=Comando pre-lanzamiento:
|
||||
settings.advanced.precall_command.prompt=El comando se ejecuta antes de que los juegos se lance
|
||||
settings.advanced.process_priority=Prioridad del proceso
|
||||
|
||||
@@ -163,6 +163,7 @@ addon.check_update.current_version=Current
|
||||
addon.check_update.failed_check=更新のチェックに失敗しました
|
||||
addon.check_update.failed_download=一部のファイルのダウンロードに失敗しました
|
||||
addon.check_update.file=ファイル
|
||||
# addon.check_update.only_preview=
|
||||
# addon.check_update.preview=
|
||||
addon.check_update.source=Source
|
||||
addon.check_update.target_version=Target
|
||||
@@ -1228,6 +1229,7 @@ settings.advanced.natives_directory=ネイティブライブラリパス(例
|
||||
# settings.advanced.natives_directory.custom.enabled=
|
||||
settings.advanced.no_jvm_args=デフォルトのJVM引数なし
|
||||
# settings.advanced.no_optimizing_jvm_args=
|
||||
# settings.advanced.not_use_javaw=
|
||||
settings.advanced.precall_command=起動前コマンド
|
||||
settings.advanced.precall_command.prompt=ゲーム開始前に実行されます
|
||||
settings.advanced.process_priority=プロセスの優先度
|
||||
|
||||
@@ -174,6 +174,7 @@ addon.check_update.empty=無改囊可迭更
|
||||
addon.check_update.failed_check=檢囊迭更未成
|
||||
addon.check_update.failed_download=有引案未成
|
||||
addon.check_update.file=案
|
||||
# addon.check_update.only_preview=
|
||||
# addon.check_update.preview=
|
||||
addon.check_update.source=源
|
||||
addon.check_update.target_version=將至之版
|
||||
@@ -1240,6 +1241,7 @@ settings.advanced.natives_directory=府庫之徑 (LWJGL)
|
||||
# settings.advanced.natives_directory.custom.enabled=
|
||||
settings.advanced.no_jvm_args=無添爪哇虛機之本通弦
|
||||
# settings.advanced.no_optimizing_jvm_args=
|
||||
# settings.advanced.not_use_javaw=
|
||||
settings.advanced.precall_command=令於戲前
|
||||
settings.advanced.precall_command.prompt=將前戲啟而行
|
||||
settings.advanced.process_priority=進程之次
|
||||
|
||||
@@ -170,6 +170,7 @@ addon.check_update.empty=Все файлы актуальны
|
||||
addon.check_update.failed_check=Не удалось проверить обновления.
|
||||
addon.check_update.failed_download=Не удалось скачать некоторые файлы.
|
||||
addon.check_update.file=Файл
|
||||
# addon.check_update.only_preview=
|
||||
# addon.check_update.preview=
|
||||
addon.check_update.source=Источник
|
||||
addon.check_update.target_version=Целевая версия
|
||||
@@ -1364,6 +1365,7 @@ settings.advanced.natives_directory=Папка с нативными библи
|
||||
settings.advanced.natives_directory.custom.enabled=Использовать свои нативные библиотеки
|
||||
settings.advanced.no_jvm_args=Не добавлять аргументы JVM по умолчанию
|
||||
settings.advanced.no_optimizing_jvm_args=Не добавлять аргументы оптимизации JVM по умолчанию
|
||||
# settings.advanced.not_use_javaw=
|
||||
settings.advanced.precall_command=Команда перед запуском
|
||||
settings.advanced.precall_command.prompt=Команды, которые необходимо выполнить перед запуском игры
|
||||
settings.advanced.process_priority=Приоритет процесса
|
||||
|
||||
@@ -176,6 +176,7 @@ addon.check_update.empty=Усі моди оновлені
|
||||
addon.check_update.failed_check=Не вдалося перевірити оновлення.
|
||||
addon.check_update.failed_download=Не вдалося завантажити деякі файли.
|
||||
addon.check_update.file=Файл
|
||||
# addon.check_update.only_preview=
|
||||
# addon.check_update.preview=
|
||||
addon.check_update.source=Джерело
|
||||
addon.check_update.target_version=Цільова версія
|
||||
@@ -1387,6 +1388,7 @@ settings.advanced.natives_directory=Шлях до нативних бібліо
|
||||
settings.advanced.natives_directory.custom.enabled=Використовувати власні нативні бібліотеки
|
||||
settings.advanced.no_jvm_args=Не додавати типові аргументи JVM
|
||||
settings.advanced.no_optimizing_jvm_args=Не додавати типові аргументи оптимізації JVM
|
||||
# settings.advanced.not_use_javaw=
|
||||
settings.advanced.precall_command=Команда перед запуском
|
||||
settings.advanced.precall_command.prompt=Команди для виконання перед запуском гри
|
||||
settings.advanced.process_priority=Пріоритет процесу
|
||||
|
||||
@@ -1245,6 +1245,7 @@ settings.advanced.natives_directory=本地库路径 (LWJGL)
|
||||
settings.advanced.natives_directory.custom.enabled=使用自定义本地库
|
||||
settings.advanced.no_jvm_args=不添加默认的 Java 虚拟机参数
|
||||
settings.advanced.no_optimizing_jvm_args=不自动添加 Java 虚拟机优化参数
|
||||
settings.advanced.not_use_javaw=不使用 javaw.exe
|
||||
settings.advanced.precall_command=游戏启动前执行命令
|
||||
settings.advanced.precall_command.prompt=将在游戏启动前调用
|
||||
settings.advanced.process_priority=进程优先级
|
||||
|
||||
@@ -1239,6 +1239,7 @@ settings.advanced.natives_directory=本機庫路徑 (LWJGL)
|
||||
settings.advanced.natives_directory.custom.enabled=使用自訂本機庫
|
||||
settings.advanced.no_jvm_args=不新增預設的 Java 虛擬機參數
|
||||
settings.advanced.no_optimizing_jvm_args=不自動新增 Java 虛擬機最佳化參數
|
||||
settings.advanced.not_use_javaw=不使用 javaw.exe
|
||||
settings.advanced.precall_command=遊戲啟動前執行指令
|
||||
settings.advanced.precall_command.prompt=將在遊戲啟動前呼叫使用
|
||||
settings.advanced.process_priority=處理程式優先度
|
||||
|
||||
@@ -33,6 +33,7 @@ public class LaunchOptions implements Serializable {
|
||||
private GameInstanceID instanceId;
|
||||
private Path gameDir;
|
||||
private JavaRuntime java;
|
||||
private Path javaExecutable;
|
||||
private String versionName;
|
||||
private String versionType;
|
||||
private String profileName;
|
||||
@@ -85,6 +86,16 @@ public class LaunchOptions implements Serializable {
|
||||
return java;
|
||||
}
|
||||
|
||||
/// Returns the executable selected for launch, falling back to [JavaRuntime#getBinary()] if none is specified.
|
||||
/// The path is used without automatic executable substitution or file validation.
|
||||
public Path getJavaExecutable() {
|
||||
if (javaExecutable != null)
|
||||
return javaExecutable;
|
||||
if (java != null)
|
||||
return java.getBinary();
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Will shown in the left bottom corner of the main menu of Minecraft.
|
||||
* null if use the id of launch version.
|
||||
@@ -336,6 +347,11 @@ public class LaunchOptions implements Serializable {
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setJavaExecutable(Path javaExecutable) {
|
||||
options.javaExecutable = javaExecutable;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setVersionName(String versionName) {
|
||||
options.versionName = versionName;
|
||||
return this;
|
||||
|
||||
@@ -98,7 +98,7 @@ public class DefaultLauncher extends Launcher {
|
||||
if (StringUtils.isNotBlank(options.getWrapper()))
|
||||
res.addAllWithoutParsing(StringUtils.tokenize(options.getWrapper(), getEnvVars(nativeFolder)));
|
||||
|
||||
res.add(options.getJava().getBinary().toString());
|
||||
res.add(options.getJavaExecutable().toString());
|
||||
|
||||
res.addAllWithoutParsingAndReadExternal(options.getOverrideJavaArguments());
|
||||
|
||||
|
||||
Reference in New Issue
Block a user