Conversation
|
需要懂其他语言的人完善 i18n |
在BMC4中测试时发现BMC4会判定所有资源包兼容,与原版表现不符,会导致启用/禁用功能失效,故添加警告
HMCLCore/src/main/java/org/jackhuang/hmcl/resourcepack/ResourcepackFile.java
Outdated
Show resolved
Hide resolved
|
我下载了 Translations for Sodium,放在 1.21.9~1.21.11 的资源包文件夹内,启动器会显示「游戏版本元数据缺失」。1.21.8 和 1.21.1 正常。不知道是不是 bug。 |
Co-authored-by: 3gf8jv4dv <[email protected]>
Co-authored-by: 3gf8jv4dv <[email protected]>
确实是,我在修 |
…esourcepack-enhancement
Solved |
HMCLCore/src/main/java/org/jackhuang/hmcl/mod/LocalAddonFile.java
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 46 out of 46 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (3)
HMCLCore/src/main/java/org/jackhuang/hmcl/resourcepack/ResourcePackFile.java:27
isFileResourcePacktreats any existing path ending with.zipas a resource pack, even if it is a directory withoutpack.mcmeta(e.g., an extracted folder namedfoo.zip). That will makeparse(...)attempt to load it as aResourcePackFolderand surface misleading warnings/log spam instead of skipping it. Consider requiringFiles.isRegularFile(file) && name.endsWith(".zip")for zip packs, andFiles.isDirectory(file) && Files.isRegularFile(file.resolve("pack.mcmeta"))for folder packs.
HMCLCore/src/main/java/org/jackhuang/hmcl/resourcepack/ResourcePackManager.java:252refresh()currently callsaddResourcePackInfo(subitem)without per-entry exception handling. If a single broken/invalid zip triggers anIOExceptionduring parsing, the whole refresh fails and the UI ends up clearing the entire list (seeResourcePackListPage.refresh()exception path). Consider catching exceptions per file inside the loop, logging, and continuing so one bad pack doesn’t prevent listing the rest.
HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/AddonUpdatesPage.java:301AddonUpdateTaskdownloads the updated file usingremote.getFile().getFilename()as the target name. For resource packs, Minecraft references enabled packs by filename inoptions.txt(e.g.,resourcePacks=["file/<filename>"]), so if the remote filename differs from the current local filename, updating will silently disable the pack and leave a stale entry pointing to a non-existent file. Consider preserving the existing filename for resource packs (download to the old name), or updatingoptions.txtto replace the old packId with the new filename after a successful update.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
# Conflicts: # HMCLCore/src/main/java/org/jackhuang/hmcl/mod/curse/CurseForgeRemoteModRepository.java
HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/ResourcePackListPage.java
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 46 out of 46 changed files in this pull request and generated 1 comment.
Comments suppressed due to low confidence (5)
HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/AddonUpdatesPage.java:318
- When restoring the state after a failed download (lines 315-317), the code calls
local.setOld(false)andlocal.markDisabled()if the file was disabled. However, these operations can throwIOExceptionwhich is not caught here. IfsetOldormarkDisabledthrows an exception, the restoration will fail silently and the file could be left in an inconsistent state. Consider wrapping these calls in a try-catch block or documenting that they shouldn't throw exceptions in this context.
HMCLCore/src/main/java/org/jackhuang/hmcl/resourcepack/ResourcePackManager.java:118 - The condition at line 118 uses a negation with OR which creates a logic error. The intent seems to be checking if NEITHER minPackVersion NOR maxPackVersion is unspecified (i.e., both are specified), but the condition
!(A || B)is equivalent to!A && !B, which is correct. However, the comment "See https://zh.minecraft.wiki/w/Pack.mcmeta" and the subsequent logic suggest this might be handling a specific case.
The real issue is that the condition should probably be !minPackVersion.isUnspecified() && !maxPackVersion.isUnspecified() for clarity, rather than using De Morgan's law. The current form is harder to read and reason about.
HMCLCore/src/main/java/org/jackhuang/hmcl/util/StringUtils.java:272
- The
compileQuerymethod doesn't handle potentialPatternSyntaxExceptionthat can be thrown byPattern.compile(). If a user provides an invalid regex pattern after "regex:", the exception will propagate uncaught. This should either catch the exception and fall back to a literal string search, or document that callers must handlePatternSyntaxException.
HMCLCore/src/main/java/org/jackhuang/hmcl/util/StringUtils.java:630 - The broad
catch (Exception e)in bothserializeStringListanddeserializeStringListsilently swallows all exceptions and returns a default value. This makes debugging difficult when JSON serialization/deserialization fails. Consider logging the exception or being more specific about which exceptions to catch (e.g.,JsonSyntaxExceptionorJsonParseException).
HMCLCore/src/main/java/org/jackhuang/hmcl/mod/modinfo/PackMcMeta.java:84 - The
isUnspecified()method checks ifgetMin().compareTo(getMax()) > 0, butgetMin()andgetMax()create newPackVersionobjects fromminandmaxfields. If eitherminormaxis negative (which signals UNSPECIFIED), the first two conditions will catch it. However, the third condition seems redundant or potentially incorrect - it would be clearer to check the rawminandmaxvalues directly rather than creating PackVersion objects and comparing them.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| public static String getNameWithoutExtension(Path file) { | ||
| return StringUtils.substringBeforeLast(getName(file), '.'); | ||
| String name = getName(file); | ||
| if (Files.isDirectory(file)) { | ||
| return name; | ||
| } | ||
| return StringUtils.substringBeforeLast(name, '.'); | ||
| } |
There was a problem hiding this comment.
The logic for checking if a path is a directory before calling substringBeforeLast is problematic because Files.isDirectory() performs I/O operations and may throw unchecked exceptions (e.g., SecurityException). Additionally, for symbolic links, Files.isDirectory() follows the link by default, which may not be the desired behavior.
More critically, this change assumes that directories should never have their extension stripped, but this breaks the contract for resource pack folders. Resource pack folders don't have extensions to strip, but the original logic would correctly return the folder name. The new logic adds an unnecessary I/O check that could fail or introduce performance issues.
# Conflicts: # HMCL/src/main/resources/assets/lang/I18N_ar.properties
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 46 out of 46 changed files in this pull request and generated 8 comments.
Comments suppressed due to low confidence (1)
HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/AddonUpdatesPage.java:318
AddonUpdateTaskperforms filesystem operations (local.setOld(true/false),local.markDisabled()) onSchedulers.javafx(). These moves/renames can block the UI noticeably when updating multiple or large files. Consider running these steps onSchedulers.io()(and only switching back to JavaFX for UI notifications) so the update flow stays responsive.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
HMCLCore/src/main/java/org/jackhuang/hmcl/resourcepack/ResourcePackManager.java
Show resolved
Hide resolved
HMCLCore/src/main/java/org/jackhuang/hmcl/resourcepack/ResourcePackManager.java
Show resolved
Hide resolved
HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/ResourcePackListPage.java
Outdated
Show resolved
Hide resolved
| protected void updateControl(ResourcePackInfoObject item, boolean empty) { | ||
| pseudoClassStateChanged(WARNING, false); | ||
|
|
||
| if (empty || item == null) return; | ||
|
|
||
| this.object = item; | ||
| ResourcePackFile file = item.getFile(); | ||
| imageContainer.setImage(item.getIcon()); | ||
|
|
||
| content.getTags().clear(); | ||
| content.setTitle(file.getFileName()); | ||
| content.setSubtitle(file.getFileNameWithExtension()); | ||
|
|
||
| FXUtils.installFastTooltip(btnReveal, i18n("reveal.in_file_manager")); | ||
| btnReveal.setOnAction(event -> FXUtils.showFileInExplorer(file.getFile())); | ||
|
|
||
| btnInfo.setOnAction(e -> Controllers.dialog(new ResourcePackInfoDialog(this.page, item))); | ||
|
|
||
| if (booleanProperty != null) { | ||
| checkBox.selectedProperty().unbindBidirectional(booleanProperty); | ||
| } | ||
| checkBox.selectedProperty().bindBidirectional(booleanProperty = item.enabledProperty()); |
There was a problem hiding this comment.
ResourcePackListCell.updateControl returns immediately for empty || item == null without unbinding the previous booleanProperty from checkBox.selectedProperty(). With cell virtualization, this can keep the checkbox bound to a stale item and cause leaks/incorrect updates. Unbind/reset bindings (and any item references) when the cell becomes empty.
| listView.getItems().clear(); | ||
|
|
||
| Predicate<@Nullable String> predicate; | ||
| if (queryString.startsWith("regex:")) { | ||
| try { | ||
| Pattern pattern = Pattern.compile(queryString.substring("regex:".length())); | ||
| predicate = s -> s != null && pattern.matcher(s).find(); | ||
| } catch (Throwable e) { | ||
| LOG.warning("Illegal regular expression", e); | ||
| return; | ||
| } | ||
| } else { | ||
| String lowerQueryString = queryString.toLowerCase(Locale.ROOT); | ||
| predicate = s -> s != null && s.toLowerCase(Locale.ROOT).contains(lowerQueryString); | ||
| try { | ||
| predicate = StringUtils.compileQuery(queryString); | ||
| } catch (Throwable e) { | ||
| LOG.warning("Illegal regular expression", e); | ||
| return; | ||
| } |
There was a problem hiding this comment.
In search(), the list is cleared before the query predicate is compiled. If StringUtils.compileQuery(queryString) throws (invalid regex:), the method returns and leaves the list empty. Compile/validate the predicate before clearing (or repopulate on error) to avoid losing the current results due to a typo in the regex.
| listView.getItems().clear(); | ||
|
|
||
| Predicate<@Nullable String> predicate; | ||
| try { | ||
| predicate = StringUtils.compileQuery(queryString); | ||
| } catch (Throwable e) { | ||
| LOG.warning("Illegal regular expression", e); | ||
| return; | ||
| } |
There was a problem hiding this comment.
In search(), the list is cleared before compiling the query predicate. If StringUtils.compileQuery(queryString) throws (e.g., invalid regex:), the method returns early and leaves the list empty. Compile/validate the predicate first (or restore the previous items on error) so a bad regex doesn’t wipe the visible list.
| public static <T extends Comparable<T>> VersionRange<T> is(T version) { | ||
| assert version != null; | ||
| return new VersionRange<>(version, version); | ||
| } |
There was a problem hiding this comment.
VersionRange.is relies on assert version != null. In production builds where assertions are disabled, is(null) would create a range with both bounds null, which makes isAll() return true and can lead to incorrect range behavior. Prefer an explicit null check (e.g., Objects.requireNonNull(version, ...) or an IllegalArgumentException) to enforce the contract regardless of JVM flags.
HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/ResourcePackListPage.java
Outdated
Show resolved
Hide resolved
| try (var stream = Files.lines(optionsFile, StandardCharsets.UTF_8)) { | ||
| stream.forEach(s -> { | ||
| if (StringUtils.isNotBlank(s)) { | ||
| var entry = s.split(":", 2); |
There was a problem hiding this comment.
为什么要用 split?我没有看到 options.txt 有选项值中不能包含 : 的要求,像 fullscreenResolution这样的选项值内本来就能有 :。
There was a problem hiding this comment.
不是限制了只能分两份了吗,键又不可能有:
具体改动
LocalAddonFile和LocalFileManager<T extends LocalAddonFile>,使更新对于模组以外的东西(如资源包和光影包)也能适用,从而实现了资源包更新(不保留旧版本)