feat: new memory settings

This commit is contained in:
huanghongxun
2021-08-28 23:34:03 +08:00
parent 924743c622
commit 90ee87062f
6 changed files with 246 additions and 55 deletions

View File

@@ -296,7 +296,11 @@ public class HMCLGameRepository extends DefaultGameRepository {
.setProfileName(Metadata.TITLE)
.setGameArguments(StringUtils.tokenize(vs.getMinecraftArgs()))
.setJavaArguments(StringUtils.tokenize(vs.getJavaArgs()))
.setMaxMemory(vs.getMaxMemory())
.setMaxMemory((int)(getAllocatedMemory(
vs.getMaxMemory(),
OperatingSystem.getPhysicalMemoryStatus().orElse(OperatingSystem.PhysicalMemoryStatus.INVALID).getAvailable(),
vs.isAutoMemory()
) / 1024 / 1024))
.setMinMemory(vs.getMinMemory())
.setMetaspace(Lang.toIntOrNull(vs.getPermSize()))
.setWidth(vs.getWidth())
@@ -400,4 +404,8 @@ public class HMCLGameRepository extends DefaultGameRepository {
return versions.containsKey(id);
}
}
public static long getAllocatedMemory(long minimum, long available, boolean auto) {
return auto ? Math.max(minimum, (long) (available * 0.8)) : minimum;
}
}

View File

@@ -237,6 +237,20 @@ public final class VersionSetting implements Cloneable {
minMemoryProperty.set(minMemory);
}
private final BooleanProperty autoMemory = new SimpleBooleanProperty(this, "autoMemory", true);
public boolean isAutoMemory() {
return autoMemory.get();
}
public BooleanProperty autoMemoryProperty() {
return autoMemory;
}
public void setAutoMemory(boolean autoMemory) {
this.autoMemory.set(autoMemory);
}
private final StringProperty preLaunchCommandProperty = new SimpleStringProperty(this, "precalledCommand", "");
public StringProperty preLaunchCommandProperty() {
@@ -558,6 +572,7 @@ public final class VersionSetting implements Cloneable {
permSizeProperty.addListener(listener);
maxMemoryProperty.addListener(listener);
minMemoryProperty.addListener(listener);
autoMemory.addListener(listener);
preLaunchCommandProperty.addListener(listener);
javaArgsProperty.addListener(listener);
minecraftArgsProperty.addListener(listener);
@@ -589,6 +604,7 @@ public final class VersionSetting implements Cloneable {
versionSetting.setPermSize(getPermSize());
versionSetting.setMaxMemory(getMaxMemory());
versionSetting.setMinMemory(getMinMemory());
versionSetting.setAutoMemory(isAutoMemory());
versionSetting.setPreLaunchCommand(getPreLaunchCommand());
versionSetting.setJavaArgs(getJavaArgs());
versionSetting.setMinecraftArgs(getMinecraftArgs());
@@ -619,6 +635,7 @@ public final class VersionSetting implements Cloneable {
obj.addProperty("minecraftArgs", src.getMinecraftArgs());
obj.addProperty("maxMemory", src.getMaxMemory() <= 0 ? OperatingSystem.SUGGESTED_MEMORY : src.getMaxMemory());
obj.addProperty("minMemory", src.getMinMemory());
obj.addProperty("autoMemory", src.isAutoMemory());
obj.addProperty("permSize", src.getPermSize());
obj.addProperty("width", src.getWidth());
obj.addProperty("height", src.getHeight());
@@ -659,6 +676,7 @@ public final class VersionSetting implements Cloneable {
vs.setMinecraftArgs(Optional.ofNullable(obj.get("minecraftArgs")).map(JsonElement::getAsString).orElse(""));
vs.setMaxMemory(maxMemoryN);
vs.setMinMemory(Optional.ofNullable(obj.get("minMemory")).map(JsonElement::getAsInt).orElse(null));
vs.setAutoMemory(Optional.ofNullable(obj.get("autoMemory")).map(JsonElement::getAsBoolean).orElse(true));
vs.setPermSize(Optional.ofNullable(obj.get("permSize")).map(JsonElement::getAsString).orElse(""));
vs.setWidth(Optional.ofNullable(obj.get("width")).map(JsonElement::getAsJsonPrimitive).map(this::parseJsonPrimitive).orElse(0));
vs.setHeight(Optional.ofNullable(obj.get("height")).map(JsonElement::getAsJsonPrimitive).map(this::parseJsonPrimitive).orElse(0));

View File

@@ -21,20 +21,17 @@ import com.jfoenix.controls.*;
import javafx.application.Platform;
import javafx.beans.InvalidationListener;
import javafx.beans.binding.Bindings;
import javafx.beans.property.ReadOnlyObjectProperty;
import javafx.beans.property.ReadOnlyObjectWrapper;
import javafx.beans.property.*;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.control.Label;
import javafx.scene.control.ScrollPane;
import javafx.scene.image.Image;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.scene.layout.*;
import javafx.stage.FileChooser;
import org.jackhuang.hmcl.game.GameDirectoryType;
import org.jackhuang.hmcl.game.HMCLGameRepository;
import org.jackhuang.hmcl.game.NativesDirectoryType;
import org.jackhuang.hmcl.game.ProcessPriority;
import org.jackhuang.hmcl.setting.LauncherVisibility;
@@ -47,8 +44,10 @@ import org.jackhuang.hmcl.ui.Controllers;
import org.jackhuang.hmcl.ui.FXUtils;
import org.jackhuang.hmcl.ui.construct.*;
import org.jackhuang.hmcl.ui.decorator.DecoratorPage;
import org.jackhuang.hmcl.util.Lang;
import org.jackhuang.hmcl.util.Logging;
import org.jackhuang.hmcl.util.io.FileUtils;
import org.jackhuang.hmcl.util.javafx.SafeStringConverter;
import org.jackhuang.hmcl.util.platform.JavaVersion;
import org.jackhuang.hmcl.util.platform.OperatingSystem;
@@ -58,6 +57,7 @@ import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Level;
import java.util.stream.Collectors;
@@ -76,7 +76,6 @@ public final class VersionSettingsPage extends StackPane implements DecoratorPag
private final VBox rootPane;
private final JFXTextField txtWidth;
private final JFXTextField txtHeight;
private final JFXTextField txtMaxMemory;
private final JFXTextField txtJVMArgs;
private final JFXTextField txtGameArgs;
private final JFXTextField txtMetaspace;
@@ -87,8 +86,8 @@ public final class VersionSettingsPage extends StackPane implements DecoratorPag
private final ComponentList componentList;
private final ComponentList iconPickerItemWrapper;
private final JFXComboBox<LauncherVisibility> cboLauncherVisibility;
private final JFXCheckBox chkAutoAllocate;
private final JFXCheckBox chkFullscreen;
private final Label lblPhysicalMemory;
private final JFXToggleButton chkNoJVMArgs;
private final JFXToggleButton chkNoGameCheck;
private final JFXToggleButton chkNoJVMCheck;
@@ -105,6 +104,10 @@ public final class VersionSettingsPage extends StackPane implements DecoratorPag
private final InvalidationListener javaListener = any -> initJavaSubtitle();
private boolean uiVisible = false;
private final IntegerProperty maxMemoryProperty = new SimpleIntegerProperty();
private final ObjectProperty<OperatingSystem.PhysicalMemoryStatus> memoryStatusProperty = new SimpleObjectProperty<>(OperatingSystem.PhysicalMemoryStatus.INVALID);
public VersionSettingsPage() {
ScrollPane scrollPane = new ScrollPane();
scrollPane.setFitToHeight(true);
@@ -163,21 +166,120 @@ public final class VersionSettingsPage extends StackPane implements DecoratorPag
gameDirItem.setCustomText(i18n("settings.custom"));
gameDirItem.setDirectory(true);
BorderPane maxMemoryPane = new BorderPane();
VBox maxMemoryPane = new VBox(8);
{
VBox vbox = new VBox();
maxMemoryPane.setLeft(vbox);
Label maxMemoryLabel = new Label(i18n("settings.max_memory"));
lblPhysicalMemory = new Label();
lblPhysicalMemory.getStyleClass().add("subtitle-label");
vbox.getChildren().setAll(maxMemoryLabel, lblPhysicalMemory);
Label title = new Label(i18n("settings.memory"));
VBox.setMargin(title, new Insets(0, 0, 8, 0));
txtMaxMemory = new JFXTextField();
maxMemoryPane.setRight(txtMaxMemory);
BorderPane.setAlignment(txtMaxMemory, Pos.CENTER_RIGHT);
FXUtils.setValidateWhileTextChanged(txtMaxMemory, true);
FXUtils.setLimitWidth(txtMaxMemory, 300);
txtMaxMemory.setValidators(new NumberValidator(i18n("input.number"), false));
chkAutoAllocate = new JFXCheckBox(i18n("settings.memory.auto_allocate"));
VBox.setMargin(chkAutoAllocate, new Insets(0, 0, 8, 5));
HBox lowerBoundPane = new HBox();
lowerBoundPane.setAlignment(Pos.CENTER);
VBox.setMargin(lowerBoundPane, new Insets(8, 0, 0, 16));
{
Label label = new Label();
label.textProperty().bind(Bindings.createStringBinding(() -> {
if (chkAutoAllocate.isSelected()) {
return i18n("settings.memory.lower_bound");
} else {
return i18n("settings.memory");
}
}, chkAutoAllocate.selectedProperty()));
JFXSlider slider = new JFXSlider(0, 1, 0);
HBox.setMargin(slider, new Insets(0, 16, 0, 16));
HBox.setHgrow(slider, Priority.ALWAYS);
slider.setValueFactory(self -> Bindings.createStringBinding(() -> (int)(self.getValue() * 100) + "%", self.valueProperty()));
AtomicBoolean changedByTextField = new AtomicBoolean(false);
FXUtils.onChangeAndOperate(maxMemoryProperty, maxMemory -> {
changedByTextField.set(true);
slider.setValue(maxMemory.intValue() * 1.0 / OperatingSystem.TOTAL_MEMORY);
changedByTextField.set(false);
});
slider.valueProperty().addListener((value, oldVal, newVal) -> {
if (changedByTextField.get()) return;
maxMemoryProperty.set((int)(value.getValue().doubleValue() * OperatingSystem.TOTAL_MEMORY));
});
JFXTextField txtMaxMemory = new JFXTextField();
FXUtils.setLimitWidth(txtMaxMemory, 60);
FXUtils.setValidateWhileTextChanged(txtMaxMemory, true);
txtMaxMemory.textProperty().bindBidirectional(maxMemoryProperty, SafeStringConverter.fromInteger());
txtMaxMemory.setValidators(new NumberValidator(i18n("input.number"), false));
lowerBoundPane.getChildren().setAll(label, slider, txtMaxMemory, new Label("MB"));
}
BorderPane titlePane = new BorderPane();
VBox.setMargin(titlePane, new Insets(0, 0, 0, 16));
{
Label left = new Label(i18n("settings.memory.used_per_total"));
left.getStyleClass().add("subtitle-label");
titlePane.setLeft(left);
Label right = new Label();
right.textProperty().bind(Bindings.createStringBinding(() -> {
if (chkAutoAllocate.isSelected()) {
return i18n("settings.memory.allocate.auto");
} else {
return i18n("settings.memory.allocate.manual");
}
}, chkAutoAllocate.selectedProperty()));
right.getStyleClass().add("subtitle-label");
titlePane.setRight(right);
}
StackPane progressBarPane = new StackPane();
progressBarPane.setAlignment(Pos.CENTER_LEFT);
VBox.setMargin(progressBarPane, new Insets(0, 0, 0, 16));
{
progressBarPane.setMinHeight(4);
progressBarPane.getStyleClass().add("memory-total");
StackPane usedMemory = new StackPane();
usedMemory.getStyleClass().add("memory-used");
usedMemory.maxWidthProperty().bind(Bindings.createDoubleBinding(() ->
progressBarPane.getWidth() *
(memoryStatusProperty.get().getUsed() * 1.0 / memoryStatusProperty.get().getTotal()), progressBarPane.widthProperty(),
memoryStatusProperty));
StackPane allocateMemory = new StackPane();
allocateMemory.getStyleClass().add("memory-allocate");
allocateMemory.maxWidthProperty().bind(Bindings.createDoubleBinding(() ->
progressBarPane.getWidth() *
Math.min(1.0,
(double)(HMCLGameRepository.getAllocatedMemory(maxMemoryProperty.get() * 1024L * 1024L, memoryStatusProperty.get().getAvailable(), chkAutoAllocate.isSelected())
+ memoryStatusProperty.get().getUsed()) / memoryStatusProperty.get().getTotal()), progressBarPane.widthProperty(),
maxMemoryProperty, memoryStatusProperty, chkAutoAllocate.selectedProperty()));
progressBarPane.getChildren().setAll(allocateMemory, usedMemory);
}
BorderPane digitalPane = new BorderPane();
VBox.setMargin(digitalPane, new Insets(0, 0, 0, 16));
{
Label lblPhysicalMemory = new Label();
lblPhysicalMemory.getStyleClass().add("memory-label");
digitalPane.setLeft(lblPhysicalMemory);
lblPhysicalMemory.textProperty().bind(Bindings.createStringBinding(() -> {
return i18n("settings.memory.used_per_total.format", memoryStatusProperty.get().getUsedGB(), memoryStatusProperty.get().getTotalGB());
}, memoryStatusProperty));
Label lblAllocateMemory = new Label();
lblAllocateMemory.textProperty().bind(Bindings.createStringBinding(() -> {
long maxMemory = Lang.parseInt(maxMemoryProperty.get(), 0) * 1024L * 1024L;
return i18n(memoryStatusProperty.get().hasAvailable() && maxMemory > memoryStatusProperty.get().getAvailable()
? (chkAutoAllocate.isSelected() ? "settings.memory.allocate.format.auto.exceeded" : "settings.memory.allocate.format.manual.exceeded")
: (chkAutoAllocate.isSelected() ? "settings.memory.allocate.format.auto" : "settings.memory.allocate.format.manual"),
OperatingSystem.PhysicalMemoryStatus.toGigaBytes(maxMemory),
OperatingSystem.PhysicalMemoryStatus.toGigaBytes(HMCLGameRepository.getAllocatedMemory(maxMemory, memoryStatusProperty.get().getAvailable(), chkAutoAllocate.isSelected())),
OperatingSystem.PhysicalMemoryStatus.toGigaBytes(memoryStatusProperty.get().getAvailable()));
}, memoryStatusProperty, maxMemoryProperty, chkAutoAllocate.selectedProperty()));
lblAllocateMemory.getStyleClass().add("memory-label");
digitalPane.setRight(lblAllocateMemory);
}
maxMemoryPane.getChildren().setAll(title, chkAutoAllocate, lowerBoundPane, titlePane, progressBarPane, digitalPane);
}
BorderPane launcherVisibilityPane = new BorderPane();
@@ -368,7 +470,7 @@ public final class VersionSettingsPage extends StackPane implements DecoratorPag
}
private void initialize() {
lblPhysicalMemory.setText(i18n("settings.physical_memory") + ": " + OperatingSystem.TOTAL_MEMORY + "MB");
memoryStatusProperty.set(OperatingSystem.getPhysicalMemoryStatus().orElse(OperatingSystem.PhysicalMemoryStatus.INVALID));
Task.supplyAsync(JavaVersion::getJavas).thenAcceptAsync(Schedulers.javafx(), list -> {
javaItem.loadChildren(list.stream()
@@ -434,7 +536,7 @@ public final class VersionSettingsPage extends StackPane implements DecoratorPag
if (lastVersionSetting != null) {
FXUtils.unbindInt(txtWidth, lastVersionSetting.widthProperty());
FXUtils.unbindInt(txtHeight, lastVersionSetting.heightProperty());
FXUtils.unbindInt(txtMaxMemory, lastVersionSetting.maxMemoryProperty());
maxMemoryProperty.unbindBidirectional(lastVersionSetting.maxMemoryProperty());
FXUtils.unbindString(javaItem.getTxtCustom(), lastVersionSetting.javaDirProperty());
FXUtils.unbindString(gameDirItem.getTxtCustom(), lastVersionSetting.gameDirProperty());
FXUtils.unbindString(nativesDirItem.getTxtCustom(), lastVersionSetting.nativesDirProperty());
@@ -444,6 +546,7 @@ public final class VersionSettingsPage extends StackPane implements DecoratorPag
FXUtils.unbindString(txtWrapper, lastVersionSetting.wrapperProperty());
FXUtils.unbindString(txtPrecallingCommand, lastVersionSetting.preLaunchCommandProperty());
FXUtils.unbindString(txtServerIP, lastVersionSetting.serverIpProperty());
FXUtils.unbindBoolean(chkAutoAllocate, lastVersionSetting.autoMemoryProperty());
FXUtils.unbindBoolean(chkFullscreen, lastVersionSetting.fullscreenProperty());
FXUtils.unbindBoolean(chkNoGameCheck, lastVersionSetting.notCheckGameProperty());
FXUtils.unbindBoolean(chkNoJVMCheck, lastVersionSetting.notCheckJVMProperty());
@@ -469,7 +572,7 @@ public final class VersionSettingsPage extends StackPane implements DecoratorPag
// bind new data fields
FXUtils.bindInt(txtWidth, versionSetting.widthProperty());
FXUtils.bindInt(txtHeight, versionSetting.heightProperty());
FXUtils.bindInt(txtMaxMemory, versionSetting.maxMemoryProperty());
maxMemoryProperty.bindBidirectional(versionSetting.maxMemoryProperty());
FXUtils.bindString(javaItem.getTxtCustom(), versionSetting.javaDirProperty());
FXUtils.bindString(gameDirItem.getTxtCustom(), versionSetting.gameDirProperty());
FXUtils.bindString(nativesDirItem.getTxtCustom(), versionSetting.nativesDirProperty());
@@ -479,6 +582,7 @@ public final class VersionSettingsPage extends StackPane implements DecoratorPag
FXUtils.bindString(txtWrapper, versionSetting.wrapperProperty());
FXUtils.bindString(txtPrecallingCommand, versionSetting.preLaunchCommandProperty());
FXUtils.bindString(txtServerIP, versionSetting.serverIpProperty());
FXUtils.bindBoolean(chkAutoAllocate, versionSetting.autoMemoryProperty());
FXUtils.bindBoolean(chkFullscreen, versionSetting.fullscreenProperty());
FXUtils.bindBoolean(chkNoGameCheck, versionSetting.notCheckGameProperty());
FXUtils.bindBoolean(chkNoJVMCheck, versionSetting.notCheckJVMProperty());

View File

@@ -42,6 +42,22 @@
-fx-text-fill: rgba(0, 0, 0, 0.5);
}
.memory-label {
-fx-font-size: 14px;
}
.memory-used {
-fx-background-color: -fx-base-darker-color;
}
.memory-allocate {
-fx-background-color: -fx-base-check-color;
}
.memory-total {
-fx-background-color: -fx-base-rippler-color;
}
.update-label {
-fx-text-fill: red;
}
@@ -530,23 +546,12 @@
* *
*******************************************************/
.jfx-slider-style {
.jfx-slider {
-jfx-indicator-position: right;
}
.jfx-slider-style > .thumb {
-fx-background-color: #03a9f4;
}
.jfx-slider-style .track {
-fx-background-color: #ff5252;
-fx-pref-height: 5px;
-fx-pref-width: 5px;
}
.jfx-slider-style .sliderValue {
-fx-stroke: WHITE;
-fx-font-size: 10.0;
.jfx-slider .thumb {
-fx-background-color: -fx-base-color;
}
/*******************************************************************************

View File

@@ -520,8 +520,17 @@ settings.launcher.proxy.socks=Socks
settings.launcher.proxy.username=账户
settings.launcher.theme=主题
settings.min_memory=最小内存MB
settings.max_memory=最大内存MB
settings.memory=游戏内存
settings.memory.allocate.auto=最低分配 / 实际分配
settings.memory.allocate.manual=游戏分配
settings.memory.allocate.format.auto=%1$.1f GB / %2$.1f GB
settings.memory.allocate.format.auto.exceeded=%1$.1f GB / %2$.1f GB (可用 %3$.1f GB)
settings.memory.allocate.format.manual=%1$.1f GB
settings.memory.allocate.format.manual.exceeded=%1$.1f GB (可用 %3$.1f GB)
settings.memory.auto_allocate=自动分配
settings.memory.lower_bound=最低分配
settings.memory.used_per_total=已使用 / 总内存
settings.memory.used_per_total.format=%1$.1f GB / %2$.1f GB
settings.physical_memory=物理内存大小
settings.show_log=查看日志
settings.tabs.installers=自动安装