From a8b6af9270f8cef41fcdcf8cec80c003259ccfd8 Mon Sep 17 00:00:00 2001 From: Juuz <6596629+Juuxel@users.noreply.github.com> Date: Fri, 6 Jan 2023 14:49:53 +0200 Subject: [PATCH] Split a lot of logic for Arch and Quilt mod metadata into new classes --- .../loom/metadata/ArchitecturyCommonJson.java | 89 ++++++++++++++ .../loom/metadata/ModMetadataFile.java | 12 ++ .../loom/metadata/QuiltModJson.java | 110 ++++++++++++++++++ .../accesswidener/AccessWidenerFile.java | 35 ++---- .../InterfaceInjectionProcessor.java | 53 ++------- .../net/fabricmc/loom/task/RemapJarTask.java | 24 +--- 6 files changed, 228 insertions(+), 95 deletions(-) create mode 100644 src/main/java/dev/architectury/loom/metadata/ArchitecturyCommonJson.java create mode 100644 src/main/java/dev/architectury/loom/metadata/ModMetadataFile.java create mode 100644 src/main/java/dev/architectury/loom/metadata/QuiltModJson.java diff --git a/src/main/java/dev/architectury/loom/metadata/ArchitecturyCommonJson.java b/src/main/java/dev/architectury/loom/metadata/ArchitecturyCommonJson.java new file mode 100644 index 00000000..04800cd2 --- /dev/null +++ b/src/main/java/dev/architectury/loom/metadata/ArchitecturyCommonJson.java @@ -0,0 +1,89 @@ +package dev.architectury.loom.metadata; + +import java.io.File; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Objects; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import org.jetbrains.annotations.Nullable; + +import net.fabricmc.loom.LoomGradlePlugin; +import net.fabricmc.loom.configuration.ifaceinject.InterfaceInjectionProcessor; + +public final class ArchitecturyCommonJson implements ModMetadataFile { + private static final String ACCESS_WIDENER_KEY = "accessWidener"; + + private final JsonObject json; + + private ArchitecturyCommonJson(JsonObject json) { + this.json = Objects.requireNonNull(json, "json"); + } + + public static ArchitecturyCommonJson of(byte[] utf8) { + return of(new String(utf8, StandardCharsets.UTF_8)); + } + + public static ArchitecturyCommonJson of(String text) { + return of(LoomGradlePlugin.GSON.fromJson(text, JsonObject.class)); + } + + public static ArchitecturyCommonJson of(Path path) throws IOException { + return of(Files.readString(path, StandardCharsets.UTF_8)); + } + + public static ArchitecturyCommonJson of(File file) throws IOException { + return of(file.toPath()); + } + + public static ArchitecturyCommonJson of(JsonObject json) { + return new ArchitecturyCommonJson(json); + } + + @Override + public @Nullable String getAccessWidener() { + if (json.has(ACCESS_WIDENER_KEY)) { + return json.get(ACCESS_WIDENER_KEY).getAsString(); + } else { + return null; + } + } + + @Override + public List getInjectedInterfaces(@Nullable String modId) { + if (modId == null) { + throw new IllegalArgumentException("visitInjectedInterfaces: mod ID has to be provided for architectury.common.json"); + } + + return getInjectedInterfaces(json, modId); + } + + static List getInjectedInterfaces(JsonObject json, String modId) { + Objects.requireNonNull(modId, "mod ID"); + + if (json.has("injected_interfaces")) { + JsonObject addedIfaces = json.getAsJsonObject("injected_interfaces"); + + final List result = new ArrayList<>(); + + for (String className : addedIfaces.keySet()) { + final JsonArray ifaceNames = addedIfaces.getAsJsonArray(className); + + for (JsonElement ifaceName : ifaceNames) { + result.add(new InterfaceInjectionProcessor.InjectedInterface(modId, className, ifaceName.getAsString())); + } + } + + return result; + } + + return Collections.emptyList(); + } +} diff --git a/src/main/java/dev/architectury/loom/metadata/ModMetadataFile.java b/src/main/java/dev/architectury/loom/metadata/ModMetadataFile.java new file mode 100644 index 00000000..d4141693 --- /dev/null +++ b/src/main/java/dev/architectury/loom/metadata/ModMetadataFile.java @@ -0,0 +1,12 @@ +package dev.architectury.loom.metadata; + +import java.util.List; + +import org.jetbrains.annotations.Nullable; + +import net.fabricmc.loom.configuration.ifaceinject.InterfaceInjectionProcessor; + +public interface ModMetadataFile { + @Nullable String getAccessWidener(); + List getInjectedInterfaces(@Nullable String modId); +} diff --git a/src/main/java/dev/architectury/loom/metadata/QuiltModJson.java b/src/main/java/dev/architectury/loom/metadata/QuiltModJson.java new file mode 100644 index 00000000..c1c75013 --- /dev/null +++ b/src/main/java/dev/architectury/loom/metadata/QuiltModJson.java @@ -0,0 +1,110 @@ +package dev.architectury.loom.metadata; + +import java.io.File; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import org.jetbrains.annotations.Nullable; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import net.fabricmc.loom.LoomGradlePlugin; +import net.fabricmc.loom.configuration.ifaceinject.InterfaceInjectionProcessor; + +public final class QuiltModJson implements ModMetadataFile { + private static final Logger LOGGER = LoggerFactory.getLogger(QuiltModJson.class); + private static final String ACCESS_WIDENER_KEY = "access_widener"; + private static final String MIXIN_KEY = "mixin"; + + private final JsonObject json; + + private QuiltModJson(JsonObject json) { + this.json = Objects.requireNonNull(json, "json"); + } + + public static QuiltModJson of(byte[] utf8) { + return of(new String(utf8, StandardCharsets.UTF_8)); + } + + public static QuiltModJson of(String text) { + return of(LoomGradlePlugin.GSON.fromJson(text, JsonObject.class)); + } + + public static QuiltModJson of(Path path) throws IOException { + return of(Files.readString(path, StandardCharsets.UTF_8)); + } + + public static QuiltModJson of(File file) throws IOException { + return of(file.toPath()); + } + + public static QuiltModJson of(JsonObject json) { + return new QuiltModJson(json); + } + + @Override + public @Nullable String getAccessWidener() { + if (json.has(ACCESS_WIDENER_KEY)) { + if (json.get(ACCESS_WIDENER_KEY).isJsonArray()) { + JsonArray array = json.get(ACCESS_WIDENER_KEY).getAsJsonArray(); + + // TODO (1.1): Support multiple access wideners in Quilt mods + if (array.size() != 1) { + throw new UnsupportedOperationException("Loom does not support multiple access wideners in one mod!"); + } + + return array.get(0).getAsString(); + } else { + return json.get(ACCESS_WIDENER_KEY).getAsString(); + } + } else { + return null; + } + } + + @Override + public List getInjectedInterfaces(@Nullable String modId) { + try { + modId = Objects.requireNonNullElseGet(modId, () -> json.getAsJsonObject("quilt_loader").get("id").getAsString()); + } catch (NullPointerException e) { + throw new IllegalArgumentException("Could not determine mod ID for Quilt mod and no fallback provided"); + } + + // Quilt injected interfaces have the same format as architectury.common.json + return ArchitecturyCommonJson.getInjectedInterfaces(json.getAsJsonObject("quilt_loom"), modId); + } + + public List getMixinConfigs() { + // RFC 0002: The `mixin` field: + // Type: Array/String + // Required: False + + if (json.has(MIXIN_KEY)) { + JsonElement mixin = json.get(MIXIN_KEY); + + if (mixin.isJsonPrimitive()) { + return List.of(mixin.getAsString()); + } else if (mixin.isJsonArray()) { + List mixinConfigs = new ArrayList<>(); + + for (JsonElement child : mixin.getAsJsonArray()) { + mixinConfigs.add(child.getAsString()); + } + + return mixinConfigs; + } else { + LOGGER.warn("'mixin' key in quilt.mod.json is of unexpected type {}", mixin.getClass()); + } + } + + return List.of(); + } +} diff --git a/src/main/java/net/fabricmc/loom/configuration/accesswidener/AccessWidenerFile.java b/src/main/java/net/fabricmc/loom/configuration/accesswidener/AccessWidenerFile.java index 31ebc2dc..8afcef1b 100644 --- a/src/main/java/net/fabricmc/loom/configuration/accesswidener/AccessWidenerFile.java +++ b/src/main/java/net/fabricmc/loom/configuration/accesswidener/AccessWidenerFile.java @@ -32,8 +32,9 @@ import java.util.Arrays; import java.util.Objects; import com.google.gson.Gson; -import com.google.gson.JsonArray; import com.google.gson.JsonObject; +import dev.architectury.loom.metadata.ArchitecturyCommonJson; +import dev.architectury.loom.metadata.QuiltModJson; import net.fabricmc.loom.util.ZipUtils; @@ -56,7 +57,7 @@ public record AccessWidenerFile( if (modJsonBytes == null) { if (ZipUtils.contains(modJarPath, "architectury.common.json")) { - String awPath = null; + String awPath; byte[] commonJsonBytes; try { @@ -66,13 +67,8 @@ public record AccessWidenerFile( } if (commonJsonBytes != null) { - JsonObject jsonObject = new Gson().fromJson(new String(commonJsonBytes, StandardCharsets.UTF_8), JsonObject.class); - - if (jsonObject.has("accessWidener")) { - awPath = jsonObject.get("accessWidener").getAsString(); - } else { - return null; - } + awPath = ArchitecturyCommonJson.of(commonJsonBytes).getAccessWidener(); + if (awPath == null) return null; } else { // ??????????? throw new IllegalArgumentException("The architectury.common.json file does not exist."); @@ -94,7 +90,7 @@ public record AccessWidenerFile( } if (ZipUtils.contains(modJarPath, "quilt.mod.json")) { - String awPath = null; + String awPath; byte[] quiltModBytes; try { @@ -104,23 +100,8 @@ public record AccessWidenerFile( } if (quiltModBytes != null) { - JsonObject jsonObject = new Gson().fromJson(new String(quiltModBytes, StandardCharsets.UTF_8), JsonObject.class); - - if (jsonObject.has("access_widener")) { - if (jsonObject.get("access_widener").isJsonArray()) { - JsonArray array = jsonObject.get("access_widener").getAsJsonArray(); - - if (array.size() != 1) { - throw new UnsupportedOperationException("Loom does not support multiple access wideners in one mod!"); - } - - awPath = array.get(0).getAsString(); - } else { - awPath = jsonObject.get("access_widener").getAsString(); - } - } else { - return null; - } + awPath = QuiltModJson.of(quiltModBytes).getAccessWidener(); + if (awPath == null) return null; } else { // ??????????? throw new IllegalArgumentException("The quilt.mod.json file does not exist."); diff --git a/src/main/java/net/fabricmc/loom/configuration/ifaceinject/InterfaceInjectionProcessor.java b/src/main/java/net/fabricmc/loom/configuration/ifaceinject/InterfaceInjectionProcessor.java index c8a87540..7838450a 100644 --- a/src/main/java/net/fabricmc/loom/configuration/ifaceinject/InterfaceInjectionProcessor.java +++ b/src/main/java/net/fabricmc/loom/configuration/ifaceinject/InterfaceInjectionProcessor.java @@ -44,10 +44,11 @@ import java.util.stream.Stream; import com.google.common.base.Preconditions; import com.google.common.hash.Hasher; import com.google.common.hash.Hashing; -import com.google.gson.Gson; import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; +import dev.architectury.loom.metadata.ArchitecturyCommonJson; +import dev.architectury.loom.metadata.QuiltModJson; import dev.architectury.tinyremapper.TinyRemapper; import org.gradle.api.Project; import org.gradle.api.tasks.SourceSet; @@ -213,16 +214,11 @@ public class InterfaceInjectionProcessor implements JarProcessor, GenerateSource .matching(patternFilterable -> patternFilterable.include("architectury.common.json")) .getSingleFile(); - final String jsonString; - try { - jsonString = Files.readString(archCommonJson.toPath(), StandardCharsets.UTF_8); + return ArchitecturyCommonJson.of(archCommonJson).getInjectedInterfaces(archCommonJson.getAbsolutePath()); } catch (IOException e2) { throw new UncheckedIOException("Failed to read architectury.common.json", e2); } - - JsonObject jsonObject = new Gson().fromJson(jsonString, JsonObject.class); - return InjectedInterface.fromJsonArch(jsonObject, archCommonJson.getAbsolutePath()); } catch (IllegalStateException e2) { File quiltModJson; @@ -231,20 +227,11 @@ public class InterfaceInjectionProcessor implements JarProcessor, GenerateSource .matching(patternFilterable -> patternFilterable.include("quilt.mod.json")) .getSingleFile(); - final String jsonString; - try { - jsonString = Files.readString(quiltModJson.toPath(), StandardCharsets.UTF_8); + return QuiltModJson.of(quiltModJson).getInjectedInterfaces(quiltModJson.getAbsolutePath()); } catch (IOException e3) { throw new UncheckedIOException("Failed to read quilt.mod.json", e3); } - - JsonObject jsonObject = new Gson().fromJson(jsonString, JsonObject.class); - - if (jsonObject.has("quilt_loom")) { - // quilt injected interfaces has the same format as architectury.common.json - return InjectedInterface.fromJsonArch(jsonObject.getAsJsonObject("quilt_loom"), quiltModJson.getAbsolutePath()); - } } catch (IllegalStateException e3) { // File not found } @@ -310,7 +297,7 @@ public class InterfaceInjectionProcessor implements JarProcessor, GenerateSource return comment; } - private record InjectedInterface(String modId, String className, String ifaceName) { + public record InjectedInterface(String modId, String className, String ifaceName) { /** * Reads the injected interfaces contained in a mod jar, or returns empty if there is none. */ @@ -327,8 +314,7 @@ public class InterfaceInjectionProcessor implements JarProcessor, GenerateSource } if (commonJsonBytes != null) { - JsonObject commonJsonObject = new Gson().fromJson(new String(commonJsonBytes, StandardCharsets.UTF_8), JsonObject.class); - return fromJsonArch(commonJsonObject, modJarPath.toString()); + return ArchitecturyCommonJson.of(commonJsonBytes).getInjectedInterfaces(modJarPath.toString()); } else { try { commonJsonBytes = ZipUtils.unpackNullable(modJarPath, "quilt.mod.json"); @@ -337,12 +323,7 @@ public class InterfaceInjectionProcessor implements JarProcessor, GenerateSource } if (commonJsonBytes != null) { - JsonObject commonJsonObject = new Gson().fromJson(new String(commonJsonBytes, StandardCharsets.UTF_8), JsonObject.class); - - if (commonJsonObject.has("quilt_loom")) { - // quilt injected interfaces has the same format as architectury.common.json - return fromJsonArch(commonJsonObject.getAsJsonObject("quilt_loom"), modJarPath.toString()); - } + return QuiltModJson.of(commonJsonBytes).getInjectedInterfaces(modJarPath.toString()); } } @@ -383,26 +364,6 @@ public class InterfaceInjectionProcessor implements JarProcessor, GenerateSource return result; } - - public static List fromJsonArch(JsonObject jsonObject, String modId) { - if (jsonObject.has("injected_interfaces")) { - JsonObject addedIfaces = jsonObject.getAsJsonObject("injected_interfaces"); - - final List result = new ArrayList<>(); - - for (String className : addedIfaces.keySet()) { - final JsonArray ifaceNames = addedIfaces.getAsJsonArray(className); - - for (JsonElement ifaceName : ifaceNames) { - result.add(new InjectedInterface(modId, className, ifaceName.getAsString())); - } - } - - return result; - } - - return Collections.emptyList(); - } } private static class InjectingClassVisitor extends ClassVisitor { diff --git a/src/main/java/net/fabricmc/loom/task/RemapJarTask.java b/src/main/java/net/fabricmc/loom/task/RemapJarTask.java index c2445184..f978e6f9 100644 --- a/src/main/java/net/fabricmc/loom/task/RemapJarTask.java +++ b/src/main/java/net/fabricmc/loom/task/RemapJarTask.java @@ -24,16 +24,13 @@ package net.fabricmc.loom.task; -import java.io.ByteArrayInputStream; import java.io.File; import java.io.IOException; -import java.io.InputStreamReader; import java.io.Serializable; import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; import java.util.Collection; -import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Map; @@ -42,14 +39,13 @@ import java.util.Set; import java.util.function.Supplier; import java.util.stream.Collectors; import java.util.stream.Stream; -import java.util.stream.StreamSupport; import javax.inject.Inject; import com.google.common.base.Suppliers; -import com.google.gson.JsonElement; import com.google.gson.JsonObject; import dev.architectury.loom.extensions.ModBuildExtensions; +import dev.architectury.loom.metadata.QuiltModJson; import dev.architectury.tinyremapper.OutputConsumerPath; import dev.architectury.tinyremapper.TinyRemapper; import org.gradle.api.artifacts.Configuration; @@ -74,7 +70,6 @@ import net.fabricmc.accesswidener.AccessWidenerReader; import net.fabricmc.accesswidener.AccessWidenerRemapper; import net.fabricmc.accesswidener.AccessWidenerWriter; import net.fabricmc.loom.LoomGradleExtension; -import net.fabricmc.loom.LoomGradlePlugin; import net.fabricmc.loom.build.MixinRefmapHelper; import net.fabricmc.loom.build.nesting.IncludedJarFactory; import net.fabricmc.loom.build.nesting.IncludedJarFactory.LazyNestedFile; @@ -233,22 +228,7 @@ public abstract class RemapJarTask extends AbstractRemapJarTask { byte[] bytes = ZipUtils.unpackNullable(getInputFile().getAsFile().get().toPath(), "quilt.mod.json"); if (bytes != null) { - JsonObject json = LoomGradlePlugin.GSON.fromJson(new InputStreamReader(new ByteArrayInputStream(bytes)), JsonObject.class); - JsonElement mixins = json.has("mixin") ? json.get("mixin") : json.get("mixins"); - - if (mixins != null) { - if (mixins.isJsonPrimitive()) { - allMixinConfigs = Collections.singletonList(mixins.getAsString()); - } else if (mixins.isJsonArray()) { - allMixinConfigs = StreamSupport.stream(mixins.getAsJsonArray().spliterator(), false) - .map(JsonElement::getAsString) - .collect(Collectors.toList()); - } else { - throw new RuntimeException("Unknown mixin type: " + mixins.getClass().getName()); - } - } else { - allMixinConfigs = Collections.emptyList(); - } + allMixinConfigs = QuiltModJson.of(bytes).getMixinConfigs(); } } catch (IOException e) { throw new RuntimeException("Cannot read file quilt.mod.json in the jar.", e);