Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ public SearchResult search(DownloadProvider downloadProvider, String gameVersion

int count = 0;
for (ModTranslations.Mod mod : ModTranslations.getTranslationsByRepositoryType(getType()).searchMod(searchFilter)) {
String englishSearchFilter = String.join(" ", StringUtils.tokenize(StringUtils.isNotBlank(mod.getSubname()) ? mod.getSubname() : mod.getName()));
String englishSearchFilter = String.join(" ", StringUtils.tokenize(StringUtils.isNotBlank(mod.subname()) ? mod.subname() : mod.name()));
if (StringUtils.isNotBlank(englishSearchFilter)) {
englishSearchFiltersSet.add(englishSearchFilter);
}
Expand Down Expand Up @@ -83,7 +83,7 @@ public SearchResult search(DownloadProvider downloadProvider, String gameVersion
}

ModTranslations.Mod chineseTranslation = ModTranslations.getTranslationsByRepositoryType(getType()).getModByCurseForgeId(remoteAddon.slug());
if (chineseTranslation != null && !StringUtils.isBlank(chineseTranslation.getName()) && StringUtils.containsChinese(chineseTranslation.getName())) {
if (chineseTranslation != null && !StringUtils.isBlank(chineseTranslation.name()) && StringUtils.containsChinese(chineseTranslation.name())) {
searchResultArray[chineseIndex++] = remoteAddon;
} else {
searchResultArray[englishIndex--] = remoteAddon;
Expand All @@ -95,10 +95,10 @@ public SearchResult search(DownloadProvider downloadProvider, String gameVersion
StringUtils.LevCalculator levCalculator = new StringUtils.LevCalculator();
return new SearchResult(Stream.concat(Arrays.stream(searchResultArray, 0, chineseIndex).map(remoteMod -> {
ModTranslations.Mod chineseRemoteMod = ModTranslations.getTranslationsByRepositoryType(getType()).getModByCurseForgeId(remoteMod.slug());
if (chineseRemoteMod == null || StringUtils.isBlank(chineseRemoteMod.getName()) || !StringUtils.containsChinese(chineseRemoteMod.getName())) {
if (chineseRemoteMod == null || StringUtils.isBlank(chineseRemoteMod.name()) || !StringUtils.containsChinese(chineseRemoteMod.name())) {
return Pair.pair(remoteMod, Integer.MAX_VALUE);
}
String chineseRemoteModName = chineseRemoteMod.getName();
String chineseRemoteModName = chineseRemoteMod.name();
if (searchFilter.isEmpty() || chineseRemoteModName.isEmpty()) {
return Pair.pair(remoteMod, Math.max(searchFilter.length(), chineseRemoteModName.length()));
}
Expand Down
29 changes: 14 additions & 15 deletions HMCL/src/main/java/org/jackhuang/hmcl/ui/download/DownloadPage.java
Original file line number Diff line number Diff line change
Expand Up @@ -68,12 +68,6 @@
import static org.jackhuang.hmcl.util.logging.Logger.LOG;

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

private final ReadOnlyObjectWrapper<DecoratorPage.State> state = new ReadOnlyObjectWrapper<>(DecoratorPage.State.fromTitle(i18n("download"), -1));
private final TabHeader tab;
Expand All @@ -92,12 +86,12 @@ public DownloadPage() {
this(null);
}

public DownloadPage(String uploadVersion) {
public DownloadPage(String updateVersion) {
newGameTab.setNodeSupplier(loadVersionFor(() -> new VersionsPage(versionPageNavigator, i18n("install.installer.choose", i18n("install.installer.game")), "", DownloadProviders.getDownloadProvider(),
"game", versionPageNavigator::onGameSelected)));
modpackTab.setNodeSupplier(loadVersionFor(() -> {
DownloadListPage page = HMCLLocalizedDownloadListPage.ofModPack((downloadProvider, repository, __, modpack, file) -> {
Versions.downloadModpackImpl(downloadProvider, repository, uploadVersion, modpack, file);
Versions.downloadModpackImpl(downloadProvider, repository, updateVersion, modpack, file);
}, false);

JFXButton installLocalModpackButton = FXUtils.newRaisedButton(i18n("install.modpack"));
Expand All @@ -106,9 +100,9 @@ public DownloadPage(String uploadVersion) {
page.getActions().add(installLocalModpackButton);
return page;
}));
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)));
modTab.setNodeSupplier(loadVersionFor(() -> HMCLLocalizedDownloadListPage.ofMod(true)));
resourcePackTab.setNodeSupplier(loadVersionFor(() -> HMCLLocalizedDownloadListPage.ofResourcePack(true)));
shaderTab.setNodeSupplier(loadVersionFor(() -> HMCLLocalizedDownloadListPage.ofShaderPack(true)));
worldTab.setNodeSupplier(loadVersionFor(() -> new DownloadListPage(CurseForgeRemoteAddonRepository.WORLDS)));
tab = new TabHeader(transitionPane, newGameTab, modpackTab, modTab, resourcePackTab, shaderTab, worldTab);

Expand All @@ -123,7 +117,7 @@ public DownloadPage(String uploadVersion) {
.startCategory(i18n("download.content").toUpperCase(Locale.ROOT))
.addNavigationDrawerTab(tab, modTab, i18n("mods"), SVG.EXTENSION, SVG.EXTENSION_FILL)
.addNavigationDrawerTab(tab, resourcePackTab, i18n("resourcepack"), SVG.TEXTURE)
.addNavigationDrawerTab(tab, shaderTab, i18n("download.shader"), SVG.WB_SUNNY, SVG.WB_SUNNY_FILL)
.addNavigationDrawerTab(tab, shaderTab, i18n("shaderpack"), SVG.WB_SUNNY, SVG.WB_SUNNY_FILL)
.addNavigationDrawerTab(tab, worldTab, i18n("world"), SVG.PUBLIC);
FXUtils.setLimitWidth(sideBar, 200);
setLeft(sideBar);
Expand Down Expand Up @@ -220,14 +214,19 @@ public void showModpackDownloads() {
tab.select(modpackTab, false);
}

public DownloadListPage showModDownloads() {
tab.select(modTab, false);
return modTab.getNode();
}

public DownloadListPage showResourcePackDownloads() {
tab.select(resourcePackTab, false);
return resourcePackTab.getNode();
}

public DownloadListPage showModDownloads() {
tab.select(modTab, false);
return modTab.getNode();
public DownloadListPage showShaderDownloads() {
tab.select(shaderTab, false);
return shaderTab.getNode();
}

public void showWorldDownloads() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,11 @@

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

public class AddonCheckUpdatesTask<T extends LocalAddonFile> extends Task<List<LocalAddonFile.AddonUpdate>> {
public class AddonCheckUpdatesTask extends Task<List<LocalAddonFile.AddonUpdate>> {
private final DownloadProvider downloadProvider;
private final List<Task<LocalAddonFile.AddonUpdate>> dependents;

public AddonCheckUpdatesTask(DownloadProvider downloadProvider, String gameVersion, Collection<T> addons) {
public AddonCheckUpdatesTask(DownloadProvider downloadProvider, String gameVersion, Collection<? extends LocalAddonFile> addons) {
this.downloadProvider = downloadProvider;
dependents = addons.stream().map(addon ->
Task.supplyAsync(Schedulers.io(), () -> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,15 +60,15 @@
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
import static org.jackhuang.hmcl.util.logging.Logger.LOG;

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

private final LocalAddonManager<F> localAddonManager;
private final Path addonsDirectory;
private final ObservableList<AddonUpdateObject> objects;

@SuppressWarnings("unchecked")
public AddonUpdatesPage(LocalAddonManager<F> localAddonManager, List<LocalAddonFile.AddonUpdate> updates) {
this.localAddonManager = localAddonManager;
public AddonUpdatesPage(Path addonsDirectory, List<LocalAddonFile.AddonUpdate> updates) {
this.addonsDirectory = addonsDirectory;

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

Expand Down Expand Up @@ -132,7 +132,7 @@ private <T> void setupCellValueFactory(TableColumn<AddonUpdateObject, T> column,

private void updateFiles() {
AddonUpdateTask task = new AddonUpdateTask(
localAddonManager.getDirectory(),
addonsDirectory,
objects.stream()
.filter(AddonUpdateObject::isEnabled)
.map(AddonUpdateObject::getData)
Expand Down Expand Up @@ -294,19 +294,18 @@ public static class AddonUpdateTask extends Task<Void> {
RemoteAddon.Version remote = addon.targetVersion();
boolean isDisabled = local.isDisabled();
String originalFileName = local.getFile().getFileName().toString();
String fileName = addon.useRemoteFileName() ? remote.file().filename() : originalFileName;
if (isDisabled)
fileName = StringUtils.addSuffix(fileName, LocalAddonManager.DISABLED_EXTENSION);
String newFileName = fileName;

dependents.add(Task
.runAsync(Schedulers.javafx(), () -> local.setOld(true))
.thenComposeAsync(() -> {
String fileName = addon.useRemoteFileName() ? remote.file().filename() : originalFileName;
if (isDisabled)
fileName = StringUtils.addSuffix(fileName, LocalAddonManager.DISABLED_EXTENSION);

var task = new FileDownloadTask(
remote.file().url(),
addonDirectory.resolve(fileName)
addonDirectory.resolve(newFileName)
);

task.setName(remote.name());
return task;
})
Expand All @@ -317,11 +316,14 @@ public static class AddonUpdateTask extends Task<Void> {
if (isDisabled)
local.markDisabled();
failedAddons.add(local);
} else if (!local.keepOldFiles()) {
try {
local.delete();
} catch (IOException e) {
LOG.warning("Failed to delete outdated addon: " + local.getFile(), e);
} else {
local.onUpdated(newFileName);
if (!local.keepOldFiles()) {
try {
local.delete();
} catch (IOException e) {
LOG.warning("Failed to delete outdated addon: " + local.getFile(), e);
}
}
}
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
import javafx.scene.control.Skin;
import javafx.stage.FileChooser;
import org.jackhuang.hmcl.game.World;
import org.jackhuang.hmcl.addon.datapack.DataPack;
import org.jackhuang.hmcl.addon.pack.datapack.DataPack;
import org.jackhuang.hmcl.task.Schedulers;
import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.ui.Controllers;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@
import javafx.scene.layout.Priority;
import javafx.scene.layout.StackPane;
import javafx.util.Duration;
import org.jackhuang.hmcl.addon.datapack.DataPack;
import org.jackhuang.hmcl.addon.pack.datapack.DataPack;
import org.jackhuang.hmcl.task.Schedulers;
import org.jackhuang.hmcl.ui.Controllers;
import org.jackhuang.hmcl.ui.FXUtils;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ public class DownloadListPage extends Control implements DecoratorPage, VersionP
private final ListProperty<RemoteAddon> items = new SimpleListProperty<>(this, "items", FXCollections.observableArrayList());
private final ObservableList<String> versions = FXCollections.observableArrayList();
private final StringProperty selectedVersion = new SimpleStringProperty();
private final DownloadPage.DownloadCallback callback;
private final DownloadPage.DownloadCallback callback; // ONLY FOR MODPACKS
private boolean searchInitialized = false;
protected final BooleanProperty supportChinese = new SimpleBooleanProperty();
private final ObservableList<Node> actions = FXCollections.observableArrayList();
Expand Down
52 changes: 35 additions & 17 deletions HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/DownloadPage.java
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,25 @@
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;

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

public static @Nullable DownloadCallback getDownloadCallbackFor(RemoteAddonRepository.Type repoType) {
return switch (repoType) {
case MOD -> FOR_MOD;
case RESOURCE_PACK -> FOR_RESOURCE_PACK;
case SHADER_PACK -> FOR_SHADER;
default -> null;
};
}

private final ReadOnlyObjectWrapper<State> state = new ReadOnlyObjectWrapper<>();
private final BooleanProperty loaded = new SimpleBooleanProperty(false);
private final BooleanProperty loading = new SimpleBooleanProperty(false);
Expand All @@ -66,7 +85,7 @@ public class DownloadPage extends Control implements DecoratorPage {
private final RemoteAddon addon;
private final ModTranslations.Mod mod;
private final HMCLGameRepository.InstanceReference instanceReference;
private final DownloadCallback callback;
private final DownloadCallback callback; // ONLY FOR MODPACKS
private final DownloadListPage page;
private final RemoteAddonRepository.Type type;

Expand Down Expand Up @@ -154,11 +173,17 @@ public void setFailed(boolean failed) {
this.failed.set(failed);
}

public @Nullable DownloadCallback getCallbackFor(RemoteAddon addon) {
if (addon.repoType() == RemoteAddonRepository.Type.MODPACK) return this.callback;
return getDownloadCallbackFor(addon.repoType());
}

public void download(RemoteAddon addon, RemoteAddon.Version file) {
if (this.callback == null) {
var callback = getCallbackFor(addon);
if (callback == null) {
saveAs(file);
} else {
this.callback.download(page.getDownloadProvider(), instanceReference.repository(), instanceReference.instanceId(), addon, file);
callback.download(page.getDownloadProvider(), instanceReference.repository(), instanceReference.instanceId(), addon, file);
}
}

Expand Down Expand Up @@ -384,21 +409,14 @@ private static final class DependencyAddonItem extends LineButton {
pane.getChildren().setAll(imageView, content);
FXUtils.setLimitHeight(this, 60);

RemoteAddonRepository.Type type = addon.repoType();
DownloadCallback callback = switch (type) {
case MOD -> org.jackhuang.hmcl.ui.download.DownloadPage.FOR_MOD;
case RESOURCE_PACK -> org.jackhuang.hmcl.ui.download.DownloadPage.FOR_RESOURCE_PACK;
case SHADER_PACK -> org.jackhuang.hmcl.ui.download.DownloadPage.FOR_SHADER;
default -> null; // Dependencies should not be modpacks, worlds or customized stuff
};
setOnAction((e) -> {
fireEvent(new DialogCloseEvent());
Controllers.navigate(new DownloadPage(page, addon, instanceReference, callback));
Controllers.navigate(new DownloadPage(page, addon, instanceReference, null));
});
setNode(IDX_LEADING, pane);

if (addon != RemoteAddon.BROKEN) {
ModTranslations.Mod mod = ModTranslations.getTranslationsByRepositoryType(type).getModByCurseForgeId(addon.slug());
ModTranslations.Mod mod = ModTranslations.getTranslationsByRepositoryType(addon.repoType()).getModByCurseForgeId(addon.slug());
content.setTitle(mod != null && I18n.isUseChinese() ? mod.getDisplayName() : addon.title());
content.setSubtitle(addon.description());
for (String category : addon.categories()) {
Expand Down Expand Up @@ -492,7 +510,7 @@ private static final class AddonItem extends StackPane {
}

private static final class AddonVersion extends JFXDialogLayout {
public AddonVersion(RemoteAddon mod, RemoteAddon.Version version, DownloadPage selfPage) {
public AddonVersion(RemoteAddon addon, RemoteAddon.Version version, DownloadPage selfPage) {
RemoteAddonRepository.Type type = selfPage.type;

String title = switch (type) {
Expand All @@ -506,7 +524,7 @@ public AddonVersion(RemoteAddon mod, RemoteAddon.Version version, DownloadPage s

VBox box = new VBox(8);
box.setPadding(new Insets(8));
AddonItem addonItem = new AddonItem(mod, version, selfPage);
AddonItem addonItem = new AddonItem(addon, version, selfPage);
addonItem.setMouseTransparent(true); // Item is displayed for info, clicking shouldn't open the dialog again
box.getChildren().setAll(addonItem);
SpinnerPane spinnerPane = new SpinnerPane();
Expand All @@ -528,14 +546,14 @@ public AddonVersion(RemoteAddon mod, RemoteAddon.Version version, DownloadPage s
this.setBody(box);

JFXButton downloadButton = null;
if (selfPage.callback != null) {
downloadButton = new JFXButton(type == RemoteAddonRepository.Type.MODPACK ? i18n("install.modpack") : i18n("mods.install"));
if (selfPage.getCallbackFor(addon) != null) {
downloadButton = new JFXButton(type == RemoteAddonRepository.Type.MODPACK ? i18n("install.modpack") : i18n("addon.install"));
downloadButton.getStyleClass().add("dialog-accept");
downloadButton.setOnAction(e -> {
if (type == RemoteAddonRepository.Type.MODPACK || !spinnerPane.isLoading() && spinnerPane.getFailedReason() == null) {
fireEvent(new DialogCloseEvent());
}
selfPage.download(mod, version);
selfPage.download(addon, version);
});
}

Expand Down
Loading