diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/SVG.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/SVG.java index 2c2862b3a..c0045427c 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/SVG.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/SVG.java @@ -258,4 +258,8 @@ public final class SVG { public static Node releaseCircleOutline(ObjectBinding fill, double width, double height) { return createSVGPath("M9,7H13A2,2 0 0,1 15,9V11C15,11.84 14.5,12.55 13.76,12.85L15,17H13L11.8,13H11V17H9V7M11,9V11H13V9H11M12,2A10,10 0 0,1 22,12A10,10 0 0,1 12,22A10,10 0 0,1 2,12A10,10 0 0,1 12,2M12,4A8,8 0 0,0 4,12C4,16.41 7.58,20 12,20A8,8 0 0,0 20,12A8,8 0 0,0 12,4Z", fill, width, height); } + + public static Node informationOutline(ObjectBinding fill, double width, double height) { + return createSVGPath("M11,9H13V7H11M12,20C7.59,20 4,16.41 4,12C4,7.59 7.59,4 12,4C16.41,4 20,7.59 20,12C20,16.41 16.41,20 12,20M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2M11,17H13V11H11V17Z", fill, width, height); + } } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/FloatListCell.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/FloatListCell.java index 24e09372b..dc4faf062 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/FloatListCell.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/construct/FloatListCell.java @@ -17,11 +17,13 @@ */ package org.jackhuang.hmcl.ui.construct; +import com.jfoenix.controls.JFXListView; import com.jfoenix.effects.JFXDepthManager; import javafx.css.PseudoClass; import javafx.geometry.Insets; import javafx.scene.Cursor; import javafx.scene.control.ListCell; +import javafx.scene.layout.Region; import javafx.scene.layout.StackPane; import org.jackhuang.hmcl.ui.FXUtils; @@ -30,7 +32,7 @@ public abstract class FloatListCell extends ListCell { protected final StackPane pane = new StackPane(); - { + public FloatListCell(JFXListView listView) { setText(null); setGraphic(null); @@ -42,6 +44,14 @@ public abstract class FloatListCell extends ListCell { FXUtils.onChangeAndOperate(selectedProperty(), selected -> { pane.pseudoClassStateChanged(SELECTED, selected); }); + + Region clippedContainer = (Region) listView.lookup(".clipped-container"); + setPrefWidth(0); + if (clippedContainer != null) { + maxWidthProperty().bind(clippedContainer.widthProperty()); + prefWidthProperty().bind(clippedContainer.widthProperty()); + minWidthProperty().bind(clippedContainer.widthProperty()); + } } @Override diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/VersionsPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/VersionsPage.java index 0a4defae7..bda956a56 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/VersionsPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/download/VersionsPage.java @@ -119,7 +119,7 @@ public final class VersionsPage extends BorderPane implements WizardPage, Refres chkSnapshot.selectedProperty().addListener(listener); chkOld.selectedProperty().addListener(listener); - list.setCellFactory(listView -> new FloatListCell() { + list.setCellFactory(listView -> new FloatListCell(list) { ImageView imageView = new ImageView(); TwoLineListItem content = new TwoLineListItem(); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/DatapackListPageSkin.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/DatapackListPageSkin.java index 9b1a0cd77..126443cfe 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/DatapackListPageSkin.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/DatapackListPageSkin.java @@ -75,7 +75,7 @@ class DatapackListPageSkin extends SkinBase { center.getStyleClass().add("large-spinner-pane"); center.loadingProperty().bind(skinnable.loadingProperty()); - listView.setCellFactory(x -> new FloatListCell() { + listView.setCellFactory(x -> new FloatListCell(listView) { JFXCheckBox checkBox = new JFXCheckBox(); TwoLineListItem content = new TwoLineListItem(); BooleanProperty booleanProperty; diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/ModDownloadListPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/ModDownloadListPage.java index b57663f04..865bd1239 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/ModDownloadListPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/ModDownloadListPage.java @@ -250,7 +250,7 @@ public class ModDownloadListPage extends Control implements DecoratorPage, Versi CurseAddon selectedItem = listView.getSelectionModel().getSelectedItem(); Controllers.navigate(new ModDownloadPage(selectedItem, getSkinnable().version.get(), getSkinnable().callback)); }); - listView.setCellFactory(x -> new FloatListCell() { + listView.setCellFactory(x -> new FloatListCell(listView) { TwoLineListItem content = new TwoLineListItem(); ImageView imageView = new ImageView(); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/ModDownloadPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/ModDownloadPage.java index cb879a019..ce28cd5b0 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/ModDownloadPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/ModDownloadPage.java @@ -191,22 +191,14 @@ public class ModDownloadPage extends Control implements DecoratorPage { JFXListView listView = new JFXListView<>(); spinnerPane.setContent(listView); Bindings.bindContent(listView.getItems(), getSkinnable().items); - listView.setCellFactory(x -> new FloatListCell() { + listView.setCellFactory(x -> new FloatListCell(listView) { TwoLineListItem content = new TwoLineListItem(); StackPane graphicPane = new StackPane(); { - Region clippedContainer = (Region)listView.lookup(".clipped-container"); - setPrefWidth(0); HBox container = new HBox(8); container.setAlignment(Pos.CENTER_LEFT); pane.getChildren().add(container); - if (clippedContainer != null) { - maxWidthProperty().bind(clippedContainer.widthProperty()); - prefWidthProperty().bind(clippedContainer.widthProperty()); - minWidthProperty().bind(clippedContainer.widthProperty()); - } - container.getChildren().setAll(graphicPane, content); } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/ModListPageSkin.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/ModListPageSkin.java index 438357c86..7d8b3562f 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/ModListPageSkin.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/versions/ModListPageSkin.java @@ -17,7 +17,9 @@ */ package org.jackhuang.hmcl.ui.versions; +import com.jfoenix.controls.JFXButton; import com.jfoenix.controls.JFXCheckBox; +import com.jfoenix.controls.JFXDialogLayout; import com.jfoenix.controls.JFXListView; import com.jfoenix.controls.datamodels.treetable.RecursiveTreeObject; import com.jfoenix.effects.JFXDepthManager; @@ -27,20 +29,38 @@ import javafx.geometry.Pos; import javafx.scene.control.Label; import javafx.scene.control.SelectionMode; import javafx.scene.control.SkinBase; +import javafx.scene.image.Image; +import javafx.scene.image.ImageView; import javafx.scene.layout.BorderPane; import javafx.scene.layout.HBox; -import javafx.scene.layout.Region; +import javafx.scene.layout.Priority; import javafx.scene.layout.StackPane; import org.jackhuang.hmcl.mod.ModInfo; +import org.jackhuang.hmcl.setting.Theme; +import org.jackhuang.hmcl.task.Schedulers; +import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.ui.Controllers; import org.jackhuang.hmcl.ui.FXUtils; import org.jackhuang.hmcl.ui.SVG; +import org.jackhuang.hmcl.ui.construct.DialogCloseEvent; import org.jackhuang.hmcl.ui.construct.FloatListCell; import org.jackhuang.hmcl.ui.construct.SpinnerPane; import org.jackhuang.hmcl.ui.construct.TwoLineListItem; +import org.jackhuang.hmcl.util.StringUtils; +import org.jackhuang.hmcl.util.io.CompressingUtils; +import org.jackhuang.hmcl.util.io.FileUtils; +import org.jackhuang.hmcl.util.io.NetworkUtils; import org.jetbrains.annotations.NotNull; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.nio.file.FileSystem; +import java.nio.file.Files; +import java.nio.file.Path; + import static org.jackhuang.hmcl.ui.ToolbarListPageSkin.createToolbarButton; +import static org.jackhuang.hmcl.util.Lang.mapOf; +import static org.jackhuang.hmcl.util.Pair.pair; import static org.jackhuang.hmcl.util.StringUtils.isNotBlank; import static org.jackhuang.hmcl.util.i18n.I18n.i18n; @@ -80,37 +100,7 @@ class ModListPageSkin extends SkinBase { center.getStyleClass().add("large-spinner-pane"); center.loadingProperty().bind(skinnable.loadingProperty()); - listView.setCellFactory(x -> new FloatListCell() { - JFXCheckBox checkBox = new JFXCheckBox(); - TwoLineListItem content = new TwoLineListItem(); - BooleanProperty booleanProperty; - - { - Region clippedContainer = (Region)listView.lookup(".clipped-container"); - setPrefWidth(0); - HBox container = new HBox(8); - container.setAlignment(Pos.CENTER_LEFT); - pane.getChildren().add(container); - if (clippedContainer != null) { - maxWidthProperty().bind(clippedContainer.widthProperty()); - prefWidthProperty().bind(clippedContainer.widthProperty()); - minWidthProperty().bind(clippedContainer.widthProperty()); - } - - container.getChildren().setAll(checkBox, content); - } - - @Override - protected void updateControl(ModInfoObject dataItem, boolean empty) { - if (empty) return; - content.setTitle(dataItem.getTitle()); - content.setSubtitle(dataItem.getSubtitle()); - if (booleanProperty != null) { - checkBox.selectedProperty().unbindBidirectional(booleanProperty); - } - checkBox.selectedProperty().bindBidirectional(booleanProperty = dataItem.active); - } - }); + listView.setCellFactory(x -> new ModInfoListCell(listView)); listView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE); Bindings.bindContent(listView.getItems(), skinnable.getItems()); @@ -164,4 +154,111 @@ class ModListPageSkin extends SkinBase { return modInfo.getFileName().toLowerCase().compareTo(o.modInfo.getFileName().toLowerCase()); } } + + static class ModInfoDialog extends JFXDialogLayout { + + ModInfoDialog(ModInfoObject modInfo) { + HBox titleContainer = new HBox(); + titleContainer.setSpacing(8); + + ImageView imageView = new ImageView(); + if (StringUtils.isNotBlank(modInfo.getModInfo().getLogoPath())) { + Task.supplyAsync(() -> { + try (FileSystem fs = CompressingUtils.createReadOnlyZipFileSystem(modInfo.getModInfo().getFile())) { + Path iconPath = fs.getPath(modInfo.getModInfo().getLogoPath()); + if (Files.exists(iconPath)) { + ByteArrayOutputStream stream = new ByteArrayOutputStream(); + Files.copy(iconPath, stream); + return new ByteArrayInputStream(stream.toByteArray()); + } + } + return null; + }).whenComplete(Schedulers.javafx(), (stream, exception) -> { + if (stream != null) { + imageView.setImage(new Image(stream, 40, 40, true, true)); + } else { + imageView.setImage(new Image("/assets/img/command.png", 40, 40, true, true)); + } + }).start(); + } + + TwoLineListItem title = new TwoLineListItem(); + title.setTitle(modInfo.getModInfo().getName()); + if (StringUtils.isNotBlank(modInfo.getModInfo().getVersion())) { + title.getTags().setAll(modInfo.getModInfo().getVersion()); + } + title.setSubtitle(FileUtils.getName(modInfo.getModInfo().getFile())); + + titleContainer.getChildren().setAll(FXUtils.limitingSize(imageView, 40, 40), title); + setHeading(titleContainer); + + Label description = new Label(modInfo.getModInfo().getDescription().toString()); + setBody(description); + + JFXButton okButton = new JFXButton(); + okButton.getStyleClass().add("dialog-accept"); + okButton.setText(i18n("button.ok")); + okButton.setOnAction(e -> fireEvent(new DialogCloseEvent())); + + JFXButton searchButton = new JFXButton(); + searchButton.getStyleClass().add("dialog-cancel"); + searchButton.setText(i18n("mods.mcmod.search")); + searchButton.setOnAction(e -> { + fireEvent(new DialogCloseEvent()); + FXUtils.openLink(NetworkUtils.withQuery("https://search.mcmod.cn/s", mapOf( + pair("key", modInfo.getModInfo().getName()), + pair("site", "all"), + pair("filter", "0") + ))); + }); + + if (StringUtils.isNotBlank(modInfo.getModInfo().getUrl())) { + JFXButton officialPageButton = new JFXButton(); + officialPageButton.getStyleClass().add("dialog-cancel"); + officialPageButton.setText(i18n("mods.url")); + officialPageButton.setOnAction(e -> { + fireEvent(new DialogCloseEvent()); + FXUtils.openLink(modInfo.getModInfo().getUrl()); + }); + + setActions(okButton, officialPageButton, searchButton); + } else { + setActions(okButton, searchButton); + } + } + } + + static class ModInfoListCell extends FloatListCell { + JFXCheckBox checkBox = new JFXCheckBox(); + TwoLineListItem content = new TwoLineListItem(); + JFXButton infoButton = new JFXButton(); + BooleanProperty booleanProperty; + + ModInfoListCell(JFXListView listView) { + super(listView); + HBox container = new HBox(8); + container.setAlignment(Pos.CENTER_LEFT); + pane.getChildren().add(container); + HBox.setHgrow(content, Priority.ALWAYS); + + infoButton.getStyleClass().add("toggle-icon4"); + infoButton.setGraphic(FXUtils.limitingSize(SVG.informationOutline(Theme.blackFillBinding(), 24, 24), 24, 24)); + + container.getChildren().setAll(checkBox, content, infoButton); + } + + @Override + protected void updateControl(ModInfoObject dataItem, boolean empty) { + if (empty) return; + content.setTitle(dataItem.getTitle()); + content.setSubtitle(dataItem.getSubtitle()); + if (booleanProperty != null) { + checkBox.selectedProperty().unbindBidirectional(booleanProperty); + } + checkBox.selectedProperty().bindBidirectional(booleanProperty = dataItem.active); + infoButton.setOnMouseClicked(e -> { + Controllers.dialog(new ModInfoDialog(dataItem)); + }); + } + } } diff --git a/HMCL/src/main/resources/assets/css/root.css b/HMCL/src/main/resources/assets/css/root.css index 455f07843..538ec5c23 100644 --- a/HMCL/src/main/resources/assets/css/root.css +++ b/HMCL/src/main/resources/assets/css/root.css @@ -188,12 +188,16 @@ .two-line-list-item > HBox > .subtitle { -fx-text-fill: rgba(0, 0, 0, 0.5); + -fx-font-weight: normal; + -fx-font-size: 12px; } .two-line-list-item > HBox > HBox > .tag { -fx-text-fill: -fx-base-color; -fx-background-color: -fx-base-rippler-color; -fx-padding: 2; + -fx-font-weight: normal; + -fx-font-size: 12px; } .bubble { diff --git a/HMCL/src/main/resources/assets/lang/I18N.properties b/HMCL/src/main/resources/assets/lang/I18N.properties index 550d45079..8fbe0384e 100644 --- a/HMCL/src/main/resources/assets/lang/I18N.properties +++ b/HMCL/src/main/resources/assets/lang/I18N.properties @@ -377,11 +377,15 @@ mods.add=Install mods mods.add.failed=Failed to install mods %s. mods.add.success=Successfully installed mods %s. mods.choose_mod=Choose your mods -mods.download=Mod Downloads -mods.enable=Enable mods.disable=Disable +mods.download=Mod Downloads +mods.download.title=Mod Downloads - %1s +mods.enable=Enable +mods.mcmod.page=MCWiki +mods.mcmod.search=Search in MCWiki mods.name=Name mods.not_modded=You should install a modloader first (Fabric, Forge or LiteLoader) +mods.url=Official Page datapack=Datapacks datapack.add=Install datapack 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 92dcf25a9..c5d3354f8 100644 --- a/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties +++ b/HMCL/src/main/resources/assets/lang/I18N_zh_CN.properties @@ -376,14 +376,17 @@ mods=模组管理 mods.add=添加模组 mods.add.failed=添加模组 %s 失败。 mods.add.success=成功添加模组 %s。 +mods.category=类别 mods.choose_mod=选择模组 +mods.disable=禁用 mods.download=模组下载 mods.download.title=模组下载 - %1s mods.enable=启用 -mods.disable=禁用 +mods.mcmod.page=MC 百科页面 +mods.mcmod.search=MC 百科搜索 mods.name=名称 -mods.category=类别 mods.not_modded=你需要先在自动安装页面安装 Fabric、Forge 或 LiteLoader 才能进行模组管理。 +mods.url=官方页面 datapack=数据包 datapack.add=添加数据包 diff --git a/HMCLCore/build.gradle b/HMCLCore/build.gradle index d8b3b6184..57259915d 100644 --- a/HMCLCore/build.gradle +++ b/HMCLCore/build.gradle @@ -3,7 +3,8 @@ plugins { } dependencies { - api group: 'com.google.code.gson', name: 'gson', version: '2.8.5' + api group: 'com.google.code.gson', name: 'gson', version: '2.8.1' + api group: 'com.moandjiezana.toml', name: 'toml4j', version: '0.7.2' api group: 'org.tukaani', name: 'xz', version: '1.8' api(group: 'org.hildan.fxgson', name: 'fx-gson', version: '3.1.0') { exclude group: 'org.jetbrains', module: 'annotations' diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/FabricModMetadata.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/FabricModMetadata.java index f71551be4..7335ec93c 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/FabricModMetadata.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/FabricModMetadata.java @@ -45,16 +45,18 @@ public final class FabricModMetadata { private final String name; private final String version; private final String description; + private final String icon; private final List authors; private final Map contact; public FabricModMetadata() { - this("", "", "", Collections.emptyList(), Collections.emptyMap()); + this("", "", "", "", Collections.emptyList(), Collections.emptyMap()); } - public FabricModMetadata(String name, String version, String description, List authors, Map contact) { + public FabricModMetadata(String name, String version, String icon, String description, List authors, Map contact) { this.name = name; this.version = version; + this.icon = icon; this.description = description; this.authors = authors; this.contact = contact; @@ -68,7 +70,7 @@ public final class FabricModMetadata { FabricModMetadata metadata = JsonUtils.fromNonNullJson(FileUtils.readText(mcmod), FabricModMetadata.class); String authors = metadata.authors == null ? "" : metadata.authors.stream().map(author -> author.name).collect(Collectors.joining(", ")); return new ModInfo(modManager, modFile, metadata.name, new ModInfo.Description(metadata.description), - authors, metadata.version, "", metadata.contact != null ? metadata.contact.getOrDefault("homepage", "") : ""); + authors, metadata.version, "", metadata.contact != null ? metadata.contact.getOrDefault("homepage", "") : "", metadata.icon); } } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/ForgeNewModMetadata.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/ForgeNewModMetadata.java new file mode 100644 index 000000000..8783a9671 --- /dev/null +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/ForgeNewModMetadata.java @@ -0,0 +1,129 @@ +package org.jackhuang.hmcl.mod; + +import com.google.gson.JsonParseException; +import com.moandjiezana.toml.Toml; +import org.jackhuang.hmcl.util.Immutable; +import org.jackhuang.hmcl.util.io.CompressingUtils; +import org.jackhuang.hmcl.util.io.FileUtils; + +import java.io.File; +import java.io.IOException; +import java.nio.file.FileSystem; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Collections; +import java.util.List; + +@Immutable +public final class ForgeNewModMetadata { + + private final String modLoader; + + private final String loaderVersion; + + private final String logoFile; + + private final String license; + + private final List mods; + + public ForgeNewModMetadata() { + this("", "", "", "", Collections.emptyList()); + } + + public ForgeNewModMetadata(String modLoader, String loaderVersion, String logoFile, String license, List mods) { + this.modLoader = modLoader; + this.loaderVersion = loaderVersion; + this.logoFile = logoFile; + this.license = license; + this.mods = mods; + } + + public String getModLoader() { + return modLoader; + } + + public String getLoaderVersion() { + return loaderVersion; + } + + public String getLogoFile() { + return logoFile; + } + + public String getLicense() { + return license; + } + + public List getMods() { + return mods; + } + + public static class Mod { + private final String modId; + private final String version; + private final String displayName; + private final String side; + private final String displayURL; + private final String authors; + private final String description; + + public Mod() { + this("", "", "", "", "", "", ""); + } + + public Mod(String modId, String version, String displayName, String side, String displayURL, String authors, String description) { + this.modId = modId; + this.version = version; + this.displayName = displayName; + this.side = side; + this.displayURL = displayURL; + this.authors = authors; + this.description = description; + } + + public String getModId() { + return modId; + } + + public String getVersion() { + return version; + } + + public String getDisplayName() { + return displayName; + } + + public String getSide() { + return side; + } + + public String getDisplayURL() { + return displayURL; + } + + public String getAuthors() { + return authors; + } + + public String getDescription() { + return description; + } + } + + public static ModInfo fromFile(ModManager modManager, File modFile) throws IOException, JsonParseException { + try (FileSystem fs = CompressingUtils.createReadOnlyZipFileSystem(modFile.toPath())) { + Path modstoml = fs.getPath("META-INF/mods.toml"); + if (Files.notExists(modstoml)) + throw new IOException("File " + modFile + " is not a Forge 1.13+ mod."); + ForgeNewModMetadata metadata = new Toml().read(FileUtils.readText(modstoml)).to(ForgeNewModMetadata.class); + if (metadata == null || metadata.getMods().isEmpty()) + throw new IOException("Mod " + modFile + " `mods.toml` is malformed.."); + Mod mod = metadata.getMods().get(0); + return new ModInfo(modManager, modFile, mod.getDisplayName(), new ModInfo.Description(mod.getDescription()), + mod.getAuthors(), mod.getVersion(), "", + mod.getDisplayURL(), + metadata.getLogoFile()); + } + } +} diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/ForgeModMetadata.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/ForgeOldModMetadata.java similarity index 83% rename from HMCLCore/src/main/java/org/jackhuang/hmcl/mod/ForgeModMetadata.java rename to HMCLCore/src/main/java/org/jackhuang/hmcl/mod/ForgeOldModMetadata.java index cf211fd08..6aa60f7ff 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/ForgeModMetadata.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/ForgeOldModMetadata.java @@ -37,7 +37,7 @@ import java.util.List; * @author huangyuhui */ @Immutable -public final class ForgeModMetadata { +public final class ForgeOldModMetadata { @SerializedName("modid") private final String modId; @@ -45,6 +45,7 @@ public final class ForgeModMetadata { private final String description; private final String author; private final String version; + private final String logoFile; private final String mcversion; private final String url; private final String updateUrl; @@ -52,16 +53,17 @@ public final class ForgeModMetadata { private final String[] authorList; private final String[] authors; - public ForgeModMetadata() { - this("", "", "", "", "", "", "", "", "", new String[0], new String[0]); + public ForgeOldModMetadata() { + this("", "", "", "", "", "", "", "", "", "", new String[0], new String[0]); } - public ForgeModMetadata(String modId, String name, String description, String author, String version, String mcversion, String url, String updateUrl, String credits, String[] authorList, String[] authors) { + public ForgeOldModMetadata(String modId, String name, String description, String author, String version, String logoFile, String mcversion, String url, String updateUrl, String credits, String[] authorList, String[] authors) { this.modId = modId; this.name = name; this.description = description; this.author = author; this.version = version; + this.logoFile = logoFile; this.mcversion = mcversion; this.url = url; this.updateUrl = updateUrl; @@ -90,6 +92,10 @@ public final class ForgeModMetadata { return version; } + public String getLogoFile() { + return logoFile; + } + public String getGameVersion() { return mcversion; } @@ -119,12 +125,12 @@ public final class ForgeModMetadata { Path mcmod = fs.getPath("mcmod.info"); if (Files.notExists(mcmod)) throw new IOException("File " + modFile + " is not a Forge mod."); - List modList = JsonUtils.GSON.fromJson(FileUtils.readText(mcmod), - new TypeToken>() { + List modList = JsonUtils.GSON.fromJson(FileUtils.readText(mcmod), + new TypeToken>() { }.getType()); if (modList == null || modList.isEmpty()) throw new IOException("Mod " + modFile + " `mcmod.info` is malformed.."); - ForgeModMetadata metadata = modList.get(0); + ForgeOldModMetadata metadata = modList.get(0); String authors = metadata.getAuthor(); if (StringUtils.isBlank(authors) && metadata.getAuthors().length > 0) authors = String.join(", ", metadata.getAuthors()); @@ -134,7 +140,8 @@ public final class ForgeModMetadata { authors = metadata.getCredits(); return new ModInfo(modManager, modFile, metadata.getName(), new ModInfo.Description(metadata.getDescription()), authors, metadata.getVersion(), metadata.getGameVersion(), - StringUtils.isBlank(metadata.getUrl()) ? metadata.getUpdateUrl() : metadata.url); + StringUtils.isBlank(metadata.getUrl()) ? metadata.getUpdateUrl() : metadata.url, + metadata.getLogoFile()); } } } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/LiteModMetadata.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/LiteModMetadata.java index 2391f2ff0..9d737736f 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/LiteModMetadata.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/LiteModMetadata.java @@ -117,7 +117,7 @@ public final class LiteModMetadata { if (metadata == null) throw new IOException("Mod " + modFile + " `litemod.json` is malformed."); return new ModInfo(modManager, modFile, metadata.getName(), new ModInfo.Description(metadata.getDescription()), metadata.getAuthor(), - metadata.getVersion(), metadata.getGameVersion(), metadata.getUpdateURI()); + metadata.getVersion(), metadata.getGameVersion(), metadata.getUpdateURI(), ""); } } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/ModInfo.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/ModInfo.java index 9872ab1aa..7bd89874e 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/ModInfo.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/ModInfo.java @@ -45,13 +45,14 @@ public final class ModInfo implements Comparable { private final String gameVersion; private final String url; private final String fileName; + private final String logoPath; private final BooleanProperty activeProperty; public ModInfo(ModManager modManager, File file, String name, Description description) { - this(modManager, file, name, description, "", "", "", ""); + this(modManager, file, name, description, "", "", "", "", ""); } - public ModInfo(ModManager modManager, File file, String name, Description description, String authors, String version, String gameVersion, String url) { + public ModInfo(ModManager modManager, File file, String name, Description description, String authors, String version, String gameVersion, String url, String logoPath) { this.file = file.toPath(); this.name = name; this.description = description; @@ -59,6 +60,7 @@ public final class ModInfo implements Comparable { this.version = version; this.gameVersion = gameVersion; this.url = url; + this.logoPath = logoPath; activeProperty = new SimpleBooleanProperty(this, "active", !modManager.isDisabled(file)) { @Override @@ -107,6 +109,10 @@ public final class ModInfo implements Comparable { return url; } + public String getLogoPath() { + return logoPath; + } + public BooleanProperty activeProperty() { return activeProperty; } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/ModManager.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/ModManager.java index f3c2720f4..098619773 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/ModManager.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/ModManager.java @@ -68,7 +68,12 @@ public final class ModManager { case "zip": case "jar": try { - return ForgeModMetadata.fromFile(this, modFile); + return ForgeOldModMetadata.fromFile(this, modFile); + } catch (Exception ignore) { + } + + try { + return ForgeNewModMetadata.fromFile(this, modFile); } catch (Exception ignore) { } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/PackMcMeta.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/PackMcMeta.java index 0f443d566..cdf29617b 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/PackMcMeta.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/PackMcMeta.java @@ -150,7 +150,7 @@ public class PackMcMeta implements Validation { if (Files.notExists(mcmod)) throw new IOException("File " + modFile + " is not a resource pack."); PackMcMeta metadata = JsonUtils.fromNonNullJson(FileUtils.readText(mcmod), PackMcMeta.class); - return new ModInfo(modManager, modFile, FileUtils.getNameWithoutExtension(modFile), metadata.pack.description, "", "", "", ""); + return new ModInfo(modManager, modFile, FileUtils.getNameWithoutExtension(modFile), metadata.pack.description, "", "", "", "", ""); } } }