feat: download concurrency settings. Closes #990.

This commit is contained in:
huanghongxun 2021-09-03 03:14:39 +08:00
parent b3d083877f
commit 243c9b83b9
11 changed files with 214 additions and 11 deletions

View File

@ -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();
}

View File

@ -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() {

View File

@ -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",

View File

@ -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);
}
}

View File

@ -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();

View File

@ -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)));

View File

@ -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 {
}

View File

@ -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

View File

@ -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=全螢幕

View File

@ -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=无法导出日志

View File

@ -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);
}
}
}