diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameRepository.java b/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameRepository.java index ba0e03a91f..0a48139f70 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameRepository.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameRepository.java @@ -430,6 +430,7 @@ public LaunchOptions.Builder getLaunchOptions(String version, JavaRuntime javaVe .setNativesDirType(vs.getNativesDirType()) .setNativesDir(vs.getNativesDir()) .setProcessPriority(vs.getProcessPriority()) + .setGraphicsBackend(vs.getGraphicsBackend()) .setRenderer(vs.getRenderer()) .setEnableDebugLogOutput(vs.isEnableDebugLogOutput()) .setUseNativeGLFW(vs.isUseNativeGLFW()) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/game/LauncherHelper.java b/HMCL/src/main/java/org/jackhuang/hmcl/game/LauncherHelper.java index 48d7a26a5f..6191a3ecdc 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/game/LauncherHelper.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/game/LauncherHelper.java @@ -174,26 +174,27 @@ private void launch0() { }), Task.composeAsync(() -> { Renderer renderer = setting.getRenderer(); - if (renderer != Renderer.DEFAULT && OperatingSystem.CURRENT_OS == OperatingSystem.WINDOWS) { - Library lib = NativePatcher.getWindowsMesaLoader(java, renderer, OperatingSystem.SYSTEM_VERSION); - if (lib == null) - return null; - Path file = dependencyManager.getGameRepository().getLibraryFile(version.get(), lib); - if (file.toAbsolutePath().toString().indexOf('=') >= 0) { - LOG.warning("Invalid character '=' in the libraries directory path, unable to attach software renderer loader"); - return null; - } + if (renderer == null || renderer.getMesaDriverName() == null + || OperatingSystem.CURRENT_OS != OperatingSystem.WINDOWS) { + return null; + } + + Library lib = NativePatcher.getWindowsMesaLoader(java, renderer, OperatingSystem.SYSTEM_VERSION); + if (lib == null) + return null; + Path file = dependencyManager.getGameRepository().getLibraryFile(version.get(), lib); + if (file.toAbsolutePath().toString().indexOf('=') >= 0) { + LOG.warning("Invalid character '=' in the libraries directory path, unable to attach software renderer loader"); + return null; + } - String agent = FileUtils.getAbsolutePath(file) + "=" + renderer.name().toLowerCase(Locale.ROOT); + String agent = FileUtils.getAbsolutePath(file) + "=" + renderer.getMesaDriverName(); - if (GameLibrariesTask.shouldDownloadLibrary(repository, version.get(), lib, integrityCheck)) { - return new LibraryDownloadTask(dependencyManager, file, lib) - .thenRunAsync(() -> javaAgents.add(agent)); - } else { - javaAgents.add(agent); - return null; - } + if (GameLibrariesTask.shouldDownloadLibrary(repository, version.get(), lib, integrityCheck)) { + return new LibraryDownloadTask(dependencyManager, file, lib) + .thenRunAsync(() -> javaAgents.add(agent)); } else { + javaAgents.add(agent); return null; } }) @@ -232,6 +233,7 @@ private void launch0() { if (quickPlayOption != null) { launchOptionsBuilder.setQuickPlayOption(quickPlayOption); } + LaunchOptions launchOptions = launchOptionsBuilder.create(); LOG.info("Here's the structure of game mod directory:\n" + FileUtils.printFileStructure(repository.getModsDirectory(selectedVersion), 10)); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/setting/VersionSetting.java b/HMCL/src/main/java/org/jackhuang/hmcl/setting/VersionSetting.java index 3443e57c6a..27f51ac250 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/setting/VersionSetting.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/setting/VersionSetting.java @@ -603,6 +603,20 @@ public void setProcessPriority(ProcessPriority processPriority) { processPriorityProperty.set(processPriority); } + private final ObjectProperty graphicsBackend = new SimpleObjectProperty<>(this, "graphicsBackend", GraphicsAPI.DEFAULT); + + public ObjectProperty graphicsBackendProperty() { + return graphicsBackend; + } + + public GraphicsAPI getGraphicsBackend() { + return graphicsBackendProperty().get(); + } + + public void setGraphicsBackend(GraphicsAPI api) { + graphicsBackendProperty().set(api); + } + private final ObjectProperty rendererProperty = new SimpleObjectProperty<>(this, "renderer", Renderer.DEFAULT); public Renderer getRenderer() { @@ -820,6 +834,7 @@ public JsonElement serialize(VersionSetting src, Type typeOfSrc, JsonSerializati } obj.addProperty("java", java); + obj.addProperty("graphicsBackend", src.getGraphicsBackend().name()); obj.addProperty("renderer", src.getRenderer().name()); if (src.getRenderer() == Renderer.LLVMPIPE) obj.addProperty("useSoftwareRenderer", true); @@ -905,6 +920,15 @@ public VersionSetting deserialize(JsonElement json, Type typeOfT, JsonDeserializ return useSoftwareRenderer ? Renderer.LLVMPIPE : Renderer.DEFAULT; })); + vs.setGraphicsBackend(Optional.ofNullable(obj.get("graphicsBackend")).map(JsonElement::getAsString) + .flatMap(name -> { + try { + return Optional.of(GraphicsAPI.valueOf(name.toUpperCase(Locale.ROOT))); + } catch (IllegalArgumentException ignored) { + return Optional.empty(); + } + }).orElseGet(() -> vs.getRenderer().getApi())); + return vs; } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/LineSelectButton.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/LineSelectButton.java index 696c6bb9e4..33f2422240 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/LineSelectButton.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/LineSelectButton.java @@ -28,6 +28,7 @@ import javafx.collections.ObservableList; import javafx.css.PseudoClass; import javafx.geometry.Pos; +import javafx.scene.Node; import javafx.scene.control.Label; import javafx.scene.input.*; import javafx.scene.layout.StackPane; @@ -49,6 +50,8 @@ public class LineSelectButton extends LineButton { private static final PseudoClass SELECTED_PSEUDO_CLASS = PseudoClass.getPseudoClass("selected"); private JFXPopup popup; + @SuppressWarnings({"FieldCanBeLocal", "unused"}) + private ObservableList popupItems; // keep a reference public LineSelectButton() { this.getStyleClass().add(DEFAULT_STYLE_CLASS); @@ -87,7 +90,7 @@ public void fire() { ripplerContainer.addEventFilter(ScrollEvent.ANY, ignored -> popup.hide()); - Bindings.bindContent(popupMenu.getContent(), MappedObservableList.create(itemsProperty(), item -> { + Bindings.bindContent(popupMenu.getContent(), popupItems = MappedObservableList.create(itemsProperty(), item -> { VBox vbox = new VBox(); var itemTitleLabel = new Label(); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/AdvancedVersionSettingPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/AdvancedVersionSettingPage.java index 6db255756f..0b2d2bbeb5 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/AdvancedVersionSettingPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/AdvancedVersionSettingPage.java @@ -25,6 +25,7 @@ import javafx.scene.control.Label; import javafx.scene.control.ScrollPane; import javafx.scene.layout.*; +import org.jackhuang.hmcl.game.GraphicsAPI; import org.jackhuang.hmcl.game.NativesDirectoryType; import org.jackhuang.hmcl.game.Renderer; import org.jackhuang.hmcl.setting.Profile; @@ -36,6 +37,7 @@ import org.jackhuang.hmcl.util.io.FileUtils; import org.jackhuang.hmcl.util.platform.OperatingSystem; import org.jackhuang.hmcl.util.platform.Platform; +import org.jackhuang.hmcl.util.versioning.GameVersionNumber; import org.jetbrains.annotations.Nullable; import java.nio.file.FileSystems; @@ -69,6 +71,7 @@ public final class AdvancedVersionSettingPage extends StackPane implements Decor private final ComponentSublist nativesDirSublist; private final MultiFileItem nativesDirItem; private final MultiFileItem.FileOption nativesDirCustomOption; + private final LineSelectButton graphicsBackendPane; private final LineSelectButton rendererPane; public AdvancedVersionSettingPage(Profile profile, @Nullable String versionId, VersionSetting versionSetting) { @@ -79,6 +82,10 @@ public AdvancedVersionSettingPage(Profile profile, @Nullable String versionId, V versionId == null ? i18n("settings.advanced") : i18n("settings.advanced.title", versionId) )); + @Nullable GameVersionNumber gameVersion = versionId != null + ? GameVersionNumber.asGameVersion(profile.getRepository().getGameVersion(versionId)) + : null; + this.getStyleClass().add("gray-background"); ScrollPane scrollPane = new ScrollPane(); @@ -185,6 +192,25 @@ public AdvancedVersionSettingPage(Profile profile, @Nullable String versionId, V nativesDirHint.setText(i18n("settings.advanced.natives_directory.hint")); nativesDirItem.getChildren().add(nativesDirHint); + graphicsBackendPane = new LineSelectButton<>(); + graphicsBackendPane.setTitle(i18n("settings.advanced.graphics_backend")); + graphicsBackendPane.setConverter(backend -> i18n("settings.advanced.graphics_backend." + backend.name().toLowerCase(Locale.ROOT))); + graphicsBackendPane.setDescriptionConverter(backend -> switch (backend) { + case DEFAULT -> i18n("settings.advanced.graphics_backend.default.desc"); + case OPENGL -> i18n("settings.advanced.graphics_backend.opengl.desc"); + case VULKAN -> { + if (gameVersion == null) + yield i18n("settings.advanced.graphics_backend.vulkan.desc.global"); + else if (gameVersion.compareTo("26.2-snapshot-2") < 0) + yield i18n("settings.advanced.graphics_backend.vulkan.desc.unsupported"); + else + yield i18n("settings.advanced.graphics_backend.vulkan.desc"); + } + default -> null; + }); + graphicsBackendPane.setValue(GraphicsAPI.DEFAULT); + graphicsBackendPane.setItems(GraphicsAPI.values()); + rendererPane = new LineSelectButton<>(); rendererPane.setTitle(i18n("settings.advanced.renderer")); rendererPane.setConverter(e -> i18n("settings.advanced.renderer." + e.name().toLowerCase(Locale.ROOT))); @@ -192,7 +218,25 @@ public AdvancedVersionSettingPage(Profile profile, @Nullable String versionId, V String bundleKey = "settings.advanced.renderer." + e.name().toLowerCase(Locale.ROOT) + ".desc"; return I18n.hasKey(bundleKey) ? i18n(bundleKey) : null; }); - rendererPane.setItems(Renderer.values()); + rendererPane.setValue(Renderer.DEFAULT); + rendererPane.setItems(Renderer.DEFAULT); + + FXUtils.onChangeAndOperate(graphicsBackendPane.valueProperty(), backend -> { + if (backend == null) { // unbind + return; + } + + rendererPane.setItems(Renderer.SUPPORTED.get(backend)); + if (backend == GraphicsAPI.DEFAULT) { + rendererPane.setDisable(true); + rendererPane.setValue(Renderer.DEFAULT); + } else { + rendererPane.setDisable(false); + if (rendererPane.getValue() == null || !rendererPane.getValue().isSupported(backend)) { + rendererPane.setValue(Renderer.DEFAULT); + } + } + }); noJVMArgsPane = new LineToggleButton(); noJVMArgsPane.setTitle(i18n("settings.advanced.no_jvm_args")); @@ -219,7 +263,7 @@ public AdvancedVersionSettingPage(Profile profile, @Nullable String versionId, V useNativeOpenALPane.setSubtitle(i18n("settings.advanced.linux_freebsd_only")); workaroundPane.getContent().setAll( - nativesDirSublist, rendererPane, noJVMArgsPane, noOptimizingJVMArgsPane, noGameCheckPane, + nativesDirSublist, graphicsBackendPane, rendererPane, noJVMArgsPane, noOptimizingJVMArgsPane, noGameCheckPane, noJVMCheckPane, noNativesPatchPane ); @@ -252,6 +296,7 @@ void bindProperties() { FXUtils.bindString(txtPreLaunchCommand, versionSetting.preLaunchCommandProperty()); FXUtils.bindString(txtPostExitCommand, versionSetting.postExitCommandProperty()); rendererPane.valueProperty().bindBidirectional(versionSetting.rendererProperty()); + graphicsBackendPane.valueProperty().bindBidirectional(versionSetting.graphicsBackendProperty()); noGameCheckPane.selectedProperty().bindBidirectional(versionSetting.notCheckGameProperty()); noJVMCheckPane.selectedProperty().bindBidirectional(versionSetting.notCheckJVMProperty()); noJVMArgsPane.selectedProperty().bindBidirectional(versionSetting.noJVMArgsProperty()); @@ -294,6 +339,7 @@ void unbindProperties() { FXUtils.unbind(txtPreLaunchCommand, versionSetting.preLaunchCommandProperty()); FXUtils.unbind(txtPostExitCommand, versionSetting.postExitCommandProperty()); rendererPane.valueProperty().unbindBidirectional(versionSetting.rendererProperty()); + graphicsBackendPane.valueProperty().unbindBidirectional(versionSetting.graphicsBackendProperty()); noGameCheckPane.selectedProperty().unbindBidirectional(versionSetting.notCheckGameProperty()); noJVMCheckPane.selectedProperty().unbindBidirectional(versionSetting.notCheckJVMProperty()); noJVMArgsPane.selectedProperty().unbindBidirectional(versionSetting.noJVMArgsProperty()); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/util/NativePatcher.java b/HMCL/src/main/java/org/jackhuang/hmcl/util/NativePatcher.java index b2798985f1..8c72acd6ad 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/util/NativePatcher.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/util/NativePatcher.java @@ -189,15 +189,16 @@ public static Version patchNative(DefaultGameRepository repository, return version.setLibraries(newLibraries); } - public static @Nullable Library getWindowsMesaLoader(@NotNull JavaRuntime javaVersion, @NotNull Renderer renderer, @NotNull OSVersion windowsVersion) { + /// @see Java Mesa Loader for Windows + public static @Nullable Library getWindowsMesaLoader(@NotNull JavaRuntime java, @NotNull Renderer renderer, @NotNull OSVersion windowsVersion) { if (renderer == Renderer.DEFAULT) return null; if (windowsVersion.isAtLeast(OSVersion.WINDOWS_10)) { - return getNatives(javaVersion.getPlatform()).get("mesa-loader"); + return getNatives(java.getPlatform()).get("mesa-loader"); } else if (windowsVersion.isAtLeast(OSVersion.WINDOWS_7)) { if (renderer == Renderer.LLVMPIPE) - return getNatives(javaVersion.getPlatform()).get("software-renderer-loader"); + return getNatives(java.getPlatform()).get("software-renderer-loader"); else return null; } else { diff --git a/HMCL/src/main/resources/assets/lang/I18N.properties b/HMCL/src/main/resources/assets/lang/I18N.properties index e49463bdbb..02d918b457 100644 --- a/HMCL/src/main/resources/assets/lang/I18N.properties +++ b/HMCL/src/main/resources/assets/lang/I18N.properties @@ -1337,6 +1337,15 @@ settings.advanced.dont_patch_natives=Do not attempt to automatically replace nat settings.advanced.environment_variables=Environment Variables settings.advanced.game_dir.default=Default (".minecraft/") settings.advanced.game_dir.independent=Isolated (".minecraft/versions//", except for assets and libraries) +settings.advanced.graphics_backend=Graphics API +settings.advanced.graphics_backend.default=Default +settings.advanced.graphics_backend.default.desc=Follow the game's settings +settings.advanced.graphics_backend.opengl=OpenGL +settings.advanced.graphics_backend.opengl.desc=Use OpenGL for rendering +settings.advanced.graphics_backend.vulkan=Vulkan +settings.advanced.graphics_backend.vulkan.desc=Use Vulkan for rendering +settings.advanced.graphics_backend.vulkan.desc.global=Use Vulkan for rendering (Since Minecraft 26.2) +settings.advanced.graphics_backend.vulkan.desc.unsupported=Use Vulkan for rendering (not supported for the current game version) settings.advanced.java_permanent_generation_space=PermGen Space settings.advanced.java_permanent_generation_space.prompt=in MiB settings.advanced.jvm=JVM Options @@ -1379,13 +1388,17 @@ settings.advanced.post_exit_command=Post-exit Command settings.advanced.post_exit_command.prompt=Commands to execute after the game exits settings.advanced.renderer=Renderer settings.advanced.renderer.default=Default -settings.advanced.renderer.default.desc=OpenGL +settings.advanced.renderer.default.desc=Use System Default settings.advanced.renderer.d3d12=Mesa D3D12 -settings.advanced.renderer.d3d12.desc=DirectX 12 (Poor performance and compatibility) +settings.advanced.renderer.d3d12.desc=OpenGL renderer based on DirectX 12 +settings.advanced.renderer.lavapipe=Mesa Lavapipe +settings.advanced.renderer.lavapipe.desc=Software Vulkan Renderer settings.advanced.renderer.llvmpipe=Mesa LLVMpipe -settings.advanced.renderer.llvmpipe.desc=Software (Poor performance, best compatibility) +settings.advanced.renderer.llvmpipe.desc=Software OpenGL Renderer +settings.advanced.renderer.dozen=Mesa Dozen +settings.advanced.renderer.dozen.desc=Vulkan renderer based on DirectX 12 (Experimental) settings.advanced.renderer.zink=Mesa Zink -settings.advanced.renderer.zink.desc=Vulkan (Best performance, poor compatibility) +settings.advanced.renderer.zink.desc=OpenGL renderer based on Vulkan settings.advanced.server_ip=Server Address settings.advanced.server_ip.prompt=Automatically join after launching the game settings.advanced.unsupported_system_options=Settings not applicable to the current system diff --git a/HMCL/src/main/resources/assets/lang/I18N_ar.properties b/HMCL/src/main/resources/assets/lang/I18N_ar.properties index 043dedf408..f82e27343d 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_ar.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_ar.properties @@ -1364,13 +1364,9 @@ settings.advanced.post_exit_command=أمر ما بعد الإغلاق settings.advanced.post_exit_command.prompt=أوامر تُنفَّذ بعد إغلاق اللعبة settings.advanced.renderer=المُصيِّر settings.advanced.renderer.default=افتراضي -settings.advanced.renderer.default.desc=OpenGL settings.advanced.renderer.d3d12=Mesa D3D12 -settings.advanced.renderer.d3d12.desc=DirectX 12 (أداء وتوافق ضعيفان) settings.advanced.renderer.llvmpipe=Mesa LLVMpipe -settings.advanced.renderer.llvmpipe.desc=برمجي (أداء ضعيف، توافق أفضل) settings.advanced.renderer.zink=Mesa Zink -settings.advanced.renderer.zink.desc=Vulkan (أفضل أداء، توافق ضعيف) settings.advanced.server_ip=عنوان الخادم settings.advanced.server_ip.prompt=الانضمام تلقائياً بعد تشغيل اللعبة settings.advanced.unsupported_system_options=إعدادات غير مطبّقة على النظام الحالي diff --git a/HMCL/src/main/resources/assets/lang/I18N_es.properties b/HMCL/src/main/resources/assets/lang/I18N_es.properties index 1795af7f07..b502a5121b 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_es.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_es.properties @@ -1268,13 +1268,9 @@ settings.advanced.post_exit_command=Comando post-salida settings.advanced.post_exit_command.prompt=El comando se ejecuta después de que el juego se detenga settings.advanced.renderer=Renderizador settings.advanced.renderer.default=Por defecto -settings.advanced.renderer.default.desc=OpenGL settings.advanced.renderer.d3d12=Mesa D3D12 -settings.advanced.renderer.d3d12.desc=DirectX 12 (Rendimiento y compatibilidad deficientes) settings.advanced.renderer.llvmpipe=Mesa LLVMpipe -settings.advanced.renderer.llvmpipe.desc=Software (Bajo rendimiento, máxima compatibilidad) settings.advanced.renderer.zink=Mesa Zink -settings.advanced.renderer.zink.desc=Vulkan (Máximo rendimiento, baja compatibilidad) settings.advanced.server_ip=Dirección del servidor settings.advanced.server_ip.prompt=Entrar automáticamente después de ejecutar el juego settings.advanced.unsupported_system_options=Configuración no aplicable al sistema actual diff --git a/HMCL/src/main/resources/assets/lang/I18N_ja.properties b/HMCL/src/main/resources/assets/lang/I18N_ja.properties index 51e4cbbd43..bd299ee5ac 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_ja.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_ja.properties @@ -792,13 +792,9 @@ settings.advanced.post_exit_command=終了後のコマンド settings.advanced.post_exit_command.prompt=ゲーム終了後に実行されます settings.advanced.renderer=レンダラー settings.advanced.renderer.default=デフォルト -settings.advanced.renderer.default.desc=OpenGL settings.advanced.renderer.d3d12=Mesa D3D12 -settings.advanced.renderer.d3d12.desc=DirectX 12 (低パフォーマンス、低互換性) settings.advanced.renderer.llvmpipe=Mesa LLVMpipe -settings.advanced.renderer.llvmpipe.desc=ソフトウェア (低速だが、互換性は最高) settings.advanced.renderer.zink=Mesa Zink -settings.advanced.renderer.zink.desc=Vulkan (最高パフォーマンス、低互換性) settings.advanced.server_ip=サーバーアドレス settings.advanced.server_ip.prompt=ゲームの起動時にサーバーに参加する settings.advanced.unsupported_system_options=サポートされていないシステムオプション diff --git a/HMCL/src/main/resources/assets/lang/I18N_lzh.properties b/HMCL/src/main/resources/assets/lang/I18N_lzh.properties index a3826ca4a1..a652f5209e 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_lzh.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_lzh.properties @@ -1085,13 +1085,9 @@ settings.advanced.post_exit_command=令於戲訖 settings.advanced.post_exit_command.prompt=將後戲訖而行 settings.advanced.renderer=繪器 settings.advanced.renderer.default=本 -settings.advanced.renderer.default.desc=開圖庫 settings.advanced.renderer.d3d12=Mesa D3D12 -settings.advanced.renderer.d3d12.desc=DirectX 12 (效與所適皆差,勘誤之用) settings.advanced.renderer.llvmpipe=Mesa LLVMpipe -settings.advanced.renderer.llvmpipe.desc=軟繪器 (效差,所適至) settings.advanced.renderer.zink=Mesa Zink -settings.advanced.renderer.zink.desc=Vulkan (效至,所適差) settings.advanced.server_ip=伺服器之址 settings.advanced.server_ip.prompt=本之址,啟訖徑入是伺服器 settings.advanced.unsupported_system_options=不逮今之械網所選 diff --git a/HMCL/src/main/resources/assets/lang/I18N_ru.properties b/HMCL/src/main/resources/assets/lang/I18N_ru.properties index 822d8ec902..fefd634e1b 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_ru.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_ru.properties @@ -1259,13 +1259,9 @@ settings.advanced.post_exit_command=Команда после выхода settings.advanced.post_exit_command.prompt=Команды, которые необходимо выполнить после выхода из игры settings.advanced.renderer=Рендерер settings.advanced.renderer.default=По умолчанию -settings.advanced.renderer.default.desc=OpenGL settings.advanced.renderer.d3d12=Mesa D3D12 -settings.advanced.renderer.d3d12.desc=DirectX 12 (Низкая производительность и совместимость) settings.advanced.renderer.llvmpipe=Mesa LLVMpipe -settings.advanced.renderer.llvmpipe.desc=ПО (Низкая производительность, лучшая совместимость) settings.advanced.renderer.zink=Mesa Zink -settings.advanced.renderer.zink.desc=Vulkan (Лучшая производительность, низкая совместимость) settings.advanced.server_ip=Адрес сервера settings.advanced.server_ip.prompt=Присоединяться к серверу при запуске игры settings.advanced.unsupported_system_options=Настройки, не применимые к текущей системе diff --git a/HMCL/src/main/resources/assets/lang/I18N_uk.properties b/HMCL/src/main/resources/assets/lang/I18N_uk.properties index 443400764f..9920651da2 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_uk.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_uk.properties @@ -1206,13 +1206,9 @@ settings.advanced.post_exit_command=Команда після виходу settings.advanced.post_exit_command.prompt=Команди для виконання після виходу з гри settings.advanced.renderer=Рендерер settings.advanced.renderer.default=По умолчанию -settings.advanced.renderer.default.desc=OpenGL settings.advanced.renderer.d3d12=Mesa D3D12 -settings.advanced.renderer.d3d12.desc=DirectX 12 (Погана продуктивність та сумісність) settings.advanced.renderer.llvmpipe=Mesa LLVMpipe -settings.advanced.renderer.llvmpipe.desc=Програмний (Погана продуктивність, найкраща сумісність) settings.advanced.renderer.zink=Mesa Zink -settings.advanced.renderer.zink.desc=Vulkan (Найкраща продуктивність, погана сумісність) settings.advanced.server_ip=Адреса сервера settings.advanced.server_ip.prompt=Автоматично приєднатися після запуску гри settings.advanced.unsupported_system_options=Налаштування, що не застосовуються до поточної системи diff --git a/HMCL/src/main/resources/assets/lang/I18N_zh.properties b/HMCL/src/main/resources/assets/lang/I18N_zh.properties index 9611bfdb14..2bcfec2041 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh.properties @@ -1127,6 +1127,15 @@ settings.advanced.dont_patch_natives=不嘗試自動取代本機庫 settings.advanced.environment_variables=環境變數 settings.advanced.game_dir.default=預設 (".minecraft/") settings.advanced.game_dir.independent=各實例獨立 (".minecraft/versions/<實例名>/",除 assets、libraries 外) +settings.advanced.graphics_backend=圖形 API +settings.advanced.graphics_backend.default=預設 +settings.advanced.graphics_backend.default.desc=遵循遊戲設定 +settings.advanced.graphics_backend.opengl=OpenGL +settings.advanced.graphics_backend.opengl.desc=使用 OpenGL 渲染 +settings.advanced.graphics_backend.vulkan=Vulkan +settings.advanced.graphics_backend.vulkan.desc=使用 Vulkan 渲染 +settings.advanced.graphics_backend.vulkan.desc.global=使用 Vulkan 渲染 (適用於 Minecraft 26.2 及更高版本) +settings.advanced.graphics_backend.vulkan.desc.unsupported=使用 Vulkan 渲染 (不適用於當前遊戲版本) settings.advanced.java_permanent_generation_space=記憶體永久儲存區域 settings.advanced.java_permanent_generation_space.prompt=單位 MiB settings.advanced.jvm=Java 虛擬機設定 @@ -1166,13 +1175,17 @@ settings.advanced.post_exit_command=遊戲結束後執行指令 settings.advanced.post_exit_command.prompt=將在遊戲結束後呼叫使用 settings.advanced.renderer=繪製器 settings.advanced.renderer.default=預設 -settings.advanced.renderer.default.desc=OpenGL +settings.advanced.renderer.default.desc=使用系統預設繪製器 settings.advanced.renderer.d3d12=Mesa D3D12 -settings.advanced.renderer.d3d12.desc=DirectX 12 (效能與相容性較差,用於除錯) +settings.advanced.renderer.d3d12.desc=基於 DirectX 12 的 OpenGL 繪製器 +settings.advanced.renderer.lavapipe=Mesa Lavapipe +settings.advanced.renderer.lavapipe.desc=軟體 Vulkan 繪製器 settings.advanced.renderer.llvmpipe=Mesa LLVMpipe -settings.advanced.renderer.llvmpipe.desc=軟繪製器 (效能較差,相容性最好) +settings.advanced.renderer.llvmpipe.desc=軟體 OpenGL 繪製器 +settings.advanced.renderer.dozen=Mesa Dozen +settings.advanced.renderer.dozen.desc=基於 DirectX 12 的 Vulkan 繪製器 (實驗性) settings.advanced.renderer.zink=Mesa Zink -settings.advanced.renderer.zink.desc=Vulkan (效能最好,相容性較差) +settings.advanced.renderer.zink.desc=基於 Vulkan 的 OpenGL 繪製器 settings.advanced.server_ip=伺服器位址 settings.advanced.server_ip.prompt=預設,啟動遊戲後直接進入對應伺服器 settings.advanced.unsupported_system_options=不適用於目前系統的選項 diff --git a/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties b/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties index 06b58b2bd1..ba95ad274e 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties @@ -1132,6 +1132,15 @@ settings.advanced.dont_patch_natives=不尝试自动替换本地库 settings.advanced.environment_variables=环境变量 settings.advanced.game_dir.default=默认 (".minecraft/") settings.advanced.game_dir.independent=各实例独立 (存放在 ".minecraft/versions/<实例名>/",除 assets、libraries 外) +settings.advanced.graphics_backend=图形 API +settings.advanced.graphics_backend.default=默认 +settings.advanced.graphics_backend.default.desc=遵循游戏设置 +settings.advanced.graphics_backend.opengl=OpenGL +settings.advanced.graphics_backend.opengl.desc=使用 OpenGL 渲染 +settings.advanced.graphics_backend.vulkan=Vulkan +settings.advanced.graphics_backend.vulkan.desc=使用 Vulkan 渲染 +settings.advanced.graphics_backend.vulkan.desc.global=使用 Vulkan 渲染 (适用于 Minecraft 26.2 及更高版本) +settings.advanced.graphics_backend.vulkan.desc.unsupported=使用 Vulkan 渲染 (不适用于当前游戏版本) settings.advanced.java_permanent_generation_space=内存永久保存区域 settings.advanced.java_permanent_generation_space.prompt=单位 MiB settings.advanced.jvm=Java 虚拟机设置 @@ -1171,13 +1180,17 @@ settings.advanced.post_exit_command=游戏结束后执行命令 settings.advanced.post_exit_command.prompt=将在游戏结束后调用 settings.advanced.renderer=渲染器 settings.advanced.renderer.default=默认 -settings.advanced.renderer.default.desc=OpenGL +settings.advanced.renderer.default.desc=使用系统默认渲染器 settings.advanced.renderer.d3d12=Mesa D3D12 -settings.advanced.renderer.d3d12.desc=DirectX 12 (性能与兼容性较差,用于调试) +settings.advanced.renderer.d3d12.desc=基于 DirectX 12 的 OpenGL 渲染器 +settings.advanced.renderer.dozen=Mesa Dozen +settings.advanced.renderer.dozen.desc=基于 DirectX 12 的 Vulkan 渲染器 (实验性) +settings.advanced.renderer.lavapipe=Mesa Lavapipe +settings.advanced.renderer.lavapipe.desc=软件 Vulkan 渲染器 settings.advanced.renderer.llvmpipe=Mesa LLVMpipe -settings.advanced.renderer.llvmpipe.desc=软渲染器 (性能较差,兼容性最好) +settings.advanced.renderer.llvmpipe.desc=软件 OpenGL 渲染器 settings.advanced.renderer.zink=Mesa Zink -settings.advanced.renderer.zink.desc=Vulkan (性能最好,兼容性较差) +settings.advanced.renderer.zink.desc=基于 Vulkan 的 OpenGL 渲染器 settings.advanced.server_ip=服务器地址 settings.advanced.server_ip.prompt=默认,启动游戏后可以直接进入对应服务器 settings.advanced.unsupported_system_options=不适用于当前系统的选项 diff --git a/HMCL/src/main/resources/assets/natives.json b/HMCL/src/main/resources/assets/natives.json index cd84c8cfc6..86b2e7baa6 100644 --- a/HMCL/src/main/resources/assets/natives.json +++ b/HMCL/src/main/resources/assets/natives.json @@ -4403,13 +4403,13 @@ }, "windows-x86_64": { "mesa-loader": { - "name": "org.glavo:mesa-loader-windows:25.0.3:x64", + "name": "org.glavo:mesa-loader-windows:26.0.4:x64", "downloads": { "artifact": { - "path": "org/glavo/mesa-loader-windows/25.0.3/mesa-loader-windows-25.0.3-x64.jar", - "url": "https://repo1.maven.org/maven2/org/glavo/mesa-loader-windows/25.0.3/mesa-loader-windows-25.0.3-x64.jar", - "sha1": "b2552fcc8c98e95b4559c39f2ff87cdb3aaaa513", - "size": 27971214 + "path": "org/glavo/mesa-loader-windows/26.0.4/mesa-loader-windows-26.0.4-x64.jar", + "url": "https://repo1.maven.org/maven2/org/glavo/mesa-loader-windows/26.0.4/mesa-loader-windows-26.0.4-x64.jar", + "sha1": "f8da709c59ef61f531c91434ca0e3b4f39202981", + "size": 49873846 } } }, @@ -4427,13 +4427,13 @@ }, "windows-x86": { "mesa-loader": { - "name": "org.glavo:mesa-loader-windows:25.0.3:x86", + "name": "org.glavo:mesa-loader-windows:26.0.4:x86", "downloads": { "artifact": { - "path": "org/glavo/mesa-loader-windows/25.0.3/mesa-loader-windows-25.0.3-x86.jar", - "url": "https://repo1.maven.org/maven2/org/glavo/mesa-loader-windows/25.0.3/mesa-loader-windows-25.0.3-x86.jar", - "sha1": "6fd0fd7da88ca6636f735e1e0f6feb7f62f59715", - "size": 23311299 + "path": "org/glavo/mesa-loader-windows/26.0.4/mesa-loader-windows-26.0.4-x86.jar", + "url": "https://repo1.maven.org/maven2/org/glavo/mesa-loader-windows/26.0.4/mesa-loader-windows-26.0.4-x86.jar", + "sha1": "ac6afaa8baa7c17468267c09e77e1296ee92d5ed", + "size": 41742113 } } } @@ -4816,13 +4816,13 @@ "com.mojang:text2speech:1.12.4:natives": null, "com.mojang:text2speech:1.13.9:natives-windows": null, "mesa-loader": { - "name": "org.glavo:mesa-loader-windows:25.0.3:arm64", + "name": "org.glavo:mesa-loader-windows:26.0.4:arm64", "downloads": { "artifact": { - "path": "org/glavo/mesa-loader-windows/25.0.3/mesa-loader-windows-25.0.3-arm64.jar", - "url": "https://repo1.maven.org/maven2/org/glavo/mesa-loader-windows/25.0.3/mesa-loader-windows-25.0.3-arm64.jar", - "sha1": "40ca5de3351bf48b7b9efa5d2598d5b6b4d1ab8e", - "size": 24905270 + "path": "org/glavo/mesa-loader-windows/26.0.4/mesa-loader-windows-26.0.4-arm64.jar", + "url": "https://repo1.maven.org/maven2/org/glavo/mesa-loader-windows/26.0.4/mesa-loader-windows-26.0.4-arm64.jar", + "sha1": "5c761a344700a07eaded51c5cf0cde36ee614706", + "size": 43504189 } } } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GraphicsAPI.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GraphicsAPI.java new file mode 100644 index 0000000000..a84ab4452a --- /dev/null +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/GraphicsAPI.java @@ -0,0 +1,33 @@ +/* + * Hello Minecraft! Launcher + * Copyright (C) 2026 huangyuhui and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.jackhuang.hmcl.game; + +import java.util.Locale; + +/// The Graphics API. +public enum GraphicsAPI { + DEFAULT, + OPENGL, + VULKAN; + + private final String minecraftArg = name().toLowerCase(Locale.ROOT); + + public String getMinecraftArg() { + return minecraftArg; + } +} diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/LaunchOptions.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/LaunchOptions.java index 52c660061d..cd7d960d03 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/LaunchOptions.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/LaunchOptions.java @@ -56,6 +56,7 @@ public class LaunchOptions implements Serializable { private NativesDirectoryType nativesDirType; private String nativesDir; private ProcessPriority processPriority = ProcessPriority.NORMAL; + private GraphicsAPI graphicsBackend = GraphicsAPI.DEFAULT; private Renderer renderer = Renderer.DEFAULT; private boolean useNativeGLFW; private boolean useNativeOpenAL; @@ -244,7 +245,11 @@ public ProcessPriority getProcessPriority() { return processPriority; } - public Renderer getRenderer() { + public @NotNull GraphicsAPI getGraphicsBackend() { + return graphicsBackend; + } + + public @NotNull Renderer getRenderer() { return renderer; } @@ -435,8 +440,13 @@ public Builder setProcessPriority(@NotNull ProcessPriority processPriority) { return this; } - public Builder setRenderer(@NotNull Renderer renderer) { - options.renderer = renderer; + public Builder setGraphicsBackend(GraphicsAPI backend) { + options.graphicsBackend = Objects.requireNonNullElse(backend, GraphicsAPI.DEFAULT); + return this; + } + + public Builder setRenderer(Renderer renderer) { + options.renderer = Objects.requireNonNullElse(renderer, Renderer.DEFAULT); return this; } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/Renderer.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/Renderer.java index c8576856b3..a0b0c68db6 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/Renderer.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/Renderer.java @@ -17,9 +17,73 @@ */ package org.jackhuang.hmcl.game; +import org.jetbrains.annotations.NotNullByDefault; +import org.jetbrains.annotations.Nullable; + +import java.util.EnumMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Stream; + +/// @author Glavo +@NotNullByDefault public enum Renderer { - DEFAULT, - ZINK, - LLVMPIPE, - D3D12 + DEFAULT(GraphicsAPI.DEFAULT, null, null), + + LAVAPIPE(GraphicsAPI.VULKAN, "lavapipe", "lvp"), + + // Currently, Dozen does not support the VK_KHR_push_descriptor feature, so it cannot launch Minecraft 26.2 + // Using Dozen can run Minecraft 1.21.11 + VulkanMod, but it will cause the game to crash after playing for a while + DOZEN(GraphicsAPI.VULKAN, "dzn", "dzn"), + + LLVMPIPE(GraphicsAPI.OPENGL, "llvmpipe", null), + ZINK(GraphicsAPI.OPENGL, "zink", null), + D3D12(GraphicsAPI.OPENGL, "d3d12", null), + ; + + /// Map the graphics API to supported renderers. + public static final Map> SUPPORTED; + + static { + var map = new EnumMap>(GraphicsAPI.class); + + for (var api : GraphicsAPI.values()) { + map.put(api, Stream.of(values()).filter(it -> it.isSupported(api)).toList()); + } + + SUPPORTED = map; + } + + private final GraphicsAPI api; + + private final @Nullable String loaderName; + private final @Nullable String icdName; + + Renderer(GraphicsAPI api, @Nullable String loaderName, @Nullable String icdName) { + this.api = api; + this.loaderName = loaderName; + this.icdName = icdName; + } + + /// Get the Graphics API used by this renderer. + public GraphicsAPI getApi() { + return api; + } + + public boolean isSupported(GraphicsAPI api) { + return this.api == api || this.api == GraphicsAPI.DEFAULT; + } + + public @Nullable String getMesaDriverName() { + return loaderName; + } + + public @Nullable String getIcdName() { + return icdName; + } + + public @Nullable String getIcdFileName() { + return icdName != null ? icdName + "_icd.json" : null; + } + } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/launch/DefaultLauncher.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/launch/DefaultLauncher.java index 04f97e6c4c..0ce41792bc 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/launch/DefaultLauncher.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/launch/DefaultLauncher.java @@ -104,7 +104,7 @@ private Command generateCommandLine(Path nativeFolder) throws IOException { // Executable if (StringUtils.isNotBlank(options.getWrapper())) - res.addAllWithoutParsing(StringUtils.tokenize(options.getWrapper(), getEnvVars())); + res.addAllWithoutParsing(StringUtils.tokenize(options.getWrapper(), getEnvVars(nativeFolder))); res.add(options.getJava().getBinary().toString()); @@ -265,6 +265,12 @@ private Command generateCommandLine(Path nativeFolder) throws IOException { res.addDefault("-Dfml.ignorePatchDiscrepancies=", "true"); } + if (OperatingSystem.CURRENT_OS == OperatingSystem.WINDOWS + && options.getRenderer() != null + && options.getRenderer().getMesaDriverName() != null) { + res.addDefault("-Dorg.glavo.mesa.loader.nativeDir=", FileUtils.getAbsolutePath(nativeFolder.resolve("mesa-loader"))); + } + Set classpath = repository.getClasspath(version); if (analyzer.has(LibraryAnalyzer.LibraryType.CLEANROOM)) { @@ -395,6 +401,12 @@ private Command generateCommandLine(Path nativeFolder) throws IOException { } } + if (options.getGraphicsBackend() != GraphicsAPI.DEFAULT + && gameVersion.isPresent() && GameVersionNumber.compare(gameVersion.get(), "26.2-snapshot-2") >= 0) { + res.add("--graphicsBackend"); + res.add(options.getGraphicsBackend().getMinecraftArg()); + } + res.addAllWithoutParsing(Arguments.parseStringArguments(options.getGameArguments(), configuration)); res.removeIf(it -> getForbiddens().containsKey(it) && getForbiddens().get(it).get()); @@ -564,8 +576,8 @@ public ManagedProcess launch() throws IOException, InterruptedException { Path runDirectory = repository.getRunDirectory(version.getId()); if (StringUtils.isNotBlank(options.getPreLaunchCommand())) { - ProcessBuilder builder = new ProcessBuilder(StringUtils.tokenize(options.getPreLaunchCommand(), getEnvVars())).directory(runDirectory.toFile()); - builder.environment().putAll(getEnvVars()); + ProcessBuilder builder = new ProcessBuilder(StringUtils.tokenize(options.getPreLaunchCommand(), getEnvVars(nativeFolder))).directory(runDirectory.toFile()); + builder.environment().putAll(getEnvVars(nativeFolder)); SystemUtils.callExternalProcess(builder); } @@ -578,7 +590,7 @@ public ManagedProcess launch() throws IOException, InterruptedException { Path appdata = options.getGameDir().toAbsolutePath().getParent(); if (appdata != null) builder.environment().put("APPDATA", appdata.toString()); - builder.environment().putAll(getEnvVars()); + builder.environment().putAll(getEnvVars(nativeFolder)); process = builder.start(); } catch (IOException e) { throw new ProcessCreationException(e); @@ -586,12 +598,35 @@ public ManagedProcess launch() throws IOException, InterruptedException { ManagedProcess p = new ManagedProcess(process, rawCommandLine); if (listener != null) - startMonitors(p, listener, command.encoding, daemon); + startMonitors(p, nativeFolder, listener, command.encoding, daemon); return p; } - private Map getEnvVars() { + private @Nullable Path findVulkanDescriptorFile(List dirs, String driverNameBase) { + String archName = switch (options.getJava().getArchitecture()) { + case X86 -> "i686"; + case X86_64 -> "x86_64"; + default -> options.getJava().getArchitecture().getCheckedName(); + }; + + for (Path dir : dirs) { + if (Files.isDirectory(dir)) { + Path file = dir.resolve(driverNameBase + "." + archName + ".json"); + if (Files.isRegularFile(file)) + return file; + + file = dir.resolve(driverNameBase + ".json"); + if (Files.isRegularFile(file)) + return file; + } + } + + return null; + } + + private Map getEnvVars(Path nativeFolder) { String versionName = Optional.ofNullable(options.getVersionName()).orElse(version.getId()); + Map env = new LinkedHashMap<>(); env.put("INST_NAME", versionName); env.put("INST_ID", versionName); @@ -602,15 +637,26 @@ private Map getEnvVars() { Renderer renderer = options.getRenderer(); if (renderer != Renderer.DEFAULT) { if (OperatingSystem.CURRENT_OS == OperatingSystem.WINDOWS) { - if (renderer != Renderer.LLVMPIPE) - env.put("GALLIUM_DRIVER", renderer.name().toLowerCase(Locale.ROOT)); + if (renderer.getMesaDriverName() != null) { + if (renderer.getApi() == GraphicsAPI.OPENGL && renderer != Renderer.LLVMPIPE) + env.put("GALLIUM_DRIVER", renderer.getMesaDriverName()); + + if (renderer.getApi() == GraphicsAPI.VULKAN) { + String icdFile = FileUtils.getAbsolutePath(nativeFolder.resolve("mesa-loader/" + renderer.getIcdFileName())); + + env.put("VK_ICD_FILENAMES", icdFile); + env.put("VK_DRIVER_FILES", icdFile); + } + } } else if (OperatingSystem.CURRENT_OS == OperatingSystem.LINUX) { - env.put("__GLX_VENDOR_LIBRARY_NAME", "mesa"); switch (renderer) { - case LLVMPIPE: + case LLVMPIPE: { + env.put("__GLX_VENDOR_LIBRARY_NAME", "mesa"); env.put("LIBGL_ALWAYS_SOFTWARE", "1"); break; - case ZINK: + } + case ZINK: { + env.put("__GLX_VENDOR_LIBRARY_NAME", "mesa"); env.put("MESA_LOADER_DRIVER_OVERRIDE", "zink"); /* * The amdgpu DDX is missing support for modifiers, causing Zink to fail. @@ -620,6 +666,19 @@ private Map getEnvVars() { */ env.put("LIBGL_KOPPER_DRI2", "1"); break; + } + case LAVAPIPE: { + Path lvp = findVulkanDescriptorFile( + List.of(Path.of("/usr/share/vulkan/icd.d"), Path.of("/etc/vulkan/icd.d")), + "lvp_icd" + ); + if (lvp != null) { + env.put("VK_ICD_FILENAMES", FileUtils.getAbsolutePath(lvp)); + env.put("VK_DRIVER_FILES", FileUtils.getAbsolutePath(lvp)); + } + + break; + } } } } @@ -677,7 +736,7 @@ else if (!isWindows && !(scriptExtension.equals("sh") || scriptExtension.equals( final Command commandLine = generateCommandLine(nativeFolder); final String command = usePowerShell ? null : commandLine.commandLine.toString(); - Map envVars = getEnvVars(); + Map envVars = getEnvVars(nativeFolder); if (isWindows && !usePowerShell) { // https://stackoverflow.com/a/28452546 @@ -820,7 +879,7 @@ else if (!isWindows && !(scriptExtension.equals("sh") || scriptExtension.equals( throw new ExecutionPolicyLimitException(); } - private void startMonitors(ManagedProcess managedProcess, ProcessListener processListener, Charset encoding, boolean isDaemon) { + private void startMonitors(ManagedProcess managedProcess, Path nativeFolder, ProcessListener processListener, Charset encoding, boolean isDaemon) { processListener.setProcess(managedProcess); Thread stdout = Lang.thread(new StreamPump(managedProcess.getProcess().getInputStream(), it -> { processListener.onLog(it, false); @@ -837,8 +896,8 @@ private void startMonitors(ManagedProcess managedProcess, ProcessListener proces if (StringUtils.isNotBlank(options.getPostExitCommand())) { try { - ProcessBuilder builder = new ProcessBuilder(StringUtils.tokenize(options.getPostExitCommand(), getEnvVars())).directory(options.getGameDir().toFile()); - builder.environment().putAll(getEnvVars()); + ProcessBuilder builder = new ProcessBuilder(StringUtils.tokenize(options.getPostExitCommand(), getEnvVars(nativeFolder))).directory(options.getGameDir().toFile()); + builder.environment().putAll(getEnvVars(nativeFolder)); SystemUtils.callExternalProcess(builder); } catch (Throwable e) { LOG.warning("An Exception happened while running exit command.", e);