Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions HMCL/src/main/java/org/jackhuang/hmcl/game/LauncherHelper.java
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
import org.jackhuang.hmcl.util.io.FileUtils;
import org.jackhuang.hmcl.util.io.ResponseCodeException;
import org.jackhuang.hmcl.util.platform.*;
import org.jackhuang.hmcl.util.platform.windows.WinReg;
import org.jackhuang.hmcl.util.versioning.GameVersionNumber;
import org.jackhuang.hmcl.util.versioning.VersionNumber;

Expand Down Expand Up @@ -86,6 +87,7 @@ public final class LauncherHelper {
private boolean showLogs;
private QuickPlayOption quickPlayOption;
private boolean disableOfflineSkin = false;
private boolean modifiedGpuReg = false;

public LauncherHelper(Profile profile, Account account, String selectedVersion) {
this.profile = Objects.requireNonNull(profile);
Expand Down Expand Up @@ -232,6 +234,30 @@ private void launch0() {
launchOptionsBuilder.setQuickPlayOption(quickPlayOption);
}

if (config().isWindowsHighPerformance()) {
try {
WinReg reg = WinReg.INSTANCE;
if (reg != null) {
Object current = reg.queryValue(
WinReg.HKEY.HKEY_CURRENT_USER,
"Software\\Microsoft\\DirectX\\UserGpuPreferences",
FileUtils.getAbsolutePath(javaVersionRef.get().getBinary())
);
if (!(current instanceof String)) {
reg.setValue(
WinReg.HKEY.HKEY_CURRENT_USER,
"Software\\Microsoft\\DirectX\\UserGpuPreferences",
FileUtils.getAbsolutePath(javaVersionRef.get().getBinary()),
"GpuPreference=2;"
);
modifiedGpuReg = true;
}
}
} catch (Exception e) {
LOG.warning("Failed to apply high performance GPU preference", e);
}
}
Comment thread
CiiLu marked this conversation as resolved.

LaunchOptions launchOptions = launchOptionsBuilder.create();

LOG.info("Here's the structure of game mod directory:\n" + FileUtils.printFileStructure(repository.getModsDirectory(selectedVersion), 10));
Expand Down Expand Up @@ -875,6 +901,21 @@ public void run() {
}

private void finishLaunch() {
if (modifiedGpuReg) {
try {
Thread.sleep(5000L);
} catch (InterruptedException ignored) {
}
try {
WinReg.INSTANCE.deleteValue(
WinReg.HKEY.HKEY_CURRENT_USER,
"Software\\Microsoft\\DirectX\\UserGpuPreferences",
process.getProcess().info().command().orElseThrow()
);
} catch (Exception e) {
LOG.warning("Failed to revert high performance GPU preference", e);
}
Comment thread
CiiLu marked this conversation as resolved.
}
switch (launcherVisibility) {
case HIDE_AND_REOPEN:
runLater(() -> {
Expand Down
15 changes: 15 additions & 0 deletions HMCL/src/main/java/org/jackhuang/hmcl/setting/Config.java
Original file line number Diff line number Diff line change
Expand Up @@ -704,6 +704,21 @@ public void setDisableAutoGameOptions(boolean disableAutoGameOptions) {
this.disableAutoGameOptions.set(disableAutoGameOptions);
}

@SerializedName("windowsHighPerformance")
private final BooleanProperty windowsHighPerformance = new SimpleBooleanProperty(false);

public BooleanProperty windowsHighPerformanceProperty() {
return windowsHighPerformance;
}

public boolean isWindowsHighPerformance() {
return windowsHighPerformance.get();
}

public void setWindowsHighPerformance(boolean windowsHighPerformance) {
this.windowsHighPerformance.set(windowsHighPerformance);
}

@SerializedName("allowAutoAgent")
private final BooleanProperty allowAutoAgent = new SimpleBooleanProperty(false);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,15 @@ else if (locale.isSameLanguage(currentLocale))
settingsPane.getContent().add(disableAutoGameOptionsPane);
}

{
LineToggleButton windowsHighPerformance = new LineToggleButton();
windowsHighPerformance.setTitle(i18n("settings.launcher.gpu_preferences"));
windowsHighPerformance.setSubtitle(i18n("settings.advanced.windows_only"));
windowsHighPerformance.selectedProperty().bindBidirectional(config().windowsHighPerformanceProperty());

settingsPane.getContent().add(windowsHighPerformance);
Comment thread
CiiLu marked this conversation as resolved.
}
Comment thread
CiiLu marked this conversation as resolved.

{
LineToggleButton allowAutoAgentPane = new LineToggleButton();
allowAutoAgentPane.setTitle(i18n("settings.launcher.allow_auto_agent"));
Expand Down
2 changes: 2 additions & 0 deletions HMCL/src/main/resources/assets/lang/I18N.properties
Original file line number Diff line number Diff line change
Expand Up @@ -1429,6 +1429,7 @@ settings.advanced.unsupported_system_options=Settings not applicable to the curr
settings.advanced.use_native_glfw=Use System GLFW
settings.advanced.use_native_openal=Use System OpenAL
settings.advanced.linux_freebsd_only=Linux/FreeBSD Only
settings.advanced.windows_only=Windows Only
settings.advanced.workaround=Workaround
settings.advanced.workaround.warning=Workaround options are intended only for advanced users. Tweaking with these options may crash the game. Unless you know what you are doing, please do not edit these options.
settings.advanced.wrapper_launcher=Wrapper Command
Expand Down Expand Up @@ -1487,6 +1488,7 @@ settings.launcher.font.anti_aliasing.auto=Auto
settings.launcher.font.anti_aliasing.gray=Grayscale
settings.launcher.font.anti_aliasing.lcd=Sub-pixel
settings.launcher.general=General
settings.launcher.gpu_preferences=Launch games with high-performance GPU
settings.launcher.language=Language
settings.launcher.launcher_log.export=Export Launcher Logs
settings.launcher.launcher_log.export.failed=Failed to export logs.
Expand Down
2 changes: 2 additions & 0 deletions HMCL/src/main/resources/assets/lang/I18N_zh.properties
Original file line number Diff line number Diff line change
Expand Up @@ -1220,6 +1220,7 @@ settings.advanced.unsupported_system_options=不適用於目前系統的選項
settings.advanced.use_native_glfw=使用系統 GLFW
settings.advanced.use_native_openal=使用系統 OpenAL
settings.advanced.linux_freebsd_only=僅限 Linux/FreeBSD
settings.advanced.windows_only=僅限 Windows
settings.advanced.workaround=除錯選項
settings.advanced.workaround.warning=除錯選項僅提供給專業玩家使用。修改除錯選項可能會導致遊戲無法啟動。除非你知道你在做什麼,否則請不要修改這些選項。
settings.advanced.wrapper_launcher=前置指令
Expand Down Expand Up @@ -1276,6 +1277,7 @@ settings.launcher.font.anti_aliasing.auto=自動
settings.launcher.font.anti_aliasing.gray=灰階
settings.launcher.font.anti_aliasing.lcd=子像素
settings.launcher.general=一般
settings.launcher.gpu_preferences=使用高效能 GPU 啟動遊戲
settings.launcher.language=語言
settings.launcher.launcher_log.export=匯出啟動器日誌
settings.launcher.launcher_log.export.failed=無法匯出日誌。
Expand Down
2 changes: 2 additions & 0 deletions HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties
Original file line number Diff line number Diff line change
Expand Up @@ -1225,6 +1225,7 @@ settings.advanced.unsupported_system_options=不适用于当前系统的选项
settings.advanced.use_native_glfw=使用系统 GLFW
settings.advanced.use_native_openal=使用系统 OpenAL
settings.advanced.linux_freebsd_only=仅 Linux/FreeBSD
settings.advanced.windows_only=仅 Windows
settings.advanced.workaround=调试选项
settings.advanced.workaround.warning=调试选项仅提供给专业玩家使用。调试选项可能会导致游戏无法启动。除非你知道你在做什么,否则请不要修改这些选项!
settings.advanced.wrapper_launcher=包装命令
Expand Down Expand Up @@ -1281,6 +1282,7 @@ settings.launcher.font.anti_aliasing.auto=自动
settings.launcher.font.anti_aliasing.gray=灰度
settings.launcher.font.anti_aliasing.lcd=子像素
settings.launcher.general=通用
settings.launcher.gpu_preferences=使用高性能 GPU 启动游戏
settings.launcher.language=语言
settings.launcher.launcher_log.export=导出启动器日志
settings.launcher.launcher_log.export.failed=无法导出日志
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,4 +56,22 @@ int RegEnumKeyExW(Pointer hKey, int dwIndex,
IntByReference lpReserved,
Pointer lpClass, IntByReference lpcchClass,
Pointer lpftLastWriteTime);

/**
* @see <a href="https://learn.microsoft.com/windows/win32/api/winreg/nf-winreg-regcreatekeyexw">RegCreateKeyExW function</a>
*/
int RegCreateKeyExW(Pointer hKey, WString lpSubKey, int Reserved, WString lpClass,
int dwOptions, int samDesired, Pointer lpSecurityAttributes,
PointerByReference phkResult, IntByReference lpdwDisposition);

/**
* @see <a href="https://learn.microsoft.com/windows/win32/api/winreg/nf-winreg-regsetvalueexw">RegSetValueExW function</a>
*/
int RegSetValueExW(Pointer hKey, WString lpValueName, int Reserved, int dwType,
Pointer lpData, int cbData);

/**
* @see <a href="https://learn.microsoft.com/windows/win32/api/winreg/nf-winreg-regdeletevaluew">RegDeleteValueW function</a>
*/
int RegDeleteValueW(Pointer hKey, WString lpValueName);
}
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ public interface WinConstants {
// https://learn.microsoft.com/windows/win32/sysinfo/registry-key-security-and-access-rights
int KEY_QUERY_VALUE = 0x0001;
int KEY_ENUMERATE_SUB_KEYS = 0x0008;
int KEY_WRITE = 0x20006;
int KEY_SET_VALUE = 0x0002;
int KEY_READ = 0x20019;

// https://learn.microsoft.com/windows/win32/sysinfo/registry-value-types
Expand Down Expand Up @@ -100,4 +102,6 @@ public interface WinConstants {
// https://learn.microsoft.com/windows/win32/api/dwmapi/ne-dwmapi-dwmwindowattribute
int DWMWA_USE_IMMERSIVE_DARK_MODE = 20;

// https://learn.microsoft.com/windows/win32/api/winreg/nf-winreg-regcreatekeyexw
int REG_OPTION_NON_VOLATILE = 0x0000;
}
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,10 @@ public List<String> querySubKeys(HKEY root, String key) {
return list;
}

public abstract boolean setValue(HKEY root, String key, String valueName, String value);

public abstract boolean deleteValue(HKEY root, String key, String valueName);
Comment on lines +91 to +93
Copy link

Copilot AI Feb 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New WinReg mutation APIs (setValue/deleteValue) are introduced, but the existing WinRegTest only covers querying. Please add JUnit coverage for setting a REG_SZ value and deleting it (including verifying behavior when the value/key does not exist), so regressions in JNA signatures/encoding are caught.

Copilot uses AI. Check for mistakes.

private static final class JNAWinReg extends WinReg {

private final Advapi32 advapi32;
Expand Down Expand Up @@ -245,6 +249,63 @@ public List<String> querySubKeyNames(HKEY root, String key) {

return Collections.emptyList();
}

@Override
public boolean setValue(HKEY root, String key, String valueName, String value) {
PointerByReference phkKey = new PointerByReference();
int status = advapi32.RegCreateKeyExW(
root.toPointer(),
new WString(key),
0,
null,
WinConstants.REG_OPTION_NON_VOLATILE,
WinConstants.KEY_WRITE,
null,
phkKey,
null
);

if (status != WinConstants.ERROR_SUCCESS) {
return false;
}

Pointer hkey = phkKey.getValue();
try {
byte[] data = (value + "\0").getBytes(StandardCharsets.UTF_16LE);
try (Memory mem = new Memory(data.length)) {
mem.write(0, data, 0, data.length);
status = advapi32.RegSetValueExW(
hkey,
new WString(valueName),
0,
WinConstants.REG_SZ,
mem,
data.length
);
}
return status == WinConstants.ERROR_SUCCESS;
} finally {
advapi32.RegCloseKey(hkey);
}
}

@Override
public boolean deleteValue(HKEY root, String key, String valueName) {
PointerByReference phkKey = new PointerByReference();
int status = advapi32.RegOpenKeyExW(root.toPointer(), new WString(key), 0, WinConstants.KEY_SET_VALUE, phkKey);

if (status != WinConstants.ERROR_SUCCESS) {
return false;
}

Pointer hkey = phkKey.getValue();
try {
status = advapi32.RegDeleteValueW(hkey, new WString(valueName));
return status == WinConstants.ERROR_SUCCESS;
} finally {
advapi32.RegCloseKey(hkey);
}
}
}

}