Compare commits
13
Commits
v3.13.0.339
...
v3.13.2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c9b1cfec5a | ||
|
|
82460ebbcb | ||
|
|
3e8a9bf1c4 | ||
|
|
a2db925e86 | ||
|
|
fe70cc3daf | ||
|
|
5991112cd8 | ||
|
|
084d91c62a | ||
|
|
4963f7a5f0 | ||
|
|
f42d5219c2 | ||
|
|
101a9b5d5e | ||
|
|
7cd79a4e2a | ||
|
|
30c3acfb23 | ||
|
|
31f04db555 |
No files matched your search
+5
-1
@@ -64,4 +64,8 @@ language-subtag-registry
|
||||
# test
|
||||
/hmcl.json
|
||||
/.hmcl.json
|
||||
/.hmcl/
|
||||
/.hmcl/
|
||||
|
||||
# AI
|
||||
/.gemini/
|
||||
!/.gemini/config.yaml
|
||||
@@ -430,6 +430,7 @@ public final class HMCLGameRepository extends DefaultGameRepository {
|
||||
.setNativesDirType(vs.getNativesDirType())
|
||||
.setNativesDir(vs.getNativesDir())
|
||||
.setProcessPriority(vs.getProcessPriority())
|
||||
.setGraphicsBackend(vs.getGraphicsBackend())
|
||||
.setRenderer(vs.getRenderer())
|
||||
.setEnableDebugLogOutput(vs.isEnableDebugLogOutput())
|
||||
.setUseNativeGLFW(vs.isUseNativeGLFW())
|
||||
|
||||
@@ -173,27 +173,27 @@ public final class LauncherHelper {
|
||||
}
|
||||
}),
|
||||
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 (OperatingSystem.CURRENT_OS != OperatingSystem.WINDOWS
|
||||
|| !(setting.getRenderer() instanceof Renderer.Driver renderer)
|
||||
|| renderer.mesaDriverName() == null)
|
||||
return null;
|
||||
|
||||
String agent = FileUtils.getAbsolutePath(file) + "=" + renderer.name().toLowerCase(Locale.ROOT);
|
||||
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 (GameLibrariesTask.shouldDownloadLibrary(repository, version.get(), lib, integrityCheck)) {
|
||||
return new LibraryDownloadTask(dependencyManager, file, lib)
|
||||
.thenRunAsync(() -> javaAgents.add(agent));
|
||||
} else {
|
||||
javaAgents.add(agent);
|
||||
return null;
|
||||
}
|
||||
String agent = FileUtils.getAbsolutePath(file) + "=" + renderer.mesaDriverName();
|
||||
|
||||
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 +232,7 @@ public final class LauncherHelper {
|
||||
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));
|
||||
|
||||
@@ -603,6 +603,20 @@ public final class VersionSetting implements Cloneable, Observable {
|
||||
processPriorityProperty.set(processPriority);
|
||||
}
|
||||
|
||||
private final ObjectProperty<GraphicsAPI> graphicsBackend = new SimpleObjectProperty<>(this, "graphicsBackend", GraphicsAPI.DEFAULT);
|
||||
|
||||
public ObjectProperty<GraphicsAPI> graphicsBackendProperty() {
|
||||
return graphicsBackend;
|
||||
}
|
||||
|
||||
public GraphicsAPI getGraphicsBackend() {
|
||||
return graphicsBackendProperty().get();
|
||||
}
|
||||
|
||||
public void setGraphicsBackend(GraphicsAPI api) {
|
||||
graphicsBackendProperty().set(api);
|
||||
}
|
||||
|
||||
private final ObjectProperty<Renderer> rendererProperty = new SimpleObjectProperty<>(this, "renderer", Renderer.DEFAULT);
|
||||
|
||||
public Renderer getRenderer() {
|
||||
@@ -820,8 +834,9 @@ public final class VersionSetting implements Cloneable, Observable {
|
||||
}
|
||||
obj.addProperty("java", java);
|
||||
|
||||
obj.addProperty("graphicsBackend", src.getGraphicsBackend().name());
|
||||
obj.addProperty("renderer", src.getRenderer().name());
|
||||
if (src.getRenderer() == Renderer.LLVMPIPE)
|
||||
if (src.getRenderer() == Renderer.OpenGL.LLVMPIPE)
|
||||
obj.addProperty("useSoftwareRenderer", true);
|
||||
|
||||
return obj;
|
||||
@@ -894,16 +909,19 @@ public final class VersionSetting implements Cloneable, Observable {
|
||||
}
|
||||
|
||||
vs.setRenderer(Optional.ofNullable(obj.get("renderer")).map(JsonElement::getAsString)
|
||||
.map(Renderer::of).orElseGet(() -> {
|
||||
boolean useSoftwareRenderer = Optional.ofNullable(obj.get("useSoftwareRenderer")).map(JsonElement::getAsBoolean).orElse(false);
|
||||
return useSoftwareRenderer ? Renderer.OpenGL.LLVMPIPE : Renderer.DEFAULT;
|
||||
}));
|
||||
|
||||
vs.setGraphicsBackend(Optional.ofNullable(obj.get("graphicsBackend")).map(JsonElement::getAsString)
|
||||
.flatMap(name -> {
|
||||
try {
|
||||
return Optional.of(Renderer.valueOf(name.toUpperCase(Locale.ROOT)));
|
||||
return Optional.of(GraphicsAPI.valueOf(name.toUpperCase(Locale.ROOT)));
|
||||
} catch (IllegalArgumentException ignored) {
|
||||
return Optional.empty();
|
||||
}
|
||||
}).orElseGet(() -> {
|
||||
boolean useSoftwareRenderer = Optional.ofNullable(obj.get("useSoftwareRenderer")).map(JsonElement::getAsBoolean).orElse(false);
|
||||
return useSoftwareRenderer ? Renderer.LLVMPIPE : Renderer.DEFAULT;
|
||||
}));
|
||||
}).orElseGet(() -> vs.getRenderer() instanceof Renderer.Driver renderer ? renderer.api() : GraphicsAPI.DEFAULT));
|
||||
|
||||
return vs;
|
||||
}
|
||||
|
||||
@@ -170,6 +170,7 @@ public final class LogWindow extends Stage {
|
||||
LogWindowImpl() {
|
||||
getStyleClass().add("log-window");
|
||||
|
||||
listView.getStyleClass().add("no-horizontal-scrollbar");
|
||||
listView.getProperties().put("no-smooth-scrolling", true);
|
||||
listView.setItems(FXCollections.observableList(new CircularArrayList<>(logs.size())));
|
||||
|
||||
|
||||
@@ -164,6 +164,9 @@ public class AccountListItem extends RadioButton {
|
||||
if (skinImg.isError()) {
|
||||
throw new InvalidSkinException("Failed to read skin image", skinImg.getException());
|
||||
}
|
||||
if (skinImg.getWidth() != 64 || (skinImg.getHeight() != 32 && skinImg.getHeight() != 64)) {
|
||||
throw new InvalidSkinException("Invalid skin size");
|
||||
}
|
||||
NormalizedSkin skin = new NormalizedSkin(skinImg);
|
||||
String model = skin.isSlim() ? "slim" : "";
|
||||
LOG.info("Uploading skin [" + selectedFile + "], model [" + model + "]");
|
||||
|
||||
@@ -28,6 +28,7 @@ import javafx.collections.FXCollections;
|
||||
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<T> extends LineButton {
|
||||
private static final PseudoClass SELECTED_PSEUDO_CLASS = PseudoClass.getPseudoClass("selected");
|
||||
|
||||
private JFXPopup popup;
|
||||
@SuppressWarnings({"FieldCanBeLocal", "unused"})
|
||||
private ObservableList<Node> popupItems; // keep a reference
|
||||
|
||||
public LineSelectButton() {
|
||||
this.getStyleClass().add(DEFAULT_STYLE_CLASS);
|
||||
@@ -87,7 +90,7 @@ public class LineSelectButton<T> extends LineButton {
|
||||
|
||||
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();
|
||||
|
||||
@@ -19,8 +19,8 @@ package org.jackhuang.hmcl.ui.construct;
|
||||
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.scene.layout.StackPane;
|
||||
import javafx.scene.paint.Color;
|
||||
import javafx.scene.shape.Rectangle;
|
||||
import org.jackhuang.hmcl.theme.Themes;
|
||||
|
||||
public class MenuSeparator extends StackPane {
|
||||
|
||||
@@ -28,7 +28,7 @@ public class MenuSeparator extends StackPane {
|
||||
Rectangle rect = new Rectangle();
|
||||
rect.widthProperty().bind(widthProperty().add(-14));
|
||||
rect.setHeight(1);
|
||||
rect.setFill(Color.GRAY);
|
||||
rect.fillProperty().bind(Themes.colorSchemeProperty().getOutlineVariant());
|
||||
maxHeightProperty().set(10);
|
||||
setPadding(new Insets(3));
|
||||
getChildren().setAll(rect);
|
||||
|
||||
@@ -147,6 +147,10 @@ public class TwoLineListItem extends VBox {
|
||||
subtitleProperty().set(subtitle);
|
||||
}
|
||||
|
||||
public HBox getFirstLine() {
|
||||
return firstLine;
|
||||
}
|
||||
|
||||
public Label getTitleLabel() {
|
||||
return lblTitle;
|
||||
}
|
||||
@@ -168,6 +172,7 @@ public class TwoLineListItem extends VBox {
|
||||
Bindings.bindContent(tagsBox.getChildren(), tags);
|
||||
|
||||
var scrollPane = new ScrollPane(tagsBox);
|
||||
scrollPane.setMinSize(0, 0);
|
||||
HBox.setHgrow(scrollPane, Priority.ALWAYS);
|
||||
lblTitle.setMinWidth(Label.USE_PREF_SIZE);
|
||||
scrollPane.setHbarPolicy(ScrollPane.ScrollBarPolicy.NEVER);
|
||||
|
||||
@@ -353,12 +353,7 @@ public class DecoratorSkin extends SkinBase<Decorator> {
|
||||
refreshNavButton.onActionProperty().bind(skinnable.onRefreshNavButtonActionProperty());
|
||||
skinnable.forbidDraggingWindow(refreshNavButton);
|
||||
|
||||
Rectangle separator = new Rectangle();
|
||||
separator.visibleProperty().bind(refreshNavButton.visibleProperty());
|
||||
separator.heightProperty().bind(navBar.heightProperty());
|
||||
separator.setFill(Color.GRAY);
|
||||
|
||||
navRight.getChildren().setAll(refreshNavButton, separator);
|
||||
navRight.getChildren().setAll(refreshNavButton);
|
||||
navBar.setRight(navRight);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ import javafx.beans.property.SimpleObjectProperty;
|
||||
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.i18n.I18n;
|
||||
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<NativesDirectoryType> nativesDirItem;
|
||||
private final MultiFileItem.FileOption<NativesDirectoryType> nativesDirCustomOption;
|
||||
private final LineSelectButton<GraphicsAPI> graphicsBackendPane;
|
||||
private final LineSelectButton<Renderer> rendererPane;
|
||||
|
||||
public AdvancedVersionSettingPage(Profile profile, @Nullable String versionId, VersionSetting versionSetting) {
|
||||
@@ -79,6 +82,10 @@ public final class AdvancedVersionSettingPage extends StackPane implements Decor
|
||||
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 final class AdvancedVersionSettingPage extends StackPane implements Decor
|
||||
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,23 @@ public final class AdvancedVersionSettingPage extends StackPane implements Decor
|
||||
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);
|
||||
|
||||
FXUtils.onChangeAndOperate(graphicsBackendPane.valueProperty(), backend -> {
|
||||
if (backend == null) { // unbind
|
||||
return;
|
||||
}
|
||||
|
||||
rendererPane.setItems(Renderer.getSupported(backend));
|
||||
if (backend == GraphicsAPI.DEFAULT) {
|
||||
rendererPane.setDisable(true);
|
||||
rendererPane.setValue(Renderer.DEFAULT);
|
||||
} else {
|
||||
rendererPane.setDisable(false);
|
||||
if (!(rendererPane.getValue() instanceof Renderer.Driver driver) || driver.api() != backend)
|
||||
rendererPane.setValue(Renderer.DEFAULT);
|
||||
}
|
||||
});
|
||||
|
||||
noJVMArgsPane = new LineToggleButton();
|
||||
noJVMArgsPane.setTitle(i18n("settings.advanced.no_jvm_args"));
|
||||
@@ -219,7 +261,7 @@ public final class AdvancedVersionSettingPage extends StackPane implements Decor
|
||||
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 +294,7 @@ public final class AdvancedVersionSettingPage extends StackPane implements Decor
|
||||
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 +337,7 @@ public final class AdvancedVersionSettingPage extends StackPane implements Decor
|
||||
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());
|
||||
|
||||
@@ -225,8 +225,10 @@ public class DownloadPage extends Control implements DecoratorPage {
|
||||
content.setSubtitle(getSkinnable().addon.getDescription());
|
||||
content.getSubtitleLabel().setWrapText(true);
|
||||
getSkinnable().addon.getCategories().stream()
|
||||
.filter(category -> getSkinnable().page.shouldDisplayCategory(category))
|
||||
.map(category -> getSkinnable().page.getLocalizedCategory(category))
|
||||
.forEach(content::addTag);
|
||||
content.getFirstLine().setMinWidth(0);
|
||||
descriptionPane.getChildren().add(content);
|
||||
|
||||
if (getSkinnable().mod != null) {
|
||||
|
||||
@@ -189,15 +189,16 @@ public final class NativePatcher {
|
||||
return version.setLibraries(newLibraries);
|
||||
}
|
||||
|
||||
public static @Nullable Library getWindowsMesaLoader(@NotNull JavaRuntime javaVersion, @NotNull Renderer renderer, @NotNull OSVersion windowsVersion) {
|
||||
/// @see <a href="https://github.com/HMCL-dev/mesa-loader-windows">Java Mesa Loader for Windows</a>
|
||||
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");
|
||||
if (renderer == Renderer.OpenGL.LLVMPIPE)
|
||||
return getNatives(java.getPlatform()).get("software-renderer-loader");
|
||||
else
|
||||
return null;
|
||||
} else {
|
||||
|
||||
@@ -96,6 +96,10 @@ public class Translator_lzh extends Translator {
|
||||
case GA -> {
|
||||
// do nothing
|
||||
}
|
||||
case SNAPSHOT -> {
|
||||
builder.append("之快照");
|
||||
appendDigitByDigit(builder, release.getEaVersion().toString());
|
||||
}
|
||||
case PRE_RELEASE -> {
|
||||
builder.append("之預");
|
||||
appendDigitByDigit(builder, release.getEaVersion().toString());
|
||||
|
||||
@@ -414,7 +414,7 @@
|
||||
}
|
||||
|
||||
.installer-item-wrapper .installer-item:list-item > .installer-item-name {
|
||||
-fx-pref-width: 80px;
|
||||
-fx-pref-width: 100px;
|
||||
}
|
||||
|
||||
.installer-item-wrapper .installer-item:list-item > .installer-item-status {
|
||||
|
||||
@@ -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/<instance name>/", 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,47 @@ 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.d3d12=Mesa D3D12
|
||||
settings.advanced.renderer.d3d12.desc=DirectX 12 (Poor performance and compatibility)
|
||||
settings.advanced.renderer.default.desc=Use System Default
|
||||
# Vulkan Renderers
|
||||
settings.advanced.renderer.lavapipe=Mesa Lavapipe
|
||||
settings.advanced.renderer.lavapipe.desc=Software Vulkan Renderer
|
||||
settings.advanced.renderer.dozen=Mesa Dozen
|
||||
settings.advanced.renderer.dozen.desc=Vulkan renderer based on DirectX 12 (Experimental)
|
||||
settings.advanced.renderer.nvidia_vulkan=NVIDIA
|
||||
settings.advanced.renderer.nvidia_vulkan.desc=Official Vulkan driver for NVIDIA GPUs
|
||||
settings.advanced.renderer.nvidia_nvk=NVIDIA (NVK)
|
||||
settings.advanced.renderer.nvidia_nvk.desc=Open-source Vulkan driver for NVIDIA GPUs
|
||||
settings.advanced.renderer.amdvlk=AMD
|
||||
settings.advanced.renderer.amdvlk.desc=Official Vulkan driver for AMD GPUs
|
||||
settings.advanced.renderer.amd_radv=AMD (RADV)
|
||||
settings.advanced.renderer.amd_radv.desc=Open-source Vulkan driver for AMD GPUs
|
||||
settings.advanced.renderer.intel_vulkan=Intel
|
||||
settings.advanced.renderer.intel_vulkan.desc=Official Vulkan driver for Intel GPUs
|
||||
settings.advanced.renderer.intel_anv=Intel (ANV)
|
||||
settings.advanced.renderer.intel_anv.desc=Open-source Vulkan driver for Intel GPUs
|
||||
settings.advanced.renderer.intel_hasvk=Intel (HASVK)
|
||||
settings.advanced.renderer.intel_hasvk.desc=Open-source Vulkan driver for older Intel GPUs
|
||||
settings.advanced.renderer.qualcomm=Qualcomm
|
||||
settings.advanced.renderer.qualcomm.desc=Official Vulkan driver for Qualcomm Adreno GPUs
|
||||
settings.advanced.renderer.turnip=Qualcomm (Turnip)
|
||||
settings.advanced.renderer.turnip.desc=Open-source Vulkan driver for Qualcomm Adreno GPUs
|
||||
settings.advanced.renderer.moltenvk=MoltenVK
|
||||
settings.advanced.renderer.moltenvk.desc=Vulkan renderer based on Metal
|
||||
settings.advanced.renderer.kosmickrisp=KosmicKrisp
|
||||
settings.advanced.renderer.kosmickrisp.desc=Vulkan renderer based on Metal 4 (Experimental)
|
||||
settings.advanced.renderer.powervr=PowerVR
|
||||
settings.advanced.renderer.powervr.desc=Open-source Vulkan driver for PowerVR GPUs (Experimental)
|
||||
settings.advanced.renderer.panvk=PanVK
|
||||
settings.advanced.renderer.panvk.desc=Open-source Vulkan driver for Arm Mali GPUs (Experimental)
|
||||
settings.advanced.renderer.v3dv=V3DV
|
||||
settings.advanced.renderer.v3dv.desc=Open-source Vulkan driver for Raspberry Pi 4/5 GPUs (Experimental)
|
||||
# OpenGL Renderers
|
||||
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.d3d12=Mesa D3D12
|
||||
settings.advanced.renderer.d3d12.desc=OpenGL renderer based on DirectX 12
|
||||
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
|
||||
|
||||
@@ -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=إعدادات غير مطبّقة على النظام الحالي
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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=サポートされていないシステムオプション
|
||||
|
||||
@@ -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=不逮今之械網所選
|
||||
|
||||
@@ -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=Настройки, не применимые к текущей системе
|
||||
|
||||
@@ -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=Налаштування, що не застосовуються до поточної системи
|
||||
|
||||
@@ -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 虛擬機設定
|
||||
@@ -1164,15 +1173,49 @@ settings.advanced.process_priority.high=高
|
||||
settings.advanced.process_priority.high.desc=優先保證遊戲執行,但可能會導致其他程式卡頓
|
||||
settings.advanced.post_exit_command=遊戲結束後執行指令
|
||||
settings.advanced.post_exit_command.prompt=將在遊戲結束後呼叫使用
|
||||
settings.advanced.renderer=繪製器
|
||||
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.default.desc=使用系統預設繪製器
|
||||
# Vulkan Renderers
|
||||
settings.advanced.renderer.lavapipe=Mesa Lavapipe
|
||||
settings.advanced.renderer.lavapipe.desc=軟體 Vulkan 繪製器
|
||||
settings.advanced.renderer.dozen=Mesa Dozen
|
||||
settings.advanced.renderer.dozen.desc=基於 DirectX 12 的 Vulkan 繪製器 (實驗性)
|
||||
settings.advanced.renderer.nvidia_vulkan=NVIDIA
|
||||
settings.advanced.renderer.nvidia_vulkan.desc=NVIDIA GPU 的官方 Vulkan 驅動
|
||||
settings.advanced.renderer.nvidia_nvk=NVIDIA (NVK)
|
||||
settings.advanced.renderer.nvidia_nvk.desc=NVIDIA GPU 的開源 Vulkan 驅動
|
||||
settings.advanced.renderer.amdvlk=AMD
|
||||
settings.advanced.renderer.amdvlk.desc=AMD GPU 的官方 Vulkan 驅動
|
||||
settings.advanced.renderer.amd_radv=AMD (RADV)
|
||||
settings.advanced.renderer.amd_radv.desc=AMD GPU 的開源 Vulkan 驅動
|
||||
settings.advanced.renderer.intel_vulkan=Intel
|
||||
settings.advanced.renderer.intel_vulkan.desc=Intel GPU 的官方 Vulkan 驅動
|
||||
settings.advanced.renderer.intel_anv=Intel (ANV)
|
||||
settings.advanced.renderer.intel_anv.desc=Intel GPU 的開源 Vulkan 驅動
|
||||
settings.advanced.renderer.intel_hasvk=Intel (HASVK)
|
||||
settings.advanced.renderer.intel_hasvk.desc=Intel 舊 GPU 的開源 Vulkan 驅動
|
||||
settings.advanced.renderer.qualcomm=高通
|
||||
settings.advanced.renderer.qualcomm.desc=高通 Adreno GPU 的官方 Vulkan 驅動
|
||||
settings.advanced.renderer.turnip=高通 (Turnip)
|
||||
settings.advanced.renderer.turnip.desc=高通 Adreno GPU 的開源 Vulkan 驅動
|
||||
settings.advanced.renderer.moltenvk=MoltenVK
|
||||
settings.advanced.renderer.moltenvk.desc=基於 Metal 的 Vulkan 繪製器
|
||||
settings.advanced.renderer.kosmickrisp=KosmicKrisp
|
||||
settings.advanced.renderer.kosmickrisp.desc=基於 Metal 4 的 Vulkan 繪製器 (實驗性)
|
||||
settings.advanced.renderer.powervr=PowerVR
|
||||
settings.advanced.renderer.powervr.desc=PowerVR GPU 的開源 Vulkan 驅動 (實驗性)
|
||||
settings.advanced.renderer.panvk=PanVK
|
||||
settings.advanced.renderer.panvk.desc=Arm Mali GPU 的開源 Vulkan 驅動 (實驗性)
|
||||
settings.advanced.renderer.v3dv=V3DV
|
||||
settings.advanced.renderer.v3dv.desc=樹莓派 4/5 GPU 的開源 Vulkan 驅動 (實驗性)
|
||||
# OpenGL Renderers
|
||||
settings.advanced.renderer.llvmpipe=Mesa LLVMpipe
|
||||
settings.advanced.renderer.llvmpipe.desc=軟繪製器 (效能較差,相容性最好)
|
||||
settings.advanced.renderer.llvmpipe.desc=軟體 OpenGL 繪製器
|
||||
settings.advanced.renderer.d3d12=Mesa D3D12
|
||||
settings.advanced.renderer.d3d12.desc=基於 DirectX 12 的 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=不適用於目前系統的選項
|
||||
|
||||
@@ -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 虚拟机设置
|
||||
@@ -1169,15 +1178,49 @@ settings.advanced.process_priority.high=高
|
||||
settings.advanced.process_priority.high.desc=优先保证游戏运行,但可能会导致其他程序卡顿
|
||||
settings.advanced.post_exit_command=游戏结束后执行命令
|
||||
settings.advanced.post_exit_command.prompt=将在游戏结束后调用
|
||||
settings.advanced.renderer=渲染器
|
||||
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.default.desc=使用系统默认渲染器
|
||||
# Vulkan Renderers
|
||||
settings.advanced.renderer.lavapipe=Mesa Lavapipe
|
||||
settings.advanced.renderer.lavapipe.desc=软件 Vulkan 渲染器
|
||||
settings.advanced.renderer.dozen=Mesa Dozen
|
||||
settings.advanced.renderer.dozen.desc=基于 DirectX 12 的 Vulkan 渲染器 (实验性)
|
||||
settings.advanced.renderer.nvidia_vulkan=NVIDIA
|
||||
settings.advanced.renderer.nvidia_vulkan.desc=NVIDIA GPU 的官方 Vulkan 驱动
|
||||
settings.advanced.renderer.nvidia_nvk=NVIDIA (NVK)
|
||||
settings.advanced.renderer.nvidia_nvk.desc=NVIDIA GPU 的开源 Vulkan 驱动
|
||||
settings.advanced.renderer.amdvlk=AMD
|
||||
settings.advanced.renderer.amdvlk.desc=AMD GPU 的官方 Vulkan 驱动
|
||||
settings.advanced.renderer.amd_radv=AMD (RADV)
|
||||
settings.advanced.renderer.amd_radv.desc=AMD GPU 的开源 Vulkan 驱动
|
||||
settings.advanced.renderer.intel_vulkan=Intel
|
||||
settings.advanced.renderer.intel_vulkan.desc=Intel GPU 的官方 Vulkan 驱动
|
||||
settings.advanced.renderer.intel_anv=Intel (ANV)
|
||||
settings.advanced.renderer.intel_anv.desc=Intel GPU 的开源 Vulkan 驱动
|
||||
settings.advanced.renderer.intel_hasvk=Intel (HASVK)
|
||||
settings.advanced.renderer.intel_hasvk.desc=Intel 旧 GPU 的开源 Vulkan 驱动
|
||||
settings.advanced.renderer.qualcomm=高通
|
||||
settings.advanced.renderer.qualcomm.desc=高通 Adreno GPU 的官方 Vulkan 驱动
|
||||
settings.advanced.renderer.turnip=高通 (Turnip)
|
||||
settings.advanced.renderer.turnip.desc=高通 Adreno GPU 的开源 Vulkan 驱动
|
||||
settings.advanced.renderer.moltenvk=MoltenVK
|
||||
settings.advanced.renderer.moltenvk.desc=基于 Metal 的 Vulkan 渲染器
|
||||
settings.advanced.renderer.kosmickrisp=KosmicKrisp
|
||||
settings.advanced.renderer.kosmickrisp.desc=基于 Metal 4 的 Vulkan 渲染器 (实验性)
|
||||
settings.advanced.renderer.powervr=PowerVR
|
||||
settings.advanced.renderer.powervr.desc=PowerVR GPU 的开源 Vulkan 驱动 (实验性)
|
||||
settings.advanced.renderer.panvk=PanVK
|
||||
settings.advanced.renderer.panvk.desc=Arm Mali GPU 的开源 Vulkan 驱动 (实验性)
|
||||
settings.advanced.renderer.v3dv=V3DV
|
||||
settings.advanced.renderer.v3dv.desc=树莓派 4/5 GPU 的开源 Vulkan 驱动 (实验性)
|
||||
# OpenGL Renderers
|
||||
settings.advanced.renderer.llvmpipe=Mesa LLVMpipe
|
||||
settings.advanced.renderer.llvmpipe.desc=软渲染器 (性能较差,兼容性最好)
|
||||
settings.advanced.renderer.llvmpipe.desc=软件 OpenGL 渲染器
|
||||
settings.advanced.renderer.d3d12=Mesa D3D12
|
||||
settings.advanced.renderer.d3d12.desc=基于 DirectX 12 的 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=不适用于当前系统的选项
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher
|
||||
* Copyright (C) 2026 huangyuhui <huanghongxun2008@126.com> 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -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 class LaunchOptions implements Serializable {
|
||||
return processPriority;
|
||||
}
|
||||
|
||||
public Renderer getRenderer() {
|
||||
public @NotNull GraphicsAPI getGraphicsBackend() {
|
||||
return graphicsBackend;
|
||||
}
|
||||
|
||||
public @NotNull Renderer getRenderer() {
|
||||
return renderer;
|
||||
}
|
||||
|
||||
@@ -435,8 +440,13 @@ public class LaunchOptions implements Serializable {
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@@ -17,9 +17,529 @@
|
||||
*/
|
||||
package org.jackhuang.hmcl.game;
|
||||
|
||||
public enum Renderer {
|
||||
DEFAULT,
|
||||
ZINK,
|
||||
LLVMPIPE,
|
||||
D3D12
|
||||
import org.jackhuang.hmcl.util.Lang;
|
||||
import org.jackhuang.hmcl.util.io.FileUtils;
|
||||
import org.jackhuang.hmcl.util.platform.*;
|
||||
import org.jackhuang.hmcl.util.platform.hardware.GraphicsCard;
|
||||
import org.jackhuang.hmcl.util.platform.hardware.HardwareVendor;
|
||||
import org.jackhuang.hmcl.util.platform.macos.HomebrewUtils;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNullByDefault;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.annotations.Unmodifiable;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
|
||||
|
||||
/// @author Glavo
|
||||
@NotNullByDefault
|
||||
public sealed interface Renderer permits Renderer.Default, Renderer.Driver, Renderer.Unknown {
|
||||
|
||||
/// Default renderer.
|
||||
Default DEFAULT = new Default();
|
||||
|
||||
/// Parse a renderer from a string.
|
||||
static Renderer of(String name) {
|
||||
if ("DEFAULT".equalsIgnoreCase(name) || name.isBlank())
|
||||
return DEFAULT;
|
||||
|
||||
String upper = name.toUpperCase(Locale.ROOT).trim();
|
||||
|
||||
try {
|
||||
return OpenGL.valueOf(upper);
|
||||
} catch (IllegalArgumentException ignored) {
|
||||
}
|
||||
|
||||
try {
|
||||
return Vulkan.valueOf(upper);
|
||||
} catch (IllegalArgumentException ignored) {
|
||||
}
|
||||
|
||||
return new Unknown(name);
|
||||
}
|
||||
|
||||
/// Get all supported renderers for a given graphics API.
|
||||
static @Unmodifiable List<Renderer> getSupported(GraphicsAPI api) {
|
||||
return switch (api) {
|
||||
case DEFAULT -> List.of(DEFAULT);
|
||||
case VULKAN -> Vulkan.Holder.SUPPORTED;
|
||||
case OPENGL -> OpenGL.SUPPORTED;
|
||||
};
|
||||
}
|
||||
|
||||
String name();
|
||||
|
||||
final class Default implements Renderer {
|
||||
private Default() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return "DEFAULT";
|
||||
}
|
||||
}
|
||||
|
||||
sealed interface Driver extends Renderer permits Vulkan, OpenGL {
|
||||
|
||||
/// The graphics API that this driver belongs to.
|
||||
GraphicsAPI api();
|
||||
|
||||
/// If this driver can be loaded via [mesa-loader-windows](https://github.com/HMCL-dev/mesa-loader-windows),
|
||||
/// return the driver name, otherwise return `null`.
|
||||
@Contract(pure = true)
|
||||
default @Nullable String mesaDriverName() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
enum Vulkan implements Driver {
|
||||
/// Mesa Lavapipe driver.
|
||||
///
|
||||
/// It is a software rasterizer.
|
||||
///
|
||||
/// @see <a href="https://docs.mesa3d.org/drivers/llvmpipe.html">LLVMpipe - The Mesa 3D Graphics Library</a>
|
||||
LAVAPIPE("lvp") {
|
||||
@Override
|
||||
public String mesaDriverName() {
|
||||
return "lavapipe";
|
||||
}
|
||||
},
|
||||
|
||||
/// Mesa Dozen driver.
|
||||
///
|
||||
/// It is a Vulkan driver based on DirectX 12.
|
||||
///
|
||||
/// ## Note
|
||||
/// 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("dzn") {
|
||||
@Override
|
||||
public boolean isSupported(Platform platform, @Nullable List<GraphicsCard> cards) {
|
||||
return platform.os() == OperatingSystem.WINDOWS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String mesaDriverName() {
|
||||
return "dzn";
|
||||
}
|
||||
},
|
||||
|
||||
/// NVIDIA Vulkan driver.
|
||||
///
|
||||
/// It is a Vulkan driver for NVIDIA GPUs.
|
||||
///
|
||||
/// @see <a href="https://developer.nvidia.com/vulkan">Vulkan Open Standard Modern GPU API | NVIDIA Developer</a>
|
||||
NVIDIA_VULKAN("nvidia") {
|
||||
@Override
|
||||
public boolean isSupported(Platform platform, @Nullable List<GraphicsCard> cards) {
|
||||
return Vulkan.hasCard(cards, HardwareVendor.NVIDIA);
|
||||
}
|
||||
},
|
||||
|
||||
/// Mesa NVK driver.
|
||||
///
|
||||
/// It is a Vulkan driver for NVIDIA GPUs.
|
||||
///
|
||||
/// @see <a href="https://docs.mesa3d.org/drivers/nvk.html">NVK - The Mesa 3D Graphics Library</a>
|
||||
NVIDIA_NVK("nouveau") {
|
||||
@Override
|
||||
public boolean isSupported(Platform platform, @Nullable List<GraphicsCard> cards) {
|
||||
return platform.os() == OperatingSystem.LINUX && Vulkan.hasCard(cards, HardwareVendor.NVIDIA);
|
||||
}
|
||||
},
|
||||
|
||||
/// AMD Open Source Driver for Vulkan
|
||||
///
|
||||
/// It is a Vulkan driver for AMD GPUs.
|
||||
///
|
||||
/// @see <a href="https://github.com/GPUOpen-Drivers/AMDVLK">GPUOpen-Drivers/AMDVLK - GitHub</a>
|
||||
AMDVLK("amd") {
|
||||
@Override
|
||||
public boolean isSupported(Platform platform, @Nullable List<GraphicsCard> cards) {
|
||||
return platform.os() == OperatingSystem.LINUX && Vulkan.hasCard(cards, HardwareVendor.AMD);
|
||||
}
|
||||
},
|
||||
|
||||
/// Mesa RADV driver.
|
||||
///
|
||||
/// It is a Vulkan driver for AMD GCN/RDNA GPUs.
|
||||
///
|
||||
/// @see <a href="https://docs.mesa3d.org/drivers/radv.html">RADV - The Mesa 3D Graphics Library</a>
|
||||
AMD_RADV("radeon") {
|
||||
@Override
|
||||
public boolean isSupported(Platform platform, @Nullable List<GraphicsCard> cards) {
|
||||
return Vulkan.hasCard(cards, HardwareVendor.AMD);
|
||||
}
|
||||
},
|
||||
|
||||
/// Intel Vulkan driver.
|
||||
///
|
||||
/// It is a Vulkan driver for Intel GPUs.
|
||||
INTEL_VULKAN("ig") {
|
||||
@Override
|
||||
public boolean isSupported(Platform platform, @Nullable List<GraphicsCard> cards) {
|
||||
return platform.os() == OperatingSystem.WINDOWS && Vulkan.hasCard(cards, HardwareVendor.INTEL);
|
||||
}
|
||||
},
|
||||
|
||||
/// Mesa ANV driver.
|
||||
///
|
||||
/// It is a Vulkan driver for Intel GPUs.
|
||||
///
|
||||
/// @see <a href="https://docs.mesa3d.org/drivers/anv.html">ANV - The Mesa 3D Graphics Library</a>
|
||||
INTEL_ANV("intel") {
|
||||
@Override
|
||||
public boolean isSupported(Platform platform, @Nullable List<GraphicsCard> cards) {
|
||||
return platform.os() != OperatingSystem.WINDOWS && Vulkan.hasCard(cards, HardwareVendor.INTEL);
|
||||
}
|
||||
},
|
||||
|
||||
/// Intel HASVK driver.
|
||||
///
|
||||
/// It is a Vulkan driver for Intel Gen7 (Ivy Bridge / Haswell) and Gen8 (Broadwell) graphics.
|
||||
///
|
||||
/// @see <a href="https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/18208">intel: split vulkan driver between gfx7/8 and above</a>
|
||||
INTEL_HASVK("intel_hasvk") {
|
||||
@Override
|
||||
public boolean isSupported(Platform platform, @Nullable List<GraphicsCard> cards) {
|
||||
return platform.os() != OperatingSystem.WINDOWS
|
||||
&& cards != null
|
||||
&& cards.stream().anyMatch(card -> card.getVendor() == HardwareVendor.INTEL && card.getName().startsWith("Intel HD Graphics "));
|
||||
}
|
||||
},
|
||||
|
||||
/// Qualcomm Vulkan driver.
|
||||
///
|
||||
/// It is a Vulkan driver for Qualcomm Adreno GPUs.
|
||||
QUALCOMM("qc") {
|
||||
@Override
|
||||
public boolean isSupported(Platform platform, @Nullable List<GraphicsCard> cards) {
|
||||
return platform.equals(Platform.WINDOWS_ARM64);
|
||||
}
|
||||
},
|
||||
|
||||
/// Mesa Turnip driver.
|
||||
///
|
||||
/// It is a Vulkan driver for Qualcomm Adreno GPUs.
|
||||
///
|
||||
/// @see <a href="https://docs.mesa3d.org/drivers/freedreno.html">Freedreno - The Mesa 3D Graphics Library</a>
|
||||
TURNIP("freedreno") {
|
||||
@Override
|
||||
public boolean isSupported(Platform platform, @Nullable List<GraphicsCard> cards) {
|
||||
return platform.os() != OperatingSystem.WINDOWS && platform.arch().isArm();
|
||||
}
|
||||
},
|
||||
|
||||
/// MoltenVK driver.
|
||||
///
|
||||
/// It is a Vulkan driver for macOS, iOS, tvOS, and visionOS.
|
||||
///
|
||||
/// @see <a href="https://github.com/KhronosGroup/MoltenVK">KhronosGroup/MoltenVK - GitHub</a>
|
||||
MOLTENVK("MoltenVK") {
|
||||
@Override
|
||||
public boolean isSupported(Platform platform, @Nullable List<GraphicsCard> cards) {
|
||||
return platform.os() == OperatingSystem.MACOS;
|
||||
}
|
||||
},
|
||||
|
||||
/// Mesa KosmicKrisp driver.
|
||||
///
|
||||
/// It is a Vulkan driver for Apple Silicon hardware.
|
||||
///
|
||||
/// @see <a href="https://docs.mesa3d.org/drivers/kosmickrisp.html">KosmicKrisp - The Mesa 3D Graphics Library</a>
|
||||
KOSMICKRISP("kosmickrisp_mesa") {
|
||||
@Override
|
||||
public boolean isSupported(Platform platform, @Nullable List<GraphicsCard> cards) {
|
||||
return platform.os() == OperatingSystem.MACOS && platform.arch() == Architecture.ARM64;
|
||||
}
|
||||
},
|
||||
|
||||
/// Mesa PowerVR driver.
|
||||
///
|
||||
/// It is a Vulkan driver for Imagination Technologies PowerVR GPUs.
|
||||
///
|
||||
/// @see <a href="https://docs.mesa3d.org/drivers/powervr.html">PowerVR - The Mesa 3D Graphics Library</a>
|
||||
POWERVR("powervr") {
|
||||
@Override
|
||||
public boolean isSupported(Platform platform, @Nullable List<GraphicsCard> cards) {
|
||||
return platform.os() == OperatingSystem.LINUX && Vulkan.hasCard(cards, HardwareVendor.IMG);
|
||||
}
|
||||
},
|
||||
|
||||
/// Mesa PanVK driver.
|
||||
///
|
||||
/// It is a Vulkan driver for ARM Mali GPUs.
|
||||
///
|
||||
/// @see <a href="https://docs.mesa3d.org/drivers/panfrost.html">Panfrost - The Mesa 3D Graphics Library</a>
|
||||
PANVK("panfrost") {
|
||||
@Override
|
||||
public boolean isSupported(Platform platform, @Nullable List<GraphicsCard> cards) {
|
||||
return platform.os() == OperatingSystem.LINUX && platform.arch().isArm();
|
||||
}
|
||||
},
|
||||
|
||||
/// Mesa V3DV driver.
|
||||
///
|
||||
/// It is a Vulkan driver for the Raspberry Pi 4 and Raspberry Pi 5.
|
||||
///
|
||||
/// @see <a href="https://docs.mesa3d.org/drivers/v3dv.html">V3DV - The Mesa 3D Graphics Library</a>
|
||||
V3DV("broadcom") {
|
||||
@Override
|
||||
public boolean isSupported(Platform platform, @Nullable List<GraphicsCard> cards) {
|
||||
return platform.os() == OperatingSystem.LINUX && platform.arch().isArm()
|
||||
&& Vulkan.hasCard(cards, HardwareVendor.BROADCOM);
|
||||
}
|
||||
};
|
||||
|
||||
private static final class Holder {
|
||||
static final List<Renderer> SUPPORTED;
|
||||
static final Map<Vulkan, Path> DRIVER_TO_ICD_FILE;
|
||||
|
||||
static {
|
||||
var driverToIcdFile = new EnumMap<Vulkan, Path>(Vulkan.class);
|
||||
var supported = new LinkedHashSet<Renderer>();
|
||||
|
||||
|
||||
// Helper
|
||||
String archName = switch (Architecture.SYSTEM_ARCH) {
|
||||
case X86 -> "i686";
|
||||
case X86_64 -> "x86_64";
|
||||
case ARM64 -> "aarch64";
|
||||
default -> Architecture.SYSTEM_ARCH.getCheckedName();
|
||||
};
|
||||
|
||||
var icdFileNamePattern = Pattern.compile("(?<name>[a-zA-Z0-9_-]+)_icd(?:\\." + Pattern.quote(archName) + ")?\\.json");
|
||||
Map<String, Vulkan> icdNameToDriver = Stream.of(values()).collect(Collectors.toMap(Vulkan::icdName, Function.identity()));
|
||||
|
||||
supported.add(DEFAULT);
|
||||
|
||||
if (OperatingSystem.CURRENT_OS == OperatingSystem.WINDOWS) {
|
||||
supported.addAll(List.of(LAVAPIPE, DOZEN));
|
||||
|
||||
List<GraphicsCard> graphicsCards = SystemInfo.getGraphicsCards();
|
||||
if (graphicsCards != null) {
|
||||
EnumSet<Vulkan> foundSupported = EnumSet.noneOf(Vulkan.class);
|
||||
for (GraphicsCard card : graphicsCards) {
|
||||
if (!card.getVulkanDriverFiles().isEmpty()) {
|
||||
for (Path icdFile : card.getVulkanDriverFiles()) {
|
||||
String fileName = FileUtils.getName(icdFile);
|
||||
if (!fileName.endsWith(".json"))
|
||||
continue;
|
||||
|
||||
Vulkan driver;
|
||||
|
||||
Matcher matcher = icdFileNamePattern.matcher(fileName);
|
||||
if (matcher.matches()) {
|
||||
String icdName = matcher.group("name");
|
||||
driver = icdNameToDriver.get(icdName);
|
||||
} else {
|
||||
switch (fileName.substring(0, fileName.length() - ".json".length())) {
|
||||
case "igvk64" -> {
|
||||
if (Architecture.SYSTEM_ARCH.getBits() == Bits.BIT_64)
|
||||
driver = INTEL_VULKAN;
|
||||
else
|
||||
continue;
|
||||
}
|
||||
case "igvk32" -> {
|
||||
if (Architecture.SYSTEM_ARCH.getBits() == Bits.BIT_32)
|
||||
driver = INTEL_VULKAN;
|
||||
else
|
||||
continue;
|
||||
}
|
||||
case "nv-vk64" -> {
|
||||
if (Architecture.SYSTEM_ARCH.getBits() == Bits.BIT_64)
|
||||
driver = NVIDIA_VULKAN;
|
||||
else
|
||||
continue;
|
||||
}
|
||||
case "nv-vk32" -> {
|
||||
if (Architecture.SYSTEM_ARCH.getBits() == Bits.BIT_32)
|
||||
driver = NVIDIA_VULKAN;
|
||||
else
|
||||
continue;
|
||||
}
|
||||
case "amd-vulkan64" -> {
|
||||
if (Architecture.SYSTEM_ARCH.getBits() == Bits.BIT_64)
|
||||
driver = AMDVLK;
|
||||
else
|
||||
continue;
|
||||
}
|
||||
case "amd-vulkan32" -> {
|
||||
if (Architecture.SYSTEM_ARCH.getBits() == Bits.BIT_32)
|
||||
driver = AMDVLK;
|
||||
else
|
||||
continue;
|
||||
}
|
||||
case "qcvk_icd_arm64x" -> {
|
||||
if (Architecture.SYSTEM_ARCH == Architecture.ARM64)
|
||||
driver = QUALCOMM;
|
||||
else
|
||||
continue;
|
||||
}
|
||||
default -> {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
driverToIcdFile.putIfAbsent(driver, icdFile);
|
||||
foundSupported.add(driver);
|
||||
}
|
||||
}
|
||||
}
|
||||
supported.addAll(foundSupported);
|
||||
}
|
||||
|
||||
} else if (OperatingSystem.CURRENT_OS == OperatingSystem.MACOS) {
|
||||
// LWJGL integrates MoltenVK, so it is always available
|
||||
supported.add(MOLTENVK);
|
||||
|
||||
// We need libvulkan.1.dylib to load custom Vulkan drivers
|
||||
if (Files.isRegularFile(HomebrewUtils.LIB_VULKAN)) {
|
||||
Path kosmickrispIcd = HomebrewUtils.HOMEBREW_PREFIX.resolve("share/vulkan/icd.d/kosmickrisp_mesa_icd." + archName + ".json");
|
||||
if (Files.isRegularFile(kosmickrispIcd)) {
|
||||
driverToIcdFile.put(KOSMICKRISP, kosmickrispIcd);
|
||||
supported.add(KOSMICKRISP);
|
||||
}
|
||||
|
||||
Path lvpIcd = HomebrewUtils.HOMEBREW_PREFIX.resolve("share/vulkan/icd.d/lvp_icd." + archName + ".json");
|
||||
if (Files.isRegularFile(lvpIcd)) {
|
||||
driverToIcdFile.put(LAVAPIPE, lvpIcd);
|
||||
supported.add(LAVAPIPE);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
List<Path> icdDirs = switch (OperatingSystem.CURRENT_OS) {
|
||||
case LINUX -> List.of(
|
||||
Path.of("/usr/share/vulkan/icd.d"),
|
||||
Path.of("/etc/vulkan/icd.d")
|
||||
);
|
||||
case FREEBSD -> List.of(Path.of("/usr/local/share/vulkan/icd.d"));
|
||||
default -> List.of();
|
||||
};
|
||||
|
||||
EnumSet<Vulkan> foundSupported = EnumSet.noneOf(Vulkan.class);
|
||||
for (Path icdDir : icdDirs) {
|
||||
if (!Files.isDirectory(icdDir))
|
||||
continue;
|
||||
try (Stream<Path> stream = Files.list(icdDir)) {
|
||||
for (Path icdFile : Lang.toIterable(stream)) {
|
||||
String fileName = icdFile.getFileName().toString();
|
||||
|
||||
Matcher matcher = icdFileNamePattern.matcher(fileName);
|
||||
if (matcher.matches()) {
|
||||
String icdName = matcher.group("name");
|
||||
|
||||
Vulkan driver = icdNameToDriver.get(icdName);
|
||||
if (driver != null) {
|
||||
driverToIcdFile.put(driver, icdFile);
|
||||
|
||||
if (driver.isSupported(Platform.CURRENT_PLATFORM, SystemInfo.getGraphicsCards())) {
|
||||
foundSupported.add(driver);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
LOG.warning("Failed to read Vulkan ICD files in " + icdDir, e);
|
||||
}
|
||||
}
|
||||
|
||||
supported.addAll(foundSupported);
|
||||
}
|
||||
|
||||
SUPPORTED = List.copyOf(supported);
|
||||
DRIVER_TO_ICD_FILE = Collections.unmodifiableMap(driverToIcdFile);
|
||||
}
|
||||
}
|
||||
|
||||
private final String icdName;
|
||||
|
||||
Vulkan(String icdName) {
|
||||
this.icdName = icdName;
|
||||
}
|
||||
|
||||
private static boolean hasCard(@Nullable List<GraphicsCard> cards, HardwareVendor vendor) {
|
||||
return cards != null && cards.stream().anyMatch(card -> card.getVendor() == vendor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GraphicsAPI api() {
|
||||
return GraphicsAPI.VULKAN;
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
public String icdName() {
|
||||
return icdName;
|
||||
}
|
||||
|
||||
/// Get the path to the ICD file for this driver.
|
||||
///
|
||||
/// If the ICD file does not exist, return `null`.
|
||||
@Contract(pure = true)
|
||||
public @Nullable Path icdFile() {
|
||||
return Holder.DRIVER_TO_ICD_FILE.get(this);
|
||||
}
|
||||
}
|
||||
|
||||
enum OpenGL implements Driver {
|
||||
/// @see <a href="https://docs.mesa3d.org/drivers/llvmpipe.html">LLVMpipe - The Mesa 3D Graphics Library</a>
|
||||
LLVMPIPE {
|
||||
@Override
|
||||
public String mesaDriverName() {
|
||||
return "llvmpipe";
|
||||
}
|
||||
},
|
||||
ZINK {
|
||||
@Override
|
||||
public String mesaDriverName() {
|
||||
return "zink";
|
||||
}
|
||||
},
|
||||
D3D12 {
|
||||
@Override
|
||||
public boolean isSupported(Platform platform, @Nullable List<GraphicsCard> cards) {
|
||||
return platform.os() == OperatingSystem.WINDOWS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String mesaDriverName() {
|
||||
return "d3d12";
|
||||
}
|
||||
};
|
||||
|
||||
static final List<Renderer> SUPPORTED =
|
||||
Stream.concat(
|
||||
Stream.of(DEFAULT),
|
||||
Stream.of(OpenGL.values()).filter(it -> it.isSupported(
|
||||
Platform.CURRENT_PLATFORM,
|
||||
null // We don't need to pass graphics cards for OpenGL yet
|
||||
))
|
||||
).toList();
|
||||
|
||||
@Override
|
||||
public GraphicsAPI api() {
|
||||
return GraphicsAPI.OPENGL;
|
||||
}
|
||||
}
|
||||
|
||||
/// Unknown renderer.
|
||||
record Unknown(String name) implements Renderer {
|
||||
}
|
||||
|
||||
/// Whether this renderer is supported on the given platform and graphics cards.
|
||||
///
|
||||
/// If `cards` is `null`, it means that the graphics cards are unknown.
|
||||
default boolean isSupported(Platform platform, @Nullable List<GraphicsCard> cards) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -27,6 +27,7 @@ import org.jackhuang.hmcl.util.gson.UUIDTypeAdapter;
|
||||
import org.jackhuang.hmcl.util.io.FileUtils;
|
||||
import org.jackhuang.hmcl.util.io.Unzipper;
|
||||
import org.jackhuang.hmcl.util.platform.*;
|
||||
import org.jackhuang.hmcl.util.platform.macos.HomebrewUtils;
|
||||
import org.jackhuang.hmcl.util.versioning.GameVersionNumber;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -104,7 +105,7 @@ public class DefaultLauncher extends Launcher {
|
||||
|
||||
// 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 +266,21 @@ public class DefaultLauncher extends Launcher {
|
||||
res.addDefault("-Dfml.ignorePatchDiscrepancies=", "true");
|
||||
}
|
||||
|
||||
if (OperatingSystem.CURRENT_OS == OperatingSystem.WINDOWS
|
||||
&& options.getRenderer() instanceof Renderer.Driver renderer
|
||||
&& renderer.mesaDriverName() != null) {
|
||||
res.addDefault("-Dorg.glavo.mesa.loader.nativeDir=", FileUtils.getAbsolutePath(nativeFolder.resolve("mesa-loader")));
|
||||
}
|
||||
|
||||
if (OperatingSystem.CURRENT_OS == OperatingSystem.MACOS
|
||||
&& options.getJava().getArchitecture() == Architecture.SYSTEM_ARCH
|
||||
&& options.getRenderer() instanceof Renderer.Vulkan vulkanDriver
|
||||
&& vulkanDriver.icdFile() != null) {
|
||||
if (Files.isRegularFile(HomebrewUtils.LIB_VULKAN)) {
|
||||
res.addDefault("-Dorg.lwjgl.vulkan.libname=", FileUtils.getAbsolutePath(HomebrewUtils.LIB_VULKAN));
|
||||
}
|
||||
}
|
||||
|
||||
Set<String> classpath = repository.getClasspath(version);
|
||||
|
||||
if (analyzer.has(LibraryAnalyzer.LibraryType.CLEANROOM)) {
|
||||
@@ -395,6 +411,12 @@ public class DefaultLauncher extends Launcher {
|
||||
}
|
||||
}
|
||||
|
||||
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 +586,8 @@ public class DefaultLauncher extends Launcher {
|
||||
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 +600,7 @@ public class DefaultLauncher extends Launcher {
|
||||
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 +608,13 @@ public class DefaultLauncher extends Launcher {
|
||||
|
||||
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<String, String> getEnvVars() {
|
||||
private Map<String, String> getEnvVars(Path nativeFolder) {
|
||||
String versionName = Optional.ofNullable(options.getVersionName()).orElse(version.getId());
|
||||
|
||||
Map<String, String> env = new LinkedHashMap<>();
|
||||
env.put("INST_NAME", versionName);
|
||||
env.put("INST_ID", versionName);
|
||||
@@ -599,18 +622,32 @@ public class DefaultLauncher extends Launcher {
|
||||
env.put("INST_MC_DIR", FileUtils.getAbsolutePath(repository.getRunDirectory(version.getId())));
|
||||
env.put("INST_JAVA", options.getJava().getBinary().toString());
|
||||
|
||||
Renderer renderer = options.getRenderer();
|
||||
if (renderer != Renderer.DEFAULT) {
|
||||
if (options.getRenderer() instanceof Renderer.Driver driver) {
|
||||
if (OperatingSystem.CURRENT_OS == OperatingSystem.WINDOWS) {
|
||||
if (renderer != Renderer.LLVMPIPE)
|
||||
env.put("GALLIUM_DRIVER", renderer.name().toLowerCase(Locale.ROOT));
|
||||
} else if (OperatingSystem.CURRENT_OS == OperatingSystem.LINUX) {
|
||||
env.put("__GLX_VENDOR_LIBRARY_NAME", "mesa");
|
||||
switch (renderer) {
|
||||
case LLVMPIPE:
|
||||
if (driver.mesaDriverName() != null) {
|
||||
if (driver instanceof Renderer.OpenGL && driver != Renderer.OpenGL.LLVMPIPE)
|
||||
env.put("GALLIUM_DRIVER", driver.mesaDriverName());
|
||||
else if (driver instanceof Renderer.Vulkan vulkanDriver) {
|
||||
String icdFile = FileUtils.getAbsolutePath(nativeFolder.resolve("mesa-loader/" + vulkanDriver.icdName() + "_icd.json"));
|
||||
|
||||
env.put("VK_ICD_FILENAMES", icdFile);
|
||||
env.put("VK_DRIVER_FILES", icdFile);
|
||||
}
|
||||
} else if (driver instanceof Renderer.Vulkan vulkanDriver
|
||||
&& vulkanDriver.icdFile() != null
|
||||
&& options.getJava().getArchitecture() == Architecture.SYSTEM_ARCH) {
|
||||
String icdFile = FileUtils.getAbsolutePath(vulkanDriver.icdFile());
|
||||
|
||||
env.put("VK_ICD_FILENAMES", icdFile);
|
||||
env.put("VK_DRIVER_FILES", icdFile);
|
||||
}
|
||||
} else if (OperatingSystem.CURRENT_OS.isLinuxOrBSD()) {
|
||||
if (driver instanceof Renderer.OpenGL oglDriver) {
|
||||
if (oglDriver == Renderer.OpenGL.LLVMPIPE) {
|
||||
env.put("__GLX_VENDOR_LIBRARY_NAME", "mesa");
|
||||
env.put("LIBGL_ALWAYS_SOFTWARE", "1");
|
||||
break;
|
||||
case ZINK:
|
||||
} else if (oglDriver == Renderer.OpenGL.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.
|
||||
@@ -619,7 +656,23 @@ public class DefaultLauncher extends Launcher {
|
||||
* Link: https://gitlab.freedesktop.org/mesa/mesa/-/issues/10093
|
||||
*/
|
||||
env.put("LIBGL_KOPPER_DRI2", "1");
|
||||
break;
|
||||
}
|
||||
} else if (driver instanceof Renderer.Vulkan vulkanDriver
|
||||
&& options.getJava().getArchitecture() == Architecture.SYSTEM_ARCH) {
|
||||
if (vulkanDriver.icdFile() != null) {
|
||||
String absolutePath = FileUtils.getAbsolutePath(vulkanDriver.icdFile());
|
||||
env.put("VK_ICD_FILENAMES", absolutePath);
|
||||
env.put("VK_DRIVER_FILES", absolutePath);
|
||||
}
|
||||
}
|
||||
} else if (OperatingSystem.CURRENT_OS == OperatingSystem.MACOS
|
||||
&& options.getJava().getArchitecture() == Architecture.SYSTEM_ARCH) {
|
||||
if (driver instanceof Renderer.Vulkan vulkanDriver
|
||||
&& vulkanDriver != Renderer.Vulkan.MOLTENVK
|
||||
&& vulkanDriver.icdFile() != null) {
|
||||
String absolutePath = FileUtils.getAbsolutePath(vulkanDriver.icdFile());
|
||||
env.put("VK_ICD_FILENAMES", absolutePath);
|
||||
env.put("VK_DRIVER_FILES", absolutePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -677,7 +730,7 @@ public class DefaultLauncher extends Launcher {
|
||||
|
||||
final Command commandLine = generateCommandLine(nativeFolder);
|
||||
final String command = usePowerShell ? null : commandLine.commandLine.toString();
|
||||
Map<String, String> envVars = getEnvVars();
|
||||
Map<String, String> envVars = getEnvVars(nativeFolder);
|
||||
|
||||
if (isWindows && !usePowerShell) {
|
||||
// https://stackoverflow.com/a/28452546
|
||||
@@ -820,7 +873,7 @@ public class DefaultLauncher extends Launcher {
|
||||
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 +890,8 @@ public class DefaultLauncher extends Launcher {
|
||||
|
||||
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);
|
||||
|
||||
@@ -24,6 +24,7 @@ import org.jackhuang.hmcl.mod.RemoteModRepository;
|
||||
import org.jackhuang.hmcl.util.Immutable;
|
||||
import org.jackhuang.hmcl.util.Lang;
|
||||
import org.jackhuang.hmcl.util.Pair;
|
||||
import org.jackhuang.hmcl.util.versioning.GameVersionNumber;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.IOException;
|
||||
@@ -585,7 +586,7 @@ public class CurseAddon implements RemoteMod.IMod {
|
||||
}
|
||||
return RemoteMod.Dependency.ofGeneral(RELATION_TYPE.get(dependency.getRelationType()), CurseForgeRemoteModRepository.MODS, Integer.toString(dependency.getModId()));
|
||||
}).distinct().filter(Objects::nonNull).collect(Collectors.toList()),
|
||||
gameVersions.stream().filter(ver -> ver.startsWith("1.") || ver.contains("w")).collect(Collectors.toList()),
|
||||
gameVersions.stream().filter(GameVersionNumber::isKnown).toList(),
|
||||
gameVersions.stream().flatMap(version -> {
|
||||
if ("fabric".equalsIgnoreCase(version)) return Stream.of(ModLoaderType.FABRIC);
|
||||
else if ("forge".equalsIgnoreCase(version)) return Stream.of(ModLoaderType.FORGE);
|
||||
|
||||
@@ -86,6 +86,10 @@ public enum Architecture {
|
||||
return this == X86 || this == X86_64;
|
||||
}
|
||||
|
||||
public boolean isArm() {
|
||||
return this == ARM32 || this == ARM64;
|
||||
}
|
||||
|
||||
public static final Architecture CURRENT_ARCH;
|
||||
public static final Architecture SYSTEM_ARCH;
|
||||
|
||||
|
||||
+38
-16
@@ -18,18 +18,22 @@
|
||||
package org.jackhuang.hmcl.util.platform.hardware;
|
||||
|
||||
import org.jackhuang.hmcl.util.StringUtils;
|
||||
import org.jetbrains.annotations.NotNullByDefault;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.annotations.UnknownNullability;
|
||||
import org.jetbrains.annotations.Unmodifiable;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* @author Glavo
|
||||
*/
|
||||
/// @author Glavo
|
||||
@NotNullByDefault
|
||||
public final class GraphicsCard {
|
||||
|
||||
public static String cleanName(String name) {
|
||||
@UnknownNullability
|
||||
public static String cleanName(@UnknownNullability String name) {
|
||||
if (name == null)
|
||||
return null;
|
||||
|
||||
@@ -58,13 +62,15 @@ public final class GraphicsCard {
|
||||
private final @Nullable Type type;
|
||||
private final @Nullable String driver;
|
||||
private final @Nullable String driverVersion;
|
||||
private final @Unmodifiable List<Path> vulkanDriverFiles;
|
||||
|
||||
private GraphicsCard(String name, @Nullable HardwareVendor vendor, @Nullable Type type, @Nullable String driver, @Nullable String driverVersion) {
|
||||
private GraphicsCard(String name, @Nullable HardwareVendor vendor, @Nullable Type type, @Nullable String driver, @Nullable String driverVersion, List<Path> vulkanDriverFiles) {
|
||||
this.name = Objects.requireNonNull(name);
|
||||
this.vendor = vendor;
|
||||
this.type = type;
|
||||
this.driver = driver;
|
||||
this.driverVersion = driverVersion;
|
||||
this.vulkanDriverFiles = vulkanDriverFiles;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
@@ -79,6 +85,10 @@ public final class GraphicsCard {
|
||||
return driverVersion;
|
||||
}
|
||||
|
||||
public List<Path> getVulkanDriverFiles() {
|
||||
return vulkanDriverFiles;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder builder = new StringBuilder(name);
|
||||
@@ -96,11 +106,12 @@ public final class GraphicsCard {
|
||||
}
|
||||
|
||||
public static final class Builder {
|
||||
private String name;
|
||||
private HardwareVendor vendor;
|
||||
private Type type;
|
||||
private String driver;
|
||||
private String driverVersion;
|
||||
private @Nullable String name;
|
||||
private @Nullable HardwareVendor vendor;
|
||||
private @Nullable Type type;
|
||||
private @Nullable String driver;
|
||||
private @Nullable String driverVersion;
|
||||
private @Nullable List<Path> vulkanDriverFiles;
|
||||
|
||||
public GraphicsCard build() {
|
||||
String name = this.name;
|
||||
@@ -111,10 +122,12 @@ public final class GraphicsCard {
|
||||
name = "Unknown";
|
||||
}
|
||||
|
||||
return new GraphicsCard(name, vendor, type, driver, driverVersion);
|
||||
List<Path> vulkanDriverFiles = Objects.requireNonNullElse(this.vulkanDriverFiles, List.of());
|
||||
|
||||
return new GraphicsCard(name, vendor, type, driver, driverVersion, List.copyOf(vulkanDriverFiles));
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
public @Nullable String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@@ -123,7 +136,7 @@ public final class GraphicsCard {
|
||||
return this;
|
||||
}
|
||||
|
||||
public HardwareVendor getVendor() {
|
||||
public @Nullable HardwareVendor getVendor() {
|
||||
return vendor;
|
||||
}
|
||||
|
||||
@@ -132,7 +145,7 @@ public final class GraphicsCard {
|
||||
return this;
|
||||
}
|
||||
|
||||
public Type getType() {
|
||||
public @Nullable Type getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
@@ -141,7 +154,7 @@ public final class GraphicsCard {
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getDriver() {
|
||||
public @Nullable String getDriver() {
|
||||
return driver;
|
||||
}
|
||||
|
||||
@@ -150,7 +163,7 @@ public final class GraphicsCard {
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getDriverVersion() {
|
||||
public @Nullable String getDriverVersion() {
|
||||
return driverVersion;
|
||||
}
|
||||
|
||||
@@ -158,5 +171,14 @@ public final class GraphicsCard {
|
||||
this.driverVersion = driverVersion;
|
||||
return this;
|
||||
}
|
||||
|
||||
public @Nullable List<Path> getVulkanDriverFiles() {
|
||||
return vulkanDriverFiles;
|
||||
}
|
||||
|
||||
public Builder setVulkanDriverFiles(@Nullable List<Path> files) {
|
||||
this.vulkanDriverFiles = files;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher
|
||||
* Copyright (C) 2026 huangyuhui <huanghongxun2008@126.com> 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.jackhuang.hmcl.util.platform.macos;
|
||||
|
||||
import org.jackhuang.hmcl.util.platform.Architecture;
|
||||
import org.jackhuang.hmcl.util.platform.OperatingSystem;
|
||||
import org.jetbrains.annotations.NotNullByDefault;
|
||||
|
||||
import java.nio.file.Path;
|
||||
|
||||
/// @author Glavo
|
||||
@NotNullByDefault
|
||||
public final class HomebrewUtils {
|
||||
|
||||
/// The path to Homebrew prefix.
|
||||
///
|
||||
/// - For macOS x86_64, it is `/usr/local`;
|
||||
/// - For macOS ARM64, it is `/opt/homebrew`;
|
||||
/// - For other operating systems, it is undefined.
|
||||
public static final Path HOMEBREW_PREFIX;
|
||||
|
||||
/// The path to `libvulkan.1.dylib`.
|
||||
///
|
||||
/// For non-macOS operating systems, it is undefined.
|
||||
public static final Path LIB_VULKAN;
|
||||
|
||||
static {
|
||||
if (OperatingSystem.CURRENT_OS == OperatingSystem.MACOS) {
|
||||
// TODO: custom Homebrew prefix for macOS
|
||||
HOMEBREW_PREFIX = Architecture.SYSTEM_ARCH.isX86()
|
||||
? Path.of("/usr/local")
|
||||
: Path.of("/opt/homebrew");
|
||||
|
||||
LIB_VULKAN = HOMEBREW_PREFIX.resolve("lib/libvulkan.1.dylib");
|
||||
} else {
|
||||
// For other operating systems, we don't need Homebrew.
|
||||
var placeholder = Path.of("");
|
||||
HOMEBREW_PREFIX = placeholder;
|
||||
LIB_VULKAN = placeholder;
|
||||
}
|
||||
}
|
||||
|
||||
private HomebrewUtils() {
|
||||
}
|
||||
}
|
||||
+25
@@ -28,8 +28,11 @@ import org.jetbrains.annotations.Nullable;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.InvalidPathException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static org.jackhuang.hmcl.util.logging.Logger.LOG;
|
||||
|
||||
@@ -48,6 +51,25 @@ final class WindowsGPUDetector {
|
||||
}
|
||||
}
|
||||
|
||||
private static @Nullable List<Path> parseVulkanDriverFiles(Object vulkanDriverFiles) {
|
||||
if (vulkanDriverFiles instanceof String file) {
|
||||
try {
|
||||
return List.of(Path.of(file));
|
||||
} catch (InvalidPathException e) {
|
||||
LOG.warning("Failed to parse Vulkan ICD file path: " + file, e);
|
||||
return null;
|
||||
}
|
||||
} else if (vulkanDriverFiles instanceof String[] files) {
|
||||
try {
|
||||
return Stream.of(files).map(Path::of).toList();
|
||||
} catch (InvalidPathException e) {
|
||||
LOG.warning("Failed to parse Vulkan ICD file paths: " + Arrays.toString(files), e);
|
||||
return null;
|
||||
}
|
||||
} else
|
||||
return null;
|
||||
}
|
||||
|
||||
private static List<GraphicsCard> detectByCim() {
|
||||
try {
|
||||
String getCimInstance = OperatingSystem.SYSTEM_VERSION.getVersion().startsWith("6.1")
|
||||
@@ -116,6 +138,7 @@ final class WindowsGPUDetector {
|
||||
String vendor = regValueToString(reg.queryValue(hkey, subkey, "ProviderName"));
|
||||
String driverVersion = regValueToString(reg.queryValue(hkey, subkey, "DriverVersion"));
|
||||
String dacType = regValueToString(reg.queryValue(hkey, subkey, "HardwareInformation.DacType"));
|
||||
Object vulkanDriverName = reg.queryValue(hkey, subkey, "VulkanDriverName");
|
||||
|
||||
GraphicsCard.Builder builder = GraphicsCard.builder();
|
||||
if (name != null)
|
||||
@@ -126,6 +149,8 @@ final class WindowsGPUDetector {
|
||||
builder.setDriverVersion(driverVersion);
|
||||
if (dacType != null)
|
||||
builder.setType(fromDacType(dacType));
|
||||
if (vulkanDriverName != null)
|
||||
builder.setVulkanDriverFiles(parseVulkanDriverFiles(vulkanDriverName));
|
||||
result.add(builder.build());
|
||||
}
|
||||
return result;
|
||||
|
||||
@@ -95,6 +95,18 @@ public abstract sealed class GameVersionNumber implements Comparable<GameVersion
|
||||
return VersionRange.atMost(asGameVersion(maximum));
|
||||
}
|
||||
|
||||
/// Determines whether the given version string corresponds to a known Minecraft version.
|
||||
///
|
||||
/// If the version string cannot be parsed as any known version type (release, snapshot, pre-release, etc.)
|
||||
/// and is not a known April Fools snapshot, it is considered an unknown version.
|
||||
///
|
||||
/// @param version the version string to check, e.g. `"1.21.4"`, `"25w14craftmine"`, etc.
|
||||
/// @return {@code true} if the version string corresponds to a known Minecraft version, {@code false} otherwise
|
||||
/// @see #asGameVersion(String)
|
||||
public static boolean isKnown(String version) {
|
||||
return !(asGameVersion(version) instanceof Special special && special.prev == null);
|
||||
}
|
||||
|
||||
final String value;
|
||||
final String normalized;
|
||||
|
||||
@@ -151,7 +163,7 @@ public abstract sealed class GameVersionNumber implements Comparable<GameVersion
|
||||
///
|
||||
/// ```java
|
||||
/// GameVersionNumber.asVersion("...").isAtLeast("1.13", "17w43a");
|
||||
///```
|
||||
/// ```
|
||||
///
|
||||
/// @param strictReleaseVersion When `strictReleaseVersion` is `false`, `releaseVersion` is considered less than
|
||||
/// its corresponding pre/rc versions.
|
||||
|
||||
@@ -914,4 +914,6 @@
|
||||
26.1
|
||||
26.1.1-rc-1
|
||||
26.1.1
|
||||
26.1.2-rc-1
|
||||
26.1.2
|
||||
26w14a
|
||||
Reference in new issue
Block a user