mirror of
https://github.com/HMCL-dev/HMCL.git
synced 2025-09-17 15:57:18 -04:00
feat: download concurrency settings. Closes #990.
This commit is contained in:
parent
b3d083877f
commit
243c9b83b9
@ -122,6 +122,12 @@ public final class Config implements Cloneable, Observable {
|
||||
@SerializedName("localization")
|
||||
private ObjectProperty<SupportedLocale> localization = new SimpleObjectProperty<>(Locales.DEFAULT);
|
||||
|
||||
@SerializedName("autoDownloadThreads")
|
||||
private BooleanProperty autoDownloadThreads = new SimpleBooleanProperty(false);
|
||||
|
||||
@SerializedName("downloadThreads")
|
||||
private IntegerProperty downloadThreads = new SimpleIntegerProperty(64);
|
||||
|
||||
@SerializedName("downloadType")
|
||||
private StringProperty downloadType = new SimpleStringProperty("mcbbs");
|
||||
|
||||
@ -389,6 +395,30 @@ public final class Config implements Cloneable, Observable {
|
||||
return localization;
|
||||
}
|
||||
|
||||
public boolean getAutoDownloadThreads() {
|
||||
return autoDownloadThreads.get();
|
||||
}
|
||||
|
||||
public BooleanProperty autoDownloadThreadsProperty() {
|
||||
return autoDownloadThreads;
|
||||
}
|
||||
|
||||
public void setAutoDownloadThreads(boolean autoDownloadThreads) {
|
||||
this.autoDownloadThreads.set(autoDownloadThreads);
|
||||
}
|
||||
|
||||
public int getDownloadThreads() {
|
||||
return downloadThreads.get();
|
||||
}
|
||||
|
||||
public IntegerProperty downloadThreadsProperty() {
|
||||
return downloadThreads;
|
||||
}
|
||||
|
||||
public void setDownloadThreads(int downloadThreads) {
|
||||
this.downloadThreads.set(downloadThreads);
|
||||
}
|
||||
|
||||
public String getDownloadType() {
|
||||
return downloadType.get();
|
||||
}
|
||||
|
@ -17,14 +17,19 @@
|
||||
*/
|
||||
package org.jackhuang.hmcl.setting;
|
||||
|
||||
import javafx.beans.InvalidationListener;
|
||||
import org.jackhuang.hmcl.download.*;
|
||||
import org.jackhuang.hmcl.task.FetchTask;
|
||||
import org.jackhuang.hmcl.ui.FXUtils;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static org.jackhuang.hmcl.setting.ConfigHolder.config;
|
||||
import static org.jackhuang.hmcl.task.FetchTask.DEFAULT_CONCURRENCY;
|
||||
import static org.jackhuang.hmcl.util.Lang.mapOf;
|
||||
import static org.jackhuang.hmcl.util.Pair.pair;
|
||||
|
||||
@ -44,6 +49,8 @@ public final class DownloadProviders {
|
||||
public static final String DEFAULT_PROVIDER_ID = "balanced";
|
||||
public static final String DEFAULT_RAW_PROVIDER_ID = "mcbbs";
|
||||
|
||||
private static final InvalidationListener observer;
|
||||
|
||||
static {
|
||||
String bmclapiRoot = "https://bmclapi2.bangbang93.com";
|
||||
String bmclapiRootOverride = System.getProperty("hmcl.bmclapi.override");
|
||||
@ -66,6 +73,11 @@ public final class DownloadProviders {
|
||||
pair("official", new AutoDownloadProvider(MOJANG, fileProvider)),
|
||||
pair("balanced", new AutoDownloadProvider(balanced, fileProvider)),
|
||||
pair("mirror", new AutoDownloadProvider(MCBBS, fileProvider)));
|
||||
|
||||
observer = FXUtils.observeWeak(() -> {
|
||||
FetchTask.setDownloadExecutorConcurrency(
|
||||
config().getAutoDownloadThreads() ? DEFAULT_CONCURRENCY : config().getDownloadThreads());
|
||||
}, config().autoDownloadThreadsProperty(), config().downloadThreadsProperty());
|
||||
}
|
||||
|
||||
static void init() {
|
||||
|
@ -415,6 +415,12 @@ public final class SVG {
|
||||
fill, width, height);
|
||||
}
|
||||
|
||||
public static Node bell(ObjectBinding<? extends Paint> fill, double width, double height) {
|
||||
return createSVGPath(
|
||||
"M21,19V20H3V19L5,17V11C5,7.9 7.03,5.17 10,4.29C10,4.19 10,4.1 10,4A2,2 0 0,1 12,2A2,2 0 0,1 14,4C14,4.1 14,4.19 14,4.29C16.97,5.17 19,7.9 19,11V17L21,19M14,21A2,2 0 0,1 12,23A2,2 0 0,1 10,21",
|
||||
fill, width, height);
|
||||
}
|
||||
|
||||
public static Node contentSaveMoveOutline(ObjectBinding<? extends Paint> fill, double width, double height) {
|
||||
return createSVGPath(
|
||||
"M13 17H17V14L22 18.5L17 23V20H13V17M14 12.8C13.5 12.31 12.78 12 12 12C10.34 12 9 13.34 9 15C9 16.31 9.84 17.41 11 17.82C11.07 15.67 12.27 13.8 14 12.8M11.09 19H5V5H16.17L19 7.83V12.35C19.75 12.61 20.42 13 21 13.54V7L17 3H5C3.89 3 3 3.9 3 5V19C3 20.1 3.89 21 5 21H11.81C11.46 20.39 11.21 19.72 11.09 19M6 10H15V6H6V10Z",
|
||||
|
@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Hello Minecraft! Launcher
|
||||
* Copyright (C) 2021 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.ui.construct;
|
||||
|
||||
import javafx.beans.property.SimpleStringProperty;
|
||||
import javafx.beans.property.StringProperty;
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.layout.HBox;
|
||||
import javafx.scene.layout.VBox;
|
||||
import javafx.scene.paint.Color;
|
||||
import javafx.scene.text.Text;
|
||||
import javafx.scene.text.TextFlow;
|
||||
import org.jackhuang.hmcl.setting.Theme;
|
||||
import org.jackhuang.hmcl.ui.SVG;
|
||||
import org.jackhuang.hmcl.util.javafx.BindingMapping;
|
||||
|
||||
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
||||
|
||||
public class HintPane extends VBox {
|
||||
private final Text label = new Text();
|
||||
private final StringProperty text = new SimpleStringProperty(this, "text");
|
||||
|
||||
public HintPane(MessageDialogPane.MessageType type) {
|
||||
setFillWidth(true);
|
||||
getStyleClass().add("hint");
|
||||
HBox hbox = new HBox();
|
||||
hbox.setAlignment(Pos.CENTER_LEFT);
|
||||
hbox.getChildren().setAll(
|
||||
SVG.informationOutline(Theme.blackFillBinding(), 16, 16),
|
||||
new Label(i18n("message.info"))
|
||||
);
|
||||
TextFlow flow = new TextFlow();
|
||||
flow.getChildren().setAll(label);
|
||||
getChildren().setAll(hbox, flow);
|
||||
label.textProperty().bind(text);
|
||||
VBox.setMargin(flow, new Insets(2, 2, 0, 2));
|
||||
|
||||
label.fillProperty().bind(BindingMapping.of(disabledProperty()).map(disabled -> disabled ? new Color(0, 0, 0, 0.5) : Color.BLACK));
|
||||
}
|
||||
|
||||
public String getText() {
|
||||
return text.get();
|
||||
}
|
||||
|
||||
public StringProperty textProperty() {
|
||||
return text;
|
||||
}
|
||||
|
||||
public void setText(String text) {
|
||||
this.text.set(text);
|
||||
}
|
||||
}
|
@ -22,17 +22,18 @@ import javafx.geometry.HPos;
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.control.Label;
|
||||
|
||||
import javafx.scene.control.ScrollPane;
|
||||
import javafx.scene.control.ToggleGroup;
|
||||
import javafx.scene.layout.*;
|
||||
import org.jackhuang.hmcl.setting.DownloadProviders;
|
||||
import org.jackhuang.hmcl.task.FetchTask;
|
||||
import org.jackhuang.hmcl.ui.FXUtils;
|
||||
import org.jackhuang.hmcl.ui.construct.ComponentList;
|
||||
import org.jackhuang.hmcl.ui.construct.NumberValidator;
|
||||
import org.jackhuang.hmcl.ui.construct.Validator;
|
||||
import org.jackhuang.hmcl.ui.construct.*;
|
||||
import org.jackhuang.hmcl.util.javafx.SafeStringConverter;
|
||||
|
||||
import java.net.Proxy;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import static org.jackhuang.hmcl.setting.ConfigHolder.config;
|
||||
import static org.jackhuang.hmcl.ui.FXUtils.stringConverter;
|
||||
@ -103,6 +104,68 @@ public class DownloadSettingsPage extends StackPane {
|
||||
content.getChildren().addAll(ComponentList.createComponentListTitle(i18n("settings.launcher.version_list_source")), downloadSource);
|
||||
}
|
||||
|
||||
{
|
||||
ComponentList downloadThreads = new ComponentList();
|
||||
|
||||
{
|
||||
VBox pane = new VBox(16);
|
||||
pane.setPadding(new Insets(8, 0, 8, 0));
|
||||
|
||||
{
|
||||
JFXCheckBox chkAutoDownloadThreads = new JFXCheckBox(i18n("settings.launcher.download.threads.auto"));
|
||||
chkAutoDownloadThreads.selectedProperty().bindBidirectional(config().autoDownloadThreadsProperty());
|
||||
pane.getChildren().add(chkAutoDownloadThreads);
|
||||
|
||||
chkAutoDownloadThreads.selectedProperty().addListener((a, b, newValue) -> {
|
||||
if (newValue) {
|
||||
config().downloadThreadsProperty().set(FetchTask.DEFAULT_CONCURRENCY);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
{
|
||||
HBox hbox = new HBox(8);
|
||||
hbox.setAlignment(Pos.CENTER);
|
||||
hbox.setPadding(new Insets(0, 0, 0, 30));
|
||||
hbox.disableProperty().bind(config().autoDownloadThreadsProperty());
|
||||
Label label = new Label(i18n("settings.launcher.download.threads"));
|
||||
|
||||
JFXSlider slider = new JFXSlider(1, 256, 64);
|
||||
HBox.setHgrow(slider, Priority.ALWAYS);
|
||||
|
||||
JFXTextField threadsField = new JFXTextField();
|
||||
FXUtils.setLimitWidth(threadsField, 60);
|
||||
threadsField.textProperty().bindBidirectional(config().downloadThreadsProperty(), SafeStringConverter.fromInteger());
|
||||
|
||||
AtomicBoolean changedByTextField = new AtomicBoolean(false);
|
||||
FXUtils.onChangeAndOperate(config().downloadThreadsProperty(), value -> {
|
||||
changedByTextField.set(true);
|
||||
slider.setValue(value.intValue());
|
||||
changedByTextField.set(false);
|
||||
});
|
||||
slider.valueProperty().addListener((value, oldVal, newVal) -> {
|
||||
if (changedByTextField.get()) return;
|
||||
config().downloadThreadsProperty().set(value.getValue().intValue());
|
||||
});
|
||||
|
||||
hbox.getChildren().setAll(label, slider, threadsField);
|
||||
pane.getChildren().add(hbox);
|
||||
}
|
||||
|
||||
{
|
||||
HintPane hintPane = new HintPane(MessageDialogPane.MessageType.INFORMATION);
|
||||
VBox.setMargin(hintPane, new Insets(0, 0, 0, 30));
|
||||
hintPane.disableProperty().bind(config().autoDownloadThreadsProperty());
|
||||
hintPane.setText(i18n("settings.launcher.download.threads.hint"));
|
||||
pane.getChildren().add(hintPane);
|
||||
}
|
||||
|
||||
downloadThreads.getContent().add(pane);
|
||||
}
|
||||
|
||||
content.getChildren().addAll(ComponentList.createComponentListTitle(i18n("download")), downloadThreads);
|
||||
}
|
||||
|
||||
{
|
||||
ComponentList proxyList = new ComponentList();
|
||||
|
||||
|
@ -67,14 +67,14 @@ public class LauncherSettingsPage extends BorderPane implements DecoratorPage {
|
||||
{
|
||||
AdvancedListBox sideBar = new AdvancedListBox()
|
||||
.addNavigationDrawerItem(settingsItem -> {
|
||||
settingsItem.setTitle(i18n("settings.type.global.manage"));
|
||||
settingsItem.setTitle(i18n("settings.game.current"));
|
||||
settingsItem.setLeftGraphic(wrap(SVG.gamepad(null, 20, 20)));
|
||||
settingsItem.activeProperty().bind(tab.getSelectionModel().selectedItemProperty().isEqualTo(gameTab));
|
||||
settingsItem.setOnAction(e -> tab.getSelectionModel().select(gameTab));
|
||||
})
|
||||
.startCategory(i18n("settings.launcher"))
|
||||
.startCategory(i18n("launcher"))
|
||||
.addNavigationDrawerItem(settingsItem -> {
|
||||
settingsItem.setTitle(i18n("settings.launcher"));
|
||||
settingsItem.setTitle(i18n("settings.launcher.general"));
|
||||
settingsItem.setLeftGraphic(wrap(SVG.applicationOutline(null, 20, 20)));
|
||||
settingsItem.activeProperty().bind(tab.getSelectionModel().selectedItemProperty().isEqualTo(settingsTab));
|
||||
settingsItem.setOnAction(e -> tab.getSelectionModel().select(settingsTab));
|
||||
@ -91,6 +91,7 @@ public class LauncherSettingsPage extends BorderPane implements DecoratorPage {
|
||||
downloadItem.activeProperty().bind(tab.getSelectionModel().selectedItemProperty().isEqualTo(downloadTab));
|
||||
downloadItem.setOnAction(e -> tab.getSelectionModel().select(downloadTab));
|
||||
})
|
||||
.startCategory(i18n("help"))
|
||||
.addNavigationDrawerItem(helpItem -> {
|
||||
helpItem.setTitle(i18n("help"));
|
||||
helpItem.setLeftGraphic(wrap(SVG.helpCircleOutline(null, 20, 20)));
|
||||
|
@ -42,6 +42,15 @@
|
||||
-fx-text-fill: rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
.hint {
|
||||
-fx-border-color: #b8daff;
|
||||
-fx-background-color: #cce5ff;
|
||||
-fx-background-radius: 5;
|
||||
-fx-border-width: 1;
|
||||
-fx-border-radius: 5;
|
||||
-fx-padding: 6;
|
||||
}
|
||||
|
||||
.memory-label {
|
||||
}
|
||||
|
||||
|
@ -530,6 +530,7 @@ settings.advanced.wrapper_launcher=Wrapper Launcher (i.e. optirun...)
|
||||
settings.custom=Custom
|
||||
|
||||
settings.game=Games
|
||||
settings.game.current=Game
|
||||
settings.game.dimension=Game Window Dimension
|
||||
settings.game.exploration=Explore
|
||||
settings.game.fullscreen=Fullscreen
|
||||
@ -546,10 +547,14 @@ settings.launcher=Settings
|
||||
settings.launcher.appearance=Appearance
|
||||
settings.launcher.common_path.tooltip=This app will cache all downloads here.
|
||||
settings.launcher.debug=Debug
|
||||
settings.launcher.download.threads=Threads
|
||||
settings.launcher.download.threads.auto=Auto Determined
|
||||
settings.launcher.download.threads.hint=Too large concurrency may cause system to freeze. Download speed may be affected by ICP and destination server.
|
||||
settings.launcher.download_source=Download Source
|
||||
settings.launcher.download_source.auto=Auto choose download source
|
||||
settings.launcher.enable_game_list=Show version list in main page
|
||||
settings.launcher.font=Font
|
||||
settings.launcher.general=General
|
||||
settings.launcher.language=Language
|
||||
settings.launcher.launcher_log.export=Export launcher logs
|
||||
settings.launcher.launcher_log.export.failed=Failed to export logs
|
||||
|
@ -380,6 +380,7 @@ settings.advanced.wrapper_launcher=前置指令(不必填寫,如 optirun)
|
||||
settings.custom=自訂
|
||||
|
||||
settings.game=遊戲設定
|
||||
settings.game.current=遊戲
|
||||
settings.game.dimension=遊戲介面解析度大小
|
||||
settings.game.exploration=瀏覽
|
||||
settings.game.fullscreen=全螢幕
|
||||
|
@ -538,6 +538,7 @@ settings.advanced.wrapper_launcher=前置指令(不必填写,如 optirun)
|
||||
settings.custom=自定义
|
||||
|
||||
settings.game=游戏设置
|
||||
settings.game.current=游戏
|
||||
settings.game.dimension=游戏窗口分辨率
|
||||
settings.game.exploration=浏览
|
||||
settings.game.fullscreen=全屏
|
||||
@ -554,10 +555,15 @@ settings.launcher=启动器设置
|
||||
settings.launcher.appearance=外观
|
||||
settings.launcher.common_path.tooltip=启动器将所有游戏资源及依赖库文件放于此集中管理,如果游戏文件夹内有现成的将不会使用公共库文件
|
||||
settings.launcher.debug=调试
|
||||
settings.launcher.download=下载
|
||||
settings.launcher.download.threads=并发数
|
||||
settings.launcher.download.threads.auto=自动选择并发数
|
||||
settings.launcher.download.threads.hint=并发数过大可能导致系统卡顿。你的下载速度会受到宽带运营商、服务器等方面的影响,调大下载并发数不一定能大幅提升总下载速度。
|
||||
settings.launcher.download_source=下载源
|
||||
settings.launcher.download_source.auto=自动选择下载源
|
||||
settings.launcher.enable_game_list=在主页内显示版本列表
|
||||
settings.launcher.font=字体
|
||||
settings.launcher.general=通用
|
||||
settings.launcher.language=语言
|
||||
settings.launcher.launcher_log.export=导出启动器日志
|
||||
settings.launcher.launcher_log.export.failed=无法导出日志
|
||||
|
@ -268,8 +268,9 @@ public abstract class FetchTask<T> extends Task<T> {
|
||||
|
||||
}
|
||||
|
||||
private static int downloadExecutorConcurrency = Math.min(Runtime.getRuntime().availableProcessors() * 4, 64);
|
||||
private static volatile ExecutorService DOWNLOAD_EXECUTOR;
|
||||
public static int DEFAULT_CONCURRENCY = Math.min(Runtime.getRuntime().availableProcessors() * 4, 64);
|
||||
private static int downloadExecutorConcurrency = DEFAULT_CONCURRENCY;
|
||||
private static volatile ThreadPoolExecutor DOWNLOAD_EXECUTOR;
|
||||
|
||||
/**
|
||||
* Get singleton instance of the thread pool for file downloading.
|
||||
@ -292,8 +293,8 @@ public abstract class FetchTask<T> extends Task<T> {
|
||||
synchronized (Schedulers.class) {
|
||||
downloadExecutorConcurrency = concurrency;
|
||||
if (DOWNLOAD_EXECUTOR != null) {
|
||||
DOWNLOAD_EXECUTOR.shutdownNow();
|
||||
DOWNLOAD_EXECUTOR = null;
|
||||
DOWNLOAD_EXECUTOR.setCorePoolSize(concurrency);
|
||||
DOWNLOAD_EXECUTOR.setMaximumPoolSize(concurrency);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user