WIP AT -> JarProcessor

This commit is contained in:
shedaniel
2021-04-04 20:19:18 +08:00
parent 6a25a40ff9
commit 4fe9b81e32
20 changed files with 706 additions and 228 deletions

View File

@@ -26,15 +26,24 @@ package net.fabricmc.loom.api.decompilers;
import java.nio.file.Path;
import java.util.Collection;
import java.util.function.Function;
import java.util.function.Predicate;
import org.jetbrains.annotations.Nullable;
import net.fabricmc.loom.task.GenerateSourcesTask;
public class DecompilationMetadata {
public final int numberOfThreads;
public final Path javaDocs;
public final Collection<Path> libraries;
@Nullable
public final Predicate<String> classFilter;
public DecompilationMetadata(int numberOfThreads, Path javaDocs, Collection<Path> libraries) {
public DecompilationMetadata(int numberOfThreads, Path javaDocs, Collection<Path> libraries, Function<String, GenerateSourcesTask.SkipState> classFilter) {
this.numberOfThreads = numberOfThreads;
this.javaDocs = javaDocs;
this.libraries = libraries;
this.classFilter = s -> GenerateSourcesTask.SkipState.SKIP != classFilter.apply(s);
}
}

View File

@@ -0,0 +1,232 @@
package net.fabricmc.loom.configuration.accesstransformer;
import java.io.File;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.lang.reflect.Field;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import me.shedaniel.architectury.refmapremapper.remapper.SimpleReferenceRemapper;
import net.minecraftforge.accesstransformer.AccessTransformerEngine;
import net.minecraftforge.accesstransformer.TransformerProcessor;
import net.minecraftforge.accesstransformer.parser.AccessTransformerList;
import org.apache.commons.io.FileUtils;
import org.apache.logging.log4j.util.Strings;
import org.gradle.api.Project;
import org.gradle.api.plugins.JavaPluginConvention;
import org.gradle.api.tasks.SourceSet;
import org.jetbrains.annotations.Nullable;
import org.zeroturnaround.zip.ZipUtil;
import net.fabricmc.loom.LoomGradleExtension;
import net.fabricmc.loom.configuration.processors.JarProcessor;
import net.fabricmc.loom.configuration.providers.mappings.MappingsProvider;
import net.fabricmc.loom.util.Checksum;
import net.fabricmc.loom.util.JarUtil;
import net.fabricmc.loom.util.srg.AtRemapper;
import net.fabricmc.mapping.tree.TinyTree;
public class AccessTransformerJarProcessor implements JarProcessor {
private final Path projectAt;
private final Project project;
private final Set<String> affectedClasses = new HashSet<>();
private Path remappedProjectAt;
public AccessTransformerJarProcessor(Path projectAt, Project project) {
this.projectAt = projectAt;
this.project = project;
}
public static void addTo(Project project, LoomGradleExtension extension) {
SourceSet main = project.getConvention().findPlugin(JavaPluginConvention.class).getSourceSets().getByName("main");
for (File srcDir : main.getResources().getSrcDirs()) {
File projectAt = new File(srcDir, "META-INF/accesstransformer.cfg");
if (projectAt.exists()) {
extension.addJarProcessor(new AccessTransformerJarProcessor(projectAt.toPath(), project));
return;
}
}
}
@Override
public void setup() {
affectedClasses.clear();
remappedProjectAt = null;
getRemappedAt();
}
@Override
public void process(File file) {
try {
accessTransformForge(file, file, false, getRemappedAt());
} catch (Exception e) {
throw new RuntimeException(e);
}
}
@Override
public boolean isInvalid(File file) {
byte[] hash = ZipUtil.unpackEntry(file, "loom-at.sha256");
if (hash == null) {
return true;
}
return !Arrays.equals(getInputHash(false, getRemappedAt()), hash);
}
@Override
public boolean doesProcessClass(String classInternalName) {
getRemappedAt();
return affectedClasses.contains(classInternalName);
}
private File getRemappedAt() {
if (remappedProjectAt == null) {
try {
LoomGradleExtension extension = project.getExtensions().getByType(LoomGradleExtension.class);
MappingsProvider mappingsProvider = extension.getMappingsProvider();
TinyTree mappings = extension.isForge() ? mappingsProvider.getMappingsWithSrg() : mappingsProvider.getMappings();
File file = File.createTempFile("at-remapped-", ".at");
file.delete();
String sourceAt = FileUtils.readFileToString(projectAt.toFile(), StandardCharsets.UTF_8);
String remappedAt = AtRemapper.remapAt(project.getLogger(), sourceAt, new SimpleReferenceRemapper.Remapper() {
@Override
@Nullable
public String mapClass(String value) {
return mappings.getClasses().stream()
.filter(classDef -> Objects.equals(classDef.getName("srg"), value))
.findFirst()
.map(classDef -> classDef.getName("named"))
.orElse(null);
}
@Override
@Nullable
public String mapMethod(@Nullable String className, String methodName, String methodDescriptor) {
return mappings.getClasses().stream()
.flatMap(classDef -> classDef.getMethods().stream())
.filter(methodDef -> Objects.equals(methodDef.getName("srg"), methodName))
.filter(methodDef -> Objects.equals(methodDef.getDescriptor("srg"), methodDescriptor))
.findFirst()
.map(classDef -> classDef.getName("named"))
.orElse(null);
}
@Override
@Nullable
public String mapField(@Nullable String className, String fieldName, String fieldDescriptor) {
return mappings.getClasses().stream()
.flatMap(classDef -> classDef.getFields().stream())
.filter(methodDef -> Objects.equals(methodDef.getName("srg"), fieldName))
.findFirst()
.map(classDef -> classDef.getName("named"))
.orElse(null);
}
});
for (String line : remappedAt.split("\n")) {
{
int indexOf = line.indexOf('#');
if (indexOf != -1) {
line = line.substring(0, indexOf);
}
line = line.trim();
}
if (!Strings.isBlank(line)) {
affectedClasses.add(line.split("\\s+")[1].replace('.', '/'));
}
}
FileUtils.write(file, remappedAt, StandardCharsets.UTF_8);
file.deleteOnExit();
return (remappedProjectAt = file.toPath()).toFile();
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
return remappedProjectAt.toFile();
}
public static byte[] getInputHash(boolean addEmbeddedAt, File... extraAts) {
byte[] inputHash = new byte[] {(byte) (addEmbeddedAt ? 1 : 0)};
for (File extraAt : extraAts) {
inputHash = concat(inputHash, Checksum.sha256(extraAt));
}
return inputHash;
}
public static byte[] concat(byte[] a, byte[] b) {
byte[] out = new byte[a.length + b.length];
System.arraycopy(a, 0, out, 0, a.length);
System.arraycopy(b, 0, out, a.length, b.length);
return out;
}
public static void accessTransformForge(File input, File target, boolean addEmbeddedAt, File... extraAts) throws Exception {
byte[] inputHash = getInputHash(addEmbeddedAt, extraAts);
File inputCopied = File.createTempFile("at-input-", ".jar");
File at = File.createTempFile("at-", ".cfg");
FileUtils.copyFile(input, inputCopied);
if (target.exists()) {
byte[] hash = ZipUtil.unpackEntry(target, "loom-at.sha256");
if (hash != null && Arrays.equals(inputHash, hash)) {
return;
}
}
target.delete();
String[] args = new String[] {
"--inJar", inputCopied.getAbsolutePath(),
"--outJar", target.getAbsolutePath()
};
if (addEmbeddedAt) {
JarUtil.extractFile(inputCopied, "META-INF/accesstransformer.cfg", at);
args = Arrays.copyOf(args, args.length + 2);
args[args.length - 2] = "--atFile";
args[args.length - 1] = at.getAbsolutePath();
}
for (File extraAt : extraAts) {
args = Arrays.copyOf(args, args.length + 2);
args[args.length - 2] = "--atFile";
args[args.length - 1] = extraAt.getAbsolutePath();
}
resetAccessTransformerEngine();
TransformerProcessor.main(args);
inputCopied.delete();
at.delete();
resetAccessTransformerEngine();
ZipUtil.addEntry(target, "loom-at.sha256", inputHash);
}
private static void resetAccessTransformerEngine() throws Exception {
// Thank you Forge, I love you
Field field = AccessTransformerEngine.class.getDeclaredField("masterList");
field.setAccessible(true);
AccessTransformerList list = (AccessTransformerList) field.get(AccessTransformerEngine.INSTANCE);
field = AccessTransformerList.class.getDeclaredField("accessTransformers");
field.setAccessible(true);
((Map<?, ?>) field.get(list)).clear();
}
}

View File

@@ -117,6 +117,11 @@ public class AccessWidenerJarProcessor implements JarProcessor {
ZipUtil.addEntry(file, "aw.sha256", inputHash);
}
@Override
public boolean doesProcessClass(String classInternalName) {
return accessWidener.getTargets().contains(classInternalName.replace('/', '.'));
}
private ZipEntryTransformerEntry[] getTransformers(Set<String> classes) {
return classes.stream()
.map(string -> new ZipEntryTransformerEntry(string.replaceAll("\\.", "/") + ".class", getTransformer(string)))

View File

@@ -211,7 +211,7 @@ public class ModProcessor {
}
if (extension.isForge()) {
AtRemapper.remap(project.getLogger(), info.getRemappedOutput().toPath(), mappings);
AtRemapper.remapSrgToNamed(project.getLogger(), info.getRemappedOutput().toPath(), mappings);
CoreModClassRemapper.remapJar(info.getRemappedOutput().toPath(), mappings, project.getLogger());
if (ZipUtil.containsEntry(info.getRemappedOutput(), "META-INF/MANIFEST.MF")) {

View File

@@ -38,4 +38,8 @@ public interface JarProcessor {
* Return true to make all jar processors run again, return false to use the existing results of jar processing.
*/
boolean isInvalid(File file);
default boolean doesProcessClass(String classInternalName) {
return false;
}
}

View File

@@ -50,6 +50,10 @@ public class JarProcessorManager {
return jarProcessors.stream().anyMatch(jarProcessor -> jarProcessor.isInvalid(file));
}
public boolean doesProcessClass(String classInternalName) {
return jarProcessors.stream().anyMatch(jarProcessor -> jarProcessor.doesProcessClass(classInternalName));
}
public void process(File file) {
for (JarProcessor jarProcessor : jarProcessors) {
jarProcessor.process(file);

View File

@@ -50,9 +50,7 @@ public class MinecraftProcessedProvider extends MinecraftMappedProvider {
@Override
protected void addDependencies(DependencyInfo dependency, Consumer<Runnable> postPopulationScheduler) {
boolean isForgeAtDirty = getExtension().isForge() && getExtension().getMappingsProvider().patchedProvider.isAtDirty();
if (jarProcessorManager.isInvalid(projectMappedJar) || isRefreshDeps() || isForgeAtDirty) {
if (jarProcessorManager.isInvalid(projectMappedJar) || isRefreshDeps()) {
getProject().getLogger().info(":processing mapped jar");
invalidateJars();

View File

@@ -26,15 +26,12 @@ package net.fabricmc.loom.configuration.providers.forge;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.io.UncheckedIOException;
import java.lang.reflect.Field;
import java.net.URI;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
@@ -44,7 +41,7 @@ import java.nio.file.StandardCopyOption;
import java.util.Arrays;
import java.util.Collections;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
@@ -54,18 +51,13 @@ import com.google.common.base.Predicates;
import com.google.common.collect.ImmutableMap;
import com.google.gson.JsonParser;
import de.oceanlabs.mcp.mcinjector.adaptors.ParameterAnnotationFixer;
import net.minecraftforge.accesstransformer.AccessTransformerEngine;
import net.minecraftforge.accesstransformer.TransformerProcessor;
import net.minecraftforge.accesstransformer.parser.AccessTransformerList;
import net.minecraftforge.binarypatcher.ConsoleTool;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.io.output.NullOutputStream;
import org.gradle.api.Project;
import org.gradle.api.artifacts.Dependency;
import org.gradle.api.logging.Logger;
import org.gradle.api.plugins.JavaPluginConvention;
import org.gradle.api.tasks.SourceSet;
import org.jetbrains.annotations.Nullable;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
@@ -73,14 +65,12 @@ import org.objectweb.asm.tree.ClassNode;
import org.zeroturnaround.zip.ZipUtil;
import net.fabricmc.loom.configuration.DependencyProvider;
import net.fabricmc.loom.configuration.accesstransformer.AccessTransformerJarProcessor;
import net.fabricmc.loom.configuration.providers.MinecraftProvider;
import net.fabricmc.loom.configuration.providers.mappings.MappingsProvider;
import net.fabricmc.loom.configuration.providers.minecraft.MinecraftMappedProvider;
import net.fabricmc.loom.util.Checksum;
import net.fabricmc.loom.util.Constants;
import net.fabricmc.loom.util.DownloadUtil;
import net.fabricmc.loom.util.FileSystemUtil;
import net.fabricmc.loom.util.JarUtil;
import net.fabricmc.loom.util.ThreadingUtils;
import net.fabricmc.loom.util.TinyRemapperMappingsHelper;
import net.fabricmc.loom.util.function.FsPathConsumer;
@@ -98,18 +88,11 @@ public class MinecraftPatchedProvider extends DependencyProvider {
// Step 2: Binary Patch
private File minecraftClientPatchedSrgJar;
private File minecraftServerPatchedSrgJar;
// Step 3: Access Transform
private File minecraftClientPatchedSrgATJar;
private File minecraftServerPatchedSrgATJar;
// Step 4: Remap Patched AT to Official
// Step 3: Remap Patched Jar to Official
private File minecraftClientPatchedOfficialJar;
private File minecraftServerPatchedOfficialJar;
// Step 5: Merge
// Step 4: Merge
private File minecraftMergedPatchedJar;
private File projectAtHash;
@Nullable
private File projectAt = null;
private boolean atDirty = false;
public MinecraftPatchedProvider(MappingsProvider mappingsProvider, Project project) {
super(project);
@@ -117,34 +100,6 @@ public class MinecraftPatchedProvider extends DependencyProvider {
}
public void initFiles() throws IOException {
projectAtHash = new File(getExtension().getProjectPersistentCache(), "at.sha256");
SourceSet main = getProject().getConvention().findPlugin(JavaPluginConvention.class).getSourceSets().getByName("main");
for (File srcDir : main.getResources().getSrcDirs()) {
File projectAt = new File(srcDir, "META-INF/accesstransformer.cfg");
if (projectAt.exists()) {
this.projectAt = projectAt;
break;
}
}
if (isRefreshDeps() || !projectAtHash.exists()) {
writeAtHash();
atDirty = projectAt != null;
} else {
byte[] expected = com.google.common.io.Files.asByteSource(projectAtHash).read();
byte[] current = projectAt != null ? Checksum.sha256(projectAt) : Checksum.sha256("");
boolean mismatched = !Arrays.equals(current, expected);
if (mismatched) {
writeAtHash();
}
atDirty = mismatched;
}
MinecraftProvider minecraftProvider = getExtension().getMinecraftProvider();
PatchProvider patchProvider = getExtension().getPatchProvider();
String minecraftVersion = minecraftProvider.getMinecraftVersion();
@@ -157,21 +112,18 @@ public class MinecraftPatchedProvider extends DependencyProvider {
minecraftProvider.setJarSuffix(jarSuffix);
File globalCache = getExtension().getUserCache();
File cache = usesProjectCache() ? getExtension().getProjectPersistentCache() : globalCache;
minecraftClientSrgJar = new File(globalCache, "minecraft-" + minecraftVersion + "-client-srg.jar");
minecraftServerSrgJar = new File(globalCache, "minecraft-" + minecraftVersion + "-server-srg.jar");
minecraftClientPatchedSrgJar = new File(globalCache, "minecraft-" + minecraftVersion + "-client-srg" + jarSuffix + ".jar");
minecraftServerPatchedSrgJar = new File(globalCache, "minecraft-" + minecraftVersion + "-server-srg" + jarSuffix + ".jar");
minecraftClientPatchedSrgATJar = new File(cache, "minecraft-" + minecraftVersion + "-client-srg-at" + jarSuffix + ".jar");
minecraftServerPatchedSrgATJar = new File(cache, "minecraft-" + minecraftVersion + "-server-srg-at" + jarSuffix + ".jar");
minecraftClientPatchedOfficialJar = new File(cache, "minecraft-" + minecraftVersion + "-client" + jarSuffix + ".jar");
minecraftServerPatchedOfficialJar = new File(cache, "minecraft-" + minecraftVersion + "-server" + jarSuffix + ".jar");
minecraftMergedPatchedJar = new File(cache, "minecraft-" + minecraftVersion + "-merged" + jarSuffix + ".jar");
minecraftClientPatchedSrgJar = new File(globalCache, "minecraft-" + minecraftVersion + "-client-srg-patched" + jarSuffix + ".jar");
minecraftServerPatchedSrgJar = new File(globalCache, "minecraft-" + minecraftVersion + "-server-srg-patched" + jarSuffix + ".jar");
minecraftClientPatchedOfficialJar = new File(globalCache, "minecraft-" + minecraftVersion + "-client-patched" + jarSuffix + ".jar");
minecraftServerPatchedOfficialJar = new File(globalCache, "minecraft-" + minecraftVersion + "-server-patched" + jarSuffix + ".jar");
minecraftMergedPatchedJar = new File(globalCache, "minecraft-" + minecraftVersion + "-merged-patched" + jarSuffix + ".jar");
if (isRefreshDeps() || Stream.of(getGlobalCaches()).anyMatch(Predicates.not(File::exists))) {
cleanAllCache();
} else if (atDirty || Stream.of(getProjectCache()).anyMatch(Predicates.not(File::exists))) {
} else if (Stream.of(getProjectCache()).anyMatch(Predicates.not(File::exists))) {
cleanProjectCache();
}
}
@@ -201,8 +153,6 @@ public class MinecraftPatchedProvider extends DependencyProvider {
private File[] getProjectCache() {
return new File[] {
minecraftClientPatchedSrgATJar,
minecraftServerPatchedSrgATJar,
minecraftClientPatchedOfficialJar,
minecraftServerPatchedOfficialJar,
minecraftMergedPatchedJar
@@ -213,10 +163,6 @@ public class MinecraftPatchedProvider extends DependencyProvider {
public void provide(DependencyInfo dependency, Consumer<Runnable> postPopulationScheduler) throws Exception {
initFiles();
if (atDirty) {
getProject().getLogger().lifecycle(":found dirty access transformers");
}
boolean dirty = false;
if (!minecraftClientSrgJar.exists() || !minecraftServerSrgJar.exists()) {
@@ -229,10 +175,6 @@ public class MinecraftPatchedProvider extends DependencyProvider {
dirty = true;
patchJars(getProject().getLogger());
injectForgeClasses(getProject().getLogger());
}
if (atDirty || !minecraftClientPatchedSrgATJar.exists() || !minecraftServerPatchedSrgATJar.exists()) {
dirty = true;
accessTransformForge(getProject().getLogger());
}
@@ -245,16 +187,6 @@ public class MinecraftPatchedProvider extends DependencyProvider {
}
}
private void writeAtHash() throws IOException {
try (FileOutputStream out = new FileOutputStream(projectAtHash)) {
if (projectAt != null) {
out.write(Checksum.sha256(projectAt));
} else {
out.write(Checksum.sha256(""));
}
}
}
private void createSrgJars(Logger logger) throws Exception {
McpConfigProvider mcpProvider = getExtension().getMcpConfigProvider();
@@ -280,8 +212,10 @@ public class MinecraftPatchedProvider extends DependencyProvider {
throw new IllegalStateException("Failed to find mappings '" + mappingsPath[0] + "' in " + mcpProvider.getMcp().getAbsolutePath() + "!");
}
File specialSourceJar = new File(getExtension().getUserCache(), "SpecialSource-1.8.3-shaded.jar");
DownloadUtil.downloadIfChanged(new URL("https://repo1.maven.org/maven2/net/md-5/SpecialSource/1.8.3/SpecialSource-1.8.3-shaded.jar"), specialSourceJar, getProject().getLogger(), true);
Dependency dependency = getProject().getDependencies().create("net.md-5:SpecialSource:1.8.3:shaded");
Set<File> specialSourceJars = getProject().getConfigurations().detachedConfiguration(dependency).resolve();
if (specialSourceJars.isEmpty()) throw new IllegalStateException("Failed to resolve net.md-5:SpecialSource:1.8.3:shaded");
File specialSourceJar = specialSourceJars.iterator().next();
ThreadingUtils.run(() -> {
Files.copy(SpecialSourceExecutor.produceSrgJar(getProject(), mappingsProvider, "client", specialSourceJar, minecraftProvider.minecraftClientJar.toPath(), tmpSrg[0]), minecraftClientSrgJar.toPath());
@@ -348,65 +282,30 @@ public class MinecraftPatchedProvider extends DependencyProvider {
String side = environment.side();
logger.lifecycle(":access transforming minecraft (" + side + ")");
File input = environment.patchedSrgJar.apply(this);
File inputCopied = File.createTempFile("at" + side, ".jar");
FileUtils.copyFile(input, inputCopied);
File target = environment.patchedSrgATJar.apply(this);
target.delete();
File at = File.createTempFile("at" + side, ".cfg");
JarUtil.extractFile(inputCopied, "META-INF/accesstransformer.cfg", at);
String[] args = new String[] {
"--inJar", inputCopied.getAbsolutePath(),
"--outJar", target.getAbsolutePath(),
"--atFile", at.getAbsolutePath()
};
if (usesProjectCache()) {
args = Arrays.copyOf(args, args.length + 2);
args[args.length - 2] = "--atFile";
args[args.length - 1] = projectAt.getAbsolutePath();
}
resetAccessTransformerEngine();
TransformerProcessor.main(args);
inputCopied.delete();
File file = environment.patchedSrgJar.apply(this);
AccessTransformerJarProcessor.accessTransformForge(file, file, true);
}
}
private void resetAccessTransformerEngine() throws Exception {
// Thank you Forge, I love you
Field field = AccessTransformerEngine.class.getDeclaredField("masterList");
field.setAccessible(true);
AccessTransformerList list = (AccessTransformerList) field.get(AccessTransformerEngine.INSTANCE);
field = AccessTransformerList.class.getDeclaredField("accessTransformers");
field.setAccessible(true);
((Map<?, ?>) field.get(list)).clear();
}
private enum Environment {
CLIENT(provider -> provider.minecraftClientSrgJar,
provider -> provider.minecraftClientPatchedSrgJar,
provider -> provider.minecraftClientPatchedSrgATJar,
provider -> provider.minecraftClientPatchedOfficialJar
),
SERVER(provider -> provider.minecraftServerSrgJar,
provider -> provider.minecraftServerPatchedSrgJar,
provider -> provider.minecraftServerPatchedSrgATJar,
provider -> provider.minecraftServerPatchedOfficialJar
);
final Function<MinecraftPatchedProvider, File> srgJar;
final Function<MinecraftPatchedProvider, File> patchedSrgJar;
final Function<MinecraftPatchedProvider, File> patchedSrgATJar;
final Function<MinecraftPatchedProvider, File> patchedOfficialJar;
Environment(Function<MinecraftPatchedProvider, File> srgJar,
Function<MinecraftPatchedProvider, File> patchedSrgJar,
Function<MinecraftPatchedProvider, File> patchedSrgATJar,
Function<MinecraftPatchedProvider, File> patchedOfficialJar) {
this.srgJar = srgJar;
this.patchedSrgJar = patchedSrgJar;
this.patchedSrgATJar = patchedSrgATJar;
this.patchedOfficialJar = patchedOfficialJar;
}
@@ -422,7 +321,7 @@ public class MinecraftPatchedProvider extends DependencyProvider {
logger.lifecycle(":remapping minecraft (TinyRemapper, " + environment.side() + ", srg -> official)");
TinyTree mappingsWithSrg = getExtension().getMappingsProvider().getMappingsWithSrg();
Path input = environment.patchedSrgATJar.apply(this).toPath();
Path input = environment.patchedSrgJar.apply(this).toPath();
Path output = environment.patchedOfficialJar.apply(this).toPath();
Files.deleteIfExists(output);
@@ -570,14 +469,6 @@ public class MinecraftPatchedProvider extends DependencyProvider {
return minecraftMergedPatchedJar;
}
public boolean usesProjectCache() {
return projectAt != null;
}
public boolean isAtDirty() {
return atDirty;
}
@Override
public String getTargetConfig() {
return Constants.Configurations.MINECRAFT;

View File

@@ -47,6 +47,8 @@ import org.apache.commons.io.FileUtils;
import org.apache.tools.ant.util.StringUtils;
import org.gradle.api.Project;
import org.gradle.api.artifacts.Configuration;
import org.gradle.api.plugins.JavaPluginConvention;
import org.gradle.api.tasks.SourceSet;
import org.zeroturnaround.zip.FileSource;
import org.zeroturnaround.zip.ZipEntrySource;
import org.zeroturnaround.zip.ZipUtil;
@@ -54,6 +56,7 @@ import org.zeroturnaround.zip.ZipUtil;
import net.fabricmc.loom.LoomGradleExtension;
import net.fabricmc.loom.LoomGradlePlugin;
import net.fabricmc.loom.configuration.DependencyProvider;
import net.fabricmc.loom.configuration.accesstransformer.AccessTransformerJarProcessor;
import net.fabricmc.loom.configuration.accesswidener.AccessWidenerJarProcessor;
import net.fabricmc.loom.configuration.processors.JarProcessorManager;
import net.fabricmc.loom.configuration.processors.MinecraftProcessedProvider;
@@ -240,6 +243,10 @@ public class MappingsProvider extends DependencyProvider {
extension.addJarProcessor(new AccessWidenerJarProcessor(getProject()));
}
if (extension.isForge()) {
AccessTransformerJarProcessor.addTo(getProject(), extension);
}
JarProcessorManager processorManager = new JarProcessorManager(extension.getJarProcessors());
extension.setJarProcessorManager(processorManager);
processorManager.setupProcessors();
@@ -249,7 +256,7 @@ public class MappingsProvider extends DependencyProvider {
patchedProvider.provide(dependency, postPopulationScheduler);
}
if (processorManager.active() || (extension.isForge() && patchedProvider.usesProjectCache())) {
if (processorManager.active()) {
mappedProvider = new MinecraftProcessedProvider(getProject(), processorManager);
getProject().getLogger().lifecycle("Using project based jar storage");
} else {

View File

@@ -89,9 +89,7 @@ public class MinecraftMappedProvider extends DependencyProvider {
throw new RuntimeException("input merged jar not found");
}
boolean isForgeAtDirty = getExtension().isForge() && getExtension().getMappingsProvider().patchedProvider.isAtDirty();
if (!minecraftMappedJar.exists() || !getIntermediaryJar().exists() || (getExtension().isForge() && !getSrgJar().exists()) || isRefreshDeps() || isForgeAtDirty) {
if (!minecraftMappedJar.exists() || !getIntermediaryJar().exists() || (getExtension().isForge() && !getSrgJar().exists()) || isRefreshDeps()) {
if (minecraftMappedJar.exists()) {
minecraftMappedJar.delete();
}
@@ -199,7 +197,7 @@ public class MinecraftMappedProvider extends DependencyProvider {
}
TinyTree yarnWithSrg = getExtension().getMappingsProvider().getMappingsWithSrg();
AtRemapper.remap(getProject().getLogger(), output, yarnWithSrg);
AtRemapper.remapSrgToNamed(getProject().getLogger(), output, yarnWithSrg);
CoreModClassRemapper.remapJar(output, yarnWithSrg, getProject().getLogger());
}
}
@@ -281,6 +279,10 @@ public class MinecraftMappedProvider extends DependencyProvider {
return minecraftMappedJar;
}
public final File getUnprocessedMappedJar() {
return minecraftMappedJar;
}
public File getUnpickedJar() {
return new File(getJarDirectory(getExtension().getUserCache(), "mapped"), "minecraft-" + getJarVersionString("unpicked") + ".jar");
}

View File

@@ -177,6 +177,22 @@ public class FabricCFRDecompiler implements LoomDecompiler {
List<Future<?>> futures = new LinkedList<>();
for (String clazz : classes) {
if (metaData.classFilter != null) {
String classInternalName = clazz;
if (classInternalName.length() >= 1 && classInternalName.charAt(0) == '/') {
classInternalName = classInternalName.substring(1);
}
classInternalName = classInternalName.substring(0, classInternalName.length() - 6);
if (!metaData.classFilter.test(classInternalName)) {
return;
} else {
System.out.println(classInternalName);
}
}
futures.add(executorService.submit(() -> {
loggerMap.computeIfAbsent(Thread.currentThread().getId(), createLogger).progress(clazz);
driver.analyse(Collections.singletonList(clazz));

View File

@@ -26,13 +26,25 @@ package net.fabricmc.loom.decompilers.fernflower;
import static java.text.MessageFormat.format;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.net.URI;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Stack;
import java.util.function.Supplier;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import org.gradle.api.Project;
import org.gradle.api.internal.project.ProjectInternal;
@@ -77,7 +89,7 @@ public abstract class AbstractFernFlowerDecompiler implements LoomDecompiler {
List<String> args = new ArrayList<>();
options.forEach((k, v) -> args.add(format("-{0}={1}", k, v)));
args.add(absolutePathOf(compiledJar));
args.add(absolutePathOf(transformBinary(compiledJar, metaData)));
args.add("-o=" + absolutePathOf(sourcesDestination));
args.add("-l=" + absolutePathOf(linemapDestination));
args.add("-m=" + absolutePathOf(metaData.javaDocs));
@@ -150,6 +162,52 @@ public abstract class AbstractFernFlowerDecompiler implements LoomDecompiler {
result.assertNormalExitValue();
}
private Path transformBinary(Path compiledJar, DecompilationMetadata metaData) {
if (metaData.classFilter == null) return compiledJar;
try {
Path tempFile = Files.createTempFile("loom-", null);
Files.deleteIfExists(tempFile);
tempFile.toFile().deleteOnExit();
try (ZipOutputStream tempZipOut = new ZipOutputStream(new FileOutputStream(tempFile.toFile()))) {
try (FileSystem compiledJarSystem = FileSystems.newFileSystem(URI.create("jar:" + compiledJar.toUri()), new HashMap<>())) {
Files.walkFileTree(compiledJarSystem.getPath("/"), new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
if (file.toString().endsWith(".class")) {
String sourcePath = file.toString();
if (sourcePath.length() >= 1 && sourcePath.charAt(0) == '/') {
sourcePath = sourcePath.substring(1);
}
sourcePath = sourcePath.substring(0, sourcePath.length() - 6);
if (!metaData.classFilter.test(sourcePath)) {
return FileVisitResult.CONTINUE;
} else {
System.out.println(sourcePath);
}
}
String resourcePath = file.toString();
tempZipOut.putNextEntry(new ZipEntry(resourcePath));
tempZipOut.write(Files.readAllBytes(file));
tempZipOut.closeEntry();
return FileVisitResult.CONTINUE;
}
});
}
}
return tempFile;
} catch (IOException exception) {
throw new UncheckedIOException(exception);
}
}
private static String absolutePathOf(Path path) {
return path.toAbsolutePath().toString();
}

View File

@@ -28,10 +28,11 @@ import org.gradle.api.DefaultTask;
import org.gradle.api.tasks.Internal;
import net.fabricmc.loom.LoomGradleExtension;
import net.fabricmc.loom.util.Constants;
public abstract class AbstractLoomTask extends DefaultTask {
public AbstractLoomTask() {
setGroup("fabric");
setGroup(Constants.TASK_CATEGORY);
}
@Internal

View File

@@ -35,13 +35,14 @@ import org.gradle.api.Project;
import org.gradle.api.tasks.JavaExec;
import net.fabricmc.loom.configuration.ide.RunConfig;
import net.fabricmc.loom.util.Constants;
public abstract class AbstractRunTask extends JavaExec {
private final RunConfig config;
public AbstractRunTask(Function<Project, RunConfig> configProvider) {
super();
setGroup("fabric");
setGroup(Constants.TASK_CATEGORY);
this.config = configProvider.apply(getProject());
setClasspath(config.sourceSet.getRuntimeClasspath());

View File

@@ -0,0 +1,15 @@
package net.fabricmc.loom.task;
import static net.fabricmc.loom.task.GenerateSourcesTask.getMappedJarFileWithSuffix;
import java.nio.file.Files;
import org.gradle.api.tasks.TaskAction;
public class CleanSourcesTask extends AbstractLoomTask {
@TaskAction
public void doTask() throws Throwable {
Files.deleteIfExists(getMappedJarFileWithSuffix(getProject(), "-sources.jar", false).toPath());
Files.deleteIfExists(getMappedJarFileWithSuffix(getProject(), "-sources.jar", true).toPath());
}
}

View File

@@ -0,0 +1,128 @@
package net.fabricmc.loom.task;
import java.io.File;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.net.URI;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.StandardCopyOption;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.function.Consumer;
import javax.inject.Inject;
import org.gradle.api.tasks.TaskAction;
import net.fabricmc.loom.api.decompilers.LoomDecompiler;
import net.fabricmc.loom.configuration.processors.JarProcessorManager;
import net.fabricmc.loom.configuration.processors.MinecraftProcessedProvider;
import net.fabricmc.stitch.util.StitchUtil;
public class GenerateIncrementalSourcesTask extends AbstractLoomTask {
public final LoomDecompiler decompiler;
@Inject
public GenerateIncrementalSourcesTask(LoomDecompiler decompiler) {
this.decompiler = decompiler;
getOutputs().upToDateWhen((o) -> false);
}
@TaskAction
public void doTask() throws Throwable {
GenerateSourcesTask.generateSources(getProject(), getClass(), decompiler, null, false, true);
if (getExtension().getMappingsProvider().mappedProvider instanceof MinecraftProcessedProvider) {
JarProcessorManager processorManager = getExtension().getJarProcessorManager();
Path unprocessedCompiledJar = GenerateSourcesTask.getMappedJarFileWithSuffix(getProject(), null, false).toPath();
Path compiledJar = GenerateSourcesTask.getMappedJarFileWithSuffix(getProject(), null, true).toPath();
Path unprocessedSourcesDestination = GenerateSourcesTask.getMappedJarFileWithSuffix(getProject(), "-sources.jar", false).toPath();
Map<String, byte[]> unprocessedSources = new HashMap<>();
Set<String> affectedSources = new HashSet<>();
try (FileSystem system = FileSystems.newFileSystem(URI.create("jar:" + unprocessedSourcesDestination.toUri()), new HashMap<>())) {
Files.walkFileTree(system.getPath("/"), new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
if (file.toString().endsWith(".java")) {
String sourcePath = file.toString();
if (sourcePath.length() >= 1 && sourcePath.charAt(0) == '/') {
sourcePath = sourcePath.substring(1);
}
sourcePath = sourcePath.substring(0, sourcePath.length() - 5);
unprocessedSources.put(sourcePath, Files.readAllBytes(file));
if (processorManager.doesProcessClass(sourcePath)) {
affectedSources.add(sourcePath);
}
}
return FileVisitResult.CONTINUE;
}
});
} catch (IOException exception) {
exception.printStackTrace();
unprocessedSources.clear();
}
Consumer<Path> linemapConsumer = linemap -> {
try {
try (StitchUtil.FileSystemDelegate unprocessedFs = StitchUtil.getJarFileSystem(unprocessedCompiledJar.toFile(), true);
StitchUtil.FileSystemDelegate processedFs = StitchUtil.getJarFileSystem(compiledJar.toFile(), true)) {
int i = 0;
for (Path path : (Iterable<? extends Path>) Files.walk(processedFs.get().getPath("/"))::iterator) {
if (path.toString().endsWith(".class")) {
String sourcePath = path.toString();
if (sourcePath.length() >= 1 && sourcePath.charAt(0) == '/') {
sourcePath = sourcePath.substring(1);
}
sourcePath = sourcePath.substring(0, sourcePath.length() - 6);
Path unprocessedPath = unprocessedFs.get().getPath(path.toString());
if (Files.exists(unprocessedPath) && !processorManager.doesProcessClass(sourcePath)) {
i++;
Files.copy(unprocessedPath, path, StandardCopyOption.REPLACE_EXISTING);
}
}
}
getLogger().lifecycle(":copied {} linemap remapped classes from unprocessed jar", i);
}
GenerateSourcesTask.remapLinemap(getProject(), getClass(), compiledJar, linemap, true);
} catch (IOException exception) {
throw new UncheckedIOException(exception);
}
};
getLogger().lifecycle(":skipping {} unprocessed classes (excluding {} processed classes)", unprocessedSources.size() - affectedSources.size(), affectedSources.size());
GenerateSourcesTask.generateSources(getProject(), getClass(), decompiler, className -> {
int i = className.indexOf('$');
if (i >= 0) className = className.substring(0, i);
if (affectedSources.contains(className)) return GenerateSourcesTask.SkipState.GENERATE;
if (unprocessedSources.containsKey(className)) return GenerateSourcesTask.SkipState.SKIP;
return null;
}, true, true, linemapConsumer);
}
}
public GenerateIncrementalSourcesTask setInputJar(File inputJar) {
this.inputJar = inputJar;
return this;
}
}

View File

@@ -26,19 +26,34 @@ package net.fabricmc.loom.task;
import java.io.File;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.net.URI;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.StandardCopyOption;
import java.nio.file.StandardOpenOption;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.Collection;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;
import javax.inject.Inject;
import com.google.common.base.MoreObjects;
import org.gradle.api.Project;
import org.gradle.api.tasks.InputFile;
import org.gradle.api.tasks.TaskAction;
import net.fabricmc.loom.LoomGradleExtension;
import net.fabricmc.loom.LoomGradlePlugin;
import net.fabricmc.loom.api.decompilers.DecompilationMetadata;
import net.fabricmc.loom.api.decompilers.LoomDecompiler;
import net.fabricmc.loom.configuration.providers.mappings.MappingsProvider;
@@ -56,25 +71,100 @@ public class GenerateSourcesTask extends AbstractLoomTask {
public GenerateSourcesTask(LoomDecompiler decompiler) {
this.decompiler = decompiler;
setGroup("fabric");
getOutputs().upToDateWhen((o) -> false);
}
@TaskAction
public void doTask() throws Throwable {
int threads = Runtime.getRuntime().availableProcessors();
Path javaDocs = getExtension().getMappingsProvider().tinyMappings.toPath();
Collection<Path> libraries = getProject().getConfigurations().getByName(Constants.Configurations.MINECRAFT_DEPENDENCIES).getFiles()
.stream().map(File::toPath).collect(Collectors.toSet());
generateSources(getProject(), getClass(), decompiler, null, true, false);
}
DecompilationMetadata metadata = new DecompilationMetadata(threads, javaDocs, libraries);
Path runtimeJar = getExtension().getMappingsProvider().mappedProvider.getMappedJar().toPath();
Path sourcesDestination = getMappedJarFileWithSuffix("-sources.jar").toPath();
Path linemap = getMappedJarFileWithSuffix("-sources.lmap").toPath();
public static void generateSources(Project project, Class<?> taskClass, LoomDecompiler decompiler, Function<String, SkipState> additionalClassFilter, boolean processed, boolean incremental)
throws IOException {
generateSources(project, taskClass, decompiler, additionalClassFilter, processed, incremental, linemap -> {
try {
Path compiledJar = getMappedJarFileWithSuffix(project, null, processed).toPath();
remapLinemap(project, taskClass, compiledJar, linemap, processed);
} catch (IOException exception) {
throw new UncheckedIOException(exception);
}
});
}
public static void generateSources(Project project, Class<?> taskClass, LoomDecompiler decompiler, Function<String, SkipState> additionalClassFilter, boolean processed, boolean incremental, Consumer<Path> linemapConsumer)
throws IOException {
LoomGradleExtension extension = project.getExtensions().getByType(LoomGradleExtension.class);
int threads = Runtime.getRuntime().availableProcessors();
Path javaDocs = extension.getMappingsProvider().tinyMappings.toPath();
Collection<Path> libraries = project.getConfigurations().getByName(Constants.Configurations.MINECRAFT_DEPENDENCIES).getFiles()
.stream().map(File::toPath).collect(Collectors.toSet());
Path runtimeJar = getMappedJarFileWithSuffix(project, null, processed).toPath();
Path sourcesDestination = getMappedJarFileWithSuffix(project, "-sources.jar", processed).toPath();
Path linemap = getMappedJarFileWithSuffix(project, "-sources.lmap", processed).toPath();
Map<String, byte[]> remappedClasses = new HashMap<>();
if (!LoomGradlePlugin.refreshDeps && incremental && Files.exists(sourcesDestination)) {
try (FileSystem system = FileSystems.newFileSystem(URI.create("jar:" + sourcesDestination.toUri()), new HashMap<>())) {
Files.walkFileTree(system.getPath("/"), new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
if (file.toString().endsWith(".java")) {
String sourcePath = file.toString();
if (sourcePath.length() >= 1 && sourcePath.charAt(0) == '/') {
sourcePath = sourcePath.substring(1);
}
remappedClasses.put(sourcePath.substring(0, sourcePath.length() - 5), Files.readAllBytes(file));
}
return FileVisitResult.CONTINUE;
}
});
} catch (IOException exception) {
exception.printStackTrace();
remappedClasses.clear();
}
}
Files.deleteIfExists(sourcesDestination);
if (incremental) {
project.getLogger().lifecycle(":incremental source generation: skipping {} classes", remappedClasses.size());
}
Function<String, SkipState> function = MoreObjects.firstNonNull(additionalClassFilter, s -> null);
Function<String, SkipState> classFilter = remappedClasses.isEmpty() ? function : s -> {
SkipState apply = function.apply(s);
if (apply != null) return apply;
int i = s.indexOf('$');
if (i >= 0) s = s.substring(0, i);
return remappedClasses.containsKey(s) ? SkipState.SKIP : SkipState.GENERATE;
};
DecompilationMetadata metadata = new DecompilationMetadata(threads, javaDocs, libraries, classFilter);
decompiler.decompile(inputJar.toPath(), sourcesDestination, linemap, metadata);
if (incremental && !remappedClasses.isEmpty()) {
try (FileSystem system = FileSystems.newFileSystem(URI.create("jar:" + sourcesDestination.toUri()), new HashMap<String, String>() {
{
put("create", "true");
}
})) {
for (Map.Entry<String, byte[]> entry : remappedClasses.entrySet()) {
if (additionalClassFilter != null && SkipState.SKIP != additionalClassFilter.apply(entry.getKey())) continue;
Path path = system.getPath(entry.getKey() + ".java");
Path parent = path.getParent();
if (parent != null) Files.createDirectories(parent);
Files.write(path, entry.getValue(), StandardOpenOption.CREATE);
}
}
}
}
public static void remapLinemap(Project project, Class<?> taskClass, Path compiledJar, Path linemap, boolean processed) throws IOException {
if (Files.exists(linemap)) {
Path linemappedJarDestination = getMappedJarFileWithSuffix("-linemapped.jar").toPath();
Path linemappedJarDestination = getMappedJarFileWithSuffix(project, "-linemapped.jar", processed).toPath();
// Line map the actually jar used to run the game, not the one used to decompile
remapLineNumbers(runtimeJar, linemap, linemappedJarDestination);
@@ -84,12 +174,13 @@ public class GenerateSourcesTask extends AbstractLoomTask {
}
}
private void remapLineNumbers(Path oldCompiledJar, Path linemap, Path linemappedJarDestination) throws IOException {
getProject().getLogger().info(":adjusting line numbers");
private static void remapLineNumbers(Project project, Class<?> taskClass, Path oldCompiledJar, Path linemap, Path linemappedJarDestination)
throws IOException {
project.getLogger().info(":adjusting line numbers");
LineNumberRemapper remapper = new LineNumberRemapper();
remapper.readMappings(linemap.toFile());
ProgressLogger progressLogger = ProgressLogger.getProgressFactory(getProject(), getClass().getName());
ProgressLogger progressLogger = ProgressLogger.getProgressFactory(project, taskClass.getName());
progressLogger.start("Adjusting line numbers", "linemap");
try (StitchUtil.FileSystemDelegate inFs = StitchUtil.getJarFileSystem(oldCompiledJar.toFile(), true);
@@ -100,19 +191,27 @@ public class GenerateSourcesTask extends AbstractLoomTask {
progressLogger.completed();
}
private File getMappedJarFileWithSuffix(String suffix) {
LoomGradleExtension extension = getProject().getExtensions().getByType(LoomGradleExtension.class);
public static File getMappedJarFileWithSuffix(Project project, String suffix, boolean processed) {
LoomGradleExtension extension = project.getExtensions().getByType(LoomGradleExtension.class);
MappingsProvider mappingsProvider = extension.getMappingsProvider();
File mappedJar = mappingsProvider.mappedProvider.getMappedJar();
File mappedJar = processed ? mappingsProvider.mappedProvider.getMappedJar() : mappingsProvider.mappedProvider.getUnprocessedMappedJar();
String path = mappedJar.getAbsolutePath();
if (!path.toLowerCase(Locale.ROOT).endsWith(".jar")) {
throw new RuntimeException("Invalid mapped JAR path: " + path);
}
if (suffix == null) {
return new File(path);
}
return new File(path.substring(0, path.length() - 4) + suffix);
}
public enum SkipState {
SKIP, GENERATE;
}
@InputFile
public File getInputJar() {
return inputJar;

View File

@@ -35,6 +35,7 @@ import net.fabricmc.loom.api.decompilers.LoomDecompiler;
import net.fabricmc.loom.configuration.ide.RunConfigSettings;
import net.fabricmc.loom.configuration.providers.mappings.MappingsProvider;
import net.fabricmc.loom.decompilers.fernflower.FabricFernFlowerDecompiler;
import net.fabricmc.loom.util.Constants;
public final class LoomTasks {
private LoomTasks() {
@@ -50,7 +51,7 @@ public final class LoomTasks {
tasks.register("remapJar", RemapJarTask.class, t -> {
t.setDescription("Remaps the built project jar to intermediary mappings.");
t.setGroup("fabric");
t.setGroup(Constants.TASK_CATEGORY);
});
tasks.register("downloadAssets", DownloadAssetsTask.class, t -> t.setDescription("Downloads required assets for Fabric."));
@@ -59,6 +60,8 @@ public final class LoomTasks {
registerIDETasks(tasks);
registerRunTasks(tasks, project);
registerDecompileTasks(tasks, project);
tasks.register("cleanSources", CleanSourcesTask.class);
}
private static void registerIDETasks(TaskContainer tasks) {
@@ -97,7 +100,6 @@ public final class LoomTasks {
tasks.register(taskName, RunGameTask.class, config).configure(t -> {
t.setDescription("Starts the '" + config.getConfigName() + "' run configuration");
t.setGroup("fabric");
if (config.getEnvironment().equals("client")) {
t.dependsOn("downloadAssets");
@@ -130,6 +132,7 @@ public final class LoomTasks {
for (LoomDecompiler decompiler : extension.getDecompilers()) {
String taskName = decompiler instanceof FabricFernFlowerDecompiler ? "genSources" : "genSourcesWith" + decompiler.name();
String incrementalTaskName = decompiler instanceof FabricFernFlowerDecompiler ? "genIncrementalSources" : "genIncrementalSourcesWith" + decompiler.name();
// decompiler will be passed to the constructor of GenerateSourcesTask
GenerateSourcesTask generateSourcesTask = tasks.register(taskName, GenerateSourcesTask.class, decompiler).get();
generateSourcesTask.setInputJar(inputJar);
@@ -137,6 +140,13 @@ public final class LoomTasks {
if (mappingsProvider.hasUnpickDefinitions()) {
generateSourcesTask.dependsOn(tasks.getByName("unpickJar"));
}
GenerateIncrementalSourcesTask generateIncrementalSourcesTask = tasks.register(incrementalTaskName, GenerateIncrementalSourcesTask.class, decompiler).get();
generateIncrementalSourcesTask.setInputJar(inputJar);
if (mappingsProvider.hasUnpickDefinitions()) {
generateIncrementalSourcesTask.dependsOn(tasks.getByName("unpickJar"));
}
}
});
}

View File

@@ -35,6 +35,7 @@ import net.fabricmc.loom.util.gradle.GradleSupport;
public class Constants {
public static final String PLUGIN_ID = "forgified-fabric-loom";
public static final String TASK_CATEGORY = "loom";
public static final String LIBRARIES_BASE = "https://libraries.minecraft.net/";
public static final String RESOURCES_BASE = "http://resources.download.minecraft.net/";
public static final String VERSION_MANIFESTS = "https://launchermeta.mojang.com/mc/game/version_manifest_v2.json";

View File

@@ -24,111 +24,108 @@
package net.fabricmc.loom.util.srg;
import static me.shedaniel.architectury.refmapremapper.utils.DescriptorRemapper.remapDescriptor;
import java.io.IOException;
import java.io.StringReader;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.function.UnaryOperator;
import java.util.Objects;
import java.util.zip.ZipEntry;
import me.shedaniel.architectury.refmapremapper.remapper.SimpleReferenceRemapper;
import org.apache.logging.log4j.util.Strings;
import org.gradle.api.logging.Logger;
import org.jetbrains.annotations.Nullable;
import org.zeroturnaround.zip.ZipUtil;
import org.zeroturnaround.zip.transform.StringZipEntryTransformer;
import org.zeroturnaround.zip.transform.ZipEntryTransformerEntry;
import net.fabricmc.loom.util.function.CollectionUtil;
import net.fabricmc.mapping.tree.TinyTree;
/**
* Remaps AT classes from SRG to Yarn.
* Remaps AT contents.
*
* @author Juuz
*/
public final class AtRemapper {
public static void remap(Logger logger, Path jar, TinyTree mappings) throws IOException {
ZipUtil.transformEntries(jar.toFile(), new ZipEntryTransformerEntry[] {(new ZipEntryTransformerEntry("META-INF/accesstransformer.cfg", new StringZipEntryTransformer() {
public static void remapSrgToNamed(Logger logger, Path jar, TinyTree mappings) throws IOException {
ZipUtil.transformEntry(jar.toFile(), "META-INF/accesstransformer.cfg", new StringZipEntryTransformer() {
@Override
protected String transform(ZipEntry zipEntry, String input) {
String[] lines = input.split("\n");
List<String> output = new ArrayList<>(lines.length);
for (int i = 0; i < lines.length; i++) {
String line = lines[i].trim();
if (line.startsWith("#") || Strings.isBlank(line)) {
output.add(i, line);
continue;
return remapAt(logger, input, new SimpleReferenceRemapper.Remapper() {
@Override
@Nullable
public String mapClass(String value) {
return mappings.getClasses().stream()
.filter(classDef -> Objects.equals(classDef.getName("srg"), value))
.findFirst()
.map(classDef -> classDef.getName("named"))
.orElse(null);
}
String[] parts = line.split("\\s+");
if (parts.length < 2) {
logger.warn("Invalid AT Line: " + line);
output.add(i, line);
continue;
@Override
@Nullable
public String mapMethod(@Nullable String className, String methodName, String methodDescriptor) {
return null;
}
String name = parts[1].replace('.', '/');
parts[1] = CollectionUtil.find(
mappings.getClasses(),
def -> def.getName("srg").equals(name)
).map(def -> def.getName("named")).orElse(name).replace('/', '.');
if (parts.length >= 3) {
if (parts[2].contains("(")) {
parts[2] = parts[2].substring(0, parts[2].indexOf('(')) + remapDescriptor(parts[2].substring(parts[2].indexOf('(')), s -> {
return CollectionUtil.find(
mappings.getClasses(),
def -> def.getName("srg").equals(s)
).map(def -> def.getName("named")).orElse(s);
});
}
@Override
@Nullable
public String mapField(@Nullable String className, String fieldName, String fieldDescriptor) {
return null;
}
output.add(i, String.join(" ", parts));
}
return String.join("\n", output);
});
}
}))});
});
}
private static String remapDescriptor(String original, UnaryOperator<String> classMappings) {
try {
StringReader reader = new StringReader(original);
StringBuilder result = new StringBuilder();
boolean insideClassName = false;
StringBuilder className = new StringBuilder();
public static String remapAt(Logger logger, String sourceAt, SimpleReferenceRemapper.Remapper remapper) {
String[] lines = sourceAt.split("\n");
StringBuilder builder = new StringBuilder();
while (true) {
int c = reader.read();
for (String line : lines) {
{
int indexOf = line.indexOf('#');
if (c == -1) {
break;
if (indexOf != -1) {
line = line.substring(0, indexOf);
}
if ((char) c == ';') {
insideClassName = false;
result.append(classMappings.apply(className.toString()));
}
line = line.trim();
}
if (insideClassName) {
className.append((char) c);
if (Strings.isBlank(line)) {
builder.append(line).append('\n');
continue;
}
String[] parts = line.split("\\s+");
if (parts.length < 2) {
logger.warn("Invalid AT Line: " + line);
builder.append(line).append('\n');
continue;
}
String originalClassName = parts[1].replace('.', '/');
parts[1] = either(remapper.mapClass(originalClassName), parts[1]).replace('/', '.');
if (parts.length >= 3) {
if (parts[2].contains("(")) {
String methodName = parts[2].substring(0, parts[2].indexOf('('));
String methodDescriptor = parts[2].substring(parts[2].indexOf('('));
parts[2] = either(remapper.mapMethod(originalClassName, methodName, methodDescriptor), methodName)
+ remapDescriptor(methodDescriptor, it -> either(remapper.mapClass(it), it));
} else {
result.append((char) c);
}
if (!insideClassName && (char) c == 'L') {
insideClassName = true;
className.setLength(0);
parts[2] = either(remapper.mapField(originalClassName, parts[2], null), parts[2]);
}
}
return result.toString();
} catch (IOException e) {
throw new AssertionError(e);
builder.append(String.join(" ", parts)).append('\n');
}
return builder.toString();
}
private static <T> T either(@Nullable T first, @Nullable T second) {
return first == null ? second : first;
}
}