diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/game/LocalizedRemoteAddonRepository.java b/HMCL/src/main/java/org/jackhuang/hmcl/game/LocalizedRemoteAddonRepository.java index dc54158d23..83f64f4880 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/game/LocalizedRemoteAddonRepository.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/game/LocalizedRemoteAddonRepository.java @@ -39,8 +39,6 @@ public abstract class LocalizedRemoteAddonRepository implements RemoteAddonRepos protected abstract RemoteAddonRepository getBackedRemoteModRepository(); - protected abstract SortType getBackedRemoteModRepositorySortOrder(); - @Override public String getApiBaseUrl() { return getBackedRemoteModRepository().getApiBaseUrl(); @@ -80,8 +78,8 @@ public SearchResult search(DownloadProvider downloadProvider, String gameVersion SearchResult searchResult = null; List remoteAddons = List.of(); for (String englishSearchFilter : englishSearchFiltersSet) { - searchResult = getBackedRemoteModRepository().search(downloadProvider, gameVersion, category, pageOffset, pageSize, englishSearchFilter, getBackedRemoteModRepositorySortOrder(), sortOrder); - remoteAddons = searchResult.getUnsortedResults().toList(); + searchResult = getBackedRemoteModRepository().search(downloadProvider, gameVersion, category, pageOffset, pageSize, englishSearchFilter, SortType.RELEVANCY, sortOrder); + remoteAddons = searchResult.results().toList(); if (!remoteAddons.isEmpty()) { break; } @@ -100,7 +98,7 @@ public SearchResult search(DownloadProvider downloadProvider, String gameVersion searchResultArray[englishIndex--] = remoteAddon; } } - totalPages = searchResult.getTotalPages(); + totalPages = searchResult.totalPages(); } StringUtils.LevCalculator levCalculator = new StringUtils.LevCalculator(); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/DownloadListPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/DownloadListPage.java index f39535a4c3..d6d6fd0cff 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/DownloadListPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/DownloadListPage.java @@ -120,7 +120,7 @@ public void loadInstance(HMCLGameRepository repository, @Nullable GameInstanceID if (!searchInitialized) { searchInitialized = true; - search("", null, 0, "", RemoteAddonRepository.SortType.POPULARITY); + search("", null, 0, "", RemoteAddonRepository.SortType.RELEVANCY); } if (instanceSelection) { @@ -181,8 +181,8 @@ private void search(String userGameVersion, RemoteAddonRepository.Category categ setLoading(false); if (exception == null) { - items.setAll(result.getResults().collect(Collectors.toList())); - pageCount.set(result.getTotalPages()); + items.setAll(result.results().collect(Collectors.toList())); + pageCount.set(result.totalPages()); failed.set(false); } else { failed.set(true); @@ -370,7 +370,7 @@ protected ModDownloadListPageSkin(DownloadListPage control) { sortStackPane.getChildren().setAll(sortComboBox); sortComboBox.prefWidthProperty().bind(sortStackPane.widthProperty()); sortComboBox.getStyleClass().add("fit-width"); - sortComboBox.setConverter(stringConverter(sortType -> i18n("curse.sort." + sortType.name().toLowerCase(Locale.ROOT)))); + sortComboBox.setConverter(stringConverter(sortType -> i18n("addon.sort." + sortType.name().toLowerCase(Locale.ROOT)))); sortComboBox.getItems().setAll(RemoteAddonRepository.SortType.values()); sortComboBox.getSelectionModel().select(0); searchPane.addRow(rowIndex++, new Label(i18n("addon.category")), categoryStackPane, new Label(i18n("search.sort")), sortStackPane); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/HMCLLocalizedDownloadListPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/HMCLLocalizedDownloadListPage.java index b2e8dbd76c..f9fbae792e 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/HMCLLocalizedDownloadListPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/HMCLLocalizedDownloadListPage.java @@ -118,15 +118,6 @@ protected RemoteAddonRepository getBackedRemoteModRepository() { } } - @Override - protected SortType getBackedRemoteModRepositorySortOrder() { - if ("addon.modrinth".equals(downloadSource.get())) { - return SortType.NAME; - } else { - return SortType.POPULARITY; - } - } - @Override public RemoteAddon.Type getType() { return type; diff --git a/HMCL/src/main/resources/assets/lang/I18N.properties b/HMCL/src/main/resources/assets/lang/I18N.properties index ca60b1b0a0..951f33a6c4 100644 --- a/HMCL/src/main/resources/assets/lang/I18N.properties +++ b/HMCL/src/main/resources/assets/lang/I18N.properties @@ -202,6 +202,11 @@ addon.download.recommend=Recommended Version for Minecraft %1s addon.download.title.release=Minecraft %s addon.download.title.snapshot=Minecraft %s (Snapshots) addon.modrinth=Modrinth +addon.sort.date_created=Date Created +addon.sort.last_updated=Last Updated +addon.sort.popularity=Popularity +addon.sort.relevancy=Relevancy +addon.sort.total_downloads=Total Downloads archive.author=Author(s) archive.date=Publish Date @@ -384,13 +389,6 @@ curse.category.6553=Realistic curse.category.6554=Fantasy curse.category.6555=Vanilla -curse.sort.author=Author -curse.sort.date_created=Date Created -curse.sort.last_updated=Last Updated -curse.sort.name=Name -curse.sort.popularity=Popularity -curse.sort.total_downloads=Total Downloads - datetime.format=MMM d, yyyy, h\:mm\:ss a download=Download diff --git a/HMCL/src/main/resources/assets/lang/I18N_ar.properties b/HMCL/src/main/resources/assets/lang/I18N_ar.properties index 05b429a107..8b9b0e3deb 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_ar.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_ar.properties @@ -184,6 +184,10 @@ addon.check_update.source=المصدر addon.check_update.target_version=الإصدار المستهدف addon.curseforge=CurseForge addon.modrinth=Modrinth +addon.sort.date_created=تاريخ الإنشاء +addon.sort.last_updated=آخر تحديث +addon.sort.popularity=الشعبية +addon.sort.total_downloads=إجمالي التنزيلات archive.author=المؤلف/المؤلفون archive.date=تاريخ النشر @@ -354,13 +358,6 @@ curse.category.4550=مهام curse.category.4555=توليد العالم curse.category.4552=سكريبتات -curse.sort.author=المؤلف -curse.sort.date_created=تاريخ الإنشاء -curse.sort.last_updated=آخر تحديث -curse.sort.name=الاسم -curse.sort.popularity=الشعبية -curse.sort.total_downloads=إجمالي التنزيلات - datetime.format=MMM d, yyyy, h\:mm\:ss a download=تنزيل diff --git a/HMCL/src/main/resources/assets/lang/I18N_es.properties b/HMCL/src/main/resources/assets/lang/I18N_es.properties index c1ab2c80d9..deb9a47065 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_es.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_es.properties @@ -165,6 +165,10 @@ addon.check_update.target_version=Versión de destino addon.curseforge=CurseForge addon.download.recommend=Versión recomendada - Minecraft %1s addon.modrinth=Modrinth +addon.sort.date_created=Fecha de creación +addon.sort.last_updated=Última actualización +addon.sort.popularity=Popularidad +addon.sort.total_downloads=Descargas totales archive.author=Autor(es) archive.date=Fecha de publicación @@ -334,13 +338,6 @@ curse.category.4550=Quests curse.category.4555=Generación del mundo curse.category.4552=Scripts -curse.sort.author=Autor -curse.sort.date_created=Fecha de creación -curse.sort.last_updated=Última actualización -curse.sort.name=Nombre -curse.sort.popularity=Popularidad -curse.sort.total_downloads=Descargas totales - datetime.format=d MMM yyyy, H\:mm\:ss download=Descargar diff --git a/HMCL/src/main/resources/assets/lang/I18N_ja.properties b/HMCL/src/main/resources/assets/lang/I18N_ja.properties index a7a0ff236d..284d96b24a 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_ja.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_ja.properties @@ -128,6 +128,10 @@ addon.check_update.source=Source addon.check_update.target_version=Target addon.curseforge=CurseForge addon.modrinth=Modrinth +addon.sort.date_created=作成日 +addon.sort.last_updated=最終更新 +addon.sort.popularity=人気 +addon.sort.total_downloads=合計ダウンロード数 archive.author=作成者 archive.date=公開日 @@ -287,13 +291,6 @@ curse.category.4550=クエスト curse.category.4555=World Gen curse.category.4552=スクリプト -curse.sort.author=Author -curse.sort.date_created=作成日 -curse.sort.last_updated=最終更新 -curse.sort.name=名前 -curse.sort.popularity=人気 -curse.sort.total_downloads=合計ダウンロード数 - datetime.format=yyyy/MM/dd H:mm:ss download=ダウンロード diff --git a/HMCL/src/main/resources/assets/lang/I18N_lzh.properties b/HMCL/src/main/resources/assets/lang/I18N_lzh.properties index fef9114fe9..a26f7f30d9 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_lzh.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_lzh.properties @@ -168,6 +168,10 @@ addon.check_update.target_version=將至之版 addon.curseforge=CurseForge addon.download.recommend=宜版 - 礦藝 %1s addon.modrinth=Modrinth +addon.sort.date_created=創立之期 +addon.sort.last_updated=近日迭更 +addon.sort.popularity=幾熱 +addon.sort.total_downloads=引數 archive.author=作者 archive.date=廣佈之期 @@ -333,13 +337,6 @@ curse.category.4550=任務 curse.category.4555=生界新創 curse.category.4552=角本 -curse.sort.author=作者 -curse.sort.date_created=創立之期 -curse.sort.last_updated=近日迭更 -curse.sort.name=名 -curse.sort.popularity=幾熱 -curse.sort.total_downloads=引數 - datetime.format=yyyy 年 MM 月 dd 日 HH:mm:ss download=引 diff --git a/HMCL/src/main/resources/assets/lang/I18N_ru.properties b/HMCL/src/main/resources/assets/lang/I18N_ru.properties index 9c603092df..31bda7a113 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_ru.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_ru.properties @@ -163,6 +163,10 @@ addon.check_update.source=Источник addon.check_update.target_version=Целевая версия addon.curseforge=CurseForge addon.modrinth=Modrinth +addon.sort.date_created=Создан +addon.sort.last_updated=Обновлён +addon.sort.popularity=Популярность +addon.sort.total_downloads=Скачиваний archive.author=Автор(ы) archive.date=Дата публикации @@ -330,13 +334,6 @@ curse.category.4550=Квесты curse.category.4555=Генерация карты curse.category.4552=Скрипты -curse.sort.author=Автор -curse.sort.date_created=Создан -curse.sort.last_updated=Обновлён -curse.sort.name=Название -curse.sort.popularity=Популярность -curse.sort.total_downloads=Скачиваний - datetime.format=d MMM yyyy г., HH\:mm\:ss download=Скачать diff --git a/HMCL/src/main/resources/assets/lang/I18N_uk.properties b/HMCL/src/main/resources/assets/lang/I18N_uk.properties index ee0dd985b8..38b40d4222 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_uk.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_uk.properties @@ -164,6 +164,10 @@ addon.check_update.source=Джерело addon.check_update.target_version=Цільова версія addon.curseforge=CurseForge addon.modrinth=Modrinth +addon.sort.date_created=Дата створення +addon.sort.last_updated=Останнє оновлення +addon.sort.popularity=Популярність +addon.sort.total_downloads=Всього завантажень archive.author=Автор(и) archive.date=Дата публікації @@ -330,13 +334,6 @@ curse.category.4550=Квести curse.category.4555=Генерація світу curse.category.4552=Скрипти -curse.sort.author=Автор -curse.sort.date_created=Дата створення -curse.sort.last_updated=Останнє оновлення -curse.sort.name=Назва -curse.sort.popularity=Популярність -curse.sort.total_downloads=Всього завантажень - datetime.format=dd.MM.yyyy, HH:mm:ss download=Завантажити diff --git a/HMCL/src/main/resources/assets/lang/I18N_zh.properties b/HMCL/src/main/resources/assets/lang/I18N_zh.properties index 5eeafafcb3..3201fad275 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh.properties @@ -200,6 +200,11 @@ addon.download.recommend=推薦版本 - Minecraft %1s addon.download.title.release=Minecraft %s addon.download.title.snapshot=Minecraft %s (快照) addon.modrinth=Modrinth +addon.sort.date_created=建立日期 +addon.sort.last_updated=最近更新 +addon.sort.popularity=熱度 +addon.sort.relevancy=相關度 +addon.sort.total_downloads=下載量 archive.author=作者 archive.date=發布日期 @@ -382,13 +387,6 @@ curse.category.6553=寫實 curse.category.6554=幻想 curse.category.6555=原生 -curse.sort.author=作者 -curse.sort.date_created=建立日期 -curse.sort.last_updated=最近更新 -curse.sort.name=名稱 -curse.sort.popularity=熱度 -curse.sort.total_downloads=下載量 - datetime.format=yyyy 年 MM 月 dd 日 HH:mm:ss download=下載 diff --git a/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties b/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties index 6fe08134e5..ffe27b0c32 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties @@ -202,6 +202,11 @@ addon.download.recommend=推荐版本 - Minecraft %1s addon.download.title.release=Minecraft %s addon.download.title.snapshot=Minecraft %s (快照) addon.modrinth=Modrinth +addon.sort.date_created=创建日期 +addon.sort.last_updated=最近更新 +addon.sort.popularity=热度 +addon.sort.relevancy=相关度 +addon.sort.total_downloads=下载量 archive.author=作者 archive.date=发布日期 @@ -384,13 +389,6 @@ curse.category.6553=写实 curse.category.6554=幻想 curse.category.6555=原生 -curse.sort.author=作者 -curse.sort.date_created=创建日期 -curse.sort.last_updated=最近更新 -curse.sort.name=名称 -curse.sort.popularity=热度 -curse.sort.total_downloads=下载量 - datetime.format=yyyy 年 MM 月 dd 日 HH:mm:ss download=下载 diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/RemoteAddonRepository.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/RemoteAddonRepository.java index 9c850c4b9f..39b8b79a8c 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/RemoteAddonRepository.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/RemoteAddonRepository.java @@ -35,52 +35,6 @@ public interface RemoteAddonRepository { String getBaseUrl(); - enum SortType { - POPULARITY, - NAME, - DATE_CREATED, - LAST_UPDATED, - AUTHOR, - TOTAL_DOWNLOADS - } - - enum SortOrder { - ASC, - DESC - } - - class SearchResult { - private final Stream sortedResults; - - private final Stream unsortedResults; - - private final int totalPages; - - public SearchResult(Stream sortedResults, Stream unsortedResults, int totalPages) { - this.sortedResults = sortedResults; - this.unsortedResults = unsortedResults; - this.totalPages = totalPages; - } - - public SearchResult(Stream sortedResults, int pages) { - this.sortedResults = sortedResults; - this.unsortedResults = sortedResults; - this.totalPages = pages; - } - - public Stream getResults() { - return this.sortedResults; - } - - public Stream getUnsortedResults() { - return this.unsortedResults; - } - - public int getTotalPages() { - return this.totalPages; - } - } - SearchResult search(DownloadProvider downloadProvider, String gameVersion, @Nullable Category category, int pageOffset, int pageSize, String searchFilter, SortType sortType, SortOrder sortOrder) throws IOException; @@ -106,4 +60,20 @@ default RemoteAddon resolveDependency(DownloadProvider downloadProvider, String record Category(Object self, String id, List subcategories) { } + + enum SortType { + RELEVANCY, + POPULARITY, + DATE_CREATED, + LAST_UPDATED, + TOTAL_DOWNLOADS + } + + enum SortOrder { + ASC, + DESC + } + + record SearchResult(Stream results, int totalPages) { + } } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/repository/CurseForgeRemoteAddonRepository.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/repository/CurseForgeRemoteAddonRepository.java index e33849960b..7c2188489b 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/repository/CurseForgeRemoteAddonRepository.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/repository/CurseForgeRemoteAddonRepository.java @@ -24,7 +24,6 @@ import org.jackhuang.hmcl.download.DownloadProvider; import org.jackhuang.hmcl.util.Immutable; import org.jackhuang.hmcl.util.MurmurHash2; -import org.jackhuang.hmcl.util.Pair; import org.jackhuang.hmcl.util.StringUtils; import org.jackhuang.hmcl.util.io.HttpRequest; import org.jackhuang.hmcl.util.io.JarUtils; @@ -59,8 +58,6 @@ public final class CurseForgeRemoteAddonRepository implements RemoteAddonReposit public static final String API_KEY = System.getProperty("hmcl.curseforge.apikey", JarUtils.getAttribute("hmcl.curseforge.apikey", "")); - private static final int WORD_PERFECT_MATCH_WEIGHT = 5; - private static R withApiKey(R request) { if (request.getUrl().startsWith(PREFIX) && !API_KEY.isEmpty()) { request.header("X-API-KEY", API_KEY); @@ -105,13 +102,11 @@ public String getBaseUrl() { private static int toModsSearchSortField(SortType sort) { // https://docs.curseforge.com/rest-api/#tocS_ModsSearchSortField return switch (sort) { - case DATE_CREATED -> 1; + case RELEVANCY -> 1; // This represents Featured, however test shows that they are quite similar case POPULARITY -> 2; + case DATE_CREATED -> 11; case LAST_UPDATED -> 3; - case NAME -> 4; - case AUTHOR -> 5; case TOTAL_DOWNLOADS -> 6; - default -> 8; }; } @@ -158,9 +153,6 @@ public SearchResult search(DownloadProvider downloadProvider, String gameVersion try { response = withApiKey(HttpRequest.GET(candidate.toString())) .getJson(Response.typeOf(listTypeOf(CurseAddon.class))); - if (searchFilter.isEmpty()) { - return new SearchResult(response.data().stream().map(CurseAddon::toAddon), calculateTotalPages(response, pageSize)); - } break; } catch (IOException e) { LOG.warning("Failed to search addons: " + candidate, e); @@ -175,31 +167,10 @@ public SearchResult search(DownloadProvider downloadProvider, String gameVersion } } - if (response == null) { - throw exception != null ? exception : new IOException("No candidates found"); - } - - // https://github.com/HMCL-dev/HMCL/issues/1549 - String lowerCaseSearchFilter = searchFilter.toLowerCase(Locale.ROOT); - Map searchFilterWords = new HashMap<>(); - for (String s : StringUtils.tokenize(lowerCaseSearchFilter)) { - searchFilterWords.put(s, searchFilterWords.getOrDefault(s, 0) + 1); - } - - StringUtils.LevCalculator levCalculator = new StringUtils.LevCalculator(); - - return new SearchResult(response.data().stream().map(CurseAddon::toAddon).map(remoteAddon -> { - String lowerCaseResult = remoteAddon.title().toLowerCase(Locale.ROOT); - int diff = levCalculator.calc(lowerCaseSearchFilter, lowerCaseResult); - - for (String s : StringUtils.tokenize(lowerCaseResult)) { - if (searchFilterWords.containsKey(s)) { - diff -= WORD_PERFECT_MATCH_WEIGHT * searchFilterWords.get(s) * s.length(); - } - } + if (response != null) + return new SearchResult(response.data().stream().map(CurseAddon::toAddon), calculateTotalPages(response, pageSize)); - return pair(remoteAddon, diff); - }).sorted(Comparator.comparingInt(Pair::getValue)).map(Pair::getKey), response.data().stream().map(CurseAddon::toAddon), calculateTotalPages(response, pageSize)); + throw exception != null ? exception : new IOException("No candidates found"); } finally { SEMAPHORE.release(); } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/repository/ModrinthRemoteAddonRepository.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/repository/ModrinthRemoteAddonRepository.java index b8de101b0e..3cdc4b3586 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/repository/ModrinthRemoteAddonRepository.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/repository/ModrinthRemoteAddonRepository.java @@ -142,11 +142,11 @@ public String getBaseUrl() { private static String convertSortType(SortType sortType) { return switch (sortType) { + case RELEVANCY -> "relevance"; + case POPULARITY -> "follows"; case DATE_CREATED -> "newest"; - case POPULARITY, NAME, AUTHOR -> "relevance"; case LAST_UPDATED -> "updated"; case TOTAL_DOWNLOADS -> "downloads"; - default -> throw new IllegalArgumentException("Unsupported sort type " + sortType); }; }