From 47c44245bea5f15d14edb28d38a8633596c11a8a Mon Sep 17 00:00:00 2001 From: shedaniel Date: Wed, 20 Apr 2022 16:27:14 +0800 Subject: [PATCH] Add quilt mode Squashed commit of the following: commit 2f5cd64dd232996e92e8d1f71e6ce5deb747d247 Author: shedaniel Date: Wed Apr 20 16:23:18 2022 +0800 Make installer json ignore fabric's on quilt mode Signed-off-by: shedaniel commit 381b346c88f22f30acd9620f57f6ef03e6c50102 Merge: f272145a 1c48b6cb Author: shedaniel Date: Wed Apr 20 15:43:46 2022 +0800 Merge remote-tracking branch 'architectury/dev/0.11.0' into feature/0.11.0-quilt commit f272145a19949c72bf1ebdcec3f7c13ed498eb5a Author: shedaniel Date: Sat Apr 16 14:22:07 2022 +0800 Match quilt-loom up to 44030f5d4659c425cb6f1450f46be5a0546964f2 Signed-off-by: shedaniel commit 0e084be5e391e79a75467d1c9a56efd02821247a Merge: 471d6a06 4a7a2660 Author: shedaniel Date: Sat Apr 16 14:13:40 2022 +0800 Merge remote-tracking branch 'architectury/dev/0.11.0' into feature/0.11.0-quilt commit 471d6a065a18839bbd4d724d8f4383de53752e8b Author: shedaniel Date: Mon Mar 28 18:47:20 2022 +0800 Add QMJ support Signed-off-by: shedaniel --- .../loom/api/LoomGradleExtensionAPI.java | 4 ++ .../loom/build/ModCompileRemapper.java | 4 +- .../build/nesting/IncludedJarFactory.java | 8 ++- .../loom/build/nesting/JarNester.java | 46 ++++++++++++-- .../configuration/FileDependencyInfo.java | 21 +++++++ .../configuration/LoomDependencyManager.java | 16 +++-- .../accesswidener/AccessWidenerFile.java | 49 ++++++++++++++- .../InterfaceInjectionProcessor.java | 41 ++++++++++++- .../loom/configuration/mods/ModProcessor.java | 19 +++++- .../dependency/ModDependencyInfo.java | 29 +++++++-- .../extension/LoomGradleExtensionApiImpl.java | 8 ++- .../net/fabricmc/loom/task/RemapJarTask.java | 60 ++++++++++++++++--- .../task/launch/GenerateDLIConfigTask.java | 10 +++- .../net/fabricmc/loom/util/ModPlatform.java | 15 ++++- .../java/net/fabricmc/loom/util/ModUtils.java | 15 +++-- .../java/net/fabricmc/loom/util/ZipUtils.java | 5 +- 16 files changed, 312 insertions(+), 38 deletions(-) diff --git a/src/main/java/net/fabricmc/loom/api/LoomGradleExtensionAPI.java b/src/main/java/net/fabricmc/loom/api/LoomGradleExtensionAPI.java index 7bbb9d01..d51843a5 100644 --- a/src/main/java/net/fabricmc/loom/api/LoomGradleExtensionAPI.java +++ b/src/main/java/net/fabricmc/loom/api/LoomGradleExtensionAPI.java @@ -172,6 +172,10 @@ public interface LoomGradleExtensionAPI { return getPlatform().get() == ModPlatform.FORGE; } + default boolean isQuilt() { + return getPlatform().get() == ModPlatform.QUILT; + } + void setGenerateSrgTiny(Boolean generateSrgTiny); boolean shouldGenerateSrgTiny(); diff --git a/src/main/java/net/fabricmc/loom/build/ModCompileRemapper.java b/src/main/java/net/fabricmc/loom/build/ModCompileRemapper.java index df8d2931..cf71658a 100644 --- a/src/main/java/net/fabricmc/loom/build/ModCompileRemapper.java +++ b/src/main/java/net/fabricmc/loom/build/ModCompileRemapper.java @@ -94,7 +94,7 @@ public class ModCompileRemapper { String name = extension.isForgeAndOfficial() ? "B" + checksum.get() : artifact.getModuleVersion().getId().getName(); String version = extension.isForgeAndOfficial() ? "B" + checksum.get() : replaceIfNullOrEmpty(artifact.getModuleVersion().getId().getVersion(), () -> Checksum.truncatedSha256(artifact.getFile())); - if (!ModUtils.shouldRemapMod(project.getLogger(), artifact.getFile(), artifact.getId(), extension.isForge(), sourceConfig.getName())) { + if (!ModUtils.shouldRemapMod(project.getLogger(), artifact.getFile(), artifact.getId(), extension.getPlatform().get(), sourceConfig.getName())) { addToRegularCompile(project, regularConfig, artifact); continue; } @@ -121,7 +121,7 @@ public class ModCompileRemapper { // Create a mod dependency for each file in the file collection for (File artifact : files) { - if (!ModUtils.shouldRemapMod(project.getLogger(), artifact, artifact.getName(), extension.isForge(), sourceConfig.getName())) { + if (!ModUtils.shouldRemapMod(project.getLogger(), artifact, artifact.getName(), extension.getPlatform().get(), sourceConfig.getName())) { dependencies.add(regularConfig.getName(), project.files(artifact)); continue; } diff --git a/src/main/java/net/fabricmc/loom/build/nesting/IncludedJarFactory.java b/src/main/java/net/fabricmc/loom/build/nesting/IncludedJarFactory.java index 1a833a76..83bb1ddd 100644 --- a/src/main/java/net/fabricmc/loom/build/nesting/IncludedJarFactory.java +++ b/src/main/java/net/fabricmc/loom/build/nesting/IncludedJarFactory.java @@ -53,6 +53,7 @@ import org.jetbrains.annotations.Nullable; import net.fabricmc.loom.LoomGradleExtension; import net.fabricmc.loom.LoomGradlePlugin; import net.fabricmc.loom.task.RemapTaskConfiguration; +import net.fabricmc.loom.util.ModPlatform; import net.fabricmc.loom.util.ModUtils; import net.fabricmc.loom.util.ZipUtils; @@ -143,7 +144,7 @@ public final class IncludedJarFactory { } private File getNestableJar(final File input, final Metadata metadata) { - if (ModUtils.isMod(input)) { + if (ModUtils.isMod(input, LoomGradleExtension.get(project).getPlatform().get())) { // Input is a mod, nothing needs to be done. return input; } @@ -163,6 +164,11 @@ public final class IncludedJarFactory { try { FileUtils.copyFile(input, tempFile); + + if (extension.getPlatform().get() == ModPlatform.QUILT) { + throw new UnsupportedOperationException("Generating Quilt mods for JiJ is not yet implemented!"); + } + ZipUtils.add(tempFile.toPath(), "fabric.mod.json", generateModForDependency(metadata).getBytes(StandardCharsets.UTF_8)); } catch (IOException e) { throw new UncheckedIOException("Failed to add dummy mod while including %s".formatted(input), e); diff --git a/src/main/java/net/fabricmc/loom/build/nesting/JarNester.java b/src/main/java/net/fabricmc/loom/build/nesting/JarNester.java index 06bf4b57..10990203 100644 --- a/src/main/java/net/fabricmc/loom/build/nesting/JarNester.java +++ b/src/main/java/net/fabricmc/loom/build/nesting/JarNester.java @@ -38,18 +38,19 @@ import com.google.gson.JsonObject; import org.gradle.api.UncheckedIOException; import org.slf4j.Logger; +import net.fabricmc.loom.util.ModPlatform; import net.fabricmc.loom.util.ModUtils; import net.fabricmc.loom.util.Pair; import net.fabricmc.loom.util.ZipUtils; public class JarNester { - public static void nestJars(Collection jars, File modJar, Logger logger) { + public static void nestJars(Collection jars, File modJar, ModPlatform platform, Logger logger) { if (jars.isEmpty()) { logger.debug("Nothing to nest into " + modJar.getName()); return; } - Preconditions.checkArgument(ModUtils.isMod(modJar), "Cannot nest jars into none mod jar " + modJar.getName()); + Preconditions.checkArgument(ModUtils.isMod(modJar, platform), "Cannot nest jars into none mod jar " + modJar.getName()); try { ZipUtils.add(modJar.toPath(), jars.stream().map(file -> { @@ -60,7 +61,7 @@ public class JarNester { } }).collect(Collectors.toList())); - int count = ZipUtils.transformJson(JsonObject.class, modJar.toPath(), Stream.of(new Pair<>("fabric.mod.json", json -> { + int count = ZipUtils.transformJson(JsonObject.class, modJar.toPath(), Stream.of(platform == ModPlatform.FABRIC ? new Pair<>("fabric.mod.json", json -> { JsonArray nestedJars = json.getAsJsonArray("jars"); if (nestedJars == null || !json.has("jars")) { @@ -69,7 +70,7 @@ public class JarNester { for (File file : jars) { String nestedJarPath = "META-INF/jars/" + file.getName(); - Preconditions.checkArgument(ModUtils.isMod(file), "Cannot nest none mod jar: " + file.getName()); + Preconditions.checkArgument(ModUtils.isMod(file, platform), "Cannot nest none mod jar: " + file.getName()); for (JsonElement nestedJar : nestedJars) { JsonObject jsonObject = nestedJar.getAsJsonObject(); @@ -89,7 +90,42 @@ public class JarNester { json.add("jars", nestedJars); return json; - }))); + }) : platform == ModPlatform.QUILT ? new Pair<>("quilt.mod.json", json -> { + JsonObject loader; + + if (json.has("quilt_loader")) { + loader = json.getAsJsonObject("quilt_loader"); + } else { + json.add("quilt_loader", loader = new JsonObject()); + } + + JsonArray nestedJars = loader.getAsJsonArray("jars"); + + if (nestedJars == null || !loader.has("jars")) { + nestedJars = new JsonArray(); + } + + for (File file : jars) { + String nestedJarPath = "META-INF/jars/" + file.getName(); + Preconditions.checkArgument(ModUtils.isMod(file, platform), "Cannot nest none mod jar: " + file.getName()); + + for (JsonElement nestedJar : nestedJars) { + String nestedJarString = nestedJar.getAsString(); + + if (nestedJarPath.equals(nestedJarString)) { + throw new IllegalStateException("Cannot nest 2 jars at the same path: " + nestedJarString); + } + } + + nestedJars.add(nestedJarPath); + + logger.debug("Nested " + nestedJarPath + " into " + modJar.getName()); + } + + loader.add("jars", nestedJars); + + return json; + }) : null)); Preconditions.checkState(count > 0, "Failed to transform fabric.mod.json"); } catch (IOException e) { diff --git a/src/main/java/net/fabricmc/loom/configuration/FileDependencyInfo.java b/src/main/java/net/fabricmc/loom/configuration/FileDependencyInfo.java index 2f55bbe0..53be6ae3 100644 --- a/src/main/java/net/fabricmc/loom/configuration/FileDependencyInfo.java +++ b/src/main/java/net/fabricmc/loom/configuration/FileDependencyInfo.java @@ -115,6 +115,27 @@ public class FileDependencyInfo extends DependencyInfo { } version = json.get("version").getAsString(); + } else if ("jar".equals(FilenameUtils.getExtension(root.getName())) && (modJson = ZipUtils.unpackNullable(root.toPath(), "quilt.mod.json")) != null) { + //It's a Fabric mod, see how much we can extract out + JsonObject json = new Gson().fromJson(new String(modJson, StandardCharsets.UTF_8), JsonObject.class); + + if (json == null || !json.has("quilt_loader")) { + throw new IllegalArgumentException("Invalid Quilt mod jar: " + root + " (malformed json: " + json + ')'); + } + + JsonObject loader = json.getAsJsonObject("quilt_loader"); + + if (!loader.has("id") || !loader.has("version")) { + throw new IllegalArgumentException("Invalid Quilt mod jar: " + root + " (malformed json: " + json + ')'); + } + + if (loader.has("metadata") && loader.get("metadata").getAsJsonObject().has("name")) { //Go for the name field if it's got one + name = loader.get("metadata").getAsJsonObject().get("name").getAsString(); + } else { + name = loader.get("id").getAsString(); + } + + version = loader.get("version").getAsString(); } else { //Not a Fabric mod, just have to make something up name = FilenameUtils.removeExtension(root.getName()); diff --git a/src/main/java/net/fabricmc/loom/configuration/LoomDependencyManager.java b/src/main/java/net/fabricmc/loom/configuration/LoomDependencyManager.java index 6e6e629e..98c600c4 100644 --- a/src/main/java/net/fabricmc/loom/configuration/LoomDependencyManager.java +++ b/src/main/java/net/fabricmc/loom/configuration/LoomDependencyManager.java @@ -61,7 +61,7 @@ public class LoomDependencyManager { for (Dependency dependency : configuration.getAllDependencies()) { for (File input : configuration.files(dependency)) { - JsonObject jsonObject = readInstallerJson(input); + JsonObject jsonObject = readInstallerJson(input, extension.isQuilt()); if (jsonObject != null) { if (extension.getInstallerData() != null) { @@ -77,7 +77,11 @@ public class LoomDependencyManager { } if (extension.getInstallerData() == null) { - project.getLogger().warn("fabric-installer.json not found in classpath!"); + if (extension.isQuilt()) { + project.getLogger().warn("quilt_installer.json not found in classpath!"); + } else { + project.getLogger().warn("fabric-installer.json not found in classpath!"); + } } } @@ -94,9 +98,13 @@ public class LoomDependencyManager { } } - public static JsonObject readInstallerJson(File file) { + public static JsonObject readInstallerJson(File file, boolean quilt) { try { - byte[] bytes = ZipUtils.unpackNullable(file.toPath(), "fabric-installer.json"); + byte[] bytes = quilt ? null : ZipUtils.unpackNullable(file.toPath(), "fabric-installer.json"); + + if (bytes == null && quilt) { + bytes = ZipUtils.unpackNullable(file.toPath(), "quilt_installer.json"); + } if (bytes == null) { return null; 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 e322f8b2..6a243627 100644 --- a/src/main/java/net/fabricmc/loom/configuration/accesswidener/AccessWidenerFile.java +++ b/src/main/java/net/fabricmc/loom/configuration/accesswidener/AccessWidenerFile.java @@ -32,6 +32,7 @@ import java.util.Arrays; import java.util.Objects; import com.google.gson.Gson; +import com.google.gson.JsonArray; import com.google.gson.JsonObject; import net.fabricmc.loom.util.ZipUtils; @@ -70,7 +71,7 @@ public record AccessWidenerFile( if (jsonObject.has("accessWidener")) { awPath = jsonObject.get("accessWidener").getAsString(); } else { - throw new IllegalArgumentException("The architectury.common.json file does not contain an accessWidener field."); + return null; } } else { // ??????????? @@ -92,6 +93,52 @@ public record AccessWidenerFile( ); } + if (ZipUtils.contains(modJarPath, "quilt.mod.json")) { + String awPath = null; + byte[] quiltModBytes; + + try { + quiltModBytes = ZipUtils.unpackNullable(modJarPath, "quilt.mod.json"); + } catch (IOException e) { + throw new UncheckedIOException("Failed to read quilt.mod.json file from: " + modJarPath.toAbsolutePath(), e); + } + + 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; + } + } else { + // ??????????? + throw new IllegalArgumentException("The quilt.mod.json file does not exist."); + } + + byte[] content; + + try { + content = ZipUtils.unpack(modJarPath, awPath); + } catch (IOException e) { + throw new UncheckedIOException("Could not find access widener file (%s) defined in the quilt.mod.json file of %s".formatted(awPath, modJarPath.toAbsolutePath()), e); + } + + return new AccessWidenerFile( + awPath, + modJarPath.getFileName().toString(), + content + ); + } + return null; } 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 7acfaed5..2ada1000 100644 --- a/src/main/java/net/fabricmc/loom/configuration/ifaceinject/InterfaceInjectionProcessor.java +++ b/src/main/java/net/fabricmc/loom/configuration/ifaceinject/InterfaceInjectionProcessor.java @@ -226,7 +226,31 @@ public class InterfaceInjectionProcessor implements JarProcessor, GenerateSource JsonObject jsonObject = new Gson().fromJson(jsonString, JsonObject.class); return InjectedInterface.fromJsonArch(jsonObject, archCommonJson.getAbsolutePath()); } catch (IllegalStateException e2) { - // File not found + File quiltModJson; + + try { + quiltModJson = sourceSet.getResources() + .matching(patternFilterable -> patternFilterable.include("quilt.mods.json")) + .getSingleFile(); + + final String jsonString; + + try { + jsonString = Files.readString(quiltModJson.toPath(), StandardCharsets.UTF_8); + } 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 + } + return Collections.emptyList(); } } @@ -313,6 +337,21 @@ public class InterfaceInjectionProcessor implements JarProcessor, GenerateSource if (commonJsonBytes != null) { JsonObject jsonObject = new Gson().fromJson(new String(commonJsonBytes, StandardCharsets.UTF_8), JsonObject.class); return fromJsonArch(jsonObject, modJarPath.toString()); + } else { + try { + commonJsonBytes = ZipUtils.unpackNullable(modJarPath, "quilt.mod.json"); + } catch (IOException e) { + throw new UncheckedIOException("Failed to read quilt.mod.json file from: " + modJarPath.toAbsolutePath(), e); + } + + if (commonJsonBytes != null) { + JsonObject jsonObject = new Gson().fromJson(new String(commonJsonBytes, StandardCharsets.UTF_8), JsonObject.class); + + if (jsonObject.has("quilt_loom")) { + // quilt injected interfaces has the same format as architectury.common.json + return fromJsonArch(jsonObject.getAsJsonObject("quilt_loom"), modJarPath.toString()); + } + } } return Collections.emptyList(); diff --git a/src/main/java/net/fabricmc/loom/configuration/mods/ModProcessor.java b/src/main/java/net/fabricmc/loom/configuration/mods/ModProcessor.java index dde7ef15..2ec40aab 100644 --- a/src/main/java/net/fabricmc/loom/configuration/mods/ModProcessor.java +++ b/src/main/java/net/fabricmc/loom/configuration/mods/ModProcessor.java @@ -108,7 +108,24 @@ public class ModProcessor { } private void stripNestedJars(File file) { - if (!ZipUtils.contains(file.toPath(), "fabric.mod.json")) return; + if (!ZipUtils.contains(file.toPath(), "fabric.mod.json")) { + if (ZipUtils.contains(file.toPath(), "quilt.mod.json")) { + // Strip out all contained jar info as we dont want loader to try and load the jars contained in dev. + try { + ZipUtils.transformJson(JsonObject.class, file.toPath(), Map.of("quilt.mod.json", json -> { + if (json.has("quilt_loader")) { + json.getAsJsonObject("quilt_loader").remove("jars"); + } + + return json; + })); + } catch (IOException e) { + throw new UncheckedIOException("Failed to strip nested jars from %s".formatted(file), e); + } + } + + return; + } // Strip out all contained jar info as we dont want loader to try and load the jars contained in dev. try { diff --git a/src/main/java/net/fabricmc/loom/configuration/processors/dependency/ModDependencyInfo.java b/src/main/java/net/fabricmc/loom/configuration/processors/dependency/ModDependencyInfo.java index 5f6d0377..f96de92b 100644 --- a/src/main/java/net/fabricmc/loom/configuration/processors/dependency/ModDependencyInfo.java +++ b/src/main/java/net/fabricmc/loom/configuration/processors/dependency/ModDependencyInfo.java @@ -31,6 +31,7 @@ import java.io.UncheckedIOException; import java.nio.charset.StandardCharsets; import java.nio.file.Path; +import com.google.gson.JsonArray; import com.google.gson.JsonObject; import org.apache.commons.io.FileUtils; import org.gradle.api.artifacts.Configuration; @@ -192,25 +193,43 @@ public class ModDependencyInfo { } private static AccessWidenerData tryReadAccessWidenerData(Path inputJar) throws IOException { + String fieldName = "accessWidener"; byte[] modJsonBytes = ZipUtils.unpackNullable(inputJar, "fabric.mod.json"); if (modJsonBytes == null) { modJsonBytes = ZipUtils.unpackNullable(inputJar, "architectury.common.json"); if (modJsonBytes == null) { - // No access widener data - // We can just ignore in architectury - return null; + modJsonBytes = ZipUtils.unpackNullable(inputJar, "quilt.mod.json"); + + if (modJsonBytes != null) { + fieldName = "access_widener"; + } else { + // No access widener data + // We can just ignore in architectury + return null; + } } } JsonObject jsonObject = LoomGradlePlugin.GSON.fromJson(new String(modJsonBytes, StandardCharsets.UTF_8), JsonObject.class); - if (!jsonObject.has("accessWidener")) { + if (!jsonObject.has(fieldName)) { return null; } - String accessWidenerPath = jsonObject.get("accessWidener").getAsString(); + String accessWidenerPath; + + if (fieldName.equals("access_widener") && jsonObject.get(fieldName).isJsonArray()) { + JsonArray array = jsonObject.get(fieldName).getAsJsonArray(); + if (array.size() != 1) { + throw new UnsupportedOperationException("Loom does not support multiple access wideners in one mod!"); + } + accessWidenerPath = array.get(0).getAsString(); + } else { + accessWidenerPath = jsonObject.get(fieldName).getAsString(); + } + byte[] accessWidener = ZipUtils.unpack(inputJar, accessWidenerPath); AccessWidenerReader.Header header = AccessWidenerReader.readHeader(accessWidener); diff --git a/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionApiImpl.java b/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionApiImpl.java index 127eba4a..b6a6fb42 100644 --- a/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionApiImpl.java +++ b/src/main/java/net/fabricmc/loom/extension/LoomGradleExtensionApiImpl.java @@ -150,7 +150,13 @@ public abstract class LoomGradleExtensionApiImpl implements LoomGradleExtensionA Object platformProperty = project.findProperty(PLATFORM_PROPERTY); if (platformProperty != null) { - return ModPlatform.valueOf(Objects.toString(platformProperty).toUpperCase(Locale.ROOT)); + ModPlatform platform = ModPlatform.valueOf(Objects.toString(platformProperty).toUpperCase(Locale.ROOT)); + + if (platform.isExperimental()) { + project.getLogger().warn("Project " + project.getPath() + " is using experimental mod platform " + platform.name() + ". Please report any issues!"); + } + + return platform; } Object forgeProperty = project.findProperty(FORGE_PROPERTY); diff --git a/src/main/java/net/fabricmc/loom/task/RemapJarTask.java b/src/main/java/net/fabricmc/loom/task/RemapJarTask.java index ff19c903..acbefec0 100644 --- a/src/main/java/net/fabricmc/loom/task/RemapJarTask.java +++ b/src/main/java/net/fabricmc/loom/task/RemapJarTask.java @@ -29,6 +29,7 @@ import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; +import java.io.InputStreamReader; import java.io.Serializable; import java.io.UncheckedIOException; import java.io.Writer; @@ -39,6 +40,7 @@ import java.nio.file.Files; import java.nio.file.NoSuchFileException; import java.nio.file.Path; import java.util.Collection; +import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Objects; @@ -48,11 +50,13 @@ import java.util.jar.Attributes; import java.util.jar.JarFile; import java.util.jar.Manifest; import java.util.stream.Collectors; +import java.util.stream.StreamSupport; import javax.inject.Inject; import com.google.common.base.Preconditions; import com.google.common.base.Suppliers; +import com.google.gson.JsonElement; import com.google.gson.JsonObject; import dev.architectury.tinyremapper.OutputConsumerPath; import dev.architectury.tinyremapper.TinyRemapper; @@ -80,6 +84,7 @@ 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.JarNester; @@ -91,6 +96,7 @@ import net.fabricmc.loom.task.service.TinyRemapperService; import net.fabricmc.loom.util.Constants; import net.fabricmc.loom.util.FileSystemUtil; import net.fabricmc.loom.util.LfWriter; +import net.fabricmc.loom.util.ModPlatform; import net.fabricmc.loom.util.ZipUtils; import net.fabricmc.loom.util.aw2at.Aw2At; import net.fabricmc.loom.util.service.UnsafeWorkQueueHelper; @@ -194,7 +200,7 @@ public abstract class RemapJarTask extends AbstractRemapJarTask { throw new RuntimeException("Forge must have useLegacyMixinAp enabled"); } - params.getForge().set(extension.isForge()); + params.getPlatform().set(extension.getPlatform()); if (getInjectAccessWidener().get() && extension.getAccessWidenerPath().isPresent()) { params.getInjectAccessWidener().set(extension.getAccessWidenerPath()); @@ -212,14 +218,46 @@ public abstract class RemapJarTask extends AbstractRemapJarTask { private void setupLegacyMixinRefmapRemapping(RemapParams params) { final LoomGradleExtension extension = LoomGradleExtension.get(getProject()); final MixinExtension mixinExtension = extension.getMixin(); - final Collection allMixinConfigs; + Collection allMixinConfigs = null; - final JsonObject fabricModJson = MixinRefmapHelper.readFabricModJson(getInputFile().getAsFile().get()); + final JsonObject fabricModJson = extension.getPlatform().get() == ModPlatform.FABRIC ? MixinRefmapHelper.readFabricModJson(getInputFile().getAsFile().get()) : null; if (fabricModJson == null) { - if (getReadMixinConfigsFromManifest().get()) { + if (extension.getPlatform().get() == ModPlatform.QUILT) { + try { + 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(); + } + } + } catch (IOException e) { + throw new RuntimeException("Cannot read file quilt.mod.json in the jar.", e); + } + } + + if (allMixinConfigs == null && getReadMixinConfigsFromManifest().get()) { allMixinConfigs = readMixinConfigsFromManifest(); } else { + if (extension.getPlatform().get() == ModPlatform.QUILT) { + getProject().getLogger().warn("Could not find quilt.mod.json file in: " + getInputFile().getAsFile().get().getName()); + return; + } + getProject().getLogger().warn("Could not find fabric.mod.json file in: " + getInputFile().getAsFile().get().getName()); return; } @@ -292,7 +330,7 @@ public abstract class RemapJarTask extends AbstractRemapJarTask { ConfigurableFileCollection getNestedJars(); ConfigurableFileCollection getRemapClasspath(); - Property getForge(); + Property getPlatform(); RegularFileProperty getInjectAccessWidener(); @@ -335,7 +373,7 @@ public abstract class RemapJarTask extends AbstractRemapJarTask { addNestedJars(); convertAwToAt(); - if (!getParameters().getForge().get()) { + if (getParameters().getPlatform().get() != ModPlatform.FORGE) { modifyJarManifest(); } @@ -369,6 +407,14 @@ public abstract class RemapJarTask extends AbstractRemapJarTask { ZipUtils.add(outputFile, path.getFileName().toString(), remapped); + if (getParameters().getPlatform().get() == ModPlatform.QUILT) { + ZipUtils.transformJson(JsonObject.class, outputFile, Map.of("quilt.mod.json", json -> { + json.addProperty("access_widener", path.getFileName().toString()); + return json; + })); + return true; + } + ZipUtils.transformJson(JsonObject.class, outputFile, Map.of("fabric.mod.json", json -> { json.addProperty("accessWidener", path.getFileName().toString()); return json; @@ -463,7 +509,7 @@ public abstract class RemapJarTask extends AbstractRemapJarTask { return; } - JarNester.nestJars(nestedJars.getFiles(), outputFile.toFile(), LOGGER); + JarNester.nestJars(nestedJars.getFiles(), outputFile.toFile(), getParameters().getPlatform().get(), LOGGER); } private void modifyJarManifest() throws IOException { diff --git a/src/main/java/net/fabricmc/loom/task/launch/GenerateDLIConfigTask.java b/src/main/java/net/fabricmc/loom/task/launch/GenerateDLIConfigTask.java index dae61c4a..28fdac41 100644 --- a/src/main/java/net/fabricmc/loom/task/launch/GenerateDLIConfigTask.java +++ b/src/main/java/net/fabricmc/loom/task/launch/GenerateDLIConfigTask.java @@ -50,8 +50,8 @@ public abstract class GenerateDLIConfigTask extends AbstractLoomTask { final String nativesPath = getExtension().getFiles().getNativesDirectory(getProject()).getAbsolutePath(); final LaunchConfig launchConfig = new LaunchConfig() - .property("fabric.development", "true") - .property("fabric.remapClasspathFile", getExtension().getFiles().getRemapClasspathFile().getAbsolutePath()) + .property(!getExtension().isQuilt() ? "fabric.development" : "loader.development", "true") + .property(!getExtension().isQuilt() ? "fabric.remapClasspathFile" : "loader.remapClasspathFile", getExtension().getFiles().getRemapClasspathFile().getAbsolutePath()) .property("log4j.configurationFile", getAllLog4JConfigFiles()) .property("log4j2.formatMsgNoLookups", "true") @@ -65,6 +65,12 @@ public abstract class GenerateDLIConfigTask extends AbstractLoomTask { .argument("client", "--assetsDir") .argument("client", new File(getExtension().getFiles().getUserCache(), "assets").getAbsolutePath()); } + + if (getExtension().isQuilt()) { + launchConfig + .argument("client", "--version") + .argument("client", "Architectury Loom"); + } if (getExtension().isForge()) { launchConfig diff --git a/src/main/java/net/fabricmc/loom/util/ModPlatform.java b/src/main/java/net/fabricmc/loom/util/ModPlatform.java index d1112d09..856ff465 100644 --- a/src/main/java/net/fabricmc/loom/util/ModPlatform.java +++ b/src/main/java/net/fabricmc/loom/util/ModPlatform.java @@ -34,8 +34,19 @@ import net.fabricmc.loom.LoomGradleExtension; import net.fabricmc.loom.api.LoomGradleExtensionAPI; public enum ModPlatform { - FABRIC, - FORGE; + FABRIC(false), + FORGE(false), + QUILT(true); + + boolean experimental; + + ModPlatform(boolean experimental) { + this.experimental = experimental; + } + + public boolean isExperimental() { + return experimental; + } public static void assertPlatform(Project project, ModPlatform platform) { assertPlatform(LoomGradleExtension.get(project), platform); diff --git a/src/main/java/net/fabricmc/loom/util/ModUtils.java b/src/main/java/net/fabricmc/loom/util/ModUtils.java index 9342c1ca..c24fd9c9 100644 --- a/src/main/java/net/fabricmc/loom/util/ModUtils.java +++ b/src/main/java/net/fabricmc/loom/util/ModUtils.java @@ -32,16 +32,21 @@ public final class ModUtils { private ModUtils() { } - public static boolean isMod(File input) { + public static boolean isMod(File input, ModPlatform platform) { + if (platform == ModPlatform.FORGE) { + return ZipUtils.contains(input.toPath(), "META-INF/mods.toml"); + } else if (platform == ModPlatform.QUILT) { + return ZipUtils.contains(input.toPath(), "quilt.mod.json"); + } + return ZipUtils.contains(input.toPath(), "fabric.mod.json"); } - public static boolean shouldRemapMod(Logger logger, File input, Object id, boolean forge, String config) { + public static boolean shouldRemapMod(Logger logger, File input, Object id, ModPlatform platform, String config) { if (ZipUtils.contains(input.toPath(), "architectury.common.marker")) return true; - if (forge && ZipUtils.contains(input.toPath(), "META-INF/mods.toml")) return true; - if (!forge && isMod(input)) return true; + if (isMod(input, platform)) return true; - if (forge) { + if (platform == ModPlatform.FORGE) { logger.lifecycle(":could not find forge mod in " + config + " but forcing: {}", id); return true; } diff --git a/src/main/java/net/fabricmc/loom/util/ZipUtils.java b/src/main/java/net/fabricmc/loom/util/ZipUtils.java index c4a35a3a..f3a875f1 100644 --- a/src/main/java/net/fabricmc/loom/util/ZipUtils.java +++ b/src/main/java/net/fabricmc/loom/util/ZipUtils.java @@ -237,7 +237,10 @@ public class ZipUtils { while (iterator.hasNext()) { Pair> next = iterator.next(); - map.put(next.left(), next.right()); + + if (next != null) { + map.put(next.left(), next.right()); + } } return map;