try to remap patches
This commit is contained in:
parent
a1acc70992
commit
cecca9d248
3 changed files with 474 additions and 2 deletions
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) }
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue