Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 37 additions & 32 deletions HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameRepository.java
Original file line number Diff line number Diff line change
Expand Up @@ -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 -> {
}
}
Expand Down Expand Up @@ -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,
Expand All @@ -694,26 +695,31 @@ private record InstanceGameSettingsLoadResult(
public LaunchOptions.Builder getLaunchOptions(String version, JavaRuntime javaVersion, Path gameDir, List<String> javaAgents, List<String> 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));

@Nullable Integer maxMemory;
if (autoMemory) {
maxMemory = noJVMOptions
? null
: Math.toIntExact(getAutoAllocatedMemory(SystemInfo.getPhysicalMemoryStatus().available()) / 1024L / 1024L);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve memory floors for auto allocation

When auto memory is enabled this now derives -Xmx only from current available RAM, ignoring the effective maxMemory value. That value is still used as a required floor elsewhere: the MCBBS post-install task writes manifest.getLaunchInfo().getMinMemory() into maxMemory while preserving the effective auto-memory setting. For a pack that requires e.g. 6 GiB on a machine where the auto heuristic suggests 2 GiB, launch gets the 2 GiB heap and can fail despite the installer recording the modpack's minimum.

Useful? React with 👍 / 👎.

} else {
maxMemory = vs.getMaxMemory();
}

LaunchOptions.Builder builder = new LaunchOptions.Builder()
.setGameDir(gameDir)
.setJava(javaVersion)
.setVersionType(Metadata.TITLE)
.setVersionName(version)
.setProfileName(Metadata.TITLE)
.setGameArguments(StringUtils.tokenize(vs.get(GameSettings::gameArgumentsProperty)))
.setOverrideJavaArguments(StringUtils.tokenize(vs.get(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)))
.setGameArguments(StringUtils.tokenize(vs.getInheritable(GameSettings::gameArgumentsProperty)))
.setOverrideJavaArguments(StringUtils.tokenize(vs.getInheritable(GameSettings::jvmOptionsProperty)))
.setMaxMemory(maxMemory)
.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('=');
Expand All @@ -731,16 +737,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);
Expand Down Expand Up @@ -838,22 +844,21 @@ public boolean versionIdConflicts(String id) {
}
}

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;
}
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 = Math.min(available <= threshold
? (long) (available * 0.8)
: (long) (threshold * 0.8 + (available - threshold) * 0.2),
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 Math.max(minimum, suggested);
} else {
return minimum;
}
return suggested;
}

public static ProxyOption getProxyOption() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Warn for oversized auto memory on 32-bit JVMs

With a 32-bit Java runtime and auto memory enabled, the new auto-memory path can still choose more than the 32-bit heap limit when the machine has several GiB free, but this condition now suppresses the existing warning. In that scenario HMCL proceeds with an oversized -Xmx without telling the user why the launch may fail; the check should use the computed auto allocation rather than skipping auto mode entirely.

Useful? React with 👍 / 👎.

// 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"));
Expand Down Expand Up @@ -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"));
}
Expand All @@ -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));
}

Expand Down
6 changes: 3 additions & 3 deletions HMCL/src/main/java/org/jackhuang/hmcl/game/ModpackHelper.java
Original file line number Diff line number Diff line change
Expand Up @@ -358,10 +358,10 @@ private static Task<Void> 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));
}
});
}
Expand Down
Loading