From 8afd8bde8b25168a0c31bf1496037871e9be4ec8 Mon Sep 17 00:00:00 2001 From: huanghongxun Date: Sat, 10 Apr 2021 17:09:09 +0800 Subject: [PATCH] Fix: failed to parse datapack pack.mcmeta in 6th format. Closes #840. --- .../ui/versions/DatapackListPageSkin.java | 2 +- .../java/org/jackhuang/hmcl/mod/Datapack.java | 6 +- .../jackhuang/hmcl/mod/FabricModMetadata.java | 2 +- .../jackhuang/hmcl/mod/ForgeModMetadata.java | 2 +- .../jackhuang/hmcl/mod/LiteModMetadata.java | 2 +- .../java/org/jackhuang/hmcl/mod/ModInfo.java | 59 ++++++++++++-- .../org/jackhuang/hmcl/mod/ModManager.java | 2 +- .../org/jackhuang/hmcl/mod/PackMcMeta.java | 76 +++++++++++++++++-- 8 files changed, 130 insertions(+), 21 deletions(-) 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 2c5ec3d34..9b1a0cd77 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 @@ -132,7 +132,7 @@ class DatapackListPageSkin extends SkinBase { } String getSubtitle() { - return StringUtils.parseColorEscapes(packInfo.getDescription()); + return StringUtils.parseColorEscapes(packInfo.getDescription().toString()); } Datapack.Pack getPackInfo() { diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/Datapack.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/Datapack.java index 94709419b..29c8d5c09 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/Datapack.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/Datapack.java @@ -202,10 +202,10 @@ public class Datapack { private Path file; private final BooleanProperty active; private final String id; - private final String description; + private final ModInfo.Description description; private final Datapack datapack; - public Pack(Path file, String id, String description, Datapack datapack) { + public Pack(Path file, String id, ModInfo.Description description, Datapack datapack) { this.file = file; this.id = id; this.description = description; @@ -235,7 +235,7 @@ public class Datapack { return id; } - public String getDescription() { + public ModInfo.Description getDescription() { return description; } 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 316a99196..ee331bb6c 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/FabricModMetadata.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/FabricModMetadata.java @@ -67,7 +67,7 @@ public final class FabricModMetadata { throw new IOException("File " + modFile + " is not a Fabric mod."); 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, metadata.description, + return new ModInfo(modManager, modFile, metadata.name, new ModInfo.Description(metadata.description), authors, metadata.version, "", metadata.contact != null ? metadata.contact.getOrDefault("homepage", "") : ""); } } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/ForgeModMetadata.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/ForgeModMetadata.java index fddf15c57..cf211fd08 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/ForgeModMetadata.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/ForgeModMetadata.java @@ -132,7 +132,7 @@ public final class ForgeModMetadata { authors = String.join(", ", metadata.getAuthorList()); if (StringUtils.isBlank(authors)) authors = metadata.getCredits(); - return new ModInfo(modManager, modFile, metadata.getName(), metadata.getDescription(), + 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); } 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 eb461c284..2391f2ff0 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/LiteModMetadata.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/LiteModMetadata.java @@ -116,7 +116,7 @@ public final class LiteModMetadata { LiteModMetadata metadata = JsonUtils.GSON.fromJson(IOUtils.readFullyAsString(zipFile.getInputStream(entry)), LiteModMetadata.class); if (metadata == null) throw new IOException("Mod " + modFile + " `litemod.json` is malformed."); - return new ModInfo(modManager, modFile, metadata.getName(), metadata.getDescription(), metadata.getAuthor(), + return new ModInfo(modManager, modFile, metadata.getName(), new ModInfo.Description(metadata.getDescription()), metadata.getAuthor(), 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 9dfaca8b6..f65b6d875 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/ModInfo.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/ModInfo.java @@ -17,16 +17,17 @@ */ package org.jackhuang.hmcl.mod; +import javafx.beans.property.BooleanProperty; +import javafx.beans.property.SimpleBooleanProperty; import org.jackhuang.hmcl.util.Logging; import org.jackhuang.hmcl.util.StringUtils; import org.jackhuang.hmcl.util.io.FileUtils; -import javafx.beans.property.BooleanProperty; -import javafx.beans.property.SimpleBooleanProperty; - import java.io.File; import java.io.IOException; import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; import java.util.Objects; import java.util.logging.Level; @@ -38,7 +39,7 @@ public final class ModInfo implements Comparable { private Path file; private final String name; - private final String description; + private final Description description; private final String authors; private final String version; private final String gameVersion; @@ -46,11 +47,11 @@ public final class ModInfo implements Comparable { private final String fileName; private final BooleanProperty activeProperty; - public ModInfo(ModManager modManager, File file, String name, String description) { + public ModInfo(ModManager modManager, File file, String name, Description description) { this(modManager, file, name, description, "", "", "", ""); } - public ModInfo(ModManager modManager, File file, String name, String 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) { this.file = file.toPath(); this.name = name; this.description = description; @@ -86,7 +87,7 @@ public final class ModInfo implements Comparable { return name; } - public String getDescription() { + public Description getDescription() { return description; } @@ -136,4 +137,48 @@ public final class ModInfo implements Comparable { public int hashCode() { return Objects.hash(getFileName()); } + + public static class Description { + private final List parts; + + public Description(String text) { + this.parts = new ArrayList<>(); + this.parts.add(new Part(text, "black")); + } + + public Description(List parts) { + this.parts = parts; + } + + public List getParts() { + return parts; + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + for (Part part : parts) { + builder.append(part.text); + } + return builder.toString(); + } + + public static class Part { + private final String text; + private final String color; + + public Part(String text, String color) { + this.text = Objects.requireNonNull(text); + this.color = Objects.requireNonNull(color); + } + + public String getText() { + return text; + } + + public String getColor() { + return color; + } + } + } } 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 53203fa18..f3c2720f4 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/ModManager.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/ModManager.java @@ -94,7 +94,7 @@ public final class ModManager { default: throw new IllegalArgumentException("File " + modFile + " is not a mod file."); } - return new ModInfo(this, modFile, FileUtils.getNameWithoutExtension(modFile), description); + return new ModInfo(this, modFile, FileUtils.getNameWithoutExtension(modFile), new ModInfo.Description(description)); } public void refreshMods() throws IOException { 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 f0be8149d..0f443d566 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/PackMcMeta.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/mod/PackMcMeta.java @@ -17,7 +17,8 @@ */ package org.jackhuang.hmcl.mod; -import com.google.gson.JsonParseException; +import com.google.gson.*; +import com.google.gson.annotations.JsonAdapter; import com.google.gson.annotations.SerializedName; import org.jackhuang.hmcl.util.Immutable; import org.jackhuang.hmcl.util.gson.JsonUtils; @@ -27,9 +28,13 @@ import org.jackhuang.hmcl.util.io.FileUtils; import java.io.File; import java.io.IOException; +import java.lang.reflect.Type; import java.nio.file.FileSystem; import java.nio.file.Files; import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; @Immutable public class PackMcMeta implements Validation { @@ -55,18 +60,19 @@ public class PackMcMeta implements Validation { throw new JsonParseException("pack cannot be null"); } + @JsonAdapter(PackInfoDeserializer.class) public static class PackInfo { @SerializedName("pack_format") private final int packFormat; @SerializedName("description") - private final String description; + private final ModInfo.Description description; public PackInfo() { - this(0, ""); + this(0, new ModInfo.Description(Collections.emptyList())); } - public PackInfo(int packFormat, String description) { + public PackInfo(int packFormat, ModInfo.Description description) { this.packFormat = packFormat; this.description = description; } @@ -75,18 +81,76 @@ public class PackMcMeta implements Validation { return packFormat; } - public String getDescription() { + public ModInfo.Description getDescription() { return description; } } + public static class PackInfoDeserializer implements JsonDeserializer { + + private String parseText(JsonElement json) throws JsonParseException { + if (json.isJsonPrimitive()) { + JsonPrimitive primitive = json.getAsJsonPrimitive(); + if (primitive.isBoolean()) { + return Boolean.toString(primitive.getAsBoolean()); + } else if (primitive.isNumber()) { + return primitive.getAsNumber().toString(); + } else if (primitive.isString()) { + return primitive.getAsString(); + } else { + throw new JsonParseException("pack.mcmeta text not boolean nor number nor string???"); + } + } else if (json.isJsonArray()) { + JsonArray arr = json.getAsJsonArray(); + if (arr.size() == 0) { + return ""; + } else { + return parseText(arr.get(0)); + } + } else { + throw new JsonParseException("pack.mcmeta text should be a string, a boolean, a number or a list of raw JSON text components"); + } + } + + public ModInfo.Description.Part deserialize(JsonElement json, JsonDeserializationContext context) throws JsonParseException { + if (json.isJsonPrimitive()) { + return new ModInfo.Description.Part(parseText(json)); + } else if (json.isJsonObject()) { + JsonObject obj = json.getAsJsonObject(); + String text = parseText(obj.get("text")); + return new ModInfo.Description.Part(text); + } else { + throw new JsonParseException("pack.mcmeta Raw JSON text should be string or an object"); + } + } + + @Override + public PackInfo deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { + List parts = new ArrayList<>(); + JsonObject packInfo = json.getAsJsonObject(); + int packFormat = packInfo.get("pack_format").getAsInt(); + JsonElement description = packInfo.get("description"); + if (description.isJsonPrimitive()) { + parts.add(new ModInfo.Description.Part(parseText(description))); + } else if (description.isJsonArray()) { + for (JsonElement element : description.getAsJsonArray()) { + JsonObject descriptionPart = element.getAsJsonObject(); + parts.add(new ModInfo.Description.Part(descriptionPart.get("text").getAsString(), descriptionPart.get("color").getAsString())); + } + } else { + throw new JsonParseException("pack.mcmeta::pack::description should be String or array of text objects with text and color fields"); + } + return new PackInfo(packFormat, new ModInfo.Description(parts)); + } + } + public static ModInfo fromFile(ModManager modManager, File modFile) throws IOException, JsonParseException { try (FileSystem fs = CompressingUtils.createReadOnlyZipFileSystem(modFile.toPath())) { Path mcmod = fs.getPath("pack.mcmeta"); 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, metadata.pack.description, "", "", "", "", ""); + return new ModInfo(modManager, modFile, FileUtils.getNameWithoutExtension(modFile), metadata.pack.description, "", "", "", ""); } } }