From 8aecbe89f91954fdb98fcf00c56847320bc10983 Mon Sep 17 00:00:00 2001 From: Glavo Date: Wed, 21 Jan 2026 20:40:28 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E6=B8=B8=E6=88=8F=E4=BB=A3?= =?UTF-8?q?=E7=90=86=E5=8F=82=E6=95=B0=E8=AE=BE=E7=BD=AE=20(#5273)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../hmcl/game/HMCLGameRepository.java | 48 ++++++++++--- .../jackhuang/hmcl/game/LaunchOptions.java | 57 ++------------- .../org/jackhuang/hmcl/game/ProxyOption.java | 63 ++++++++++++++++ .../hmcl/launch/DefaultLauncher.java | 71 +++++++++++-------- 4 files changed, 145 insertions(+), 94 deletions(-) create mode 100644 HMCLCore/src/main/java/org/jackhuang/hmcl/game/ProxyOption.java diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameRepository.java b/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameRepository.java index 5d01934f2..2426a1ced 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameRepository.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameRepository.java @@ -46,6 +46,7 @@ import org.jackhuang.hmcl.util.versioning.VersionNumber; import org.jetbrains.annotations.Nullable; import java.io.IOException; +import java.net.Proxy; import java.nio.file.Files; import java.nio.file.InvalidPathException; import java.nio.file.Path; @@ -421,6 +422,7 @@ public final class HMCLGameRepository extends DefaultGameRepository { .setHeight(vs.getHeight()) .setFullscreen(vs.isFullscreen()) .setWrapper(vs.getWrapper()) + .setProxyOption(getProxyOption()) .setPreLaunchCommand(vs.getPreLaunchCommand()) .setPostExitCommand(vs.getPostExitCommand()) .setNoGeneratedJVMArgs(vs.isNoJVMArgs()) @@ -440,17 +442,6 @@ public final class HMCLGameRepository extends DefaultGameRepository { builder.setQuickPlayOption(new QuickPlayOption.MultiPlayer(vs.getServerIp())); } - if (config().hasProxy()) { - builder.setProxyType(config().getProxyType()); - builder.setProxyHost(config().getProxyHost()); - builder.setProxyPort(config().getProxyPort()); - - if (config().hasProxyAuth()) { - builder.setProxyUser(config().getProxyUser()); - builder.setProxyPass(config().getProxyPass()); - } - } - Path json = getModpackConfiguration(version); if (Files.exists(json)) { try { @@ -560,4 +551,39 @@ public final class HMCLGameRepository extends DefaultGameRepository { return minimum; } } + + public static ProxyOption getProxyOption() { + if (!config().hasProxy() || config().getProxyType() == null) { + return ProxyOption.Default.INSTANCE; + } + + return switch (config().getProxyType()) { + case DIRECT -> ProxyOption.Direct.INSTANCE; + case HTTP, SOCKS -> { + String proxyHost = config().getProxyHost(); + int proxyPort = config().getProxyPort(); + + if (StringUtils.isBlank(proxyHost) || proxyPort < 0 || proxyPort > 0xFFFF) { + yield ProxyOption.Default.INSTANCE; + } + + String proxyUser = config().getProxyUser(); + String proxyPass = config().getProxyPass(); + + if (StringUtils.isBlank(proxyUser)) { + proxyUser = null; + proxyPass = null; + } else if (proxyPass == null) { + proxyPass = ""; + } + + if (config().getProxyType() == Proxy.Type.HTTP) { + yield new ProxyOption.Http(proxyHost, proxyPort, proxyUser, proxyPass); + } else { + yield new ProxyOption.Socks(proxyHost, proxyPort, proxyUser, proxyPass); + } + } + default -> ProxyOption.Default.INSTANCE; + }; + } } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/LaunchOptions.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/LaunchOptions.java index dacf26181..52c660061 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/LaunchOptions.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/LaunchOptions.java @@ -21,7 +21,6 @@ import org.jackhuang.hmcl.java.JavaRuntime; import org.jetbrains.annotations.NotNull; import java.io.Serializable; -import java.net.Proxy; import java.nio.file.Path; import java.util.*; @@ -49,11 +48,7 @@ public class LaunchOptions implements Serializable { private boolean fullscreen; private QuickPlayOption quickPlayOption; private String wrapper; - private Proxy.Type proxyType; - private String proxyHost; - private int proxyPort; - private String proxyUser; - private String proxyPass; + private ProxyOption proxyOption; private boolean noGeneratedJVMArgs; private boolean noGeneratedOptimizingJVMArgs; private String preLaunchCommand; @@ -195,30 +190,8 @@ public class LaunchOptions implements Serializable { return wrapper; } - public Proxy.Type getProxyType() { - return proxyType; - } - - public String getProxyHost() { - return proxyHost; - } - - public int getProxyPort() { - return proxyPort; - } - - /** - * The user name of the proxy, optional. - */ - public String getProxyUser() { - return proxyUser; - } - - /** - * The password of the proxy, optional - */ - public String getProxyPass() { - return proxyPass; + public ProxyOption getProxyOption() { + return proxyOption; } /** @@ -422,28 +395,8 @@ public class LaunchOptions implements Serializable { return this; } - public Builder setProxyType(Proxy.Type proxyType) { - options.proxyType = proxyType; - return this; - } - - public Builder setProxyHost(String proxyHost) { - options.proxyHost = proxyHost; - return this; - } - - public Builder setProxyPort(int proxyPort) { - options.proxyPort = proxyPort; - return this; - } - - public Builder setProxyUser(String proxyUser) { - options.proxyUser = proxyUser; - return this; - } - - public Builder setProxyPass(String proxyPass) { - options.proxyPass = proxyPass; + public Builder setProxyOption(ProxyOption proxyOption) { + options.proxyOption = proxyOption; return this; } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/game/ProxyOption.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/ProxyOption.java new file mode 100644 index 000000000..6121c79ec --- /dev/null +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/game/ProxyOption.java @@ -0,0 +1,63 @@ +/* + * Hello Minecraft! Launcher + * Copyright (C) 2026 huangyuhui and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package org.jackhuang.hmcl.game; + +import org.jackhuang.hmcl.util.StringUtils; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/// @author Glavo +public sealed interface ProxyOption { + final class Direct implements ProxyOption { + public static final Direct INSTANCE = new Direct(); + + private Direct() { + } + } + + final class Default implements ProxyOption { + public static final Default INSTANCE = new Default(); + + private Default() { + } + } + + record Http(@NotNull String host, int port, @Nullable String username, + @Nullable String password) implements ProxyOption { + public Http { + if (StringUtils.isBlank(host)) { + throw new IllegalArgumentException("Host cannot be blank"); + } + if (port < 0 || port > 0xFFFF) { + throw new IllegalArgumentException("Illegal port: " + port); + } + } + } + + record Socks(@NotNull String host, int port, @Nullable String username, + @Nullable String password) implements ProxyOption { + public Socks { + if (StringUtils.isBlank(host)) { + throw new IllegalArgumentException("Host cannot be blank"); + } + if (port < 0 || port > 0xFFFF) { + throw new IllegalArgumentException("Illegal port: " + port); + } + } + } +} diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/launch/DefaultLauncher.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/launch/DefaultLauncher.java index 3f5105b7a..037093ec8 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/launch/DefaultLauncher.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/launch/DefaultLauncher.java @@ -30,7 +30,6 @@ import org.jackhuang.hmcl.util.platform.*; import org.jackhuang.hmcl.util.versioning.GameVersionNumber; import java.io.*; -import java.net.Proxy; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.nio.file.Files; @@ -170,22 +169,36 @@ public class DefaultLauncher extends Launcher { if (OperatingSystem.CURRENT_OS != OperatingSystem.WINDOWS) res.addDefault("-Duser.home=", options.getGameDir().toAbsolutePath().getParent().toString()); - Proxy.Type proxyType = options.getProxyType(); - if (proxyType == null) { - res.addDefault("-Djava.net.useSystemProxies=", "true"); - } else { - String proxyHost = options.getProxyHost(); - int proxyPort = options.getProxyPort(); + boolean addProxyOptions = res.noneMatch(arg -> + arg.startsWith("-Djava.net.useSystemProxies=") + || arg.startsWith("-Dhttp.proxy") + || arg.startsWith("-Dhttps.proxy") + || arg.startsWith("-DsocksProxy") + || arg.startsWith("-Djava.net.socks.") + ); - if (StringUtils.isNotBlank(proxyHost) && proxyPort >= 0 && proxyPort <= 0xFFFF) { - if (proxyType == Proxy.Type.HTTP) { - res.addDefault("-Dhttp.proxyHost=", proxyHost); - res.addDefault("-Dhttp.proxyPort=", String.valueOf(proxyPort)); - res.addDefault("-Dhttps.proxyHost=", proxyHost); - res.addDefault("-Dhttps.proxyPort=", String.valueOf(proxyPort)); - } else if (proxyType == Proxy.Type.SOCKS) { - res.addDefault("-DsocksProxyHost=", proxyHost); - res.addDefault("-DsocksProxyPort=", String.valueOf(proxyPort)); + if (addProxyOptions) { + if (options.getProxyOption() == null || options.getProxyOption() == ProxyOption.Default.INSTANCE) { + res.add("-Djava.net.useSystemProxies=", "true"); + } else if (options.getProxyOption() instanceof ProxyOption.Http httpProxy) { + res.add("-Dhttp.proxyHost=" + httpProxy.host()); + res.add("-Dhttp.proxyPort=" + httpProxy.port()); + res.add("-Dhttps.proxyHost=" + httpProxy.host()); + res.add("-Dhttps.proxyPort=" + httpProxy.port()); + + if (StringUtils.isNotBlank(httpProxy.username())) { + res.add("-Dhttp.proxyUser=" + httpProxy.username()); + res.add("-Dhttp.proxyPassword=" + Objects.requireNonNullElse(httpProxy.password(), "")); + res.add("-Dhttps.proxyUser=" + httpProxy.username()); + res.add("-Dhttps.proxyPassword=" + Objects.requireNonNullElse(httpProxy.password(), "")); + } + } else if (options.getProxyOption() instanceof ProxyOption.Socks socksProxy) { + res.add("-DsocksProxyHost=" + socksProxy.host()); + res.add("-DsocksProxyPort=" + socksProxy.port()); + + if (StringUtils.isNotBlank(socksProxy.username())) { + res.add("-Djava.net.socks.username=" + socksProxy.username()); + res.add("-Djava.net.socks.password=" + Objects.requireNonNullElse(socksProxy.password(), "")); } } } @@ -334,21 +347,17 @@ public class DefaultLauncher extends Launcher { if (options.isFullscreen()) res.add("--fullscreen"); - if (options.getProxyType() == Proxy.Type.SOCKS) { - String proxyHost = options.getProxyHost(); - int proxyPort = options.getProxyPort(); - - if (StringUtils.isNotBlank(proxyHost) && proxyPort >= 0 && proxyPort <= 0xFFFF) { - res.add("--proxyHost"); - res.add(proxyHost); - res.add("--proxyPort"); - res.add(String.valueOf(proxyPort)); - if (StringUtils.isNotBlank(options.getProxyUser()) && StringUtils.isNotBlank(options.getProxyPass())) { - res.add("--proxyUser"); - res.add(options.getProxyUser()); - res.add("--proxyPass"); - res.add(options.getProxyPass()); - } + // https://github.com/HMCL-dev/HMCL/issues/774 + if (options.getProxyOption() instanceof ProxyOption.Socks socksProxy) { + res.add("--proxyHost"); + res.add(socksProxy.host()); + res.add("--proxyPort"); + res.add(String.valueOf(socksProxy.port())); + if (StringUtils.isNotBlank(socksProxy.username())) { + res.add("--proxyUser"); + res.add(socksProxy.username()); + res.add("--proxyPass"); + res.add(Objects.requireNonNullElse(socksProxy.password(), "")); } }