From 05cede47730a6c2460dc05fa3c7e99732530c4c2 Mon Sep 17 00:00:00 2001 From: Glavo Date: Thu, 9 Jul 2026 21:34:12 +0800 Subject: [PATCH 1/3] refactor: replace SettingProperty with InheritableProperty in GameSettings for improved memory management --- .../hmcl/game/HMCLGameRepository.java | 20 +++---- .../jackhuang/hmcl/game/LauncherHelper.java | 6 +- .../jackhuang/hmcl/game/ModpackHelper.java | 6 +- .../jackhuang/hmcl/setting/GameSettings.java | 60 +++++++++---------- .../hmcl/ui/export/ExportWizardProvider.java | 2 +- .../hmcl/ui/export/ModpackInfoPage.java | 6 +- .../hmcl/ui/game/GameSettingsPage.java | 2 +- .../jackhuang/hmcl/util/NativePatcher.java | 8 +-- 8 files changed, 55 insertions(+), 55 deletions(-) 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 b250406fa2d..1f9289d7620 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameRepository.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameRepository.java @@ -694,7 +694,7 @@ private record InstanceGameSettingsLoadResult( public LaunchOptions.Builder getLaunchOptions(String version, JavaRuntime javaVersion, Path gameDir, List javaAgents, List javaArguments, boolean makeLaunchScript) { GameSettings.Effective vs = getEffectiveGameSettings(version); boolean noJVMOptions = vs.getInheritable(GameSettings::noJVMOptionsProperty); - boolean autoMemory = vs.get(GameSettings::autoMemoryProperty); + boolean autoMemory = vs.getInheritable(GameSettings::autoMemoryProperty); GameVersionNumber gameVersionNumber = GameVersionNumber.asGameVersion(getGameVersion(version)); LaunchOptions.Builder builder = new LaunchOptions.Builder() @@ -703,17 +703,17 @@ public LaunchOptions.Builder getLaunchOptions(String version, JavaRuntime javaVe .setVersionType(Metadata.TITLE) .setVersionName(version) .setProfileName(Metadata.TITLE) - .setGameArguments(StringUtils.tokenize(vs.get(GameSettings::gameArgumentsProperty))) - .setOverrideJavaArguments(StringUtils.tokenize(vs.get(GameSettings::jvmOptionsProperty))) + .setGameArguments(StringUtils.tokenize(vs.getInheritable(GameSettings::gameArgumentsProperty))) + .setOverrideJavaArguments(StringUtils.tokenize(vs.getInheritable(GameSettings::jvmOptionsProperty))) .setMaxMemory(noJVMOptions && autoMemory ? null : (int) (getAllocatedMemory( vs.getMaxMemory() * 1024L * 1024L, SystemInfo.getPhysicalMemoryStatus().available(), autoMemory ) / 1024 / 1024)) - .setMinMemory(vs.get(GameSettings::minMemoryProperty)) - .setMetaspace(Lang.toIntOrNull(vs.get(GameSettings::permSizeProperty))) + .setMinMemory(vs.getInheritable(GameSettings::minMemoryProperty)) + .setMetaspace(Lang.toIntOrNull(vs.getInheritable(GameSettings::permSizeProperty))) .setEnvironmentVariables( - Lang.mapOf(StringUtils.tokenize(vs.get(GameSettings::environmentVariablesProperty)) + Lang.mapOf(StringUtils.tokenize(vs.getInheritable(GameSettings::environmentVariablesProperty)) .stream() .map(it -> { int idx = it.indexOf('='); @@ -731,16 +731,16 @@ public LaunchOptions.Builder getLaunchOptions(String version, JavaRuntime javaVe .setPostExitCommand(vs.getInheritable(GameSettings::postExitCommandProperty)) .setNoGeneratedJVMArgs(noJVMOptions) .setNoGeneratedOptimizingJVMArgs(vs.getInheritable(GameSettings::noOptimizingJVMOptionsProperty)) - .setUseCustomNatives(vs.get(GameSettings::useCustomNativesProperty)) - .setNativesDir(vs.get(GameSettings::nativesDirectoryProperty)) + .setUseCustomNatives(vs.getInheritable(GameSettings::useCustomNativesProperty)) + .setNativesDir(vs.getInheritable(GameSettings::nativesDirectoryProperty)) .setProcessPriority(vs.getInheritable(GameSettings::processPriorityProperty)) .setGraphicsBackend(vs.getInheritable(GameSettings::graphicsBackendProperty)) .setRenderer(vs.getRenderer(gameVersionNumber)) .setEnableDebugLogOutput(vs.getInheritable(GameSettings::enableDebugLogOutputProperty)) .setAllowAutoAgent(vs.getInheritable(GameSettings::allowAutoAgentProperty)) .setDisableAutoGameOptions(vs.getInheritable(GameSettings::disableAutoGameOptionsProperty)) - .setUseNativeGLFW(vs.get(GameSettings::useNativeGLFWProperty)) - .setUseNativeOpenAL(vs.get(GameSettings::useNativeOpenALProperty)) + .setUseNativeGLFW(vs.getInheritable(GameSettings::useNativeGLFWProperty)) + .setUseNativeOpenAL(vs.getInheritable(GameSettings::useNativeOpenALProperty)) .setDaemon(!makeLaunchScript && vs.getInheritable(GameSettings::launcherVisibilityProperty).isDaemon()) .setJavaAgents(javaAgents) .setJavaArguments(javaArguments); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/game/LauncherHelper.java b/HMCL/src/main/java/org/jackhuang/hmcl/game/LauncherHelper.java index 3e7ef722194..41154875ff2 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/game/LauncherHelper.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/game/LauncherHelper.java @@ -544,7 +544,7 @@ else if (violatedMandatoryConstraints.contains(JavaVersionConstraint.VANILLA)) } if (violatedMandatoryConstraints.contains(JavaVersionConstraint.VANILLA_LINUX_JAVA_8)) { - if (!setting.get(GameSettings::useCustomNativesProperty)) { + if (!setting.getInheritable(GameSettings::useCustomNativesProperty)) { FXUtils.runInFX(() -> Controllers.dialog(i18n("launch.advice.vanilla_linux_java_8"), i18n("message.error"), MessageType.ERROR, breakAction)); return result; } else { @@ -581,7 +581,7 @@ else if (violatedMandatoryConstraints.contains(JavaVersionConstraint.VANILLA)) } // 32-bit JVM cannot make use of too much memory. - if (java.getBits() == Bits.BIT_32 && setting.getMaxMemory() > 1.5 * 1024) { + if (java.getBits() == Bits.BIT_32 && !setting.getInheritable(GameSettings::autoMemoryProperty) && setting.getMaxMemory() > 1.5 * 1024) { // 1.5 * 1024 is an inaccurate number. // Actual memory limit depends on operating system and memory. suggestions.add(i18n("launch.advice.too_large_memory_for_32bit")); @@ -628,7 +628,7 @@ else if (violatedMandatoryConstraints.contains(JavaVersionConstraint.VANILLA)) suggestions.add(i18n("launch.advice.modlauncher8")); break; case VANILLA_X86: - if (!setting.get(GameSettings::useCustomNativesProperty) + if (!setting.getInheritable(GameSettings::useCustomNativesProperty) && Platform.isSupportedTranslationX86_64()) { suggestions.add(i18n("launch.advice.vanilla_x86.translation")); } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/game/ModpackHelper.java b/HMCL/src/main/java/org/jackhuang/hmcl/game/ModpackHelper.java index e84044e1b88..6857f208388 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/game/ModpackHelper.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/game/ModpackHelper.java @@ -358,10 +358,10 @@ private static Task createMcbbsPostInstallTask(HMCLGameRepository reposito GameSettings.PROPERTY_MAX_MEMORY, GameSettings.PROPERTY_PERM_SIZE )); - setting.autoMemoryProperty().setValue(effective.get(GameSettings::autoMemoryProperty)); - setting.minMemoryProperty().setValue(effective.get(GameSettings::minMemoryProperty)); + setting.autoMemoryProperty().setValue(effective.getInheritable(GameSettings::autoMemoryProperty)); + setting.minMemoryProperty().setValue(effective.getInheritable(GameSettings::minMemoryProperty)); setting.maxMemoryProperty().setValue(manifest.getLaunchInfo().getMinMemory()); - setting.permSizeProperty().setValue(effective.get(GameSettings::permSizeProperty)); + setting.permSizeProperty().setValue(effective.getInheritable(GameSettings::permSizeProperty)); } }); } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/setting/GameSettings.java b/HMCL/src/main/java/org/jackhuang/hmcl/setting/GameSettings.java index 81fdd30229e..1fdef09757d 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/setting/GameSettings.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/setting/GameSettings.java @@ -331,6 +331,11 @@ public boolean isEmpty() { } /// Creates a new inheritable property. + protected final InheritableProperty<@Nullable T> newInheritableProperty(String name) { + return new SimpleInheritableProperty<>(this, name, null); + } + + /// Creates a new inheritable property with the given default value. protected final InheritableProperty newInheritableProperty(String name, T defaultValue) { return new SimpleInheritableProperty<>(this, name, defaultValue); } @@ -388,10 +393,10 @@ public InheritableProperty detectedJavaProperty() { /// User customized JVM options. @SerializedName(PROPERTY_JVM_OPTIONS) - private final SettingProperty jvmOptions = newSettingProperty(PROPERTY_JVM_OPTIONS, ""); + private final InheritableProperty jvmOptions = newInheritableProperty(PROPERTY_JVM_OPTIONS, ""); /// Returns the user customized JVM options property. - public SettingProperty jvmOptionsProperty() { + public InheritableProperty jvmOptionsProperty() { return jvmOptions; } @@ -448,10 +453,10 @@ public InheritableProperty notCheckGameProperty() { /// If `true`, HMCL will automatically adjust memory allocation. @SerializedName(PROPERTY_AUTO_MEMORY) - private final SettingProperty autoMemory = newSettingProperty(PROPERTY_AUTO_MEMORY, true); + private final InheritableProperty autoMemory = newInheritableProperty(PROPERTY_AUTO_MEMORY, true); /// Returns the automatic memory allocation property. - public SettingProperty autoMemoryProperty() { + public InheritableProperty autoMemoryProperty() { return autoMemory; } @@ -460,10 +465,10 @@ public SettingProperty autoMemoryProperty() { /// The minimum heap memory in MiB. @SerializedName(PROPERTY_MIN_MEMORY) - private final SettingProperty<@Nullable Integer> minMemory = newSettingProperty(PROPERTY_MIN_MEMORY); + private final InheritableProperty<@Nullable Integer> minMemory = newInheritableProperty(PROPERTY_MIN_MEMORY); /// Returns the minimum heap memory property. - public SettingProperty<@Nullable Integer> minMemoryProperty() { + public InheritableProperty<@Nullable Integer> minMemoryProperty() { return minMemory; } @@ -472,10 +477,10 @@ public SettingProperty autoMemoryProperty() { /// The maximum heap memory in MiB. @SerializedName(PROPERTY_MAX_MEMORY) - private final SettingProperty<@Nullable Integer> maxMemory = newSettingProperty(PROPERTY_MAX_MEMORY, SUGGESTED_MEMORY); + private final InheritableProperty<@Nullable Integer> maxMemory = newInheritableProperty(PROPERTY_MAX_MEMORY, SUGGESTED_MEMORY); /// Returns the maximum heap memory property. - public SettingProperty<@Nullable Integer> maxMemoryProperty() { + public InheritableProperty<@Nullable Integer> maxMemoryProperty() { return maxMemory; } @@ -484,10 +489,10 @@ public SettingProperty autoMemoryProperty() { /// The permanent generation or metaspace size in MiB. @SerializedName(PROPERTY_PERM_SIZE) - private final SettingProperty permSize = newSettingProperty(PROPERTY_PERM_SIZE, ""); + private final InheritableProperty permSize = newInheritableProperty(PROPERTY_PERM_SIZE, ""); /// Returns the permanent generation or metaspace size property. - public SettingProperty permSizeProperty() { + public InheritableProperty permSizeProperty() { return permSize; } @@ -593,10 +598,10 @@ public InheritableProperty disableAutoGameOptionsProperty() { /// User customized arguments passed to the game. @SerializedName(PROPERTY_GAME_ARGS) - private final SettingProperty gameArguments = newSettingProperty(PROPERTY_GAME_ARGS, ""); + private final InheritableProperty gameArguments = newInheritableProperty(PROPERTY_GAME_ARGS, ""); /// Returns the customized game arguments property. - public SettingProperty gameArgumentsProperty() { + public InheritableProperty gameArgumentsProperty() { return gameArguments; } @@ -643,10 +648,10 @@ public InheritableProperty vulkanRendererProperty() { /// User customized environment variables passed to the game. @SerializedName(PROPERTY_ENVIRONMENT_VARIABLES) - private final SettingProperty environmentVariables = newSettingProperty(PROPERTY_ENVIRONMENT_VARIABLES, ""); + private final InheritableProperty environmentVariables = newInheritableProperty(PROPERTY_ENVIRONMENT_VARIABLES, ""); /// Returns the customized environment variables property. - public SettingProperty environmentVariablesProperty() { + public InheritableProperty environmentVariablesProperty() { return environmentVariables; } @@ -763,10 +768,10 @@ public InheritableProperty enableDebugLogOutputProperty() { /// If `true`, HMCL does not patch native libraries. @SerializedName(PROPERTY_NOT_PATCH_NATIVES) - private final SettingProperty notPatchNatives = newSettingProperty(PROPERTY_NOT_PATCH_NATIVES, false); + private final InheritableProperty notPatchNatives = newInheritableProperty(PROPERTY_NOT_PATCH_NATIVES, false); /// Returns the native library patching property. - public SettingProperty notPatchNativesProperty() { + public InheritableProperty notPatchNativesProperty() { return notPatchNatives; } @@ -775,10 +780,10 @@ public SettingProperty notPatchNativesProperty() { /// If `true`, HMCL leaves native library management to the user. @SerializedName(PROPERTY_USE_CUSTOM_NATIVES) - private final SettingProperty useCustomNatives = newSettingProperty(PROPERTY_USE_CUSTOM_NATIVES, false); + private final InheritableProperty useCustomNatives = newInheritableProperty(PROPERTY_USE_CUSTOM_NATIVES, false); /// Returns the custom native libraries property. - public SettingProperty useCustomNativesProperty() { + public InheritableProperty useCustomNativesProperty() { return useCustomNatives; } @@ -787,10 +792,10 @@ public SettingProperty useCustomNativesProperty() { /// The path to the native library directory, or an empty string for the default path. @SerializedName(PROPERTY_NATIVES_DIRECTORY) - private final SettingProperty nativesDirectory = newSettingProperty(PROPERTY_NATIVES_DIRECTORY, ""); + private final InheritableProperty nativesDirectory = newInheritableProperty(PROPERTY_NATIVES_DIRECTORY, ""); /// Returns the native library directory property. - public SettingProperty nativesDirectoryProperty() { + public InheritableProperty nativesDirectoryProperty() { return nativesDirectory; } @@ -799,10 +804,10 @@ public SettingProperty nativesDirectoryProperty() { /// If `true`, HMCL will use native GLFW. @SerializedName(PROPERTY_USE_NATIVE_GLFW) - private final SettingProperty useNativeGLFW = newSettingProperty(PROPERTY_USE_NATIVE_GLFW, false); + private final InheritableProperty useNativeGLFW = newInheritableProperty(PROPERTY_USE_NATIVE_GLFW, false); /// Returns the native GLFW property. - public SettingProperty useNativeGLFWProperty() { + public InheritableProperty useNativeGLFWProperty() { return useNativeGLFW; } @@ -811,10 +816,10 @@ public SettingProperty useNativeGLFWProperty() { /// If `true`, HMCL will use native OpenAL. @SerializedName(PROPERTY_USE_NATIVE_OPENAL) - private final SettingProperty useNativeOpenAL = newSettingProperty(PROPERTY_USE_NATIVE_OPENAL, false); + private final InheritableProperty useNativeOpenAL = newInheritableProperty(PROPERTY_USE_NATIVE_OPENAL, false); /// Returns the native OpenAL property. - public SettingProperty useNativeOpenALProperty() { + public InheritableProperty useNativeOpenALProperty() { return useNativeOpenAL; } @@ -898,11 +903,6 @@ public Preset getPreset() { return instance; } - /// Returns the effective value for a property whose override state is tracked by the property itself. - public T get(Function> propertyGetter) { - return inherited(preset, instance, propertyGetter); - } - /// Returns the effective value for an inheritable property. public T getInheritable( Function> propertyGetter) { @@ -981,7 +981,7 @@ public void setJavaAutoSelected() { /// Returns the effective maximum heap memory in MiB. public int getMaxMemory() { - Integer value = get(GameSettings::maxMemoryProperty); + Integer value = getInheritable(GameSettings::maxMemoryProperty); return value != null && value > 0 ? value : SUGGESTED_MEMORY; } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/export/ExportWizardProvider.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/export/ExportWizardProvider.java index cc667128769..ae85a67e60b 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/export/ExportWizardProvider.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/export/ExportWizardProvider.java @@ -190,7 +190,7 @@ public void execute() { "OneSix", exportInfo.getName() + "-" + exportInfo.getVersion(), null, - Lang.toIntOrNull(setting.get(GameSettings::permSizeProperty)), + Lang.toIntOrNull(setting.getInheritable(GameSettings::permSizeProperty)), setting.getInheritable(GameSettings::commandWrapperProperty), setting.getInheritable(GameSettings::preLaunchCommandProperty), null, diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/export/ModpackInfoPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/export/ModpackInfoPage.java index 402f0fb50d3..a082e7e1e79 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/export/ModpackInfoPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/export/ModpackInfoPage.java @@ -101,9 +101,9 @@ public ModpackInfoPage(WizardController controller, HMCLGameRepository gameRepos author.set(Optional.ofNullable(Accounts.getSelectedAccount()).map(Account::getProfileName).orElse("")); GameSettings.Effective versionSetting = gameRepository.getEffectiveGameSettings(versionName); - minMemory.set(Optional.ofNullable(versionSetting.get(GameSettings::minMemoryProperty)).orElse(0)); - launchArguments.set(versionSetting.get(GameSettings::gameArgumentsProperty)); - javaArguments.set(versionSetting.get(GameSettings::jvmOptionsProperty)); + minMemory.set(Optional.ofNullable(versionSetting.getInheritable(GameSettings::minMemoryProperty)).orElse(0)); + launchArguments.set(versionSetting.getInheritable(GameSettings::gameArgumentsProperty)); + javaArguments.set(versionSetting.getInheritable(GameSettings::jvmOptionsProperty)); canIncludeLauncher = JarUtils.thisJarPath() != null; } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/game/GameSettingsPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/game/GameSettingsPage.java index 836f36c1271..0c21d97f735 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/game/GameSettingsPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/game/GameSettingsPage.java @@ -1923,7 +1923,7 @@ private void bindMemorySelectedValue(ObjectProperty memorySelectedValue } memorySelectedValue.set(resolveEffectiveSetting(setting) - .get(GameSettings::autoMemoryProperty)); + .getInheritable(GameSettings::autoMemoryProperty)); }; InvalidationListener weakRefresh = holder.weak(refresh); refreshHolder.value = weakRefresh; diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/util/NativePatcher.java b/HMCL/src/main/java/org/jackhuang/hmcl/util/NativePatcher.java index a5019d739b5..4e843180bdf 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/util/NativePatcher.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/util/NativePatcher.java @@ -78,7 +78,7 @@ public static Version patchNative(DefaultGameRepository repository, JavaRuntime javaVersion, GameSettings.Effective settings, List javaArguments) { - if (settings.get(GameSettings::useCustomNativesProperty)) { + if (settings.getInheritable(GameSettings::useCustomNativesProperty)) { if (gameVersion != null && GameVersionNumber.compare(gameVersion, "1.19") < 0) return version; @@ -96,8 +96,8 @@ public static Version patchNative(DefaultGameRepository repository, return version.setLibraries(newLibraries); } - final boolean useNativeGLFW = settings.get(GameSettings::useNativeGLFWProperty); - final boolean useNativeOpenAL = settings.get(GameSettings::useNativeOpenALProperty); + final boolean useNativeGLFW = settings.getInheritable(GameSettings::useNativeGLFWProperty); + final boolean useNativeOpenAL = settings.getInheritable(GameSettings::useNativeOpenALProperty); if (OperatingSystem.CURRENT_OS.isLinuxOrBSD() && (useNativeGLFW || useNativeOpenAL) && gameVersion != null && GameVersionNumber.compare(gameVersion, "1.19") >= 0) { @@ -124,7 +124,7 @@ public static Version patchNative(DefaultGameRepository repository, Architecture arch = javaVersion.getArchitecture(); GameVersionNumber gameVersionNumber = gameVersion != null ? GameVersionNumber.asGameVersion(gameVersion) : null; - if (settings.get(GameSettings::notPatchNativesProperty)) + if (settings.getInheritable(GameSettings::notPatchNativesProperty)) return version; if (arch.isX86() && (os == OperatingSystem.WINDOWS || os == OperatingSystem.LINUX || os == OperatingSystem.MACOS)) From 20fac426f8a41bf13b4305c6b5bb2ef278ec59c3 Mon Sep 17 00:00:00 2001 From: Glavo Date: Thu, 9 Jul 2026 22:10:22 +0800 Subject: [PATCH 2/3] refactor: enhance memory allocation handling in GameSettings and HMCLGameRepository --- .../hmcl/game/HMCLGameRepository.java | 40 ++++++++++++--- .../jackhuang/hmcl/game/LauncherHelper.java | 2 +- .../jackhuang/hmcl/setting/GameSettings.java | 2 +- .../ui/game/IndependentSettingBinder.java | 51 ++++++------------- .../resources/assets/lang/I18N.properties | 6 +-- .../resources/assets/lang/I18N_ar.properties | 6 +-- .../resources/assets/lang/I18N_es.properties | 6 +-- .../resources/assets/lang/I18N_ja.properties | 6 +-- .../resources/assets/lang/I18N_lzh.properties | 6 +-- .../resources/assets/lang/I18N_ru.properties | 6 +-- .../resources/assets/lang/I18N_uk.properties | 6 +-- .../resources/assets/lang/I18N_zh.properties | 6 +-- .../assets/lang/I18N_zh_CN.properties | 6 +-- 13 files changed, 67 insertions(+), 82 deletions(-) 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 1f9289d7620..c00ac8fc021 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameRepository.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameRepository.java @@ -382,8 +382,9 @@ private InstanceGameSettingsLoadResult loadGameSettingsFile(Path file) { + file + ", Actual: " + schemaResult.actual()); case UNEXPECTED_ID -> LOG.warning("Unexpected instance game settings schema. Expected: " + GameSettings.Instance.CURRENT_SCHEMA + ", Actual: " + schemaResult.actual()); - case UNSUPPORTED_MAJOR, READ_ONLY_PRESERVE_SCHEMA -> LOG.warning("Unsupported instance game settings schema. Expected: " - + GameSettings.Instance.CURRENT_SCHEMA + ", Actual: " + schemaResult.actual()); + case UNSUPPORTED_MAJOR, READ_ONLY_PRESERVE_SCHEMA -> + LOG.warning("Unsupported instance game settings schema. Expected: " + + GameSettings.Instance.CURRENT_SCHEMA + ", Actual: " + schemaResult.actual()); case READ_WRITE, READ_WRITE_PRESERVE_SCHEMA -> { } } @@ -684,7 +685,7 @@ private void saveGameSettingsSync(String id) throws IOException { /// Result of loading an instance-specific game settings file. /// - /// @param setting the loaded instance settings, or `null` when unavailable + /// @param setting the loaded instance settings, or `null` when unavailable /// @param allowSave whether the file may be overwritten private record InstanceGameSettingsLoadResult( @Nullable GameSettings.Instance setting, @@ -697,6 +698,15 @@ public LaunchOptions.Builder getLaunchOptions(String version, JavaRuntime javaVe boolean autoMemory = vs.getInheritable(GameSettings::autoMemoryProperty); GameVersionNumber gameVersionNumber = GameVersionNumber.asGameVersion(getGameVersion(version)); + @Nullable Integer maxMemory; + if (autoMemory) { + maxMemory = noJVMOptions + ? null + : Math.toIntExact(getAutoAllocatedMemory(SystemInfo.getPhysicalMemoryStatus().available()) / 1024L / 1024L); + } else { + maxMemory = vs.getMaxMemory(); + } + LaunchOptions.Builder builder = new LaunchOptions.Builder() .setGameDir(gameDir) .setJava(javaVersion) @@ -705,11 +715,7 @@ public LaunchOptions.Builder getLaunchOptions(String version, JavaRuntime javaVe .setProfileName(Metadata.TITLE) .setGameArguments(StringUtils.tokenize(vs.getInheritable(GameSettings::gameArgumentsProperty))) .setOverrideJavaArguments(StringUtils.tokenize(vs.getInheritable(GameSettings::jvmOptionsProperty))) - .setMaxMemory(noJVMOptions && autoMemory ? null : (int) (getAllocatedMemory( - vs.getMaxMemory() * 1024L * 1024L, - SystemInfo.getPhysicalMemoryStatus().available(), - autoMemory - ) / 1024 / 1024)) + .setMaxMemory(maxMemory) .setMinMemory(vs.getInheritable(GameSettings::minMemoryProperty)) .setMetaspace(Lang.toIntOrNull(vs.getInheritable(GameSettings::permSizeProperty))) .setEnvironmentVariables( @@ -838,6 +844,24 @@ public boolean versionIdConflicts(String id) { } } + public static long getAutoAllocatedMemory(long available) { + long usable = available - 512 * 1024 * 1024; // Reserve 512 MiB memory for off-heap memory and HMCL itself + if (usable <= 0) { + return available; + } + + final long threshold = 8L * 1024 * 1024 * 1024; // 8 GiB + final long suggested; + if (usable <= threshold) + suggested = (long) (usable * 0.8); + else + suggested = Math.min( + (long) (threshold * 0.8 + (usable - threshold) * 0.2), + 16L * 1024 * 1024 * 1024); + return suggested; + } + + public static long getAllocatedMemory(long minimum, long available, boolean auto) { if (auto) { available -= 512 * 1024 * 1024; // Reserve 512 MiB memory for off-heap memory and HMCL itself diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/game/LauncherHelper.java b/HMCL/src/main/java/org/jackhuang/hmcl/game/LauncherHelper.java index 41154875ff2..8dec8becf8b 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/game/LauncherHelper.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/game/LauncherHelper.java @@ -640,7 +640,7 @@ else if (violatedMandatoryConstraints.contains(JavaVersionConstraint.VANILLA)) // Cannot allocate too much memory exceeding free space. long totalMemorySizeMB = (long) MEGABYTES.convertFromBytes(SystemInfo.getTotalMemorySize()); - if (totalMemorySizeMB > 0 && totalMemorySizeMB < setting.getMaxMemory()) { + if (totalMemorySizeMB > 0 && !setting.getInheritable(GameSettings::autoMemoryProperty) && totalMemorySizeMB < setting.getMaxMemory()) { suggestions.add(i18n("launch.advice.not_enough_space", totalMemorySizeMB)); } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/setting/GameSettings.java b/HMCL/src/main/java/org/jackhuang/hmcl/setting/GameSettings.java index 1fdef09757d..75d2d19d33a 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/setting/GameSettings.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/setting/GameSettings.java @@ -67,7 +67,7 @@ @NotNullByDefault public sealed abstract class GameSettings extends ObservableSetting { /// Suggested maximum heap memory in MiB. - static final int SUGGESTED_MEMORY; + public static final int SUGGESTED_MEMORY; static { double totalMemoryMB = MEGABYTES.convertFromBytes(SystemInfo.getTotalMemorySize()); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/game/IndependentSettingBinder.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/game/IndependentSettingBinder.java index a062b071d1c..2d844398125 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/game/IndependentSettingBinder.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/game/IndependentSettingBinder.java @@ -617,47 +617,26 @@ private static void updateMemoryStatus( Label allocatedMemoryLabel, @Nullable Boolean autoMemory, @Nullable Integer maxMemory) { - memoryStatusBar.memoryAllocatedProperty().set(calculateAllocatedMemory(memoryStatusBar, autoMemory, maxMemory)); - updateMemoryLabels(memoryStatusBar, physicalMemoryLabel, allocatedMemoryLabel, autoMemory, maxMemory); - } + PhysicalMemoryStatus memoryStatus = memoryStatusBar.getMemoryStatus(); - private static double calculateAllocatedMemory( - MemoryStatusBar memoryStatusBar, - @Nullable Boolean autoMemory, - @Nullable Integer maxMemory) { - long maxMemoryBytes = Math.max(0, maxMemory != null ? maxMemory : 0) * 1024L * 1024L; - return HMCLGameRepository.getAllocatedMemory( - maxMemoryBytes, - memoryStatusBar.getMemoryStatus().available(), - Boolean.TRUE.equals(autoMemory)); - } + long memoryAllocated; + if (Boolean.TRUE.equals(autoMemory)) { + memoryAllocated = HMCLGameRepository.getAutoAllocatedMemory(memoryStatus.available()); + } else if (maxMemory != null && maxMemory > 0) { + memoryAllocated = maxMemory * 1024L * 1024L; + } else { + memoryAllocated = GameSettings.SUGGESTED_MEMORY * 1024L * 1024L; + } + + memoryStatusBar.memoryAllocatedProperty().set(memoryAllocated); - private static void updateMemoryLabels( - MemoryStatusBar memoryStatusBar, - Label physicalMemoryLabel, - Label allocatedMemoryLabel, - @Nullable Boolean autoMemory, - @Nullable Integer maxMemory) { - PhysicalMemoryStatus memoryStatus = memoryStatusBar.getMemoryStatus(); - long maxMemoryBytes = Math.max(0, maxMemory != null ? maxMemory : 0) * 1024L * 1024L; - boolean autoMemoryEnabled = Boolean.TRUE.equals(autoMemory); physicalMemoryLabel.setText(i18n("settings.memory.used_per_total", GIGABYTES.convertFromBytes(memoryStatus.getUsed()), GIGABYTES.convertFromBytes(memoryStatus.total()))); - allocatedMemoryLabel.setText(i18n( - memoryStatus.hasAvailable() && maxMemoryBytes > memoryStatus.available() - ? (autoMemoryEnabled - ? "settings.memory.allocate.auto.exceeded" - : "settings.memory.allocate.manual.exceeded") - : (autoMemoryEnabled - ? "settings.memory.allocate.auto" - : "settings.memory.allocate.manual"), - GIGABYTES.convertFromBytes(maxMemoryBytes), - GIGABYTES.convertFromBytes(HMCLGameRepository.getAllocatedMemory( - maxMemoryBytes, - memoryStatus.available(), - autoMemoryEnabled)), - GIGABYTES.convertFromBytes(memoryStatus.available()))); + allocatedMemoryLabel.setText( + memoryStatus.hasAvailable() && memoryAllocated > memoryStatus.available() + ? i18n("settings.memory.allocate.exceeded", GIGABYTES.convertFromBytes(memoryAllocated), GIGABYTES.convertFromBytes(memoryStatus.available())) + : i18n("settings.memory.allocate", GIGABYTES.convertFromBytes(memoryAllocated))); } private static void toggleOverride( diff --git a/HMCL/src/main/resources/assets/lang/I18N.properties b/HMCL/src/main/resources/assets/lang/I18N.properties index e1365723a82..3d37dd15f86 100644 --- a/HMCL/src/main/resources/assets/lang/I18N.properties +++ b/HMCL/src/main/resources/assets/lang/I18N.properties @@ -1610,10 +1610,8 @@ settings.launcher.version_list_source=Version List settings.launcher.background.settings.opacity=Opacity settings.memory=Memory -settings.memory.allocate.auto=%1$.1f GiB Minimum / %2$.1f GiB Allocated -settings.memory.allocate.auto.exceeded=%1$.1f GiB Minimum / %2$.1f GiB Allocated (%3$.1f GiB Available) -settings.memory.allocate.manual=%1$.1f GiB Allocated -settings.memory.allocate.manual.exceeded=%1$.1f GiB Allocated (%3$.1f GiB Available) +settings.memory.allocate=%1$.1f GiB Allocated +settings.memory.allocate.exceeded=%1$.1f GiB Allocated (%2$.1f GiB Available) settings.memory.auto_allocate=Automatically Allocate settings.memory.lower_bound=Minimum Memory settings.memory.manual_allocate=Manually Allocate Memory diff --git a/HMCL/src/main/resources/assets/lang/I18N_ar.properties b/HMCL/src/main/resources/assets/lang/I18N_ar.properties index 58144270e0b..29e15ac5f25 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_ar.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_ar.properties @@ -1428,10 +1428,8 @@ settings.launcher.version_list_source=قائمة الإصدارات settings.launcher.background.settings.opacity=الشفافية settings.memory=الذاكرة -settings.memory.allocate.auto=%1$.1f جيجابايت كحد أدنى / %2$.1f جيجابايت مخصص -settings.memory.allocate.auto.exceeded=%1$.1f جيجابايت كحد أدنى / %2$.1f جيجابايت مخصص (%3$.1f جيجابايت متاح) -settings.memory.allocate.manual=%1$.1f جيجابايت مخصص -settings.memory.allocate.manual.exceeded=%1$.1f جيجابايت مخصص (%3$.1f جيجابايت متاح) +settings.memory.allocate=%1$.1f جيجابايت مخصص +settings.memory.allocate.exceeded=%1$.1f جيجابايت مخصص (%2$.1f جيجابايت متاح) settings.memory.auto_allocate=تخصيص تلقائي settings.memory.lower_bound=الحد الأدنى للذاكرة settings.memory.unit.mib=ميجابايت diff --git a/HMCL/src/main/resources/assets/lang/I18N_es.properties b/HMCL/src/main/resources/assets/lang/I18N_es.properties index 6e93385e37d..ee5d342416e 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_es.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_es.properties @@ -1329,10 +1329,8 @@ settings.launcher.version_list_source=Lista de versiones settings.launcher.background.settings.opacity=Opacidad settings.memory=Memoria -settings.memory.allocate.auto=%1$.1f GiB Mínimo / %2$.1f GiB Asignado -settings.memory.allocate.auto.exceeded=%1$.1f GiB Mínimo / %2$.1f GiB Asignado (%3$.1f GiB Disponible) -settings.memory.allocate.manual=%1$.1f GiB Asignados -settings.memory.allocate.manual.exceeded=%1$.1f GiB Asignados (%3$.1f GiB Disponibles) +settings.memory.allocate=%1$.1f GiB Asignados +settings.memory.allocate.exceeded=%1$.1f GiB Asignados (%2$.1f GiB Disponibles) settings.memory.auto_allocate=Asignar automáticamente settings.memory.lower_bound=Memoria mínima settings.memory.unit.mib=MiB diff --git a/HMCL/src/main/resources/assets/lang/I18N_ja.properties b/HMCL/src/main/resources/assets/lang/I18N_ja.properties index e8c7856aa8e..0d962e76ef9 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_ja.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_ja.properties @@ -856,10 +856,8 @@ settings.launcher.title_transparent=タイトルの透明性 settings.launcher.version_list_source=バージョンリストソース settings.memory=ゲームメモリ -settings.memory.allocate.auto=最小 %1$.1f GiB /実際の %2$.1f GiB -settings.memory.allocate.auto.exceeded=最小 %1$.1f GiB /実際の %2$.1f GiB(%3$.1f GiB使用可能) -settings.memory.allocate.manual=割り当て %1$.1f GiB -settings.memory.allocate.manual.exceeded=割り当て %1$.1f GiB(%3$.1f GiB使用可能) +settings.memory.allocate=割り当て %1$.1f GiB +settings.memory.allocate.exceeded=割り当て %1$.1f GiB(%2$.1f GiB使用可能) settings.memory.auto_allocate=メモリサイズの自動割り当て settings.memory.lower_bound=最小メモリ settings.memory.unit.mib=MiB diff --git a/HMCL/src/main/resources/assets/lang/I18N_lzh.properties b/HMCL/src/main/resources/assets/lang/I18N_lzh.properties index 7c8c5eb4f3a..4cf3954aaf9 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_lzh.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_lzh.properties @@ -1152,10 +1152,8 @@ settings.launcher.version_list_source=版列供者 settings.launcher.background.settings.opacity=陰翳 settings.memory=戲憶 -settings.memory.allocate.auto=至低分之 %1$.1f GiB / 實分之 %2$.1f GiB -settings.memory.allocate.auto.exceeded=至低分之 %1$.1f GiB / 實分之 %2$.1f GiB (械祇 %3$.1f GiB) -settings.memory.allocate.manual=戲分之 %1$.1f GiB -settings.memory.allocate.manual.exceeded=戲分之 %1$.1f GiB (械祇 %3$.1f GiB) +settings.memory.allocate=戲分之 %1$.1f GiB +settings.memory.allocate.exceeded=戲分之 %1$.1f GiB (械祇 %2$.1f GiB) settings.memory.auto_allocate=自調其憶 settings.memory.lower_bound=至低憶界 settings.memory.unit.mib=兆二進字節 diff --git a/HMCL/src/main/resources/assets/lang/I18N_ru.properties b/HMCL/src/main/resources/assets/lang/I18N_ru.properties index e9093c84542..7c67982f9bd 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_ru.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_ru.properties @@ -1318,10 +1318,8 @@ settings.launcher.version_list_source=Список версий settings.launcher.background.settings.opacity=Непрозрачность settings.memory=Оперативная память -settings.memory.allocate.auto=Минимум %1$.1f ГиБ / Выделено %2$.1f ГиБ -settings.memory.allocate.auto.exceeded=Минимум %1$.1f ГиБ / Выделено %2$.1f ГиБ (Доступно %3$.1f ГиБ) -settings.memory.allocate.manual=Выделено %1$.1f ГиБ -settings.memory.allocate.manual.exceeded=Выделено %1$.1f ГиБ (Доступно %3$.1f ГиБ) +settings.memory.allocate=Выделено %1$.1f ГиБ +settings.memory.allocate.exceeded=Выделено %1$.1f ГиБ (Доступно %2$.1f ГиБ) settings.memory.auto_allocate=Автовыделение settings.memory.lower_bound=Мин. объём памяти settings.memory.unit.mib=МиБ diff --git a/HMCL/src/main/resources/assets/lang/I18N_uk.properties b/HMCL/src/main/resources/assets/lang/I18N_uk.properties index 3607e586ebe..04a726a6c6e 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_uk.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_uk.properties @@ -1267,10 +1267,8 @@ settings.launcher.version_list_source=Список версій settings.launcher.background.settings.opacity=Непрозорість settings.memory=Пам'ять -settings.memory.allocate.auto=%1$.1f ГіБ Мінімум / %2$.1f ГіБ Виділено -settings.memory.allocate.auto.exceeded=%1$.1f ГіБ Мінімум / %2$.1f ГіБ Виділено (%3$.1f ГіБ Доступно) -settings.memory.allocate.manual=%1$.1f ГіБ Виділено -settings.memory.allocate.manual.exceeded=%1$.1f ГіБ Виділено (%3$.1f ГіБ Доступно) +settings.memory.allocate=%1$.1f ГіБ Виділено +settings.memory.allocate.exceeded=%1$.1f ГіБ Виділено (%2$.1f ГіБ Доступно) settings.memory.auto_allocate=Автоматично виділяти settings.memory.lower_bound=Мінімальна пам'ять settings.memory.unit.mib=МіБ diff --git a/HMCL/src/main/resources/assets/lang/I18N_zh.properties b/HMCL/src/main/resources/assets/lang/I18N_zh.properties index 69a467a554d..0daadf307f1 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh.properties @@ -1401,10 +1401,8 @@ settings.launcher.version_list_source=版本清單來源 settings.launcher.background.settings.opacity=不透明度 settings.memory=遊戲記憶體 -settings.memory.allocate.auto=最低分配 %1$.1f GiB / 實際分配 %2$.1f GiB -settings.memory.allocate.auto.exceeded=最低分配 %1$.1f GiB / 實際分配 %2$.1f GiB (%3$.1f GiB 可用) -settings.memory.allocate.manual=遊戲分配 %1$.1f GiB -settings.memory.allocate.manual.exceeded=遊戲分配 %1$.1f GiB (%3$.1f GiB 可用) +settings.memory.allocate=遊戲分配 %1$.1f GiB +settings.memory.allocate.exceeded=遊戲分配 %1$.1f GiB (%2$.1f GiB 可用) settings.memory.auto_allocate=自動分配 settings.memory.lower_bound=最低分配 settings.memory.manual_allocate=手動選取記憶體 diff --git a/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties b/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties index bdf7c024f44..8774bf278c2 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties @@ -1406,10 +1406,8 @@ settings.launcher.version_list_source=版本列表源 settings.launcher.background.settings.opacity=不透明度 settings.memory=游戏内存 -settings.memory.allocate.auto=最低分配 %1$.1f GiB / 实际分配 %2$.1f GiB -settings.memory.allocate.auto.exceeded=最低分配 %1$.1f GiB / 实际分配 %2$.1f GiB (%3$.1f GiB 可用) -settings.memory.allocate.manual=游戏分配 %1$.1f GiB -settings.memory.allocate.manual.exceeded=游戏分配 %1$.1f GiB (设备仅 %3$.1f GiB 可用) +settings.memory.allocate=游戏分配 %1$.1f GiB +settings.memory.allocate.exceeded=游戏分配 %1$.1f GiB (设备仅 %2$.1f GiB 可用) settings.memory.auto_allocate=自动分配内存 settings.memory.lower_bound=最低内存分配 settings.memory.manual_allocate=手动选择内存 From 962b0a2252d71558aaa9663b22fa62db64d0b308 Mon Sep 17 00:00:00 2001 From: Glavo Date: Thu, 9 Jul 2026 22:12:13 +0800 Subject: [PATCH 3/3] refactor: remove unused getAllocatedMemory method from HMCLGameRepository --- .../hmcl/game/HMCLGameRepository.java | 21 +------------------ 1 file changed, 1 insertion(+), 20 deletions(-) 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 c00ac8fc021..ca46611666c 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameRepository.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameRepository.java @@ -383,7 +383,7 @@ private InstanceGameSettingsLoadResult loadGameSettingsFile(Path file) { case UNEXPECTED_ID -> LOG.warning("Unexpected instance game settings schema. Expected: " + GameSettings.Instance.CURRENT_SCHEMA + ", Actual: " + schemaResult.actual()); case UNSUPPORTED_MAJOR, READ_ONLY_PRESERVE_SCHEMA -> - LOG.warning("Unsupported instance game settings schema. Expected: " + LOG.warning("Unsupported instance game settings schema. Expected: " + GameSettings.Instance.CURRENT_SCHEMA + ", Actual: " + schemaResult.actual()); case READ_WRITE, READ_WRITE_PRESERVE_SCHEMA -> { } @@ -861,25 +861,6 @@ public static long getAutoAllocatedMemory(long available) { return suggested; } - - public static long getAllocatedMemory(long minimum, long available, boolean auto) { - if (auto) { - available -= 512 * 1024 * 1024; // Reserve 512 MiB memory for off-heap memory and HMCL itself - if (available <= 0) { - return minimum; - } - - final long threshold = 8L * 1024 * 1024 * 1024; // 8 GiB - final long suggested = Math.min(available <= threshold - ? (long) (available * 0.8) - : (long) (threshold * 0.8 + (available - threshold) * 0.2), - 16L * 1024 * 1024 * 1024); - return Math.max(minimum, suggested); - } else { - return minimum; - } - } - public static ProxyOption getProxyOption() { return switch (settings().proxyTypeProperty().get()) { case SYSTEM -> ProxyOption.Default.INSTANCE;