try to remap patches

This commit is contained in:
MiniDigger | Martin 2023-07-10 19:12:01 +02:00
parent a1acc70992
commit cecca9d248
3 changed files with 474 additions and 2 deletions

View file

@ -83,8 +83,8 @@ open class PatchRemapTasks(
val patchCraftBukkit by tasks.registering<ApplyCraftBukkitPatches> {
// TODO temp to speed stuff up
//sourceJar.set(spigotDecompileJar.flatMap { it.outputJar })
sourceJar.set(cache.resolve("paperweight/taskCache/spigotDecompileJar.jar"))
sourceJar.set(spigotDecompileJar.flatMap { it.outputJar })
//sourceJar.set(cache.resolve("paperweight/taskCache/spigotDecompileJar.jar"))
cleanDirPath.set("net/minecraft")
patchDir.set(extension.patchRemap.patchDir)
craftBukkitDir.set(extension.patchRemap.craftBukkitDir)
@ -92,4 +92,35 @@ open class PatchRemapTasks(
//dependsOn(cloneForPatchRemap)
}
//val filterSpigotMojMapExcludes by tasks.registering<FilterSpigotExcludes> {
// inputZip.set(spigotMojMapRemapJar.flatMap { it.outputJar })
// excludesFile.set(extension.patchRemap.excludesFile)
//}
//
//val spigotDecompileMojMapJar by tasks.registering<SpigotDecompileJar> {
// inputJar.set(filterSpigotMojMapExcludes.flatMap { it.outputZip })
// fernFlowerJar.set(extension.patchRemap.fernFlowerJar)
// decompileCommand.set(buildDataInfo.map { it.decompileCommand })
//}
val remapCraftBukkitSources by tasks.registering<RemapSources> {
vanillaJar.set(allTasks.extractFromBundler.flatMap { it.serverJar })
//mojangMappedVanillaJar.set(fixJar.flatMap { it.outputJar })
//vanillaRemappedSpigotJar.set(filterSpigotExcludes.flatMap { it.outputZip })
//mappings.set(generateSpigotMappings.flatMap { it.outputMappings })
mappings.set(cache.resolve(SPIGOT_MOJANG_YARN_MAPPINGS))
//sources.set(patchCraftBukkit.flatMap { it.outputDir }) // todo temp to speed stuff up
sources.set(extension.patchRemap.patchedCraftBukkitDir);
//spigotDeps.from(downloadSpigotDependencies.map { it.outputDir.asFileTree })
//additionalAts.set(mergePaperAts.flatMap { it.outputFile })
}
// todo first try diffing against remapped mcp config source
// if that fails, we have to diff against spigot decompiled mojmap+yarn mapped vanilla jar
val diffCraftBukkitAgainstVanilla by tasks.registering<DiffCraftBukkitAgainstVanilla> {
craftBukkit.set(remapCraftBukkitSources.flatMap { it.sourcesOutputZip })
//vanilla.set(allTasks.prepareBase.flatMap { it.output })
vanilla.set(cache.resolve(BASE_PROJECT))
}
}

View file

@ -0,0 +1,56 @@
package io.papermc.paperweight.tasks.patchremap
import codechicken.diffpatch.cli.DiffOperation
import io.papermc.paperweight.tasks.BaseTask
import io.papermc.paperweight.util.defaultOutput
import io.papermc.paperweight.util.filesMatchingRecursive
import io.papermc.paperweight.util.path
import org.gradle.api.file.DirectoryProperty
import org.gradle.api.file.RegularFileProperty
import org.gradle.api.tasks.InputDirectory
import org.gradle.api.tasks.InputFile
import org.gradle.api.tasks.OutputDirectory
import org.gradle.api.tasks.TaskAction
import java.nio.file.Files
abstract class DiffCraftBukkitAgainstVanilla: BaseTask() {
@get:InputFile
abstract val craftBukkit: RegularFileProperty
@get:InputDirectory
abstract val vanilla: DirectoryProperty
@get:OutputDirectory
abstract val patches: RegularFileProperty
override fun init() {
patches.convention(defaultOutput())
}
@TaskAction
open fun run() {
val diffOp = DiffOperation.builder()
.logTo(System.out)
.aPath(vanilla.path.resolve("src/vanilla"))
.bPath(craftBukkit.path)
.outputPath(patches.path.resolve("dum"))
.verbose(false)
.summary(true)
.lineEnding("\n")
//.ignorePattern(ignorePattern.get())
.build()
diffOp.operate()
patches.path.resolve("dum").filesMatchingRecursive("*.patch").forEach { p ->
if (Files.readAllLines(p)[1].contains("+++ /dev/null")) {
Files.delete(p)
}
}
Files.walk(patches.path.resolve("dum"))
.filter { Files.isDirectory(it) }
.filter { it.toFile().listFiles()?.isEmpty() ?: false }
.forEach { Files.delete(it) }
}
}

View file

@ -0,0 +1,385 @@
/*
* paperweight is a Gradle plugin for the PaperMC project.
*
* Copyright (c) 2023 Kyle Wood (DenWav)
* Contributors
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 only, no later versions.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
* USA
*/
package io.papermc.paperweight.tasks.patchremap
import io.papermc.paperweight.tasks.JavaLauncherTask
import io.papermc.paperweight.util.*
import io.papermc.paperweight.util.constants.*
import java.nio.file.Files
import java.nio.file.Path
import javax.inject.Inject
import kotlin.io.path.*
import kotlin.streams.asSequence
import org.cadixdev.at.AccessTransformSet
import org.cadixdev.at.io.AccessTransformFormats
import org.cadixdev.mercury.Mercury
import org.cadixdev.mercury.RewriteContext
import org.cadixdev.mercury.SourceProcessor
import org.cadixdev.mercury.SourceRewriter
import org.cadixdev.mercury.at.AccessTransformerRewriter
import org.cadixdev.mercury.extra.AccessAnalyzerProcessor
import org.cadixdev.mercury.remapper.MercuryRemapper
import org.eclipse.jdt.core.JavaCore
import org.eclipse.jdt.core.dom.*
import org.gradle.api.file.ConfigurableFileCollection
import org.gradle.api.file.DirectoryProperty
import org.gradle.api.file.RegularFileProperty
import org.gradle.api.provider.ListProperty
import org.gradle.api.tasks.*
import org.gradle.kotlin.dsl.*
import org.gradle.workers.WorkAction
import org.gradle.workers.WorkParameters
import org.gradle.workers.WorkerExecutor
@CacheableTask
abstract class RemapSources : JavaLauncherTask() {
@get:CompileClasspath
abstract val vanillaJar: RegularFileProperty
//@get:CompileClasspath
//abstract val mojangMappedVanillaJar: RegularFileProperty
//@get:CompileClasspath
//abstract val vanillaRemappedSpigotJar: RegularFileProperty
@get:InputFile
@get:PathSensitive(PathSensitivity.NONE)
abstract val mappings: RegularFileProperty
//@get:CompileClasspath
//abstract val spigotDeps: ConfigurableFileCollection
@get:InputDirectory
@get:PathSensitive(PathSensitivity.RELATIVE)
abstract val sources: DirectoryProperty
//@get:Optional
//@get:InputFile
//@get:PathSensitive(PathSensitivity.NONE)
//abstract val additionalAts: RegularFileProperty
@get:OutputFile
abstract val generatedAt: RegularFileProperty
@get:OutputFile
abstract val sourcesOutputZip: RegularFileProperty
@get:Internal
abstract val jvmargs: ListProperty<String>
@get:Inject
abstract val workerExecutor: WorkerExecutor
@get:OutputFile
abstract val spigotRecompiledClasses: RegularFileProperty
override fun init() {
super.init()
jvmargs.convention(listOf("-Xmx2G"))
sourcesOutputZip.convention(defaultOutput("$name-sources", "jar"))
generatedAt.convention(defaultOutput("at"))
spigotRecompiledClasses.convention(defaultOutput("spigotRecompiledClasses", "txt"))
}
@TaskAction
fun run() {
val srcOut = findOutputDir(sourcesOutputZip.path).apply { createDirectories() }
try {
val queue = workerExecutor.processIsolation {
forkOptions.jvmArgs(jvmargs.get())
forkOptions.executable(launcher.get().executablePath.path.absolutePathString())
}
val srcDir = sources.path.resolve("src/main/java")
// Remap sources
queue.submit(RemapAction::class) {
//classpath.from(vanillaRemappedSpigotJar.path)
//classpath.from(mojangMappedVanillaJar.path)
classpath.from(vanillaJar.path)
//classpath.from(spigotApiDir.dir("src/main/java").path)
//classpath.from(spigotDeps.files.filter { it.toPath().isLibraryJar })
//additionalAts.set(this@RemapSources.additionalAts.pathOrNull)
mappings.set(this@RemapSources.mappings.path)
inputDir.set(srcDir)
cacheDir.set(this@RemapSources.layout.cache)
outputDir.set(srcOut)
generatedAtOutput.set(generatedAt.path)
}
queue.await()
zip(srcOut, sourcesOutputZip)
writeSpigotRecompiledFiles(srcOut)
} finally {
srcOut.deleteRecursively()
}
}
private fun writeSpigotRecompiledFiles(srcOut: Path) {
// Write list of java files spigot recompiles
val spigotRecompiled = Files.walk(srcOut).use { stream ->
stream.asSequence().mapNotNull {
if (!it.isRegularFile()) {
return@mapNotNull null
}
if (!it.fileName.pathString.endsWith(".java")) {
return@mapNotNull null
}
val path = srcOut.relativize(it).pathString
if (!path.startsWith("net/minecraft")) {
return@mapNotNull null
}
path.replace(".java", "")
}.sorted().joinToString("\n")
}
spigotRecompiledClasses.path.parent.createDirectories()
spigotRecompiledClasses.path.writeText(spigotRecompiled)
}
abstract class RemapAction : WorkAction<RemapParams> {
override fun execute() {
val mappingSet = MappingFormats.TINY.read(
parameters.mappings.path,
SPIGOT_NAMESPACE,
DEOBF_NAMESPACE
)
val additionalAt = parameters.additionalAts.pathOrNull?.let { AccessTransformFormats.FML.read(it) }
val processAt = AccessTransformSet.create()
val generatedAtOutPath = parameters.generatedAtOutput.pathOrNull
// Remap any references Spigot maps to mojmap+yarn
Mercury().let { merc ->
merc.sourceCompatibility = JavaCore.VERSION_17
merc.isGracefulClasspathChecks = true
merc.classPath.addAll(parameters.classpath.map { it.toPath() })
if (generatedAtOutPath != null) {
merc.processors += AccessAnalyzerProcessor.create(processAt, mappingSet)
}
merc.process(parameters.inputDir.path)
val tempOut = Files.createTempDirectory(parameters.cacheDir.path, "remap")
try {
merc.processors.clear()
merc.processors.addAll(
listOf(
ExplicitThisAdder,
OverrideRemover, // TODO meh
MercuryRemapper.create(mappingSet),
AccessTransformerRewriter.create(processAt)
)
)
if (generatedAtOutPath != null) {
merc.processors.add(AccessTransformerRewriter.create(processAt))
}
merc.rewrite(parameters.inputDir.path, tempOut)
if (additionalAt != null) {
merc.processors.clear()
merc.processors += AccessTransformerRewriter.create(additionalAt)
merc.rewrite(tempOut, parameters.outputDir.path)
} else {
tempOut.copyRecursivelyTo(parameters.outputDir.path)
}
} finally {
tempOut.deleteRecursively()
}
}
if (generatedAtOutPath != null) {
AccessTransformFormats.FML.write(generatedAtOutPath, processAt)
}
}
}
interface RemapParams : WorkParameters {
val classpath: ConfigurableFileCollection
val mappings: RegularFileProperty
val inputDir: RegularFileProperty
val additionalAts: RegularFileProperty
val cacheDir: RegularFileProperty
val generatedAtOutput: RegularFileProperty
val outputDir: RegularFileProperty
}
object OverrideRemover : SourceRewriter {
override fun getFlags(): Int = SourceProcessor.FLAG_RESOLVE_BINDINGS
override fun rewrite(context: RewriteContext) {
context.compilationUnit.accept(OverrideRemoverVisitor(context))
}
}
class OverrideRemoverVisitor(private val context: RewriteContext) : ASTVisitor() {
override fun visit(node: MarkerAnnotation): Boolean {
if (node.typeName.fullyQualifiedName == "Override") {
context.createASTRewrite().remove(node, null)
}
return super.visit(node)
}
}
object ExplicitThisAdder : SourceRewriter {
override fun getFlags(): Int = SourceProcessor.FLAG_RESOLVE_BINDINGS
override fun rewrite(context: RewriteContext) {
context.compilationUnit.accept(ExplicitThisAdderVisitor(context))
}
}
class ExplicitThisAdderVisitor(private val context: RewriteContext) : ASTVisitor() {
override fun visit(node: SimpleName): Boolean {
val binding = node.resolveBinding() ?: return false
val name = when (val declaringNode = context.compilationUnit.findDeclaringNode(binding)) {
is VariableDeclarationFragment -> declaringNode.name
is MethodDeclaration -> declaringNode.name
else -> return false
}
if (name === node) {
// this is the actual declaration
return false
}
visit(node, binding)
return false
}
private fun visit(node: SimpleName, binding: IBinding) {
if (binding.kind != IBinding.VARIABLE && binding.kind != IBinding.METHOD) {
return
}
val referringClass = when (binding) {
is IVariableBinding -> {
if (!binding.isField || binding.isEnumConstant) {
return
}
binding.declaringClass
}
is IMethodBinding -> {
if (binding.isConstructor || binding.isSynthetic) {
return
}
binding.declaringClass
}
else -> return
}
val modifiers = when (binding) {
is IVariableBinding -> binding.modifiers
is IMethodBinding -> binding.modifiers
else -> return
}
when (val p = node.parent) {
is FieldAccess, is SuperFieldAccess, is QualifiedName, is ThisExpression, is MethodReference, is SuperMethodInvocation -> return
is MethodInvocation -> {
if (p.expression != null && p.expression !== node) {
return
}
}
}
// find declaring method
var parentNode: ASTNode? = node
loop@ while (parentNode != null) {
when (parentNode) {
is MethodDeclaration, is AnonymousClassDeclaration, is LambdaExpression, is Initializer -> break@loop
}
parentNode = parentNode.parent
}
val rewrite = context.createASTRewrite()
val fieldAccess = rewrite.ast.newFieldAccess()
val expr: Expression = if (!Modifier.isStatic(modifiers)) {
rewrite.ast.newThisExpression().also { thisExpr ->
if (parentNode is LambdaExpression) {
return@also
}
if (parentNode is AnonymousClassDeclaration && referringClass.erasure != parentNode.resolveBinding().erasure) {
val name = getNameNode(referringClass) ?: return
thisExpr.qualifier = rewrite.createCopyTarget(name) as Name
return@also
}
val methodDec = parentNode as? MethodDeclaration ?: return@also
var methodClass = methodDec.resolveBinding()?.declaringClass ?: return // silently return if binding does not resolve
if (methodClass.isAnonymous) {
val name = getNameNode(referringClass) ?: return
thisExpr.qualifier = rewrite.createCopyTarget(name) as Name
return@also
}
if (referringClass.erasure != methodClass.erasure && methodClass.isNested && !Modifier.isStatic(methodClass.modifiers)) {
while (true) {
methodClass = methodClass.declaringClass ?: break
}
// Looks like the method is accessing an outer class's fields
if (referringClass.erasure == methodClass.erasure) {
val name = getNameNode(referringClass) ?: return
thisExpr.qualifier = rewrite.createCopyTarget(name) as Name
}
}
}
} else {
if (parentNode is Initializer && Modifier.isStatic(parentNode.modifiers)) {
// Can't provide explicit static receiver here
return
}
val name = getNameNode(referringClass) ?: return
rewrite.createCopyTarget(name) as Name
}
fieldAccess.expression = expr
fieldAccess.name = rewrite.createMoveTarget(node) as SimpleName
rewrite.replace(node, fieldAccess, null)
}
private fun getNameNode(dec: ITypeBinding): Name? {
val typeDec = context.compilationUnit.findDeclaringNode(dec) as? TypeDeclaration ?: return null
return typeDec.name
}
}
}