Split a lot of logic for Arch and Quilt mod metadata into new classes

This commit is contained in:
Juuz
2023-01-06 14:49:53 +02:00
parent 3a772b20d8
commit a8b6af9270
6 changed files with 228 additions and 95 deletions

View File

@@ -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<InterfaceInjectionProcessor.InjectedInterface> 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<InterfaceInjectionProcessor.InjectedInterface> getInjectedInterfaces(JsonObject json, String modId) {
Objects.requireNonNull(modId, "mod ID");
if (json.has("injected_interfaces")) {
JsonObject addedIfaces = json.getAsJsonObject("injected_interfaces");
final List<InterfaceInjectionProcessor.InjectedInterface> 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();
}
}

View File

@@ -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<InterfaceInjectionProcessor.InjectedInterface> getInjectedInterfaces(@Nullable String modId);
}

View File

@@ -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<InterfaceInjectionProcessor.InjectedInterface> 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<String> 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<String> 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();
}
}

View File

@@ -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.");

View File

@@ -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<InjectedInterface> fromJsonArch(JsonObject jsonObject, String modId) {
if (jsonObject.has("injected_interfaces")) {
JsonObject addedIfaces = jsonObject.getAsJsonObject("injected_interfaces");
final List<InjectedInterface> 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 {

View File

@@ -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);