Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions plugin/src/main/java/net/neoforged/neoform/MinecraftLibraries.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,17 @@ public static NamedDomainObjectProvider<ResolvableConfiguration> createConfigura
}
return result;
}));
// TODO: needs spec extension
var extraCompileDependencies = neoForm.getAdditionalCompileDependencies();
spec.getDependencies().addAllLater(extraCompileDependencies.map(notations -> {
var result = new ArrayList<Dependency>(notations.size());
for (var notation : notations) {
var dependency = dependencyFactory.create(notation);
dependency.endorseStrictVersions();
result.add(dependency);
}
return result;
}));
});
return configurations.resolvable("minecraftLibrariesClasspath", spec -> spec.extendsFrom(minecraftLibraries.get()));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -144,13 +144,15 @@ public void apply(Project project) {
task.setDescription("Creates a Gradle subproject under 'workspace' that contains the patched Minecraft sources to work on modifying the patches easily. Run the createPatches task to generate new patches from this workspace.");
task.getPatchesDir().set(project.getLayout().getProjectDirectory().dir("src/patches"));
task.getSourcesZip().set(decompile.flatMap(Decompile::getOutput));
task.getResourcesZip().set(prepareJarForDecompiler.flatMap(PrepareJarForDecompiler::getOutput));
task.getWorkspace().set(workspaceDir);
});
var createPatchWorkspaceForUpdate = tasks.register("createPatchWorkspaceForUpdate", CreatePatchWorkspace.class, task -> {
task.setGroup("neoform");
task.setDescription("Same as the createWorkspace task, but patches are applied fuzzily and rejected patches will be stored in the 'rejects' folder.");
task.getPatchesDir().set(project.getLayout().getProjectDirectory().dir("src/patches"));
task.getSourcesZip().set(decompile.flatMap(Decompile::getOutput));
task.getResourcesZip().set(prepareJarForDecompiler.flatMap(PrepareJarForDecompiler::getOutput));
task.getWorkspace().set(workspaceDir);
task.getUpdateMode().set(true);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ public abstract class CreatePatchWorkspace extends DefaultTask {
@InputFile
public abstract RegularFileProperty getSourcesZip();

// TODO: temporary hack since CG doesn't preserve the resources in the jar...
@InputFile
public abstract RegularFileProperty getResourcesZip();

@InputDirectory
public abstract DirectoryProperty getPatchesDir();

Expand Down Expand Up @@ -112,69 +116,78 @@ public void createWorkspace() throws IOException {
var failedPatches = new HashSet<String>();
var successfulPatches = 0;
var dirsCreated = new HashSet<Path>();
try (var zip = new ZipFile(getSourcesZip().getAsFile().get())) {
var entries = zip.entries();
while (entries.hasMoreElements()) {
var entry = entries.nextElement();
if (entry.isDirectory()) {
continue;
}

Path destination;
if (entry.getName().endsWith(".java")) {
destination = sourcesDir;
} else {
destination = resourcesDir;
}
destination = destination.resolve(entry.getName());
for (var inzip : List.of(getSourcesZip(), getResourcesZip())) {
try (var zip = new ZipFile(inzip.getAsFile().get())) {
var entries = zip.entries();
while (entries.hasMoreElements()) {
var entry = entries.nextElement();
if (entry.isDirectory()) {
continue;
}

if (dirsCreated.add(destination.getParent())) {
Files.createDirectories(destination.getParent());
}
// TODO: CG should normalize the paths to / when writing the resulting zip entries
var entryName = entry.getName().replace('\\', '/');

try (var input = zip.getInputStream(entry)) {
var patch = patches.remove(entry.getName());
if (patch != null) {
var rejectsOutput = new ByteArrayOutputStream();
var builder = PatchOperation.builder()
.logTo(line -> getLogger().lifecycle("{}", line))
.baseInput(Input.SingleInput.pipe(input, entry.getName()))
.patchesInput(Input.SingleInput.pipe(new ByteArrayInputStream(patch.content), patch.patchPath.toString()))
.patchedOutput(Output.SingleOutput.path(destination))
.level(LogLevel.WARN)
.mode(PatchMode.OFFSET);

if (updateMode) {
builder.mode(PatchMode.OFFSET)
.level(io.codechicken.diffpatch.util.LogLevel.INFO)
.rejectsOutput(Output.SingleOutput.pipe(rejectsOutput));
Path destination;
if (entry.getName().endsWith(".java")) {
if (inzip == getResourcesZip()) {
// Hackkkk
continue;
}
destination = sourcesDir;
} else {
destination = resourcesDir;
}
destination = destination.resolve(entry.getName());

var result = builder.build().operate();

if (result.exit != 0) {
problems.add(reporter.create(PATCH_FAILED, problem -> {
problem
.details("Applying the patch to " + entry.getName() + " failed.")
.fileLocation(patch.patchPath.toAbsolutePath().toString())
.severity(Severity.ERROR);
}));
getLogger().error("Applying the patch to {}} failed.", entry.getName());

if (updateMode && rejectsOutput.size() > 0) {
Path rejectsPath = rejectsDir.resolve(entry.getName() + ".patch");
if (dirsCreated.add(rejectsPath.getParent())) {
Files.createDirectories(rejectsPath.getParent());
}
Files.write(rejectsPath, rejectsOutput.toByteArray());
if (dirsCreated.add(destination.getParent())) {
Files.createDirectories(destination.getParent());
}

try (var input = zip.getInputStream(entry)) {
var patch = patches.remove(entryName);
if (patch != null) {
var rejectsOutput = new ByteArrayOutputStream();
var builder = PatchOperation.builder()
.logTo(line -> getLogger().lifecycle("{}", line))
.baseInput(Input.SingleInput.pipe(input, entryName))
.patchesInput(Input.SingleInput.pipe(new ByteArrayInputStream(patch.content), patch.patchPath.toString()))
.patchedOutput(Output.SingleOutput.path(destination))
.level(LogLevel.WARN)
.mode(PatchMode.OFFSET);

if (updateMode) {
builder.mode(PatchMode.OFFSET)
.level(io.codechicken.diffpatch.util.LogLevel.INFO)
.rejectsOutput(Output.SingleOutput.pipe(rejectsOutput));
}

failedPatches.add(entry.getName());
var result = builder.build().operate();

if (result.exit != 0) {
problems.add(reporter.create(PATCH_FAILED, problem -> {
problem
.details("Applying the patch to " + entryName + " failed.")
.fileLocation(patch.patchPath.toAbsolutePath().toString())
.severity(Severity.ERROR);
}));
getLogger().error("Applying the patch to {}} failed.", entryName);

if (updateMode && rejectsOutput.size() > 0) {
Path rejectsPath = rejectsDir.resolve(entryName + ".patch");
if (dirsCreated.add(rejectsPath.getParent())) {
Files.createDirectories(rejectsPath.getParent());
}
Files.write(rejectsPath, rejectsOutput.toByteArray());
}

failedPatches.add(entryName);
} else {
successfulPatches++;
}
} else {
successfulPatches++;
Files.copy(input, destination, StandardCopyOption.REPLACE_EXISTING);
}
} else {
Files.copy(input, destination, StandardCopyOption.REPLACE_EXISTING);
}
}
}
Expand Down
67 changes: 60 additions & 7 deletions plugin/src/main/java/net/neoforged/neoform/tasks/Decompile.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package net.neoforged.neoform.tasks;

import org.gradle.api.file.ArchiveOperations;
import org.gradle.api.file.ConfigurableFileCollection;
import org.gradle.api.file.RegularFileProperty;
import org.gradle.api.tasks.Classpath;
import org.gradle.api.tasks.InputFile;
import org.gradle.api.tasks.Optional;
import org.gradle.api.tasks.OutputFile;
import org.gradle.api.tasks.TaskAction;

Expand All @@ -12,8 +14,19 @@
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.Comparator;
import java.util.Map;
import java.util.stream.Stream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

public abstract class Decompile extends ToolAction {
@InputFile
Expand All @@ -34,22 +47,62 @@ public Decompile() {
})));
}

@Inject
protected abstract ArchiveOperations getArchiveOperations();

@TaskAction
public void execute() throws IOException {
var inputJar = getInput().getAsFile().get();
var outputZip = getOutput().getAsFile().get();
var outputFolder = getOutput().getAsFile().get().toPath().resolveSibling("temp_output_folder");
// Code from https://stackoverflow.com/questions/35988192/java-nio-most-concise-recursive-directory-delete
if (Files.exists(outputFolder)) {
try (Stream<Path> walk = Files.walk(outputFolder)) {
walk.sorted(Comparator.reverseOrder()).map(Path::toFile).forEach(File::delete);
}
}

// TODO: this implies changes in the produced neoform config
var libraries = new StringBuilder();

var librariesFile = new File(getTemporaryDir(), "libraries.cfg");
try (var writer = new BufferedWriter(new FileWriter(librariesFile, StandardCharsets.UTF_8))) {
for (var file : getInputClasspath()) {
writer.append("--add-external=").append(file.getAbsolutePath()).append('\n');
boolean first = true;
for (var file : getInputClasspath()) {
if (!first) {
libraries.append(File.pathSeparator);
}
first = false;
libraries.append(file.getAbsolutePath());
}

exec(Map.of(
"input", inputJar.getAbsolutePath(),
"inputLibraries", librariesFile.getAbsolutePath(),
"output", outputZip.getAbsolutePath()
"inputLibraries", libraries.toString(),
"output", outputFolder.toAbsolutePath().toString()
));


var outputZip = getOutput().getAsFile().get();
if (outputZip.exists()) {
Files.delete(outputZip.toPath());
}
pack(outputFolder, outputZip.toPath());
}

public static void pack(Path sourceDir, Path zipTarget) throws IOException {
Path p = Files.createFile(zipTarget);
try (ZipOutputStream zs = new ZipOutputStream(Files.newOutputStream(p));
Stream<Path> files = Files.walk(sourceDir)) {
files
.filter(path -> !Files.isDirectory(path))
.forEach(path -> {
ZipEntry zipEntry = new ZipEntry(sourceDir.relativize(path).toString());
try {
zs.putNextEntry(zipEntry);
Files.copy(path, zs);
zs.closeEntry();
} catch (IOException e) {
throw new UncheckedIOException(e);
}
});
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,10 @@ public static void configure(Project project, TaskProvider<? extends ToolAction>
var dependencies = project.getDependencyFactory();
spec.getDependencies().addAllLater(settings.getClasspath().map(i -> i.stream().<Dependency>map(notation -> {
var dependency = dependencies.create(notation);
dependency.setTransitive(false);
// TODO: hacky!
if (!taskName.equals("decompile")) {
dependency.setTransitive(false);
}
return dependency;
}).toList()));
});
Expand Down
59 changes: 41 additions & 18 deletions settings.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -28,26 +28,49 @@ neoForm {

decompiler {
classpath = [
'org.vineflower:vineflower:1.11.2',
'net.neoforged:vineflower-plugins:0.1.5'
// TODO: excessive deps
'net.javasauce:Decompiler:0.0-SNAPSHOT',
// 'org.vineflower:vineflower:1.11.2',
// 'net.neoforged:vineflower-plugins:0.1.5'
]
mainClass = 'net.covers1624.coffeegrinder.CoffeeGrinder'
jvmArgs = [
// TODO: should probably scale with the number of threads...
'-Xmx4g',
// TODO: enabling things triggers more failures
// '-ea',
]
mainClass = 'org.jetbrains.java.decompiler.main.decompiler.ConsoleDecompiler'
jvmArgs = ['-Xmx4g']
args = [
'--decompile-inner',
'--remove-bridge',
'--decompile-generics',
'--ascii-strings',
'--remove-synthetic',
'--include-classpath',
'--ignore-invalid-bytecode',
'--bytecode-source-mapping',
'--dump-code-lines',
'--indent-string= ',
'--log-level=WARN',
'-cfg={inputLibraries}',
'{input}',
'{output}'
// '--decompile-inner',
// '--remove-bridge',
// '--decompile-generics',
// '--ascii-strings',
// '--remove-synthetic',
// '--include-classpath',
// '--ignore-invalid-bytecode',
// '--bytecode-source-mapping',
// '--dump-code-lines',
// '--indent-string= ',
// '--log-level=WARN',
// '-cfg={inputLibraries}',
'-l={inputLibraries}',
'-i={input}',
'-o={output}',
'-a={output}-ast',
// Record deconstruction in switch:
'-s=com.mojang.blaze3d.opengl.GlCommandEncoder#trySetup(Lcom/mojang/blaze3d/opengl/GlRenderPass;Ljava/util/Collection;)Z',
'-s=net.minecraft.client.gui.screens.inventory.BookViewScreen#handleClickEvent(Lnet/minecraft/network/chat/ClickEvent;)Z',
'-s=net.minecraft.client.renderer.PostChain#createPass(Lnet/minecraft/client/renderer/texture/TextureManager;Lnet/minecraft/client/renderer/PostChainConfig$Pass;Lnet/minecraft/resources/Identifier;)Lnet/minecraft/client/renderer/PostPass;',
'-s=net.minecraft.commands.functions.MacroFunction#stringify(Lnet/minecraft/nbt/Tag;)Ljava/lang/String;',
'-s=net.minecraft.nbt.NbtOps#convertTo(Lcom/mojang/serialization/DynamicOps;Lnet/minecraft/nbt/Tag;)Ljava/lang/Object;',
// Generic inference problems:
'-s=net.minecraft.core.Direction#<clinit>()V',
'-s=net.minecraft.network.protocol.game.ClientboundLevelChunkPacketData#<init>(Lnet/minecraft/world/level/chunk/LevelChunk;)V',
'-s=net.minecraft.server.jsonrpc.methods.DiscoveryService$DiscoverComponents#typedSchema()Lcom/mojang/serialization/MapCodec;',
'-s=net.minecraft.world.entity.Leashable#checkElasticInteractions(Lnet/minecraft/world/entity/Entity;Lnet/minecraft/world/entity/Leashable$LeashData;)Z',
'-s=net.minecraft.world.entity.Leashable#getLeashHolder()Lnet/minecraft/world/entity/Entity;',
'-s=net.minecraft.world.entity.projectile.arrow.AbstractArrow$Pickup#<clinit>()V',
'-s=net.minecraft.world.item.DyeColor#<clinit>()V',
]
}
}
Loading
Loading