feat: display mod description and website url. Closes #945.

This commit is contained in:
huanghongxun 2021-08-07 16:18:23 +08:00
parent 1ddcc23b89
commit b2594122a8
18 changed files with 330 additions and 66 deletions

View File

@ -258,4 +258,8 @@ public final class SVG {
public static Node releaseCircleOutline(ObjectBinding<? extends Paint> fill, double width, double height) { public static Node releaseCircleOutline(ObjectBinding<? extends Paint> 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); 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<? extends Paint> 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);
}
} }

View File

@ -17,11 +17,13 @@
*/ */
package org.jackhuang.hmcl.ui.construct; package org.jackhuang.hmcl.ui.construct;
import com.jfoenix.controls.JFXListView;
import com.jfoenix.effects.JFXDepthManager; import com.jfoenix.effects.JFXDepthManager;
import javafx.css.PseudoClass; import javafx.css.PseudoClass;
import javafx.geometry.Insets; import javafx.geometry.Insets;
import javafx.scene.Cursor; import javafx.scene.Cursor;
import javafx.scene.control.ListCell; import javafx.scene.control.ListCell;
import javafx.scene.layout.Region;
import javafx.scene.layout.StackPane; import javafx.scene.layout.StackPane;
import org.jackhuang.hmcl.ui.FXUtils; import org.jackhuang.hmcl.ui.FXUtils;
@ -30,7 +32,7 @@ public abstract class FloatListCell<T> extends ListCell<T> {
protected final StackPane pane = new StackPane(); protected final StackPane pane = new StackPane();
{ public FloatListCell(JFXListView<T> listView) {
setText(null); setText(null);
setGraphic(null); setGraphic(null);
@ -42,6 +44,14 @@ public abstract class FloatListCell<T> extends ListCell<T> {
FXUtils.onChangeAndOperate(selectedProperty(), selected -> { FXUtils.onChangeAndOperate(selectedProperty(), selected -> {
pane.pseudoClassStateChanged(SELECTED, 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 @Override

View File

@ -119,7 +119,7 @@ public final class VersionsPage extends BorderPane implements WizardPage, Refres
chkSnapshot.selectedProperty().addListener(listener); chkSnapshot.selectedProperty().addListener(listener);
chkOld.selectedProperty().addListener(listener); chkOld.selectedProperty().addListener(listener);
list.setCellFactory(listView -> new FloatListCell<RemoteVersion>() { list.setCellFactory(listView -> new FloatListCell<RemoteVersion>(list) {
ImageView imageView = new ImageView(); ImageView imageView = new ImageView();
TwoLineListItem content = new TwoLineListItem(); TwoLineListItem content = new TwoLineListItem();

View File

@ -75,7 +75,7 @@ class DatapackListPageSkin extends SkinBase<DatapackListPage> {
center.getStyleClass().add("large-spinner-pane"); center.getStyleClass().add("large-spinner-pane");
center.loadingProperty().bind(skinnable.loadingProperty()); center.loadingProperty().bind(skinnable.loadingProperty());
listView.setCellFactory(x -> new FloatListCell<DatapackInfoObject>() { listView.setCellFactory(x -> new FloatListCell<DatapackInfoObject>(listView) {
JFXCheckBox checkBox = new JFXCheckBox(); JFXCheckBox checkBox = new JFXCheckBox();
TwoLineListItem content = new TwoLineListItem(); TwoLineListItem content = new TwoLineListItem();
BooleanProperty booleanProperty; BooleanProperty booleanProperty;

View File

@ -250,7 +250,7 @@ public class ModDownloadListPage extends Control implements DecoratorPage, Versi
CurseAddon selectedItem = listView.getSelectionModel().getSelectedItem(); CurseAddon selectedItem = listView.getSelectionModel().getSelectedItem();
Controllers.navigate(new ModDownloadPage(selectedItem, getSkinnable().version.get(), getSkinnable().callback)); Controllers.navigate(new ModDownloadPage(selectedItem, getSkinnable().version.get(), getSkinnable().callback));
}); });
listView.setCellFactory(x -> new FloatListCell<CurseAddon>() { listView.setCellFactory(x -> new FloatListCell<CurseAddon>(listView) {
TwoLineListItem content = new TwoLineListItem(); TwoLineListItem content = new TwoLineListItem();
ImageView imageView = new ImageView(); ImageView imageView = new ImageView();

View File

@ -191,22 +191,14 @@ public class ModDownloadPage extends Control implements DecoratorPage {
JFXListView<CurseAddon.LatestFile> listView = new JFXListView<>(); JFXListView<CurseAddon.LatestFile> listView = new JFXListView<>();
spinnerPane.setContent(listView); spinnerPane.setContent(listView);
Bindings.bindContent(listView.getItems(), getSkinnable().items); Bindings.bindContent(listView.getItems(), getSkinnable().items);
listView.setCellFactory(x -> new FloatListCell<CurseAddon.LatestFile>() { listView.setCellFactory(x -> new FloatListCell<CurseAddon.LatestFile>(listView) {
TwoLineListItem content = new TwoLineListItem(); TwoLineListItem content = new TwoLineListItem();
StackPane graphicPane = new StackPane(); StackPane graphicPane = new StackPane();
{ {
Region clippedContainer = (Region)listView.lookup(".clipped-container");
setPrefWidth(0);
HBox container = new HBox(8); HBox container = new HBox(8);
container.setAlignment(Pos.CENTER_LEFT); container.setAlignment(Pos.CENTER_LEFT);
pane.getChildren().add(container); pane.getChildren().add(container);
if (clippedContainer != null) {
maxWidthProperty().bind(clippedContainer.widthProperty());
prefWidthProperty().bind(clippedContainer.widthProperty());
minWidthProperty().bind(clippedContainer.widthProperty());
}
container.getChildren().setAll(graphicPane, content); container.getChildren().setAll(graphicPane, content);
} }

View File

@ -17,7 +17,9 @@
*/ */
package org.jackhuang.hmcl.ui.versions; package org.jackhuang.hmcl.ui.versions;
import com.jfoenix.controls.JFXButton;
import com.jfoenix.controls.JFXCheckBox; import com.jfoenix.controls.JFXCheckBox;
import com.jfoenix.controls.JFXDialogLayout;
import com.jfoenix.controls.JFXListView; import com.jfoenix.controls.JFXListView;
import com.jfoenix.controls.datamodels.treetable.RecursiveTreeObject; import com.jfoenix.controls.datamodels.treetable.RecursiveTreeObject;
import com.jfoenix.effects.JFXDepthManager; import com.jfoenix.effects.JFXDepthManager;
@ -27,20 +29,38 @@ import javafx.geometry.Pos;
import javafx.scene.control.Label; import javafx.scene.control.Label;
import javafx.scene.control.SelectionMode; import javafx.scene.control.SelectionMode;
import javafx.scene.control.SkinBase; import javafx.scene.control.SkinBase;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.BorderPane; import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox; import javafx.scene.layout.HBox;
import javafx.scene.layout.Region; import javafx.scene.layout.Priority;
import javafx.scene.layout.StackPane; import javafx.scene.layout.StackPane;
import org.jackhuang.hmcl.mod.ModInfo; 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.Controllers;
import org.jackhuang.hmcl.ui.FXUtils; import org.jackhuang.hmcl.ui.FXUtils;
import org.jackhuang.hmcl.ui.SVG; 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.FloatListCell;
import org.jackhuang.hmcl.ui.construct.SpinnerPane; import org.jackhuang.hmcl.ui.construct.SpinnerPane;
import org.jackhuang.hmcl.ui.construct.TwoLineListItem; 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 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.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.StringUtils.isNotBlank;
import static org.jackhuang.hmcl.util.i18n.I18n.i18n; import static org.jackhuang.hmcl.util.i18n.I18n.i18n;
@ -80,37 +100,7 @@ class ModListPageSkin extends SkinBase<ModListPage> {
center.getStyleClass().add("large-spinner-pane"); center.getStyleClass().add("large-spinner-pane");
center.loadingProperty().bind(skinnable.loadingProperty()); center.loadingProperty().bind(skinnable.loadingProperty());
listView.setCellFactory(x -> new FloatListCell<ModInfoObject>() { listView.setCellFactory(x -> new ModInfoListCell(listView));
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.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE); listView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
Bindings.bindContent(listView.getItems(), skinnable.getItems()); Bindings.bindContent(listView.getItems(), skinnable.getItems());
@ -164,4 +154,111 @@ class ModListPageSkin extends SkinBase<ModListPage> {
return modInfo.getFileName().toLowerCase().compareTo(o.modInfo.getFileName().toLowerCase()); 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<ModInfoObject> {
JFXCheckBox checkBox = new JFXCheckBox();
TwoLineListItem content = new TwoLineListItem();
JFXButton infoButton = new JFXButton();
BooleanProperty booleanProperty;
ModInfoListCell(JFXListView<ModInfoObject> 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));
});
}
}
} }

View File

@ -188,12 +188,16 @@
.two-line-list-item > HBox > .subtitle { .two-line-list-item > HBox > .subtitle {
-fx-text-fill: rgba(0, 0, 0, 0.5); -fx-text-fill: rgba(0, 0, 0, 0.5);
-fx-font-weight: normal;
-fx-font-size: 12px;
} }
.two-line-list-item > HBox > HBox > .tag { .two-line-list-item > HBox > HBox > .tag {
-fx-text-fill: -fx-base-color; -fx-text-fill: -fx-base-color;
-fx-background-color: -fx-base-rippler-color; -fx-background-color: -fx-base-rippler-color;
-fx-padding: 2; -fx-padding: 2;
-fx-font-weight: normal;
-fx-font-size: 12px;
} }
.bubble { .bubble {

View File

@ -377,11 +377,15 @@ 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.disable=Disable 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.name=Name
mods.not_modded=You should install a modloader first (Fabric, Forge or LiteLoader) mods.not_modded=You should install a modloader first (Fabric, Forge or LiteLoader)
mods.url=Official Page
datapack=Datapacks datapack=Datapacks
datapack.add=Install datapack datapack.add=Install datapack

View File

@ -376,14 +376,17 @@ mods=模组管理
mods.add=添加模组 mods.add=添加模组
mods.add.failed=添加模组 %s 失败。 mods.add.failed=添加模组 %s 失败。
mods.add.success=成功添加模组 %s。 mods.add.success=成功添加模组 %s。
mods.category=类别
mods.choose_mod=选择模组 mods.choose_mod=选择模组
mods.disable=禁用
mods.download=模组下载 mods.download=模组下载
mods.download.title=模组下载 - %1s mods.download.title=模组下载 - %1s
mods.enable=启用 mods.enable=启用
mods.disable=禁用 mods.mcmod.page=MC 百科页面
mods.mcmod.search=MC 百科搜索
mods.name=名称 mods.name=名称
mods.category=类别
mods.not_modded=你需要先在自动安装页面安装 Fabric、Forge 或 LiteLoader 才能进行模组管理。 mods.not_modded=你需要先在自动安装页面安装 Fabric、Forge 或 LiteLoader 才能进行模组管理。
mods.url=官方页面
datapack=数据包 datapack=数据包
datapack.add=添加数据包 datapack.add=添加数据包

View File

@ -3,7 +3,8 @@ plugins {
} }
dependencies { 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.tukaani', name: 'xz', version: '1.8'
api(group: 'org.hildan.fxgson', name: 'fx-gson', version: '3.1.0') { api(group: 'org.hildan.fxgson', name: 'fx-gson', version: '3.1.0') {
exclude group: 'org.jetbrains', module: 'annotations' exclude group: 'org.jetbrains', module: 'annotations'

View File

@ -45,16 +45,18 @@ public final class FabricModMetadata {
private final String name; private final String name;
private final String version; private final String version;
private final String description; private final String description;
private final String icon;
private final List<FabricModAuthor> authors; private final List<FabricModAuthor> authors;
private final Map<String, String> contact; private final Map<String, String> contact;
public FabricModMetadata() { public FabricModMetadata() {
this("", "", "", Collections.emptyList(), Collections.emptyMap()); this("", "", "", "", Collections.emptyList(), Collections.emptyMap());
} }
public FabricModMetadata(String name, String version, String description, List<FabricModAuthor> authors, Map<String, String> contact) { public FabricModMetadata(String name, String version, String icon, String description, List<FabricModAuthor> authors, Map<String, String> contact) {
this.name = name; this.name = name;
this.version = version; this.version = version;
this.icon = icon;
this.description = description; this.description = description;
this.authors = authors; this.authors = authors;
this.contact = contact; this.contact = contact;
@ -68,7 +70,7 @@ public final class FabricModMetadata {
FabricModMetadata metadata = JsonUtils.fromNonNullJson(FileUtils.readText(mcmod), FabricModMetadata.class); FabricModMetadata metadata = JsonUtils.fromNonNullJson(FileUtils.readText(mcmod), FabricModMetadata.class);
String authors = metadata.authors == null ? "" : metadata.authors.stream().map(author -> author.name).collect(Collectors.joining(", ")); 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), 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);
} }
} }

View File

@ -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<Mod> mods;
public ForgeNewModMetadata() {
this("", "", "", "", Collections.emptyList());
}
public ForgeNewModMetadata(String modLoader, String loaderVersion, String logoFile, String license, List<Mod> 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<Mod> 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());
}
}
}

View File

@ -37,7 +37,7 @@ import java.util.List;
* @author huangyuhui * @author huangyuhui
*/ */
@Immutable @Immutable
public final class ForgeModMetadata { public final class ForgeOldModMetadata {
@SerializedName("modid") @SerializedName("modid")
private final String modId; private final String modId;
@ -45,6 +45,7 @@ public final class ForgeModMetadata {
private final String description; private final String description;
private final String author; private final String author;
private final String version; private final String version;
private final String logoFile;
private final String mcversion; private final String mcversion;
private final String url; private final String url;
private final String updateUrl; private final String updateUrl;
@ -52,16 +53,17 @@ public final class ForgeModMetadata {
private final String[] authorList; private final String[] authorList;
private final String[] authors; private final String[] authors;
public ForgeModMetadata() { public ForgeOldModMetadata() {
this("", "", "", "", "", "", "", "", "", new String[0], new String[0]); 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.modId = modId;
this.name = name; this.name = name;
this.description = description; this.description = description;
this.author = author; this.author = author;
this.version = version; this.version = version;
this.logoFile = logoFile;
this.mcversion = mcversion; this.mcversion = mcversion;
this.url = url; this.url = url;
this.updateUrl = updateUrl; this.updateUrl = updateUrl;
@ -90,6 +92,10 @@ public final class ForgeModMetadata {
return version; return version;
} }
public String getLogoFile() {
return logoFile;
}
public String getGameVersion() { public String getGameVersion() {
return mcversion; return mcversion;
} }
@ -119,12 +125,12 @@ public final class ForgeModMetadata {
Path mcmod = fs.getPath("mcmod.info"); Path mcmod = fs.getPath("mcmod.info");
if (Files.notExists(mcmod)) if (Files.notExists(mcmod))
throw new IOException("File " + modFile + " is not a Forge mod."); throw new IOException("File " + modFile + " is not a Forge mod.");
List<ForgeModMetadata> modList = JsonUtils.GSON.fromJson(FileUtils.readText(mcmod), List<ForgeOldModMetadata> modList = JsonUtils.GSON.fromJson(FileUtils.readText(mcmod),
new TypeToken<List<ForgeModMetadata>>() { new TypeToken<List<ForgeOldModMetadata>>() {
}.getType()); }.getType());
if (modList == null || modList.isEmpty()) if (modList == null || modList.isEmpty())
throw new IOException("Mod " + modFile + " `mcmod.info` is malformed.."); throw new IOException("Mod " + modFile + " `mcmod.info` is malformed..");
ForgeModMetadata metadata = modList.get(0); ForgeOldModMetadata metadata = modList.get(0);
String authors = metadata.getAuthor(); String authors = metadata.getAuthor();
if (StringUtils.isBlank(authors) && metadata.getAuthors().length > 0) if (StringUtils.isBlank(authors) && metadata.getAuthors().length > 0)
authors = String.join(", ", metadata.getAuthors()); authors = String.join(", ", metadata.getAuthors());
@ -134,7 +140,8 @@ public final class ForgeModMetadata {
authors = metadata.getCredits(); authors = metadata.getCredits();
return new ModInfo(modManager, modFile, metadata.getName(), new ModInfo.Description(metadata.getDescription()), return new ModInfo(modManager, modFile, metadata.getName(), new ModInfo.Description(metadata.getDescription()),
authors, metadata.getVersion(), metadata.getGameVersion(), authors, metadata.getVersion(), metadata.getGameVersion(),
StringUtils.isBlank(metadata.getUrl()) ? metadata.getUpdateUrl() : metadata.url); StringUtils.isBlank(metadata.getUrl()) ? metadata.getUpdateUrl() : metadata.url,
metadata.getLogoFile());
} }
} }
} }

View File

@ -117,7 +117,7 @@ public final class LiteModMetadata {
if (metadata == null) if (metadata == null)
throw new IOException("Mod " + modFile + " `litemod.json` is malformed."); throw new IOException("Mod " + modFile + " `litemod.json` is malformed.");
return new ModInfo(modManager, modFile, metadata.getName(), new ModInfo.Description(metadata.getDescription()), metadata.getAuthor(), 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(), "");
} }
} }

View File

@ -45,13 +45,14 @@ public final class ModInfo implements Comparable<ModInfo> {
private final String gameVersion; private final String gameVersion;
private final String url; private final String url;
private final String fileName; private final String fileName;
private final String logoPath;
private final BooleanProperty activeProperty; private final BooleanProperty activeProperty;
public ModInfo(ModManager modManager, File file, String name, Description description) { 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.file = file.toPath();
this.name = name; this.name = name;
this.description = description; this.description = description;
@ -59,6 +60,7 @@ public final class ModInfo implements Comparable<ModInfo> {
this.version = version; this.version = version;
this.gameVersion = gameVersion; this.gameVersion = gameVersion;
this.url = url; this.url = url;
this.logoPath = logoPath;
activeProperty = new SimpleBooleanProperty(this, "active", !modManager.isDisabled(file)) { activeProperty = new SimpleBooleanProperty(this, "active", !modManager.isDisabled(file)) {
@Override @Override
@ -107,6 +109,10 @@ public final class ModInfo implements Comparable<ModInfo> {
return url; return url;
} }
public String getLogoPath() {
return logoPath;
}
public BooleanProperty activeProperty() { public BooleanProperty activeProperty() {
return activeProperty; return activeProperty;
} }

View File

@ -68,7 +68,12 @@ public final class ModManager {
case "zip": case "zip":
case "jar": case "jar":
try { try {
return ForgeModMetadata.fromFile(this, modFile); return ForgeOldModMetadata.fromFile(this, modFile);
} catch (Exception ignore) {
}
try {
return ForgeNewModMetadata.fromFile(this, modFile);
} catch (Exception ignore) { } catch (Exception ignore) {
} }

View File

@ -150,7 +150,7 @@ public class PackMcMeta implements Validation {
if (Files.notExists(mcmod)) if (Files.notExists(mcmod))
throw new IOException("File " + modFile + " is not a resource pack."); throw new IOException("File " + modFile + " is not a resource pack.");
PackMcMeta metadata = JsonUtils.fromNonNullJson(FileUtils.readText(mcmod), PackMcMeta.class); 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, "", "", "", "", "");
} }
} }
} }