Skip to content

Commit

Permalink
Workaround inner classes decompilation on Forge
Browse files Browse the repository at this point in the history
  • Loading branch information
shedaniel committed Apr 4, 2024
1 parent 377c0e9 commit deaee2a
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,15 @@
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.BiConsumer;
import java.util.function.Predicate;
import java.util.stream.Collectors;

import dev.architectury.loom.util.MappingOption;
import org.apache.commons.io.output.NullOutputStream;
import org.cadixdev.lorenz.MappingSet;
import org.cadixdev.mercury.Mercury;
import org.cadixdev.mercury.remapper.MercuryRemapper;
import org.jetbrains.annotations.Nullable;
import org.gradle.api.Project;

import net.fabricmc.loom.LoomGradleExtension;
Expand Down Expand Up @@ -84,17 +86,27 @@ public static void addBaseForgeSources(Project project) throws IOException {

if (!Files.exists(sourcesJar)) {
try (var serviceManager = new ScopedSharedServiceManager()) {
addForgeSources(project, serviceManager, sourcesJar);
addForgeSources(project, serviceManager, minecraftJar, sourcesJar);
}
}
}

public static void addForgeSources(Project project, SharedServiceManager serviceManager, Path sourcesJar) throws IOException {
try (FileSystemUtil.Delegate delegate = FileSystemUtil.getJarFileSystem(sourcesJar, true)) {
public static void addForgeSources(Project project, SharedServiceManager serviceManager, @Nullable Path inputJar, Path sourcesJar) throws IOException {
try (FileSystemUtil.Delegate inputFs = inputJar == null ? null : FileSystemUtil.getJarFileSystem(inputJar, true);
FileSystemUtil.Delegate outputFs = FileSystemUtil.getJarFileSystem(sourcesJar, true)) {
ThreadingUtils.TaskCompleter taskCompleter = ThreadingUtils.taskCompleter();

provideForgeSources(project, serviceManager, (path, bytes) -> {
Path fsPath = delegate.get().getPath(path);
provideForgeSources(project, serviceManager, path -> {
Path inputPath = inputFs == null ? null : inputFs.get().getPath(path.replace(".java", ".class"));

if (inputPath != null && Files.notExists(inputPath)) {
project.getLogger().info("Discarding forge source file {} as it does not exist in the input jar", path);
return false;
}

return !path.contains("$");
}, (path, bytes) -> {
Path fsPath = outputFs.get().getPath(path);

if (fsPath.getParent() != null) {
try {
Expand All @@ -105,6 +117,7 @@ public static void addForgeSources(Project project, SharedServiceManager service
}

taskCompleter.add(() -> {
project.getLogger().info("Added forge source file {}", path);
Files.write(fsPath, bytes, StandardOpenOption.CREATE);
});
});
Expand All @@ -113,17 +126,19 @@ public static void addForgeSources(Project project, SharedServiceManager service
}
}

public static void provideForgeSources(Project project, SharedServiceManager serviceManager, BiConsumer<String, byte[]> consumer) throws IOException {
public static void provideForgeSources(Project project, SharedServiceManager serviceManager, Predicate<String> classFilter, BiConsumer<String, byte[]> consumer) throws IOException {
LoomGradleExtension extension = LoomGradleExtension.get(project);
String sourceDependency = extension.getForgeUserdevProvider().getConfig().sources();
List<Path> forgeInstallerSources = new ArrayList<>();

for (File file : DependencyDownloader.download(project, sourceDependency)) {
forgeInstallerSources.add(file.toPath());
project.getLogger().info("Found forge source jar: {}", file);
}

project.getLogger().lifecycle(":found {} forge source jars", forgeInstallerSources.size());
Map<String, byte[]> forgeSources = extractSources(forgeInstallerSources);
forgeSources.keySet().removeIf(classFilter.negate());
project.getLogger().lifecycle(":extracted {} forge source classes", forgeSources.size());
remapSources(project, serviceManager, forgeSources);
forgeSources.forEach(consumer);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ public void run() throws IOException {
// Step 3: remap
remap(patched, serviceManager);
// Step 4: add Forge's own sources
ForgeSourcesRemapper.addForgeSources(getProject(), serviceManager, getOutputJar().get().getAsFile().toPath());
ForgeSourcesRemapper.addForgeSources(getProject(), serviceManager, null, getOutputJar().get().getAsFile().toPath());
}
}

Expand Down
38 changes: 36 additions & 2 deletions src/main/java/net/fabricmc/loom/task/GenerateSourcesTask.java
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,15 @@
import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.StringJoiner;
import java.util.UUID;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import javax.inject.Inject;

Expand Down Expand Up @@ -255,6 +257,7 @@ private void runWithCache(Path cacheRoot) throws IOException {

try (var timer = new Timer("Decompile")) {
outputLineNumbers = runDecompileJob(inputJar, workToDoJob.output(), existing);
removeForgeInnerClassSources(workToDoJob.output());
outputLineNumbers = filterForgeLineNumbers(outputLineNumbers);
}

Expand Down Expand Up @@ -316,6 +319,7 @@ private void runWithoutCache() throws IOException {

try (var timer = new Timer("Decompile")) {
lineNumbers = runDecompileJob(inputJar, sourcesJar, null);
removeForgeInnerClassSources(sourcesJar);
lineNumbers = filterForgeLineNumbers(lineNumbers);
}

Expand Down Expand Up @@ -393,7 +397,7 @@ private ClassLineNumbers runDecompileJob(Path inputJar, Path outputJar, @Nullabl
// Inject Forge's own sources
if (getExtension().isForgeLike()) {
try (var serviceManager = new ScopedSharedServiceManager()) {
ForgeSourcesRemapper.addForgeSources(getProject(), serviceManager, outputJar);
ForgeSourcesRemapper.addForgeSources(getProject(), serviceManager, inputJar, outputJar);
}
}

Expand All @@ -416,7 +420,7 @@ private ClassLineNumbers runDecompileJob(Path inputJar, Path outputJar, @Nullabl
// Inject Forge's own sources
if (getExtension().isForgeLike()) {
try (var serviceManager = new ScopedSharedServiceManager()) {
ForgeSourcesRemapper.addForgeSources(getProject(), serviceManager, outputJar);
ForgeSourcesRemapper.addForgeSources(getProject(), serviceManager, inputJar, outputJar);
}
}

Expand Down Expand Up @@ -448,6 +452,36 @@ private ClassLineNumbers filterForgeLineNumbers(@Nullable ClassLineNumbers lineN
}
}

/**
* Some inner classes orders are messed up with forge recompilation, I don't know if that is why the decompiler
* would occasionally split out extra inner classes (where with normal fabric setups it doesn't happen),
* but this is a workaround for that.
*/
private void removeForgeInnerClassSources(Path sourcesJar) throws IOException {
if (!getExtension().isForgeLike()) return;

try (FileSystemUtil.Delegate outputFs = FileSystemUtil.getJarFileSystem(sourcesJar, false);
Stream<Path> walk = Files.walk(outputFs.getRoot())) {
Iterator<Path> iterator = walk.iterator();

while (iterator.hasNext()) {
final Path fsPath = iterator.next();

if (fsPath.startsWith("/META-INF/")) {
continue;
}

if (!Files.isRegularFile(fsPath)) {
continue;
}

if (fsPath.toString().substring(outputFs.getRoot().toString().length()).indexOf('$') != -1) {
Files.delete(fsPath);
}
}
}
}

// Re-run the named minecraft provider to give us a fresh jar to decompile.
// This prevents re-applying line maps on an existing jar.
private MinecraftJar rebuildInputJar() {
Expand Down

0 comments on commit deaee2a

Please sign in to comment.