feat: Mod chinese search

This commit is contained in:
huanghongxun 2021-09-11 23:49:07 +08:00
parent 0df1b62c3e
commit 0032242da6
10 changed files with 4713 additions and 4533 deletions

View File

@ -85,6 +85,7 @@ if __name__ == '__main__':
for mod in data:
chinese_name = mod['name']['main']
sub_name = mod['name']['sub']
abbr = mod['name']['abbr']
if S in chinese_name:
print('Error! ' + chinese_name)
@ -132,6 +133,6 @@ if __name__ == '__main__':
mod_ids = MOD_SEPARATOR.join([str(id) for id in mod_id])
outfile.write(
f'{curseforge_id}{S}{mcmod_id}{S}{mcbbs_id}{S}{mod_ids}{S}{chinese_name}{S}{sub_name}\n')
f'{curseforge_id}{S}{mcmod_id}{S}{mcbbs_id}{S}{mod_ids}{S}{chinese_name}{S}{sub_name}{S}{abbr}\n')
print('Success!')

View File

@ -39,6 +39,7 @@ import org.jackhuang.hmcl.ui.construct.TabHeader;
import org.jackhuang.hmcl.ui.construct.TaskExecutorDialogPane;
import org.jackhuang.hmcl.ui.decorator.DecoratorPage;
import org.jackhuang.hmcl.ui.versions.DownloadListPage;
import org.jackhuang.hmcl.ui.versions.ModDownloadListPage;
import org.jackhuang.hmcl.ui.versions.VersionPage;
import org.jackhuang.hmcl.ui.versions.Versions;
import org.jackhuang.hmcl.util.io.NetworkUtils;
@ -64,7 +65,7 @@ public class DownloadPage extends BorderPane implements DecoratorPage {
public DownloadPage() {
modpackTab.setNodeSupplier(() -> new DownloadListPage(CurseModManager.SECTION_MODPACK, Versions::downloadModpackImpl));
modTab.setNodeSupplier(() -> new DownloadListPage(CurseModManager.SECTION_MOD, (profile, version, file) -> download(profile, version, file, "mods"), true));
modTab.setNodeSupplier(() -> new ModDownloadListPage(CurseModManager.SECTION_MOD, (profile, version, file) -> download(profile, version, file, "mods"), true));
resourcePackTab.setNodeSupplier(() -> new DownloadListPage(CurseModManager.SECTION_RESOURCE_PACK, (profile, version, file) -> download(profile, version, file, "resourcepacks")));
// customizationTab.setNodeSupplier(() -> new ModDownloadListPage(CurseModManager.SECTION_CUSTOMIZATION, this::download));
worldTab.setNodeSupplier(() -> new DownloadListPage(CurseModManager.SECTION_WORLD));

View File

@ -74,6 +74,12 @@ public class AboutPage extends StackPane {
mcbbs.setSubtitle(i18n("about.thanks_to.mcbbs.statement"));
mcbbs.setExternalLink("https://www.mcbbs.net/");
IconedTwoLineListItem mcmod = new IconedTwoLineListItem();
mcmod.setImage(new Image("/assets/img/mcmod.png", 32, 32, false, true));
mcmod.setTitle(i18n("about.thanks_to.mcmod"));
mcmod.setSubtitle(i18n("about.thanks_to.mcmod.statement"));
mcmod.setExternalLink("https://www.mcmod.cn/");
IconedTwoLineListItem contributors = new IconedTwoLineListItem();
contributors.setImage(new Image("/assets/img/github.png", 32, 32, false, true));
contributors.setTitle(i18n("about.thanks_to.contributors"));
@ -86,7 +92,7 @@ public class AboutPage extends StackPane {
users.setSubtitle(i18n("about.thanks_to.users.statement"));
users.setExternalLink("https://hmcl.huangyuhui.net/api/redirect/sponsor");
thanks.getContent().setAll(yushijinhun, bangbang93, mcbbs, redLnn, users, contributors);
thanks.getContent().setAll(yushijinhun, bangbang93, mcbbs, mcmod, redLnn, users, contributors);
}
ComponentList dep = new ComponentList();

View File

@ -0,0 +1,53 @@
/*
* Hello Minecraft! Launcher
* Copyright (C) 2021 huangyuhui <huanghongxun2008@126.com> and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package org.jackhuang.hmcl.ui.versions;
import org.jackhuang.hmcl.mod.curse.CurseAddon;
import org.jackhuang.hmcl.util.StringUtils;
import java.util.ArrayList;
import java.util.List;
public class ModDownloadListPage extends DownloadListPage {
public ModDownloadListPage(int section, DownloadPage.DownloadCallback callback, boolean versionSelection) {
super(section, callback, versionSelection);
}
@Override
protected List<CurseAddon> searchImpl(String gameVersion, int category, int section, int pageOffset, String searchFilter, int sort) throws Exception {
if (StringUtils.CHINESE_PATTERN.matcher(searchFilter).find()) {
List<ModTranslations.Mod> mods = ModTranslations.searchMod(searchFilter);
List<String> searchFilters = new ArrayList<>();
int count = 0;
for (ModTranslations.Mod mod : mods) {
String englishName = mod.getName();
if (StringUtils.isNotBlank(mod.getSubname())) {
englishName = mod.getSubname();
}
searchFilters.add(englishName);
count++;
if (count >= 3) break;
}
return super.searchImpl(gameVersion, category, section, pageOffset, String.join(" ", searchFilters), sort);
} else {
return super.searchImpl(gameVersion, category, section, pageOffset, searchFilter, sort);
}
}
}

View File

@ -17,6 +17,7 @@
*/
package org.jackhuang.hmcl.ui.versions;
import org.jackhuang.hmcl.util.Pair;
import org.jackhuang.hmcl.util.StringUtils;
import org.jackhuang.hmcl.util.io.IOUtils;
@ -26,6 +27,7 @@ import java.util.logging.Level;
import java.util.stream.Collectors;
import static org.jackhuang.hmcl.util.Logging.LOG;
import static org.jackhuang.hmcl.util.Pair.pair;
/**
* Parser for mod_data.txt
@ -36,6 +38,8 @@ public final class ModTranslations {
private static List<Mod> mods;
private static Map<String, Mod> modIdMap; // mod id -> mod
private static Map<String, Mod> curseForgeMap; // curseforge id -> mod
private static List<Pair<String, Mod>> keywords;
private static int maxKeywordLength = -1;
private ModTranslations(){}
@ -51,6 +55,28 @@ public final class ModTranslations {
return modIdMap.get(id);
}
public static List<Mod> searchMod(String query) {
if (!loadKeywords()) return Collections.emptyList();
StringBuilder newQuery = query.chars()
.filter(ch -> !Character.isSpaceChar(ch))
.collect(StringBuilder::new, (sb, value) -> sb.append((char)value), StringBuilder::append);
query = newQuery.toString();
StringUtils.LongestCommonSubsequence lcs = new StringUtils.LongestCommonSubsequence(query.length(), maxKeywordLength);
List<Pair<Integer, Mod>> modList = new ArrayList<>();
for (Pair<String, Mod> keyword : keywords) {
int value = lcs.calc(query, keyword.getKey());
if (value >= Math.max(1, query.length() - 3)) {
modList.add(pair(value, keyword.getValue()));
}
}
return modList.stream()
.sorted((a, b) -> -a.getKey().compareTo(b.getKey()))
.map(Pair::getValue)
.collect(Collectors.toList());
}
private static boolean loadFromResource() {
if (mods != null) return true;
try {
@ -99,6 +125,34 @@ public final class ModTranslations {
return true;
}
private static boolean loadKeywords() {
if (keywords != null) {
return true;
}
if (mods == null) {
if (!loadFromResource()) return false;
}
keywords = new ArrayList<>();
maxKeywordLength = -1;
for (Mod mod : mods) {
if (StringUtils.isNotBlank(mod.getName())) {
keywords.add(pair(mod.getName(), mod));
maxKeywordLength = Math.max(maxKeywordLength, mod.getName().length());
}
if (StringUtils.isNotBlank(mod.getSubname())) {
keywords.add(pair(mod.getSubname(), mod));
maxKeywordLength = Math.max(maxKeywordLength, mod.getSubname().length());
}
if (StringUtils.isNotBlank(mod.getAbbr())) {
keywords.add(pair(mod.getAbbr(), mod));
maxKeywordLength = Math.max(maxKeywordLength, mod.getAbbr().length());
}
}
return true;
}
public static class Mod {
private final String curseforge;
private final String mcmod;
@ -106,11 +160,12 @@ public final class ModTranslations {
private final List<String> modIds;
private final String name;
private final String subname;
private final String abbr;
public Mod(String line) {
String[] items = line.split(";", -1);
if (items.length != 6) {
throw new IllegalArgumentException("Illegal mod data line, 6 items expected " + line);
if (items.length != 7) {
throw new IllegalArgumentException("Illegal mod data line, 7 items expected " + line);
}
curseforge = items[0];
@ -119,23 +174,29 @@ public final class ModTranslations {
modIds = Collections.unmodifiableList(Arrays.asList(items[3].split(",")));
name = items[4];
subname = items[5];
abbr = items[6];
}
public Mod(String curseforge, String mcmod, String mcbbs, List<String> modIds, String name, String subname) {
public Mod(String curseforge, String mcmod, String mcbbs, List<String> modIds, String name, String subname, String abbr) {
this.curseforge = curseforge;
this.mcmod = mcmod;
this.mcbbs = mcbbs;
this.modIds = modIds;
this.name = name;
this.subname = subname;
this.abbr = abbr;
}
public String getDisplayName() {
if (StringUtils.isBlank(subname)) {
return name;
} else {
return String.format("%s (%s)", name, subname);
StringBuilder builder = new StringBuilder();
if (StringUtils.isNotBlank(abbr)) {
builder.append("[").append(abbr).append("]");
}
builder.append(name);
if (StringUtils.isNotBlank(subname)) {
builder.append(" (").append(subname).append(")");
}
return builder.toString();
}
public String getCurseforge() {
@ -161,5 +222,9 @@ public final class ModTranslations {
public String getSubname() {
return subname;
}
public String getAbbr() {
return abbr;
}
}
}

View File

@ -33,6 +33,8 @@ about.thanks_to.contributors.statement=Thanks to the open source community for s
about.thanks_to.gamerteam.statement=Default background image
about.thanks_to.mcbbs=MCBBS
about.thanks_to.mcbbs.statement=Provide MCBBS download source
about.thanks_to.mcmod=mcmod.cn
about.thanks_to.mcmod.statement=Provide Mod Chinese name translations
about.thanks_to.users=Members of HMCL User Group
about.thanks_to.users.statement=Thanks to donations, bug reports and so on.
about.thanks_to.yushijinhun.statement=authlib-injector
@ -73,15 +75,18 @@ account.manage=Account List
account.methods=Login Type
account.methods.authlib_injector=authlib-injector
account.methods.microsoft=Microsoft Account
account.methods.microsoft.birth=How to edit birthday setting...
account.methods.microsoft.close_page=Microsoft account authorization has been finished. There are some remaining logging-in steps to be finished later. You can close this page right now.
account.methods.microsoft.error.add_family=Since you are not yet 18 years old, an adult must add you to a family in order for you to play Minecraft.
account.methods.microsoft.error.add_family_probably=Please check your account settings whether you are already 18 years old or not. If not, adding your account to a family is necessary.
account.methods.microsoft.error.country_unavailable=XBox Live is not available in your country/region.
account.methods.microsoft.error.missing_xbox_account=Your Microsoft account is not connected to an Xbox account. Please create one before continuing.
account.methods.microsoft.error.no_character=Account is missing a Minecraft Java profile. While the Microsoft account is valid, it does not own the game.
account.methods.microsoft.error.unknown=Failed to log in. Microsoft respond with error code %d.
account.methods.microsoft.logging_in=Logging in...
account.methods.microsoft.hint=You should click "login" button and continue login process in newly opened browser window.
account.methods.microsoft.manual=After clicking "login" button, you should finish authorization in the newly opened browser window. If the browser window failed to show, you can click here to copy the URL, and manually open it in your browser.
account.methods.microsoft.manual=After clicking "login" button, you should finish authorization in the newly opened browser window. If the browser window failed to show, you can click here to copy the URL, and manually open it in your browser.
account.methods.microsoft.profile=Account Profile...
account.methods.microsoft.waiting_browser=Waiting for authorization in opened browser window...
account.methods.offline=Offline
account.methods.offline.uuid=UUID

View File

@ -30,6 +30,8 @@ about.thanks_to.contributors.statement=沒有開源社區的支持Hello Minec
about.thanks_to.gamerteam.statement=提供默認背景圖
about.thanks_to.mcbbs=MCBBS 我的世界中文論壇
about.thanks_to.mcbbs.statement=提供 MCBBS 下載源
about.thanks_to.mcmod=MC 百科
about.thanks_to.mcmod.statement=提供 Mod 中文名映射表與 Mod 百科
about.thanks_to.users=HMCL 用戶群成員
about.thanks_to.users.statement=感謝用戶群成員贊助充電、積極催更、迴響問題、出謀劃策
about.thanks_to.yushijinhun.statement=authlib-injector 相关支持
@ -78,8 +80,10 @@ account.manage=帳戶列表
account.methods=登入方式
account.methods.authlib_injector=authlib-injector 登入
account.methods.microsoft=微軟帳戶
account.methods.microsoft.birth=如何修改帳戶出生日期
account.methods.microsoft.close_page=已完成微軟帳號授權,接下來啟動器還需要完成剩餘登入步驟。你已經可以關閉本頁面了。
account.methods.microsoft.error.add_family=由於你未滿 18 歲你的帳號必須被加入到家庭中才能登入遊戲。你也可以更改你的帳戶的出生日期使年齡滿18歲以上以繼續登入。
account.methods.microsoft.error.add_family=由於你未滿 18 歲,你的帳戶必須被加入到家庭中才能登錄遊戲。你也可以點擊上方連結更改你的帳戶的出生日期,使年齡滿 18 歲以上以繼續登錄。
account.methods.microsoft.error.add_family_probably=請檢查你的帳戶設置,如果你未滿 18 歲,你的帳戶必須被加入到家庭中才能登錄遊戲。你也可以點擊上方連結更改你的帳戶的出生日期,使年齡滿 18 歲以上以繼續登錄。
account.methods.microsoft.error.country_unavailable=你所在的國家或地區不受 XBox Live 的支持。
account.methods.microsoft.error.missing_xbox_account=你的微軟帳號尚未關聯 XBox 帳號,你必須先創建 XBox 帳號,才能登入遊戲。
account.methods.microsoft.error.no_character=該帳號沒有包含 Minecraft Java 版購買記錄
@ -87,6 +91,7 @@ account.methods.microsoft.error.unknown=登入失敗,錯誤碼:%d
account.methods.microsoft.logging_in=登入中...
account.methods.microsoft.hint=您需要點擊登入按鈕,並在新打開的瀏覽器窗口中完成登入。
account.methods.microsoft.manual=若登入頁面未能打開,您可以點擊此處複製連結,並手動在瀏覽器中打開網頁。
account.methods.microsoft.profile=帳戶設置頁
account.methods.microsoft.waiting_browser=等待在新打開的瀏覽器窗口中完成登入...
account.methods.offline=離線模式
account.methods.offline.uuid=UUID

View File

@ -33,6 +33,8 @@ about.thanks_to.contributors.statement=没有开源社区的支持Hello Minec
about.thanks_to.gamerteam.statement=提供默认背景图
about.thanks_to.mcbbs=MCBBS 我的世界中文论坛
about.thanks_to.mcbbs.statement=提供 MCBBS 下载源
about.thanks_to.mcmod=MC 百科
about.thanks_to.mcmod.statement=提供 Mod 中文名映射表与 Mod 百科
about.thanks_to.users=HMCL 用户群成员
about.thanks_to.users.statement=感谢用户群成员赞助充电、积极催更、反馈问题、出谋划策
about.thanks_to.yushijinhun.statement=authlib-injector 相关支持
@ -73,8 +75,10 @@ account.manage=帐户列表
account.methods=登录方式
account.methods.authlib_injector=外置登录 (authlib-injector)
account.methods.microsoft=微软帐户
account.methods.microsoft.birth=如何修改帐户出生日期
account.methods.microsoft.close_page=已完成微软帐户授权,接下来启动器还需要完成剩余登录步骤。你已经可以关闭本页面了。
account.methods.microsoft.error.add_family=由于你未满 18 岁,你的帐户必须被加入到家庭中才能登录游戏。你也可以更改你的帐户的出生日期,使年龄满 18 岁以上以继续登录。
account.methods.microsoft.error.add_family=由于你未满 18 岁,你的帐户必须被加入到家庭中才能登录游戏。你也可以点击上方链接更改你的帐户的出生日期,使年龄满 18 岁以上以继续登录。
account.methods.microsoft.error.add_family_probably=请检查你的帐户设置,如果年龄未满 18 岁,你的帐户必须被加入到家庭中才能登录游戏。你也可以点击上方链接更改你的帐户的出生日期,使年龄满 18 岁以上以继续登录。
account.methods.microsoft.error.country_unavailable=你所在的国家或地区不受 XBox Live 的支持。
account.methods.microsoft.error.missing_xbox_account=你的微软帐户尚未关联 XBox 帐户,你必须先创建 XBox 帐户,才能登录游戏。
account.methods.microsoft.error.no_character=该帐户没有包含 Minecraft Java 版购买记录
@ -82,6 +86,7 @@ account.methods.microsoft.error.unknown=登录失败,错误码:%d
account.methods.microsoft.logging_in=登录中...
account.methods.microsoft.hint=您需要点击登录按钮,并在新打开的浏览器窗口中完成登录。
account.methods.microsoft.manual=若登录页面未能打开,您可以点击此处复制链接,并手动在浏览器中打开网页。
account.methods.microsoft.profile=帐户设置页
account.methods.microsoft.waiting_browser=等待在新打开的浏览器窗口中完成登录...
account.methods.offline=离线模式
account.methods.offline.uuid=UUID

File diff suppressed because it is too large Load Diff

View File

@ -25,6 +25,7 @@ import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import java.util.StringTokenizer;
import java.util.regex.Pattern;
/**
*
@ -243,4 +244,42 @@ public final class StringUtils {
}
return result.toString();
}
/**
* Class for computing the longest common subsequence between strings.
*/
public static class LongestCommonSubsequence {
// We reuse dynamic programming storage array here to reduce allocations.
private final int[][] f;
private final int maxLengthA;
private final int maxLengthB;
public LongestCommonSubsequence(int maxLengthA, int maxLengthB) {
this.maxLengthA = maxLengthA;
this.maxLengthB = maxLengthB;
f = new int[maxLengthA + 1][];
for (int i = 0; i <= maxLengthA; i++) {
f[i] = new int[maxLengthB + 1];
}
}
public int calc(CharSequence a, CharSequence b) {
if (a.length() > maxLengthA || b.length() > maxLengthB) {
throw new IllegalArgumentException("Too large length");
}
for (int i = 1; i <= a.length(); i++) {
for (int j = 1; j <= b.length(); j++) {
if (a.charAt(i - 1) == b.charAt(j - 1)) {
f[i][j] = 1 + f[i - 1][j - 1];
} else {
f[i][j] = Math.max(f[i - 1][j], f[i][j - 1]);
}
}
}
return f[a.length()][b.length()];
}
}
public static final Pattern CHINESE_PATTERN = Pattern.compile("[\\u4e00-\\u9fa5]");
}