Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
74 commits
Select commit Hold shift + click to select a range
652491c
修复资源包管理界面边框样式问题
Calboot Dec 14, 2025
2bc3fa7
修复 #4680
Calboot Dec 14, 2025
c5406e3
update
Calboot Dec 14, 2025
facdd47
修复光影包界面下载标题错误
Calboot Dec 14, 2025
d24b7b2
资源包启用/禁用功能
Calboot Dec 16, 2025
a2b5e8b
同步模组管理界面 Part1
Calboot Dec 16, 2025
2ca2f3e
为不兼容资源包添加警告
Calboot Dec 16, 2025
ce9377b
update
Calboot Dec 17, 2025
bd96337
启用/禁用资源包时添加警告
Calboot Dec 17, 2025
bc229e3
资源包详细信息界面
Calboot Dec 17, 2025
777aea3
修复esc键失效
Calboot Dec 17, 2025
e7c4f2e
Update i18n
Calboot Dec 17, 2025
07a9cc0
Update i18n
Calboot Dec 17, 2025
9aca021
完善资源包版本解析
Calboot Dec 18, 2025
eacf26b
Merge remote-tracking branch 'origin/resourcepack-enhancement' into r…
Calboot Dec 18, 2025
8d44424
从模组管理界面同步
Calboot Dec 18, 2025
00b377a
update
Calboot Dec 18, 2025
7d2ea79
update i18n
Calboot Dec 19, 2025
8ba09cf
移除资源包删除按钮
Calboot Dec 20, 2025
ae789fb
Merge branch 'HMCL-dev:main' into resourcepack-enhancement
Calboot Dec 20, 2025
9f6f42b
添加cf/modrinth页面检测 & fix i18n
Calboot Dec 20, 2025
9172c53
Apply suggestions
Calboot Dec 20, 2025
7ab6e62
update i18n
Calboot Dec 20, 2025
c7010f9
Apply suggestions from code review
Calboot Dec 21, 2025
b0fca8d
Apply suggestions from code review
Calboot Dec 21, 2025
78b5ce7
资源包更新功能
Calboot Dec 21, 2025
6709fbe
update
Calboot Dec 21, 2025
07632bb
update
Calboot Dec 21, 2025
6a6a07d
update
Calboot Dec 21, 2025
827272e
update
Calboot Dec 21, 2025
07a7b34
sync with #5016
Calboot Dec 21, 2025
4b69a17
Merge branch 'HMCL-dev:main' into resourcepack-enhancement
Calboot Dec 22, 2025
60ea149
update
Calboot Dec 23, 2025
e944351
update
Calboot Dec 23, 2025
29c34b9
update
Calboot Dec 24, 2025
b42dac6
update
Calboot Dec 24, 2025
bdc6cdb
Merge remote-tracking branch 'upstream/main' into resourcepack-enhanc…
Calboot Dec 24, 2025
3204857
update
Calboot Dec 24, 2025
423a021
update
Calboot Dec 24, 2025
194ce5f
Apply suggestions
Calboot Dec 24, 2025
79c04a1
update
Calboot Dec 25, 2025
b87dd5a
update
Calboot Dec 25, 2025
ff7f597
update
Calboot Dec 25, 2025
a915024
update
Calboot Dec 25, 2025
e1d8f5f
using config
Calboot Dec 26, 2025
f108671
update
Calboot Dec 26, 2025
87bb56d
update
Calboot Dec 26, 2025
4951406
添加资源包、光影包等的推荐下载
Calboot Dec 26, 2025
59d0dec
update
Calboot Dec 26, 2025
186ae00
update
Calboot Dec 27, 2025
cf84e5f
update
Calboot Dec 27, 2025
97d7d0f
update
Calboot Dec 27, 2025
4bf35f2
update
Calboot Dec 27, 2025
4fdab0a
update
Calboot Dec 28, 2025
ca167cb
update
Calboot Dec 28, 2025
6ab182c
update
Calboot Dec 28, 2025
c3ae0c1
update
Calboot Dec 28, 2025
d9a9dbb
update
Calboot Dec 28, 2025
701b839
清理代码
Calboot Dec 28, 2025
73662a6
Apply suggestions
Calboot Jan 1, 2026
dab739d
Merge branch 'HMCL-dev:main' into resourcepack-enhancement
Calboot Jan 1, 2026
9ddc2ed
Merge branch 'main' into resourcepack-enhancement
Calboot Jan 2, 2026
c4b4d2f
update
Calboot Jan 2, 2026
6fd1a56
update
Calboot Jan 3, 2026
106b5bb
update
Calboot Jan 3, 2026
0c9559c
Merge branch 'main' into resourcepack-enhancement
Calboot Jan 4, 2026
add29f8
update
Calboot Jan 4, 2026
598fd23
Merge branch 'main' into resourcepack-enhancement
Calboot Jan 13, 2026
7765f0c
update
Calboot Jan 13, 2026
26282ef
update
Calboot Jan 18, 2026
def0840
Merge branch 'main' into resourcepack-enhancement
Calboot Jan 18, 2026
3ecedb0
完成合并
Calboot Jan 18, 2026
1329d63
跟紧主线
Calboot Jan 18, 2026
c0e8395
Merge branch 'main' into resourcepack-enhancement
Calboot Jan 19, 2026
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
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
package org.jackhuang.hmcl.game;

import org.jackhuang.hmcl.download.DownloadProvider;
import org.jackhuang.hmcl.mod.LocalModFile;
import org.jackhuang.hmcl.mod.RemoteMod;
import org.jackhuang.hmcl.mod.RemoteModRepository;
import org.jackhuang.hmcl.ui.versions.ModTranslations;
Expand Down Expand Up @@ -110,8 +109,8 @@ public Stream<Category> getCategories() throws IOException {
}

@Override
public Optional<RemoteMod.Version> getRemoteVersionByLocalFile(LocalModFile localModFile, Path file) throws IOException {
return getBackedRemoteModRepository().getRemoteVersionByLocalFile(localModFile, file);
public Optional<RemoteMod.Version> getRemoteVersionByLocalFile(Path file) throws IOException {
return getBackedRemoteModRepository().getRemoteVersionByLocalFile(file);
}

@Override
Expand Down
11 changes: 11 additions & 0 deletions HMCL/src/main/java/org/jackhuang/hmcl/setting/GlobalConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,17 @@ public ObservableSet<String> getDisabledJava() {
return disabledJava;
}

@SerializedName("resourcePackWarningShown")
private final BooleanProperty resourcePackWarningShown = new SimpleBooleanProperty(false);

public boolean isResourcePackWarningShown() {
return resourcePackWarningShown.get();
}

public void onResourcePackWarningShown() {
resourcePackWarningShown.set(true);
}

static final class Adapter extends ObservableSetting.Adapter<GlobalConfig> {
@Override
protected GlobalConfig createInstance() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,13 @@
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;

public class DownloadPage extends DecoratorAnimatedPage implements DecoratorPage {
public static final org.jackhuang.hmcl.ui.versions.DownloadPage.DownloadCallback FOR_MOD =
(profile, version, mod, file) -> download(profile, version, file, "mods");
public static final org.jackhuang.hmcl.ui.versions.DownloadPage.DownloadCallback FOR_RESOURCE_PACK =
(profile, version, mod, file) -> download(profile, version, file, "resourcepacks");
public static final org.jackhuang.hmcl.ui.versions.DownloadPage.DownloadCallback FOR_SHADER =
(profile, version, mod, file) -> download(profile, version, file, "shaderpacks");

private final ReadOnlyObjectWrapper<DecoratorPage.State> state = new ReadOnlyObjectWrapper<>(DecoratorPage.State.fromTitle(i18n("download"), -1));
private final TabHeader tab;
private final TabHeader.Tab<VersionsPage> newGameTab = new TabHeader.Tab<>("newGameTab");
Expand Down Expand Up @@ -94,9 +101,9 @@ public DownloadPage(String uploadVersion) {
page.getActions().add(installLocalModpackButton);
return page;
}));
modTab.setNodeSupplier(loadVersionFor(() -> HMCLLocalizedDownloadListPage.ofMod((profile, version, mod, file) -> download(profile, version, file, "mods"), true)));
resourcePackTab.setNodeSupplier(loadVersionFor(() -> HMCLLocalizedDownloadListPage.ofResourcePack((profile, version, mod, file) -> download(profile, version, file, "resourcepacks"), true)));
shaderTab.setNodeSupplier(loadVersionFor(() -> HMCLLocalizedDownloadListPage.ofShaderPack((profile, version, mod, file) -> download(profile, version, file, "shaderpacks"), true)));
modTab.setNodeSupplier(loadVersionFor(() -> HMCLLocalizedDownloadListPage.ofMod(FOR_MOD, true)));
resourcePackTab.setNodeSupplier(loadVersionFor(() -> HMCLLocalizedDownloadListPage.ofResourcePack(FOR_RESOURCE_PACK, true)));
shaderTab.setNodeSupplier(loadVersionFor(() -> HMCLLocalizedDownloadListPage.ofShaderPack(FOR_SHADER, true)));
worldTab.setNodeSupplier(loadVersionFor(() -> new DownloadListPage(CurseForgeRemoteModRepository.WORLDS)));
tab = new TabHeader(transitionPane, newGameTab, modpackTab, modTab, resourcePackTab, shaderTab, worldTab);

Expand Down Expand Up @@ -195,7 +202,7 @@ public void showModpackDownloads() {
tab.select(modpackTab, false);
}

public void showResourcepackDownloads() {
public void showResourcePackDownloads() {
tab.select(resourcePackTab, false);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@
import javafx.scene.control.TableView;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import org.jackhuang.hmcl.mod.LocalModFile;
import org.jackhuang.hmcl.mod.LocalAddonFile;
import org.jackhuang.hmcl.mod.LocalFileManager;
import org.jackhuang.hmcl.mod.ModManager;
import org.jackhuang.hmcl.mod.RemoteMod;
import org.jackhuang.hmcl.task.FileDownloadTask;
Expand All @@ -45,6 +46,7 @@
import org.jackhuang.hmcl.util.TaskCancellationAction;
import org.jackhuang.hmcl.util.io.CSVTable;

import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.LocalDateTime;
Expand All @@ -59,15 +61,15 @@
import static org.jackhuang.hmcl.util.Pair.pair;
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;

public class ModUpdatesPage extends BorderPane implements DecoratorPage {
public class AddonUpdatesPage<F extends LocalAddonFile> extends BorderPane implements DecoratorPage {
private final ReadOnlyObjectWrapper<State> state = new ReadOnlyObjectWrapper<>(DecoratorPage.State.fromTitle(i18n("mods.check_updates")));

private final ModManager modManager;
private final LocalFileManager<F> localFileManager;
private final ObservableList<ModUpdateObject> objects;

@SuppressWarnings("unchecked")
public ModUpdatesPage(ModManager modManager, List<LocalModFile.ModUpdate> updates) {
this.modManager = modManager;
public AddonUpdatesPage(LocalFileManager<F> localFileManager, List<LocalAddonFile.ModUpdate> updates) {
this.localFileManager = localFileManager;

getStyleClass().add("gray-background");

Expand Down Expand Up @@ -114,7 +116,7 @@ public ModUpdatesPage(ModManager modManager, List<LocalModFile.ModUpdate> update
exportListButton.setOnAction(e -> exportList());

JFXButton nextButton = FXUtils.newRaisedButton(i18n("mods.check_updates.confirm"));
nextButton.setOnAction(e -> updateMods());
nextButton.setOnAction(e -> updateFiles());

JFXButton cancelButton = FXUtils.newRaisedButton(i18n("button.cancel"));
cancelButton.setOnAction(e -> fireEvent(new PageCloseEvent()));
Expand All @@ -129,19 +131,19 @@ private <T> void setupCellValueFactory(TableColumn<ModUpdateObject, T> column, F
column.setCellValueFactory(param -> mapper.apply(param.getValue()));
}

private void updateMods() {
ModUpdateTask task = new ModUpdateTask(
modManager,
private void updateFiles() {
UpdateTask task = new UpdateTask(
localFileManager.getDirectory(),
objects.stream()
.filter(o -> o.enabled.get())
.map(object -> pair(object.data.getLocalMod(), object.data.getCandidate()))
.collect(Collectors.toList()));
.map(object -> pair(object.data.localFile(), object.data.candidate()))
.toList());
Controllers.taskDialog(
task.whenComplete(Schedulers.javafx(), exception -> {
fireEvent(new PageCloseEvent());
if (!task.getFailedMods().isEmpty()) {
Controllers.dialog(i18n("mods.check_updates.failed_download") + "\n" +
task.getFailedMods().stream().map(LocalModFile::getFileName).collect(Collectors.joining("\n")),
task.getFailedMods().stream().map(LocalAddonFile::getFileName).collect(Collectors.joining("\n")),
i18n("install.failed"),
MessageDialogPane.MessageType.ERROR);
}
Expand Down Expand Up @@ -190,21 +192,21 @@ public ReadOnlyObjectWrapper<State> stateProperty() {
}

private static final class ModUpdateObject {
final LocalModFile.ModUpdate data;
final LocalAddonFile.ModUpdate data;
final BooleanProperty enabled = new SimpleBooleanProperty();
final StringProperty fileName = new SimpleStringProperty();
final StringProperty currentVersion = new SimpleStringProperty();
final StringProperty targetVersion = new SimpleStringProperty();
final StringProperty source = new SimpleStringProperty();

public ModUpdateObject(LocalModFile.ModUpdate data) {
public ModUpdateObject(LocalAddonFile.ModUpdate data) {
this.data = data;

enabled.set(!data.getLocalMod().getModManager().isDisabled(data.getLocalMod().getFile()));
fileName.set(data.getLocalMod().getFileName());
currentVersion.set(data.getCurrentVersion().getVersion());
targetVersion.set(data.getCandidate().getVersion());
switch (data.getCurrentVersion().getSelf().getType()) {
enabled.set(!data.localFile().isDisabled());
fileName.set(data.localFile().getFileName());
currentVersion.set(data.currentVersion().getVersion());
targetVersion.set(data.candidate().getVersion());
switch (data.currentVersion().getSelf().getType()) {
case CURSEFORGE:
source.set(i18n("mods.curseforge"));
break;
Expand Down Expand Up @@ -274,19 +276,19 @@ public void setSource(String source) {
}
}

public static class ModUpdateTask extends Task<Void> {
public static class UpdateTask extends Task<Void> {
private final Collection<Task<?>> dependents;
private final List<LocalModFile> failedMods = new ArrayList<>();
private final List<LocalAddonFile> failedMods = new ArrayList<>();

ModUpdateTask(ModManager modManager, List<Pair<LocalModFile, RemoteMod.Version>> mods) {
UpdateTask(Path modDirectory, List<Pair<LocalAddonFile, RemoteMod.Version>> mods) {
setStage("mods.check_updates.confirm");
getProperties().put("total", mods.size());

this.dependents = new ArrayList<>();
for (Pair<LocalModFile, RemoteMod.Version> mod : mods) {
LocalModFile local = mod.getKey();
for (Pair<LocalAddonFile, RemoteMod.Version> mod : mods) {
LocalAddonFile local = mod.getKey();
RemoteMod.Version remote = mod.getValue();
boolean isDisabled = local.getModManager().isDisabled(local.getFile());
boolean isDisabled = local.isDisabled();

dependents.add(Task
.runAsync(Schedulers.javafx(), () -> local.setOld(true))
Expand All @@ -297,7 +299,7 @@ public static class ModUpdateTask extends Task<Void> {

var task = new FileDownloadTask(
remote.getFile().getUrl(),
modManager.getModsDirectory().resolve(fileName));
modDirectory.resolve(fileName));

task.setName(remote.getName());
return task;
Expand All @@ -307,15 +309,21 @@ public static class ModUpdateTask extends Task<Void> {
// restore state if failed
local.setOld(false);
if (isDisabled)
local.disable();
local.markDisabled();
failedMods.add(local);
} else if (!local.keepOldFiles()) {
try {
local.delete();
} catch (IOException e) {
// ignore
}
}
})
.withCounter("mods.check_updates.confirm"));
}
}

public List<LocalModFile> getFailedMods() {
public List<LocalAddonFile> getFailedMods() {
return failedMods;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
*/
package org.jackhuang.hmcl.ui.versions;

import org.jackhuang.hmcl.mod.LocalAddonFile;
import org.jackhuang.hmcl.mod.LocalModFile;
import org.jackhuang.hmcl.mod.RemoteMod;
import org.jackhuang.hmcl.task.Schedulers;
Expand All @@ -29,25 +30,25 @@

import static org.jackhuang.hmcl.util.logging.Logger.LOG;

public class ModCheckUpdatesTask extends Task<List<LocalModFile.ModUpdate>> {
public class CheckUpdatesTask<T extends LocalAddonFile> extends Task<List<LocalAddonFile.ModUpdate>> {
private final List<Task<LocalModFile.ModUpdate>> dependents;

public ModCheckUpdatesTask(String gameVersion, Collection<LocalModFile> mods) {
public CheckUpdatesTask(String gameVersion, Collection<T> mods) {
dependents = mods.stream().map(mod ->
Task.supplyAsync(Schedulers.io(), () -> {
LocalModFile.ModUpdate candidate = null;
for (RemoteMod.Type type : RemoteMod.Type.values()) {
LocalModFile.ModUpdate update = null;
try {
update = mod.checkUpdates(gameVersion, type.getRemoteModRepository());
update = mod.checkUpdates(gameVersion, type);
} catch (IOException e) {
LOG.warning(String.format("Cannot check update for mod %s.", mod.getFileName()), e);
}
if (update == null) {
continue;
}

if (candidate == null || candidate.getCandidate().getDatePublished().isBefore(update.getCandidate().getDatePublished())) {
if (candidate == null || candidate.candidate().getDatePublished().isBefore(update.candidate().getDatePublished())) {
candidate = update;
}
}
Expand Down
Loading