Skip to content

重构启动器配置模型#6122

Open
Glavo wants to merge 488 commits into
HMCL-dev:mainfrom
Glavo:instance-setting2
Open

重构启动器配置模型#6122
Glavo wants to merge 488 commits into
HMCL-dev:mainfrom
Glavo:instance-setting2

Conversation

@Glavo

@Glavo Glavo commented May 18, 2026

Copy link
Copy Markdown
Member

No description provided.

@3gf8jv4dv

3gf8jv4dv commented May 18, 2026

Copy link
Copy Markdown
Contributor

这个 tooltip 可以写得详细一点。比如「当前状态:继承全局设置\n点击切换为覆盖全局设置」。
以我的经验来看,只有这六个字,真的有人不知道是能点的。

Image

@3gf8jv4dv

3gf8jv4dv commented May 18, 2026

Copy link
Copy Markdown
Contributor

「管理所有全局游戏设置」我一开始理解错了,点进去后发现是多 profile 的机制。

要不考虑用个更合适的词?比如配置档啥的 (这样英文也好写成 profile)。

Image

@Glavo Glavo force-pushed the instance-setting2 branch from fb4dc52 to 17fbe26 Compare May 18, 2026 12:40
@Glavo Glavo linked an issue May 22, 2026 that may be closed by this pull request
Glavo added 26 commits May 26, 2026 21:44
Glavo added 18 commits June 12, 2026 11:58
Compare migration receipts against legacy source content instead of
recorded file state so unchanged migrated data is not replayed after the
current file is deleted.
…nly view

Replace the direct merge of user and local profiles with a mutable backing list and an unmodifiable view. Introduce `addGameDirectory`, `removeGameDirectory`, and `hasGameDirectoryId` in `SettingsManager`, and delegate `Profiles` methods accordingly. Update `ProfilePage` and `ModpackHelper` to use the new `Profiles.addProfile`. Add test verifying shadowed profiles remain in backing stores.
…es class

Shift the merged profile view construction, profile addition, and profile removal from `SettingsManager` into `Profiles`, where the profile list is owned. Expose `localGameDirectories()` and `userGameDirectories()` accessors in `SettingsManager` for the backing stores and delegate profile operations to `Profiles`. Update tests accordingly.
- create default profiles when user and local game directories load empty
- ensure the merged profile view is rebuilt from initialized stores
- replace the nil profile ID test with coverage for empty-store loading
- preserve expected startup behavior instead of leaving profiles missing
- pass newly created local and user directories into profile loading
- keep freshly added game directory entries from being dropped at startup
- align profile nullability annotations and related tests with the
  updated loading behavior
@Minecraft269

Copy link
Copy Markdown

/gemini review

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request refactors the launcher's configuration and settings architecture, replacing the legacy Config and ConfigHolder with a modular, schema-validated settings model (SettingsManager, LauncherSettings, UserSettings, etc.) and introducing robust migration paths for legacy formats. The review feedback highlights a critical serialization issue in JsonSettingFile.java where using JsonUtils.GSON instead of LauncherSettings.SETTINGS_GSON fails to serialize JavaFX properties. Additionally, the reviewer identified a potential memory leak in ComponentSublistWrapper.java due to strong layout listeners, and a performance concern in Launcher.java regarding blocking disk I/O operations performed directly on the JavaFX Application Thread inside a binding.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

value.setBackupOnNextSave(false);
SettingFileUtils.backupInvalidConfig(location);
}
FileSaver.save(location, JsonUtils.GSON.toJson(value, type));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

critical

Using JsonUtils.GSON to serialize the settings object will fail to correctly serialize JavaFX properties (such as Property, ObservableList, etc.) because JsonUtils.GSON does not have the JavaFX property adapters registered. This will result in corrupted or incomplete JSON files when saving.

Please use LauncherSettings.SETTINGS_GSON instead, which is configured with JavaFxPropertyTypeAdapterFactory and other necessary type adapters.

Suggested change
FileSaver.save(location, JsonUtils.GSON.toJson(value, type));
FileSaver.save(location, LauncherSettings.SETTINGS_GSON.toJson(value, type));

Comment on lines +210 to +215
InvalidationListener listener = observable -> updateExpandedContentHeight(sublist);
node.layoutBoundsProperty().addListener(listener);
if (node instanceof Parent parent) {
parent.needsLayoutProperty().addListener(listener);
}
contentLayoutListeners.put(node, listener);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

high

Adding strong listeners to node.layoutBoundsProperty() and parent.needsLayoutProperty() will cause a memory leak. Since nodes are part of the persistent sublist.getContent() model, these strong listeners will prevent ComponentSublistWrapper (and the entire Skin/View hierarchy) from being garbage collected when the wrapper is replaced or the Skin is disposed.

Wrapping the listeners in WeakInvalidationListener solves this memory leak.

Suggested change
InvalidationListener listener = observable -> updateExpandedContentHeight(sublist);
node.layoutBoundsProperty().addListener(listener);
if (node instanceof Parent parent) {
parent.needsLayoutProperty().addListener(listener);
}
contentLayoutListeners.put(node, listener);
InvalidationListener listener = observable -> updateExpandedContentHeight(sublist);
node.layoutBoundsProperty().addListener(new javafx.beans.WeakInvalidationListener(listener));
if (node instanceof Parent parent) {
parent.needsLayoutProperty().addListener(new javafx.beans.WeakInvalidationListener(listener));
}
contentLayoutListeners.put(node, listener);

Comment on lines +172 to +179
HMCLCacheRepository.REPOSITORY.directoryProperty().bind(Bindings.createStringBinding(() -> {
String commonDirectory = SettingsManager.settings().getResolvedCommonDirectory();
if (commonDirectory != null && FileUtils.canCreateDirectory(commonDirectory)) {
return commonDirectory;
} else {
return LauncherSettings.getDefaultCommonDirectory();
}
}, SettingsManager.settings().commonDirectoryProperty(), SettingsManager.settings().commonDirectoryTypeProperty()));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

Performing blocking disk I/O operations like FileUtils.canCreateDirectory(commonDirectory) inside a JavaFX binding (Bindings.createStringBinding(...)) can cause UI lag or temporary freezes because bindings are evaluated on the JavaFX Application Thread.

Consider performing this check asynchronously or caching the result of the directory validation outside of the binding to keep the UI thread responsive.

Glavo added 8 commits June 13, 2026 12:39
- replace the warning-only dialog with a confirmation flow before
  overwriting read-only account storage
- remove the stale account entry after forcing an overwrite so the list
  reflects the new storage state
- update account list page and item UI wiring to support the revised
  restricted-account interaction flow
- improve the account management experience when offline authentication
  is restricted or storage cannot be written safely
- move page-specific bindings and drag-and-drop handling out of
  `Controllers` and into the corresponding page classes
- simplify controller setup by replacing verbose lazy initializers
  with constructor references where possible
- keep profile and account state management closer to the UI that
  consumes it to reduce central coupling and improve maintainability
- add overwrite actions for instance and preset settings when the
  current format cannot be saved safely
- keep unsupported settings pages read-only while exposing a recovery
  path instead of forcing manual file edits
- back up existing instance settings before replacement to reduce the
  risk of data loss during migration
- save migrated instance settings synchronously so migration receipts
  are not written before the new game settings reach disk
- switch shutdown-critical settings writes to synchronous saves to
  reduce data loss when the process exits immediately afterward
- treat unreadable account storage as unavailable and disable further
  writes instead of reporting read-write access for a broken file
@Wulian233

Copy link
Copy Markdown
Contributor

@codex

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8309b23ff9

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +191 to +193
profile.setName(LocalizedText.plain(txtProfileName.getText()));
if (StringUtils.isNotBlank(getLocation())) {
profile.setGameDir(Path.of(getLocation()));
profile.setPath(createPortableLocation());

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 Move edited profiles between stores when path scope changes

When an existing profile is saved after changing between an absolute path and a relative path, this branch only mutates the Profile already held by its current backing store. New profiles below are routed to user vs. local storage based on newProfile.getPath().isAbsolute(), so edits that cross that boundary leave the profile in the wrong JSON file and skip the read-only/overwrite checks, e.g. a shared profile can become workspace-relative but still be saved in user-game-directories.json.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature] 重构 VersionSetting

4 participants