From 32eb0bd3c85de2538a2872c88afd1a9d7b3987d9 Mon Sep 17 00:00:00 2001 From: modmuss50 Date: Sat, 27 Jun 2020 20:18:32 +0100 Subject: [PATCH] First pass on "shareCaches". --- .../net/fabricmc/loom/AbstractPlugin.java | 54 ++++++- .../fabricmc/loom/LoomGradleExtension.java | 21 +++ .../loom/providers/MinecraftProvider.java | 8 + .../net/fabricmc/loom/task/RemapJarTask.java | 88 +++++++++++ .../loom/task/RemapSourcesJarTask.java | 27 +++- .../loom/task/shared/RemapAllJarsTask.java | 30 ++++ .../loom/task/shared/RemapAllSourcesTask.java | 32 ++++ .../net/fabricmc/loom/util/JarRemapper.java | 141 ++++++++++++++++++ .../AccessWidenerJarProcessor.java | 20 ++- 9 files changed, 411 insertions(+), 10 deletions(-) create mode 100644 src/main/java/net/fabricmc/loom/task/shared/RemapAllJarsTask.java create mode 100644 src/main/java/net/fabricmc/loom/task/shared/RemapAllSourcesTask.java create mode 100644 src/main/java/net/fabricmc/loom/util/JarRemapper.java diff --git a/src/main/java/net/fabricmc/loom/AbstractPlugin.java b/src/main/java/net/fabricmc/loom/AbstractPlugin.java index e0641a0..8c2944b 100644 --- a/src/main/java/net/fabricmc/loom/AbstractPlugin.java +++ b/src/main/java/net/fabricmc/loom/AbstractPlugin.java @@ -24,6 +24,7 @@ package net.fabricmc.loom; +import java.io.IOException; import java.util.HashSet; import java.util.Map; import java.util.Optional; @@ -54,6 +55,8 @@ import net.fabricmc.loom.providers.MinecraftProvider; import net.fabricmc.loom.providers.MappingsCache; import net.fabricmc.loom.task.RemapJarTask; import net.fabricmc.loom.task.RemapSourcesJarTask; +import net.fabricmc.loom.task.shared.RemapAllJarsTask; +import net.fabricmc.loom.task.shared.RemapAllSourcesTask; import net.fabricmc.loom.util.Constants; import net.fabricmc.loom.util.GroovyXmlUtil; import net.fabricmc.loom.util.LoomDependencyManager; @@ -64,7 +67,9 @@ import net.fabricmc.loom.util.mixin.JavaApInvoker; import net.fabricmc.loom.util.mixin.KaptApInvoker; import net.fabricmc.loom.util.mixin.ScalaApInvoker; import net.fabricmc.loom.util.FabricApiExtension; +import net.fabricmc.loom.util.SourceRemapper; import net.fabricmc.loom.util.DownloadUtil; +import net.fabricmc.loom.util.JarRemapper; public class AbstractPlugin implements Plugin { protected Project project; @@ -268,6 +273,48 @@ public class AbstractPlugin implements Plugin { } } + SourceRemapper remapper = null; + Task parentTask = project1.getTasks().getByName("build"); + + if (extension.isShareCaches()) { + Project rootProject = project.getRootProject(); + + if (extension.isRootProject()) { + SourceRemapper sourceRemapper = new SourceRemapper(rootProject, false); + JarRemapper jarRemapper = new JarRemapper(); + + remapJarTask.jarRemapper = jarRemapper; + + rootProject.getTasks().register("remapAllSources", RemapAllSourcesTask.class, task -> { + task.sourceRemapper = sourceRemapper; + task.doLast(t -> sourceRemapper.remapAll()); + }); + + parentTask = rootProject.getTasks().getByName("remapAllSources"); + + rootProject.getTasks().register("remapAllJars", RemapAllJarsTask.class, task -> { + task.doLast(t -> { + try { + jarRemapper.remap(); + } catch (IOException e) { + throw new RuntimeException("Failed to remap jars", e); + } + }); + }); + + for (Project subProject : rootProject.getAllprojects()) { + subProject.getTasks().getByName("build").dependsOn(parentTask); + subProject.getTasks().getByName("build").dependsOn(rootProject.getTasks().getByName("remapAllJars")); + rootProject.getTasks().getByName("remapAllJars").dependsOn(subProject.getTasks().getByName("remapJar")); + } + } else { + parentTask = rootProject.getTasks().getByName("remapAllSources"); + remapper = ((RemapAllSourcesTask) parentTask).sourceRemapper; + + remapJarTask.jarRemapper = ((RemapJarTask) rootProject.getTasks().getByName("remapJar")).jarRemapper; + } + } + try { AbstractArchiveTask sourcesTask = (AbstractArchiveTask) project1.getTasks().getByName("sourcesJar"); @@ -276,7 +323,12 @@ public class AbstractPlugin implements Plugin { remapSourcesJarTask.setOutput(sourcesTask.getArchivePath()); remapSourcesJarTask.doLast(task -> project1.getArtifacts().add("archives", remapSourcesJarTask.getOutput())); remapSourcesJarTask.dependsOn(project1.getTasks().getByName("sourcesJar")); - project1.getTasks().getByName("build").dependsOn(remapSourcesJarTask); + + if (extension.isShareCaches()) { + remapSourcesJarTask.setSourceRemapper(remapper); + } + + parentTask.dependsOn(remapSourcesJarTask); } catch (UnknownTaskException e) { // pass } diff --git a/src/main/java/net/fabricmc/loom/LoomGradleExtension.java b/src/main/java/net/fabricmc/loom/LoomGradleExtension.java index f44755d..4281ec8 100644 --- a/src/main/java/net/fabricmc/loom/LoomGradleExtension.java +++ b/src/main/java/net/fabricmc/loom/LoomGradleExtension.java @@ -61,6 +61,7 @@ public class LoomGradleExtension { public String customManifest = null; public File accessWidener = null; public Function intermediaryUrl = mcVer -> "https://maven.fabricmc.net/net/fabricmc/intermediary/" + mcVer + "/intermediary-" + mcVer + "-v2.jar"; + public boolean shareCaches = false; private List unmappedModsBuilt = new ArrayList<>(); @@ -338,4 +339,24 @@ public class LoomGradleExtension { //Done like this to work around this possibly not being a java string... return s -> intermediaryUrl.apply(s).toString(); } + + public boolean isRootProject() { + return project.getRootProject() == project; + } + + public LoomGradleExtension getRootGradleExtension() { + if (isRootProject()) { + return this; + } + + return project.getRootProject().getExtensions().getByType(LoomGradleExtension.class); + } + + public LoomGradleExtension getSharedGradleExtension() { + return isShareCaches() ? getRootGradleExtension() : this; + } + + public boolean isShareCaches() { + return shareCaches; + } } diff --git a/src/main/java/net/fabricmc/loom/providers/MinecraftProvider.java b/src/main/java/net/fabricmc/loom/providers/MinecraftProvider.java index 4deed46..5b07fc9 100644 --- a/src/main/java/net/fabricmc/loom/providers/MinecraftProvider.java +++ b/src/main/java/net/fabricmc/loom/providers/MinecraftProvider.java @@ -120,6 +120,10 @@ public class MinecraftProvider extends DependencyProvider { private void downloadMcJson(boolean offline) throws IOException { File manifests = new File(getExtension().getUserCache(), "version_manifest.json"); + if (getExtension().isShareCaches() && !getExtension().isRootProject() && manifests.exists() && !isRefreshDeps()) { + return; + } + if (offline) { if (manifests.exists()) { //If there is the manifests already we'll presume that's good enough @@ -171,6 +175,10 @@ public class MinecraftProvider extends DependencyProvider { } private void downloadJars(Logger logger) throws IOException { + if (getExtension().isShareCaches() && !getExtension().isRootProject() && minecraftClientJar.exists() && minecraftServerJar.exists() && !isRefreshDeps()) { + return; + } + DownloadUtil.downloadIfChanged(new URL(versionInfo.downloads.get("client").url), minecraftClientJar, logger); DownloadUtil.downloadIfChanged(new URL(versionInfo.downloads.get("server").url), minecraftServerJar, logger); } diff --git a/src/main/java/net/fabricmc/loom/task/RemapJarTask.java b/src/main/java/net/fabricmc/loom/task/RemapJarTask.java index d668c70..a6ca6a4 100644 --- a/src/main/java/net/fabricmc/loom/task/RemapJarTask.java +++ b/src/main/java/net/fabricmc/loom/task/RemapJarTask.java @@ -26,6 +26,7 @@ package net.fabricmc.loom.task; import java.io.File; import java.io.FileNotFoundException; +import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.util.LinkedHashSet; @@ -38,6 +39,7 @@ import org.gradle.api.tasks.Input; import org.gradle.api.tasks.InputFile; import org.gradle.api.tasks.TaskAction; import org.gradle.jvm.tasks.Jar; +import org.zeroturnaround.zip.ZipUtil; import net.fabricmc.loom.LoomGradleExtension; import net.fabricmc.loom.providers.MappingsProvider; @@ -46,6 +48,8 @@ import net.fabricmc.loom.util.MixinRefmapHelper; import net.fabricmc.loom.util.NestedJars; import net.fabricmc.loom.util.TinyRemapperMappingsHelper; import net.fabricmc.loom.util.accesswidener.AccessWidenerJarProcessor; +import net.fabricmc.loom.util.JarRemapper; +import net.fabricmc.stitch.util.Pair; import net.fabricmc.tinyremapper.OutputConsumerPath; import net.fabricmc.tinyremapper.TinyRemapper; import net.fabricmc.tinyremapper.TinyUtils; @@ -54,6 +58,7 @@ public class RemapJarTask extends Jar { private RegularFileProperty input; private Property addNestedDependencies; private Property remapAccessWidener; + public JarRemapper jarRemapper; public RemapJarTask() { super(); @@ -66,6 +71,14 @@ public class RemapJarTask extends Jar { @TaskAction public void doTask() throws Throwable { + if (jarRemapper == null) { + doSingleRemap(); + } else { + scheduleRemap(); + } + } + + public void doSingleRemap() throws Throwable { Project project = getProject(); LoomGradleExtension extension = project.getExtensions().getByType(LoomGradleExtension.class); Path input = this.getInput().getAsFile().get().toPath(); @@ -150,6 +163,81 @@ public class RemapJarTask extends Jar { }*/ } + public void scheduleRemap() throws Throwable { + Project project = getProject(); + LoomGradleExtension extension = project.getExtensions().getByType(LoomGradleExtension.class); + Path input = this.getInput().getAsFile().get().toPath(); + Path output = this.getArchivePath().toPath(); + + if (!Files.exists(input)) { + throw new FileNotFoundException(input.toString()); + } + + MappingsProvider mappingsProvider = extension.getMappingsProvider(); + + String fromM = "named"; + String toM = "intermediary"; + + if (extension.isRootProject()) { + Set classpathFiles = new LinkedHashSet<>( + project.getConfigurations().getByName("compileClasspath").getFiles() + ); + + Path[] classpath = classpathFiles.stream() + .map(File::toPath) + .filter(Files::exists) + .toArray(Path[]::new); + + jarRemapper.addToClasspath(classpath); + + jarRemapper.addMappings(TinyRemapperMappingsHelper.create(mappingsProvider.getMappings(), fromM, toM, false)); + } + + File mixinMapFile = mappingsProvider.mappingsMixinExport; + Path mixinMapPath = mixinMapFile.toPath(); + + if (mixinMapFile.exists()) { + jarRemapper.addMappings(TinyUtils.createTinyMappingProvider(mixinMapPath, fromM, toM)); + } + + jarRemapper.scheduleRemap(input, output) + .supplyAccessWidener((remapData, remapper) -> { + if (getRemapAccessWidener().getOrElse(false) && extension.accessWidener != null) { + AccessWidenerJarProcessor accessWidenerJarProcessor = extension.getJarProcessorManager().getByType(AccessWidenerJarProcessor.class); + byte[] data; + + try { + data = accessWidenerJarProcessor.getRemappedAccessWidener(remapper); + } catch (IOException e) { + throw new RuntimeException("Failed to remap access widener"); + } + + return Pair.of(accessWidenerJarProcessor.getAccessWidenerPath(remapData.output), data); + } + + return null; + }) + .complete((data, accessWidener) -> { + if (!Files.exists(output)) { + throw new RuntimeException("Failed to remap " + input + " to " + output + " - file missing!"); + } + + if (MixinRefmapHelper.addRefmapName(extension.getRefmapName(), extension.getMixinJsonVersion(), output)) { + project.getLogger().debug("Transformed mixin reference maps in output JAR!"); + } + + if (getAddNestedDependencies().getOrElse(false)) { + if (NestedJars.addNestedJars(project, output)) { + project.getLogger().debug("Added nested jar paths to mod json"); + } + } + + if (accessWidener != null) { + ZipUtil.replaceEntry(data.output.toFile(), accessWidener.getLeft(), accessWidener.getRight()); + } + }); + } + @InputFile public RegularFileProperty getInput() { return input; diff --git a/src/main/java/net/fabricmc/loom/task/RemapSourcesJarTask.java b/src/main/java/net/fabricmc/loom/task/RemapSourcesJarTask.java index 9a4b8e3..c82c622 100644 --- a/src/main/java/net/fabricmc/loom/task/RemapSourcesJarTask.java +++ b/src/main/java/net/fabricmc/loom/task/RemapSourcesJarTask.java @@ -28,6 +28,7 @@ import java.io.File; import org.gradle.api.tasks.Input; import org.gradle.api.tasks.InputFile; +import org.gradle.api.tasks.Internal; import org.gradle.api.tasks.OutputFile; import org.gradle.api.tasks.TaskAction; @@ -37,10 +38,34 @@ public class RemapSourcesJarTask extends AbstractLoomTask { private Object input; private Object output; private String direction = "intermediary"; + private SourceRemapper sourceRemapper = null; @TaskAction public void remap() throws Exception { - SourceRemapper.remapSources(getProject(), getInput(), getOutput(), direction.equals("named")); + if (sourceRemapper == null) { + SourceRemapper.remapSources(getProject(), getInput(), getOutput(), direction.equals("named")); + } else { + sourceRemapper.scheduleRemapSources(getInput(), getOutput()); + } + } + + public String getDirection() { + return direction; + } + + public RemapSourcesJarTask setDirection(String direction) { + this.direction = direction; + return this; + } + + @Internal + public SourceRemapper getSourceRemapper() { + return sourceRemapper; + } + + public RemapSourcesJarTask setSourceRemapper(SourceRemapper sourceRemapper) { + this.sourceRemapper = sourceRemapper; + return this; } @InputFile diff --git a/src/main/java/net/fabricmc/loom/task/shared/RemapAllJarsTask.java b/src/main/java/net/fabricmc/loom/task/shared/RemapAllJarsTask.java new file mode 100644 index 0000000..36904fa --- /dev/null +++ b/src/main/java/net/fabricmc/loom/task/shared/RemapAllJarsTask.java @@ -0,0 +1,30 @@ +/* + * This file is part of fabric-loom, licensed under the MIT License (MIT). + * + * Copyright (c) 2016, 2017, 2018 FabricMC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.fabricmc.loom.task.shared; + +import net.fabricmc.loom.task.AbstractLoomTask; + +public class RemapAllJarsTask extends AbstractLoomTask { +} diff --git a/src/main/java/net/fabricmc/loom/task/shared/RemapAllSourcesTask.java b/src/main/java/net/fabricmc/loom/task/shared/RemapAllSourcesTask.java new file mode 100644 index 0000000..d674870 --- /dev/null +++ b/src/main/java/net/fabricmc/loom/task/shared/RemapAllSourcesTask.java @@ -0,0 +1,32 @@ +/* + * This file is part of fabric-loom, licensed under the MIT License (MIT). + * + * Copyright (c) 2016, 2017, 2018 FabricMC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.fabricmc.loom.task.shared; + +import net.fabricmc.loom.task.AbstractLoomTask; +import net.fabricmc.loom.util.SourceRemapper; + +public class RemapAllSourcesTask extends AbstractLoomTask { + public SourceRemapper sourceRemapper; +} diff --git a/src/main/java/net/fabricmc/loom/util/JarRemapper.java b/src/main/java/net/fabricmc/loom/util/JarRemapper.java new file mode 100644 index 0000000..bd070c5 --- /dev/null +++ b/src/main/java/net/fabricmc/loom/util/JarRemapper.java @@ -0,0 +1,141 @@ +/* + * This file is part of fabric-loom, licensed under the MIT License (MIT). + * + * Copyright (c) 2016, 2017, 2018 FabricMC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.fabricmc.loom.util; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.function.BiConsumer; +import java.util.function.BiFunction; + +import org.objectweb.asm.commons.Remapper; + +import net.fabricmc.stitch.util.Pair; +import net.fabricmc.tinyremapper.IMappingProvider; +import net.fabricmc.tinyremapper.InputTag; +import net.fabricmc.tinyremapper.OutputConsumerPath; +import net.fabricmc.tinyremapper.TinyRemapper; + +public class JarRemapper { + private final List mappingProviders = new ArrayList<>(); + private final Set classPath = new HashSet<>(); + private final List remapData = new ArrayList<>(); + + public void addMappings(IMappingProvider mappingProvider) { + mappingProviders.add(mappingProvider); + } + + public void addToClasspath(Path... paths) { + classPath.addAll(Arrays.asList(paths)); + } + + public RemapData scheduleRemap(Path input, Path output) { + RemapData data = new RemapData(input, output); + remapData.add(data); + return data; + } + + public void remap() throws IOException { + TinyRemapper.Builder remapperBuilder = TinyRemapper.newRemapper(); + mappingProviders.forEach(remapperBuilder::withMappings); + + TinyRemapper remapper = remapperBuilder.build(); + + Path[] remapClasspath = classPath.stream() + .filter(path -> + remapData.stream().noneMatch(remapData -> remapData.input.equals(path)) + ) + .toArray(Path[]::new); + + remapper.readClassPathAsync(remapClasspath); + + for (RemapData data : remapData) { + InputTag tag = remapper.createInputTag(); + data.tag = tag; + remapper.readInputsAsync(tag, data.input); + } + + List outputConsumers = new ArrayList<>(); + + for (RemapData data : remapData) { + OutputConsumerPath outputConsumer = new OutputConsumerPath.Builder(data.output).build(); + outputConsumers.add(outputConsumer); + + outputConsumer.addNonClassFiles(data.input); + + data.processAccessWidener(remapper.getRemapper()); + remapper.apply(outputConsumer, data.tag); + } + + remapper.finish(); + + for (OutputConsumerPath outputConsumer : outputConsumers) { + outputConsumer.close(); + } + + remapData.forEach(RemapData::complete); + } + + public static class RemapData { + public final Path input; + public final Path output; + BiFunction> accesWidenerSupplier; + BiConsumer> onComplete; + + private InputTag tag; + private Pair accessWidener; + + public RemapData(Path input, Path output) { + this.input = input; + this.output = output; + } + + public RemapData complete(BiConsumer> onComplete) { + this.onComplete = onComplete; + return this; + } + + public RemapData supplyAccessWidener(BiFunction> beforeFinish) { + this.accesWidenerSupplier = beforeFinish; + return this; + } + + private void complete() { + if (onComplete != null) { + onComplete.accept(this, accessWidener); + } + } + + private void processAccessWidener(Remapper remapper) { + if (accesWidenerSupplier != null) { + accessWidener = accesWidenerSupplier.apply(this, remapper); + } + } + } +} diff --git a/src/main/java/net/fabricmc/loom/util/accesswidener/AccessWidenerJarProcessor.java b/src/main/java/net/fabricmc/loom/util/accesswidener/AccessWidenerJarProcessor.java index 7f1af33..785928f 100644 --- a/src/main/java/net/fabricmc/loom/util/accesswidener/AccessWidenerJarProcessor.java +++ b/src/main/java/net/fabricmc/loom/util/accesswidener/AccessWidenerJarProcessor.java @@ -124,13 +124,7 @@ public class AccessWidenerJarProcessor implements JarProcessor { //Called when remapping the mod public void remapAccessWidener(Path modJarPath, Remapper asmRemapper) throws IOException { - AccessWidenerRemapper remapper = new AccessWidenerRemapper(accessWidener, asmRemapper, "intermediary"); - AccessWidener remapped = remapper.remap(); - - StringWriter writer = new StringWriter(); - remapped.write(writer); - byte[] bytes = writer.toString().getBytes(); - writer.close(); + byte[] bytes = getRemappedAccessWidener(asmRemapper); String path = getAccessWidenerPath(modJarPath); @@ -145,7 +139,17 @@ public class AccessWidenerJarProcessor implements JarProcessor { } } - private String getAccessWidenerPath(Path modJarPath) { + public byte[] getRemappedAccessWidener(Remapper asmRemapper) throws IOException { + AccessWidenerRemapper remapper = new AccessWidenerRemapper(accessWidener, asmRemapper, "intermediary"); + AccessWidener remapped = remapper.remap(); + + try (StringWriter writer = new StringWriter()) { + remapped.write(writer); + return writer.toString().getBytes(); + } + } + + public String getAccessWidenerPath(Path modJarPath) { byte[] modJsonBytes = ZipUtil.unpackEntry(modJarPath.toFile(), "fabric.mod.json"); if (modJsonBytes == null) {