mirror of
https://github.com/HMCL-dev/HMCL.git
synced 2025-09-16 07:16:27 -04:00
feat: display mod description and website url. Closes #945.
This commit is contained in:
parent
1ddcc23b89
commit
b2594122a8
@ -258,4 +258,8 @@ public final class SVG {
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -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<T> extends ListCell<T> {
|
||||
|
||||
protected final StackPane pane = new StackPane();
|
||||
|
||||
{
|
||||
public FloatListCell(JFXListView<T> listView) {
|
||||
setText(null);
|
||||
setGraphic(null);
|
||||
|
||||
@ -42,6 +44,14 @@ public abstract class FloatListCell<T> extends ListCell<T> {
|
||||
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
|
||||
|
@ -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<RemoteVersion>() {
|
||||
list.setCellFactory(listView -> new FloatListCell<RemoteVersion>(list) {
|
||||
ImageView imageView = new ImageView();
|
||||
TwoLineListItem content = new TwoLineListItem();
|
||||
|
||||
|
@ -75,7 +75,7 @@ class DatapackListPageSkin extends SkinBase<DatapackListPage> {
|
||||
center.getStyleClass().add("large-spinner-pane");
|
||||
center.loadingProperty().bind(skinnable.loadingProperty());
|
||||
|
||||
listView.setCellFactory(x -> new FloatListCell<DatapackInfoObject>() {
|
||||
listView.setCellFactory(x -> new FloatListCell<DatapackInfoObject>(listView) {
|
||||
JFXCheckBox checkBox = new JFXCheckBox();
|
||||
TwoLineListItem content = new TwoLineListItem();
|
||||
BooleanProperty booleanProperty;
|
||||
|
@ -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<CurseAddon>() {
|
||||
listView.setCellFactory(x -> new FloatListCell<CurseAddon>(listView) {
|
||||
TwoLineListItem content = new TwoLineListItem();
|
||||
ImageView imageView = new ImageView();
|
||||
|
||||
|
@ -191,22 +191,14 @@ public class ModDownloadPage extends Control implements DecoratorPage {
|
||||
JFXListView<CurseAddon.LatestFile> listView = new JFXListView<>();
|
||||
spinnerPane.setContent(listView);
|
||||
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();
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -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<ModListPage> {
|
||||
center.getStyleClass().add("large-spinner-pane");
|
||||
center.loadingProperty().bind(skinnable.loadingProperty());
|
||||
|
||||
listView.setCellFactory(x -> new FloatListCell<ModInfoObject>() {
|
||||
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<ModListPage> {
|
||||
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));
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
|
@ -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=添加数据包
|
||||
|
@ -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'
|
||||
|
@ -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<FabricModAuthor> authors;
|
||||
private final Map<String, String> contact;
|
||||
|
||||
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.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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
@ -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<ForgeModMetadata> modList = JsonUtils.GSON.fromJson(FileUtils.readText(mcmod),
|
||||
new TypeToken<List<ForgeModMetadata>>() {
|
||||
List<ForgeOldModMetadata> modList = JsonUtils.GSON.fromJson(FileUtils.readText(mcmod),
|
||||
new TypeToken<List<ForgeOldModMetadata>>() {
|
||||
}.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());
|
||||
}
|
||||
}
|
||||
}
|
@ -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(), "");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -45,13 +45,14 @@ public final class ModInfo implements Comparable<ModInfo> {
|
||||
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<ModInfo> {
|
||||
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<ModInfo> {
|
||||
return url;
|
||||
}
|
||||
|
||||
public String getLogoPath() {
|
||||
return logoPath;
|
||||
}
|
||||
|
||||
public BooleanProperty activeProperty() {
|
||||
return activeProperty;
|
||||
}
|
||||
|
@ -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) {
|
||||
}
|
||||
|
||||
|
@ -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, "", "", "", "", "");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user