feat: (WIP) Curse mod/modpack download
This commit is contained in:
@@ -61,10 +61,9 @@ public class ListPageSkin extends SkinBase<ListPage<?>> {
|
|||||||
|
|
||||||
VBox vBox = new VBox();
|
VBox vBox = new VBox();
|
||||||
{
|
{
|
||||||
|
vBox.getStyleClass().add("card-list");
|
||||||
vBox.setAlignment(Pos.BOTTOM_RIGHT);
|
vBox.setAlignment(Pos.BOTTOM_RIGHT);
|
||||||
vBox.setPickOnBounds(false);
|
vBox.setPickOnBounds(false);
|
||||||
vBox.setPadding(new Insets(15));
|
|
||||||
vBox.setSpacing(15);
|
|
||||||
|
|
||||||
JFXButton btnAdd = new JFXButton();
|
JFXButton btnAdd = new JFXButton();
|
||||||
FXUtils.setLimitWidth(btnAdd, 40);
|
FXUtils.setLimitWidth(btnAdd, 40);
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ package org.jackhuang.hmcl.ui.profile;
|
|||||||
|
|
||||||
import com.jfoenix.controls.JFXButton;
|
import com.jfoenix.controls.JFXButton;
|
||||||
import javafx.css.PseudoClass;
|
import javafx.css.PseudoClass;
|
||||||
import javafx.event.ActionEvent;
|
|
||||||
import javafx.geometry.Pos;
|
import javafx.geometry.Pos;
|
||||||
import javafx.scene.Node;
|
import javafx.scene.Node;
|
||||||
import javafx.scene.control.SkinBase;
|
import javafx.scene.control.SkinBase;
|
||||||
|
|||||||
@@ -0,0 +1,153 @@
|
|||||||
|
package org.jackhuang.hmcl.ui.versions;
|
||||||
|
|
||||||
|
import com.jfoenix.controls.JFXButton;
|
||||||
|
import com.jfoenix.controls.JFXTextField;
|
||||||
|
import javafx.beans.property.BooleanProperty;
|
||||||
|
import javafx.beans.property.SimpleBooleanProperty;
|
||||||
|
import javafx.geometry.Pos;
|
||||||
|
import javafx.scene.control.Control;
|
||||||
|
import javafx.scene.control.ScrollPane;
|
||||||
|
import javafx.scene.control.Skin;
|
||||||
|
import javafx.scene.control.SkinBase;
|
||||||
|
import javafx.scene.layout.*;
|
||||||
|
import org.jackhuang.hmcl.mod.curse.CurseModManager;
|
||||||
|
import org.jackhuang.hmcl.setting.Profile;
|
||||||
|
import org.jackhuang.hmcl.task.Schedulers;
|
||||||
|
import org.jackhuang.hmcl.task.Task;
|
||||||
|
import org.jackhuang.hmcl.ui.animation.TransitionPane;
|
||||||
|
import org.jackhuang.hmcl.ui.construct.SpinnerPane;
|
||||||
|
|
||||||
|
import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
|
||||||
|
|
||||||
|
public class ModDownloadListPage extends Control {
|
||||||
|
private final BooleanProperty loading = new SimpleBooleanProperty(false);
|
||||||
|
private final BooleanProperty failed = new SimpleBooleanProperty(false);
|
||||||
|
/**
|
||||||
|
* @see org.jackhuang.hmcl.mod.curse.CurseModManager#SECTION_MODPACK
|
||||||
|
* @see org.jackhuang.hmcl.mod.curse.CurseModManager#SECTION_MOD
|
||||||
|
*/
|
||||||
|
private final int section;
|
||||||
|
private Profile profile;
|
||||||
|
private String version;
|
||||||
|
|
||||||
|
public ModDownloadListPage(int section) {
|
||||||
|
this.section = section;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void loadVersion(Profile profile, String version) {
|
||||||
|
this.profile = profile;
|
||||||
|
this.version = version;
|
||||||
|
|
||||||
|
setLoading(false);
|
||||||
|
setFailed(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isFailed() {
|
||||||
|
return failed.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
public BooleanProperty failedProperty() {
|
||||||
|
return failed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFailed(boolean failed) {
|
||||||
|
this.failed.set(failed);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isLoading() {
|
||||||
|
return loading.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
public BooleanProperty loadingProperty() {
|
||||||
|
return loading;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLoading(boolean loading) {
|
||||||
|
this.loading.set(loading);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void search(String gameVersion, int category, int pageOffset, String searchFilter, int sort) {
|
||||||
|
setLoading(true);
|
||||||
|
Task.supplyAsync(() -> CurseModManager.searchPaginated(gameVersion, category, section, pageOffset, searchFilter, sort))
|
||||||
|
.whenComplete(Schedulers.javafx(), (exception, result) -> {
|
||||||
|
setLoading(false);
|
||||||
|
if (exception == null) {
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Skin<?> createDefaultSkin() {
|
||||||
|
return new ModDownloadListPageSkin(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class ModDownloadListPageSkin extends SkinBase<ModDownloadListPage> {
|
||||||
|
|
||||||
|
protected ModDownloadListPageSkin(ModDownloadListPage control) {
|
||||||
|
super(control);
|
||||||
|
|
||||||
|
VBox pane = new VBox();
|
||||||
|
pane.getStyleClass().add("card-list");
|
||||||
|
|
||||||
|
GridPane searchPane = new GridPane();
|
||||||
|
searchPane.getStyleClass().add("card");
|
||||||
|
|
||||||
|
ColumnConstraints column1 = new ColumnConstraints();
|
||||||
|
column1.setPercentWidth(50);
|
||||||
|
column1.setHgrow(Priority.ALWAYS);
|
||||||
|
ColumnConstraints column2 = new ColumnConstraints();
|
||||||
|
column2.setHgrow(Priority.ALWAYS);
|
||||||
|
column2.setPercentWidth(50);
|
||||||
|
searchPane.getColumnConstraints().setAll(column1, column2);
|
||||||
|
|
||||||
|
searchPane.setHgap(16);
|
||||||
|
searchPane.setVgap(10);
|
||||||
|
|
||||||
|
{
|
||||||
|
JFXTextField nameField = new JFXTextField();
|
||||||
|
nameField.setPromptText(i18n("mods.name"));
|
||||||
|
searchPane.add(nameField, 0, 0);
|
||||||
|
|
||||||
|
JFXTextField gameVersionField = new JFXTextField();
|
||||||
|
gameVersionField.setPromptText(i18n("world.game_version"));
|
||||||
|
searchPane.add(gameVersionField, 1, 0);
|
||||||
|
|
||||||
|
JFXTextField categoryField = new JFXTextField();
|
||||||
|
categoryField.setPromptText(i18n("mods.category"));
|
||||||
|
searchPane.add(categoryField, 0, 1);
|
||||||
|
|
||||||
|
JFXTextField sortField = new JFXTextField();
|
||||||
|
sortField.setPromptText(i18n("search.sort"));
|
||||||
|
searchPane.add(sortField, 1, 1);
|
||||||
|
|
||||||
|
VBox vbox = new VBox();
|
||||||
|
vbox.setAlignment(Pos.CENTER_RIGHT);
|
||||||
|
searchPane.add(vbox, 0, 2, 2, 1);
|
||||||
|
|
||||||
|
JFXButton searchButton = new JFXButton();
|
||||||
|
searchButton.setText(i18n("search"));
|
||||||
|
searchButton.setOnAction(e -> {
|
||||||
|
getSkinnable().search(gameVersionField.getText(), categoryField.getText(), 0, nameField.getText(), sortField.getText())
|
||||||
|
.whenComplete();
|
||||||
|
});
|
||||||
|
searchPane.add(searchButton, 0, 2);
|
||||||
|
vbox.getChildren().setAll(searchButton);
|
||||||
|
}
|
||||||
|
|
||||||
|
TransitionPane transitionPane = new TransitionPane();
|
||||||
|
{
|
||||||
|
|
||||||
|
SpinnerPane spinnerPane = new SpinnerPane();
|
||||||
|
spinnerPane.loadingProperty().bind(getSkinnable().loadingProperty());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
pane.getChildren().setAll(searchPane, transitionPane);
|
||||||
|
|
||||||
|
getChildren().setAll(pane);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
package org.jackhuang.hmcl.ui.versions;
|
||||||
|
|
||||||
|
import javafx.beans.property.ReadOnlyObjectProperty;
|
||||||
|
import javafx.beans.property.ReadOnlyObjectWrapper;
|
||||||
|
import javafx.scene.control.Control;
|
||||||
|
import javafx.scene.control.Skin;
|
||||||
|
import javafx.scene.control.SkinBase;
|
||||||
|
import org.jackhuang.hmcl.game.Version;
|
||||||
|
import org.jackhuang.hmcl.mod.curse.CurseAddon;
|
||||||
|
import org.jackhuang.hmcl.ui.decorator.DecoratorPage;
|
||||||
|
|
||||||
|
public class ModDownloadPage extends Control implements DecoratorPage {
|
||||||
|
private final ReadOnlyObjectWrapper<State> state = new ReadOnlyObjectWrapper<>();
|
||||||
|
private final CurseAddon addon;
|
||||||
|
private final Version version;
|
||||||
|
|
||||||
|
public ModDownloadPage(CurseAddon addon, Version version) {
|
||||||
|
this.addon = addon;
|
||||||
|
this.version = version;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ReadOnlyObjectProperty<State> stateProperty() {
|
||||||
|
return state.getReadOnlyProperty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Skin<?> createDefaultSkin() {
|
||||||
|
return new ModDownloadPageSkin(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class ModDownloadPageSkin extends SkinBase<ModDownloadPage> {
|
||||||
|
|
||||||
|
protected ModDownloadPageSkin(ModDownloadPage control) {
|
||||||
|
super(control);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -50,6 +50,8 @@ public class VersionPage extends Control implements DecoratorPage {
|
|||||||
private final VersionSettingsPage versionSettingsPage = new VersionSettingsPage();
|
private final VersionSettingsPage versionSettingsPage = new VersionSettingsPage();
|
||||||
private final TabHeader.Tab modListTab = new TabHeader.Tab("modListTab");
|
private final TabHeader.Tab modListTab = new TabHeader.Tab("modListTab");
|
||||||
private final ModListPage modListPage = new ModListPage(modListTab);
|
private final ModListPage modListPage = new ModListPage(modListTab);
|
||||||
|
private final TabHeader.Tab curseModListTab = new TabHeader.Tab("modListTab");
|
||||||
|
private final ModDownloadListPage curseModListPage = new ModDownloadListPage();
|
||||||
private final TabHeader.Tab installerListTab = new TabHeader.Tab("installerListTab");
|
private final TabHeader.Tab installerListTab = new TabHeader.Tab("installerListTab");
|
||||||
private final InstallerListPage installerListPage = new InstallerListPage();
|
private final InstallerListPage installerListPage = new InstallerListPage();
|
||||||
private final TabHeader.Tab worldListTab = new TabHeader.Tab("worldList");
|
private final TabHeader.Tab worldListTab = new TabHeader.Tab("worldList");
|
||||||
@@ -64,6 +66,7 @@ public class VersionPage extends Control implements DecoratorPage {
|
|||||||
{
|
{
|
||||||
versionSettingsTab.setNode(versionSettingsPage);
|
versionSettingsTab.setNode(versionSettingsPage);
|
||||||
modListTab.setNode(modListPage);
|
modListTab.setNode(modListPage);
|
||||||
|
curseModListTab.setNode(curseModListPage);
|
||||||
installerListTab.setNode(installerListPage);
|
installerListTab.setNode(installerListPage);
|
||||||
worldListTab.setNode(worldListPage);
|
worldListTab.setNode(worldListPage);
|
||||||
|
|
||||||
@@ -92,6 +95,7 @@ public class VersionPage extends Control implements DecoratorPage {
|
|||||||
preferredVersionName = version;
|
preferredVersionName = version;
|
||||||
|
|
||||||
versionSettingsPage.loadVersion(profile, version);
|
versionSettingsPage.loadVersion(profile, version);
|
||||||
|
curseModListPage.loadVersion(profile, version);
|
||||||
currentVersionUpgradable.set(profile.getRepository().isModpack(version));
|
currentVersionUpgradable.set(profile.getRepository().isModpack(version));
|
||||||
|
|
||||||
CompletableFuture.allOf(
|
CompletableFuture.allOf(
|
||||||
@@ -245,6 +249,14 @@ public class VersionPage extends Control implements DecoratorPage {
|
|||||||
modListItem.activeProperty().bind(control.selectedTab.isEqualTo(control.modListTab));
|
modListItem.activeProperty().bind(control.selectedTab.isEqualTo(control.modListTab));
|
||||||
modListItem.setOnAction(e -> control.selectedTab.set(control.modListTab));
|
modListItem.setOnAction(e -> control.selectedTab.set(control.modListTab));
|
||||||
|
|
||||||
|
AdvancedListItem curseModListItem = new AdvancedListItem();
|
||||||
|
curseModListItem.getStyleClass().add("navigation-drawer-item");
|
||||||
|
curseModListItem.setTitle(i18n("mods.download"));
|
||||||
|
curseModListItem.setLeftGraphic(wrap(SVG.fire(Theme.blackFillBinding(), 20, 20)));
|
||||||
|
curseModListItem.setActionButtonVisible(false);
|
||||||
|
curseModListItem.activeProperty().bind(control.selectedTab.isEqualTo(control.curseModListTab));
|
||||||
|
curseModListItem.setOnAction(e -> control.selectedTab.set(control.curseModListTab));
|
||||||
|
|
||||||
AdvancedListItem installerListItem = new AdvancedListItem();
|
AdvancedListItem installerListItem = new AdvancedListItem();
|
||||||
installerListItem.getStyleClass().add("navigation-drawer-item");
|
installerListItem.getStyleClass().add("navigation-drawer-item");
|
||||||
installerListItem.setTitle(i18n("settings.tabs.installers"));
|
installerListItem.setTitle(i18n("settings.tabs.installers"));
|
||||||
@@ -264,6 +276,7 @@ public class VersionPage extends Control implements DecoratorPage {
|
|||||||
AdvancedListBox sideBar = new AdvancedListBox()
|
AdvancedListBox sideBar = new AdvancedListBox()
|
||||||
.add(versionSettingsItem)
|
.add(versionSettingsItem)
|
||||||
.add(modListItem)
|
.add(modListItem)
|
||||||
|
.add(curseModListItem)
|
||||||
.add(installerListItem)
|
.add(installerListItem)
|
||||||
.add(worldListItem);
|
.add(worldListItem);
|
||||||
left.setCenter(sideBar);
|
left.setCenter(sideBar);
|
||||||
|
|||||||
@@ -783,6 +783,11 @@
|
|||||||
-fx-background-color: derive(-fx-base-color, 60%);
|
-fx-background-color: derive(-fx-base-color, 60%);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.card-list {
|
||||||
|
-fx-padding: 10;
|
||||||
|
-fx-spacing: 10;
|
||||||
|
}
|
||||||
|
|
||||||
.options-sublist {
|
.options-sublist {
|
||||||
-fx-background-color: white;
|
-fx-background-color: white;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
xmlns:fx="http://javafx.com/fxml"
|
xmlns:fx="http://javafx.com/fxml"
|
||||||
type="StackPane">
|
type="StackPane">
|
||||||
<ScrollPane fx:id="scroll" fitToHeight="true" fitToWidth="true" vbarPolicy="ALWAYS">
|
<ScrollPane fx:id="scroll" fitToHeight="true" fitToWidth="true" vbarPolicy="ALWAYS">
|
||||||
<VBox fx:id="rootPane" style="-fx-padding: 10;" spacing="12">
|
<VBox fx:id="rootPane" styleClass="card-list">
|
||||||
|
|
||||||
<ComponentList fx:id="iconPickerItemWrapper">
|
<ComponentList fx:id="iconPickerItemWrapper">
|
||||||
<ImagePickerItem fx:id="iconPickerItem" title="%settings.icon" onSelectButtonClicked="#onExploreIcon" onDeleteButtonClicked="#onDeleteIcon">
|
<ImagePickerItem fx:id="iconPickerItem" title="%settings.icon" onSelectButtonClicked="#onExploreIcon" onDeleteButtonClicked="#onDeleteIcon">
|
||||||
|
|||||||
@@ -307,6 +307,7 @@ mods.add=Install mods
|
|||||||
mods.add.failed=Failed to install mods %s.
|
mods.add.failed=Failed to install mods %s.
|
||||||
mods.add.success=Successfully installed mods %s.
|
mods.add.success=Successfully installed mods %s.
|
||||||
mods.choose_mod=Choose your mods
|
mods.choose_mod=Choose your mods
|
||||||
|
mods.download=Mod Downloads
|
||||||
mods.enable=Enable
|
mods.enable=Enable
|
||||||
mods.disable=Disable
|
mods.disable=Disable
|
||||||
mods.name=Name
|
mods.name=Name
|
||||||
|
|||||||
@@ -98,6 +98,63 @@ color.custom=自定义颜色
|
|||||||
crash.NoClassDefFound=请确认 Hello Minecraft! Launcher 本体是否完整,或更新您的 Java。
|
crash.NoClassDefFound=请确认 Hello Minecraft! Launcher 本体是否完整,或更新您的 Java。
|
||||||
crash.user_fault=您的系统或 Java 环境可能安装不当导致本软件崩溃,请检查您的 Java 环境或您的电脑!可以尝试重新安装 Java。
|
crash.user_fault=您的系统或 Java 环境可能安装不当导致本软件崩溃,请检查您的 Java 环境或您的电脑!可以尝试重新安装 Java。
|
||||||
|
|
||||||
|
curse.category.modpack.sci-fi=科幻
|
||||||
|
curse.category.modpack.small-light=轻量整合包
|
||||||
|
curse.category.modpack.combat-pvp=战斗 PVP
|
||||||
|
curse.category.modpack.mini-game=小游戏
|
||||||
|
curse.category.modpack.quests=任务
|
||||||
|
curse.category.modpack.multiplayer=多人
|
||||||
|
curse.category.modpack.exploration=探索
|
||||||
|
curse.category.modpack.skyblock=空岛
|
||||||
|
curse.category.modpack.adventure-and-rpg=冒险 RPG
|
||||||
|
curse.category.modpack.ftb-official-pack=FTB 整合包
|
||||||
|
curse.category.modpack.map-based=有特定地图
|
||||||
|
curse.category.modpack.hardcore=高难度
|
||||||
|
curse.category.modpack.extra-large=大型整合包
|
||||||
|
curse.category.modpack.tech=科技
|
||||||
|
curse.category.modpack.magic=魔法
|
||||||
|
|
||||||
|
curse.category.mod.map-information=信息展示
|
||||||
|
curse.category.mod.mc-addons=模组扩展
|
||||||
|
curse.category.mod.armor-weapons-tools=装备武器
|
||||||
|
curse.category.mod.world-structures=自然生成
|
||||||
|
curse.category.mod.blood-magic=血魔法
|
||||||
|
curse.category.mod.storage=存储
|
||||||
|
curse.category.mod.addons-industrialcraft=工业 (Industrialcraft)
|
||||||
|
curse.category.mod.magic=魔法
|
||||||
|
curse.category.mod.technology=科技
|
||||||
|
curse.category.mod.[4557]redstone=红石
|
||||||
|
curse.category.mod.addons-tinkers-construct=匠魂
|
||||||
|
curse.category.mod.technology-player-transport=交通运输
|
||||||
|
curse.category.mod.[4486]lucky-blocks=Lucky Blocks
|
||||||
|
curse.category.mod.addons-buildcraft=建筑 (Buildcraft)
|
||||||
|
curse.category.mod.technology-genetics=基因
|
||||||
|
curse.category.mod.twitch-integration=Twitch
|
||||||
|
curse.category.mod.world-ores-resources=矿物资源
|
||||||
|
curse.category.mod.crafttweaker=CraftTweaker
|
||||||
|
curse.category.mod.addons-thaumcraft=神秘 (Thaumcraft)
|
||||||
|
curse.category.mod.adventure-rpg=冒险 RPG
|
||||||
|
curse.category.mod.technology-processing=机器处理
|
||||||
|
curse.category.mod.technology-energy=能源
|
||||||
|
curse.category.mod.technology-item-fluid-energy-transport=物流运输
|
||||||
|
curse.category.mod.addons-forestry=林业 (Forestry)
|
||||||
|
curse.category.mod.mc-miscellaneous=其他
|
||||||
|
curse.category.mod.applied-energistics-2=应用能源 2 (Applied Energistics 2)
|
||||||
|
curse.category.mod.technology-farming=农业
|
||||||
|
curse.category.mod.library-api=支持库
|
||||||
|
curse.category.mod.fabric=Fabric
|
||||||
|
curse.category.mod.cosmetic=装饰
|
||||||
|
curse.category.mod.world-gen=世界生成
|
||||||
|
curse.category.mod.server-utility=服务器
|
||||||
|
curse.category.mod.world-mobs=生物
|
||||||
|
curse.category.mod.world-biomes=生物群系
|
||||||
|
curse.category.mod.addons-thermalexpansion=热力膨胀 (Thermal Expansion)
|
||||||
|
curse.category.mod.world-dimensions=维度
|
||||||
|
curse.category.mod.mc-food=食物
|
||||||
|
curse.category.mod.redstone=红石
|
||||||
|
curse.category.mod.technology-automation=自动化
|
||||||
|
curse.category.mod.mc-creator=MCreator
|
||||||
|
|
||||||
download=下载
|
download=下载
|
||||||
download.code.404=远程服务器不包含需要下载的文件: %s
|
download.code.404=远程服务器不包含需要下载的文件: %s
|
||||||
download.failed=下载失败: %1$s,错误码:%2$d
|
download.failed=下载失败: %1$s,错误码:%2$d
|
||||||
@@ -307,9 +364,11 @@ mods.add=添加模组
|
|||||||
mods.add.failed=添加模组 %s 失败。
|
mods.add.failed=添加模组 %s 失败。
|
||||||
mods.add.success=成功添加模组 %s。
|
mods.add.success=成功添加模组 %s。
|
||||||
mods.choose_mod=选择模组
|
mods.choose_mod=选择模组
|
||||||
|
mods.download=模组下载
|
||||||
mods.enable=启用
|
mods.enable=启用
|
||||||
mods.disable=禁用
|
mods.disable=禁用
|
||||||
mods.name=名称
|
mods.name=名称
|
||||||
|
mods.category=类别
|
||||||
mods.not_modded=你需要先在自动安装页面安装 Fabric、Forge 或 LiteLoader 才能进行模组管理。
|
mods.not_modded=你需要先在自动安装页面安装 Fabric、Forge 或 LiteLoader 才能进行模组管理。
|
||||||
|
|
||||||
datapack=数据包
|
datapack=数据包
|
||||||
@@ -352,6 +411,9 @@ profile.title=游戏目录
|
|||||||
profile.selected=已选中
|
profile.selected=已选中
|
||||||
profile.use_relative_path=若可能,游戏目录使用相对路径
|
profile.use_relative_path=若可能,游戏目录使用相对路径
|
||||||
|
|
||||||
|
search=搜索
|
||||||
|
search.sort=排序
|
||||||
|
|
||||||
selector.choose=选择
|
selector.choose=选择
|
||||||
selector.choose_file=选择文件
|
selector.choose_file=选择文件
|
||||||
selector.custom=自定义
|
selector.custom=自定义
|
||||||
|
|||||||
@@ -0,0 +1,435 @@
|
|||||||
|
package org.jackhuang.hmcl.mod.curse;
|
||||||
|
|
||||||
|
import org.jackhuang.hmcl.util.Immutable;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Immutable
|
||||||
|
public class CurseAddon {
|
||||||
|
private final int id;
|
||||||
|
private final String name;
|
||||||
|
private final List<Author> authors;
|
||||||
|
private final String websiteUrl;
|
||||||
|
private final int gameId;
|
||||||
|
private final String summary;
|
||||||
|
private final int defaultFileId;
|
||||||
|
private final List<LatestFile> latestFiles;
|
||||||
|
private final List<Category> categories;
|
||||||
|
private final int status;
|
||||||
|
private final int primaryCategoryId;
|
||||||
|
private final String slug;
|
||||||
|
private final List<GameVersionLatestFile> gameVersionLatestFiles;
|
||||||
|
private final boolean isFeatured;
|
||||||
|
private final double popularityScore;
|
||||||
|
private final int gamePopularityRank;
|
||||||
|
private final String primaryLanguage; // e.g. enUS
|
||||||
|
private final List<String> modLoaders;
|
||||||
|
private final boolean isAvailable;
|
||||||
|
private final boolean isExperimental;
|
||||||
|
|
||||||
|
public CurseAddon(int id, String name, List<Author> authors, String websiteUrl, int gameId, String summary, int defaultFileId, List<LatestFile> latestFiles, List<Category> categories, int status, int primaryCategoryId, String slug, List<GameVersionLatestFile> gameVersionLatestFiles, boolean isFeatured, double popularityScore, int gamePopularityRank, String primaryLanguage, List<String> modLoaders, boolean isAvailable, boolean isExperimental) {
|
||||||
|
this.id = id;
|
||||||
|
this.name = name;
|
||||||
|
this.authors = authors;
|
||||||
|
this.websiteUrl = websiteUrl;
|
||||||
|
this.gameId = gameId;
|
||||||
|
this.summary = summary;
|
||||||
|
this.defaultFileId = defaultFileId;
|
||||||
|
this.latestFiles = latestFiles;
|
||||||
|
this.categories = categories;
|
||||||
|
this.status = status;
|
||||||
|
this.primaryCategoryId = primaryCategoryId;
|
||||||
|
this.slug = slug;
|
||||||
|
this.gameVersionLatestFiles = gameVersionLatestFiles;
|
||||||
|
this.isFeatured = isFeatured;
|
||||||
|
this.popularityScore = popularityScore;
|
||||||
|
this.gamePopularityRank = gamePopularityRank;
|
||||||
|
this.primaryLanguage = primaryLanguage;
|
||||||
|
this.modLoaders = modLoaders;
|
||||||
|
this.isAvailable = isAvailable;
|
||||||
|
this.isExperimental = isExperimental;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Author> getAuthors() {
|
||||||
|
return authors;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getWebsiteUrl() {
|
||||||
|
return websiteUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getGameId() {
|
||||||
|
return gameId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSummary() {
|
||||||
|
return summary;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getDefaultFileId() {
|
||||||
|
return defaultFileId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<LatestFile> getLatestFiles() {
|
||||||
|
return latestFiles;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Category> getCategories() {
|
||||||
|
return categories;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getStatus() {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getPrimaryCategoryId() {
|
||||||
|
return primaryCategoryId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSlug() {
|
||||||
|
return slug;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<GameVersionLatestFile> getGameVersionLatestFiles() {
|
||||||
|
return gameVersionLatestFiles;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isFeatured() {
|
||||||
|
return isFeatured;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getPopularityScore() {
|
||||||
|
return popularityScore;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getGamePopularityRank() {
|
||||||
|
return gamePopularityRank;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPrimaryLanguage() {
|
||||||
|
return primaryLanguage;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getModLoaders() {
|
||||||
|
return modLoaders;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isAvailable() {
|
||||||
|
return isAvailable;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isExperimental() {
|
||||||
|
return isExperimental;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Immutable
|
||||||
|
public static class Author {
|
||||||
|
private final String name;
|
||||||
|
private final String url;
|
||||||
|
private final int projectId;
|
||||||
|
private final int id;
|
||||||
|
private final int userId;
|
||||||
|
private final int twitchId;
|
||||||
|
|
||||||
|
public Author(String name, String url, int projectId, int id, int userId, int twitchId) {
|
||||||
|
this.name = name;
|
||||||
|
this.url = url;
|
||||||
|
this.projectId = projectId;
|
||||||
|
this.id = id;
|
||||||
|
this.userId = userId;
|
||||||
|
this.twitchId = twitchId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUrl() {
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getProjectId() {
|
||||||
|
return projectId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getUserId() {
|
||||||
|
return userId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getTwitchId() {
|
||||||
|
return twitchId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Immutable
|
||||||
|
public static class Dependency {
|
||||||
|
private final int id;
|
||||||
|
private final int addonId;
|
||||||
|
private final int type;
|
||||||
|
private final int fileId;
|
||||||
|
|
||||||
|
public Dependency() {
|
||||||
|
this(0, 0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Dependency(int id, int addonId, int type, int fileId) {
|
||||||
|
this.id = id;
|
||||||
|
this.addonId = addonId;
|
||||||
|
this.type = type;
|
||||||
|
this.fileId = fileId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getAddonId() {
|
||||||
|
return addonId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getFileId() {
|
||||||
|
return fileId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Immutable
|
||||||
|
public static class LatestFile {
|
||||||
|
private final int id;
|
||||||
|
private final String displayName;
|
||||||
|
private final String fileDate;
|
||||||
|
private final int fileLength;
|
||||||
|
private final int releaseType;
|
||||||
|
private final int fileStatus;
|
||||||
|
private final String downloadUrl;
|
||||||
|
private final boolean isAlternate;
|
||||||
|
private final int alternateFileId;
|
||||||
|
private final List<Dependency> dependencies;
|
||||||
|
private final boolean isAvailable;
|
||||||
|
private final List<String> gameVersion;
|
||||||
|
private final boolean hasInstallScript;
|
||||||
|
private final boolean isCompatibleWIthClient;
|
||||||
|
private final int categorySectionPackageType;
|
||||||
|
private final int restrictProjectFileAccess;
|
||||||
|
private final int projectStatus;
|
||||||
|
private final int projectId;
|
||||||
|
private final int isServerPack;
|
||||||
|
private final int serverPackFileId;
|
||||||
|
|
||||||
|
public LatestFile(int id, String displayName, String fileDate, int fileLength, int releaseType, int fileStatus, String downloadUrl, boolean isAlternate, int alternateFileId, List<Dependency> dependencies, boolean isAvailable, List<String> gameVersion, boolean hasInstallScript, boolean isCompatibleWIthClient, int categorySectionPackageType, int restrictProjectFileAccess, int projectStatus, int projectId, int isServerPack, int serverPackFileId) {
|
||||||
|
this.id = id;
|
||||||
|
this.displayName = displayName;
|
||||||
|
this.fileDate = fileDate;
|
||||||
|
this.fileLength = fileLength;
|
||||||
|
this.releaseType = releaseType;
|
||||||
|
this.fileStatus = fileStatus;
|
||||||
|
this.downloadUrl = downloadUrl;
|
||||||
|
this.isAlternate = isAlternate;
|
||||||
|
this.alternateFileId = alternateFileId;
|
||||||
|
this.dependencies = dependencies;
|
||||||
|
this.isAvailable = isAvailable;
|
||||||
|
this.gameVersion = gameVersion;
|
||||||
|
this.hasInstallScript = hasInstallScript;
|
||||||
|
this.isCompatibleWIthClient = isCompatibleWIthClient;
|
||||||
|
this.categorySectionPackageType = categorySectionPackageType;
|
||||||
|
this.restrictProjectFileAccess = restrictProjectFileAccess;
|
||||||
|
this.projectStatus = projectStatus;
|
||||||
|
this.projectId = projectId;
|
||||||
|
this.isServerPack = isServerPack;
|
||||||
|
this.serverPackFileId = serverPackFileId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDisplayName() {
|
||||||
|
return displayName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFileDate() {
|
||||||
|
return fileDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getFileLength() {
|
||||||
|
return fileLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getReleaseType() {
|
||||||
|
return releaseType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getFileStatus() {
|
||||||
|
return fileStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDownloadUrl() {
|
||||||
|
return downloadUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isAlternate() {
|
||||||
|
return isAlternate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getAlternateFileId() {
|
||||||
|
return alternateFileId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Dependency> getDependencies() {
|
||||||
|
return dependencies;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isAvailable() {
|
||||||
|
return isAvailable;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getGameVersion() {
|
||||||
|
return gameVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isHasInstallScript() {
|
||||||
|
return hasInstallScript;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isCompatibleWIthClient() {
|
||||||
|
return isCompatibleWIthClient;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getCategorySectionPackageType() {
|
||||||
|
return categorySectionPackageType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getRestrictProjectFileAccess() {
|
||||||
|
return restrictProjectFileAccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getProjectStatus() {
|
||||||
|
return projectStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getProjectId() {
|
||||||
|
return projectId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getIsServerPack() {
|
||||||
|
return isServerPack;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getServerPackFileId() {
|
||||||
|
return serverPackFileId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Immutable
|
||||||
|
public static class Category {
|
||||||
|
private final int categoryId;
|
||||||
|
private final String name;
|
||||||
|
private final String url;
|
||||||
|
private final String avatarUrl;
|
||||||
|
private final int parentId;
|
||||||
|
private final int rootId;
|
||||||
|
private final int projectId;
|
||||||
|
private final int avatarId;
|
||||||
|
private final int gameId;
|
||||||
|
|
||||||
|
public Category(int categoryId, String name, String url, String avatarUrl, int parentId, int rootId, int projectId, int avatarId, int gameId) {
|
||||||
|
this.categoryId = categoryId;
|
||||||
|
this.name = name;
|
||||||
|
this.url = url;
|
||||||
|
this.avatarUrl = avatarUrl;
|
||||||
|
this.parentId = parentId;
|
||||||
|
this.rootId = rootId;
|
||||||
|
this.projectId = projectId;
|
||||||
|
this.avatarId = avatarId;
|
||||||
|
this.gameId = gameId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getCategoryId() {
|
||||||
|
return categoryId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUrl() {
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAvatarUrl() {
|
||||||
|
return avatarUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getParentId() {
|
||||||
|
return parentId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getRootId() {
|
||||||
|
return rootId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getProjectId() {
|
||||||
|
return projectId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getAvatarId() {
|
||||||
|
return avatarId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getGameId() {
|
||||||
|
return gameId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Immutable
|
||||||
|
public static class GameVersionLatestFile {
|
||||||
|
private final String gameVersion;
|
||||||
|
private final String projectFileId;
|
||||||
|
private final String projectFileName;
|
||||||
|
private final int fileType;
|
||||||
|
private final Integer modLoader; // optional
|
||||||
|
|
||||||
|
public GameVersionLatestFile(String gameVersion, String projectFileId, String projectFileName, int fileType, Integer modLoader) {
|
||||||
|
this.gameVersion = gameVersion;
|
||||||
|
this.projectFileId = projectFileId;
|
||||||
|
this.projectFileName = projectFileName;
|
||||||
|
this.fileType = fileType;
|
||||||
|
this.modLoader = modLoader;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getGameVersion() {
|
||||||
|
return gameVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getProjectFileId() {
|
||||||
|
return projectFileId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getProjectFileName() {
|
||||||
|
return projectFileName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getFileType() {
|
||||||
|
return fileType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getModLoader() {
|
||||||
|
return modLoader;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,121 @@
|
|||||||
|
package org.jackhuang.hmcl.mod.curse;
|
||||||
|
|
||||||
|
import com.google.gson.reflect.TypeToken;
|
||||||
|
import org.jackhuang.hmcl.util.gson.JsonUtils;
|
||||||
|
import org.jackhuang.hmcl.util.io.NetworkUtils;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
import static org.jackhuang.hmcl.util.Lang.mapOf;
|
||||||
|
import static org.jackhuang.hmcl.util.Pair.pair;
|
||||||
|
|
||||||
|
public class CurseModManager {
|
||||||
|
|
||||||
|
public static List<CurseAddon> searchPaginated(String gameVersion, int category, int section, int pageOffset, String searchFilter, int sort) throws IOException {
|
||||||
|
String response = NetworkUtils.doGet(new URL(NetworkUtils.withQuery("https://addons-ecs.forgesvc.net/api/v2/addon/search", mapOf(
|
||||||
|
pair("categoryId", Integer.toString(category)),
|
||||||
|
pair("gameId", "432"),
|
||||||
|
pair("gameVersion", gameVersion),
|
||||||
|
pair("index", Integer.toString(pageOffset)),
|
||||||
|
pair("pageSize", "25"),
|
||||||
|
pair("searchFilter", searchFilter),
|
||||||
|
pair("sectionId", Integer.toString(section)),
|
||||||
|
pair("sort", Integer.toString(sort))
|
||||||
|
))));
|
||||||
|
return JsonUtils.fromNonNullJson(response, new TypeToken<List<CurseAddon>>() {
|
||||||
|
}.getType());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<Category> getCategories(int section) throws IOException {
|
||||||
|
String response = NetworkUtils.doGet(NetworkUtils.toURL("https://addons-ecs.forgesvc.net/api/v2/category/section/" + section));
|
||||||
|
List<Category> categories = JsonUtils.fromNonNullJson(response, new TypeToken<List<Category>>() {
|
||||||
|
}.getType());
|
||||||
|
return reorganizeCategories(categories, section);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<Category> reorganizeCategories(List<Category> categories, int rootId) {
|
||||||
|
List<Category> result = new ArrayList<>();
|
||||||
|
|
||||||
|
Map<Integer, Category> categoryMap = new HashMap<>();
|
||||||
|
for (Category category : categories) {
|
||||||
|
categoryMap.put(category.id, category);
|
||||||
|
}
|
||||||
|
for (Category category : categories) {
|
||||||
|
if (category.parentGameCategoryId == rootId) {
|
||||||
|
result.add(category);
|
||||||
|
} else {
|
||||||
|
Category parentCategory = categoryMap.get(category.parentGameCategoryId);
|
||||||
|
if (parentCategory == null) {
|
||||||
|
// Category list is not correct, so we ignore this item.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
parentCategory.subcategories.add(category);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final int SECTION_MODPACK = 4471;
|
||||||
|
public static final int SECTION_MOD = 6;
|
||||||
|
|
||||||
|
public static class Category {
|
||||||
|
private final int id;
|
||||||
|
private final String name;
|
||||||
|
private final String slug;
|
||||||
|
private final String avatarUrl;
|
||||||
|
private final int parentGameCategoryId;
|
||||||
|
private final int rootGameCategoryId;
|
||||||
|
private final int gameId;
|
||||||
|
|
||||||
|
private final List<Category> subcategories;
|
||||||
|
|
||||||
|
public Category() {
|
||||||
|
this(0, "", "", "", 0, 0, 0, new ArrayList<>());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Category(int id, String name, String slug, String avatarUrl, int parentGameCategoryId, int rootGameCategoryId, int gameId, List<Category> subcategories) {
|
||||||
|
this.id = id;
|
||||||
|
this.name = name;
|
||||||
|
this.slug = slug;
|
||||||
|
this.avatarUrl = avatarUrl;
|
||||||
|
this.parentGameCategoryId = parentGameCategoryId;
|
||||||
|
this.rootGameCategoryId = rootGameCategoryId;
|
||||||
|
this.gameId = gameId;
|
||||||
|
this.subcategories = subcategories;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSlug() {
|
||||||
|
return slug;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAvatarUrl() {
|
||||||
|
return avatarUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getParentGameCategoryId() {
|
||||||
|
return parentGameCategoryId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getRootGameCategoryId() {
|
||||||
|
return rootGameCategoryId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getGameId() {
|
||||||
|
return gameId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Category> getSubcategories() {
|
||||||
|
return subcategories;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user