Compare commits
3 commits
Author | SHA1 | Date | |
---|---|---|---|
|
0ae3814cf5 | ||
|
2c4b1e1409 | ||
|
c4eb91fe8f |
11 changed files with 547 additions and 307 deletions
|
@ -30,7 +30,7 @@ tasks.withType<KotlinCompile> {
|
||||||
}
|
}
|
||||||
|
|
||||||
gradlePlugin {
|
gradlePlugin {
|
||||||
// we handle publications ourselves
|
// we handle publications manually
|
||||||
isAutomatedPublishing = false
|
isAutomatedPublishing = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -79,6 +79,10 @@ dependencies {
|
||||||
shade("net.fabricmc:lorenz-tiny:3.0.0")
|
shade("net.fabricmc:lorenz-tiny:3.0.0")
|
||||||
|
|
||||||
shade("io.sigpipe:jbsdiff:1.0")
|
shade("io.sigpipe:jbsdiff:1.0")
|
||||||
|
|
||||||
|
val jgraphtVersion = "1.5.0"
|
||||||
|
shade("org.jgrapht:jgrapht-core:$jgraphtVersion")
|
||||||
|
shade("org.jgrapht:jgrapht-io:$jgraphtVersion")
|
||||||
}
|
}
|
||||||
|
|
||||||
ktlint {
|
ktlint {
|
||||||
|
@ -124,13 +128,17 @@ tasks.shadowJar {
|
||||||
"io.sigpipe",
|
"io.sigpipe",
|
||||||
"me.jamiemansfield",
|
"me.jamiemansfield",
|
||||||
"net.fabricmc",
|
"net.fabricmc",
|
||||||
|
"org.antlr",
|
||||||
"org.apache.commons.codec",
|
"org.apache.commons.codec",
|
||||||
"org.apache.commons.compress",
|
"org.apache.commons.compress",
|
||||||
"org.apache.commons.logging",
|
"org.apache.commons.logging",
|
||||||
|
"org.apache.commons.text",
|
||||||
"org.apache.felix",
|
"org.apache.felix",
|
||||||
"org.apache.http",
|
"org.apache.http",
|
||||||
"org.cadixdev",
|
"org.cadixdev",
|
||||||
"org.eclipse",
|
"org.eclipse",
|
||||||
|
"org.jgrapht",
|
||||||
|
"org.jheaps",
|
||||||
"org.objectweb",
|
"org.objectweb",
|
||||||
"org.osgi",
|
"org.osgi",
|
||||||
"org.tukaani"
|
"org.tukaani"
|
||||||
|
|
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
|
@ -1,5 +1,5 @@
|
||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-bin.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-6.8-bin.zip
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
|
|
|
@ -92,10 +92,12 @@ class Paperweight : Plugin<Project> {
|
||||||
|
|
||||||
val tasks = target.createTasks()
|
val tasks = target.createTasks()
|
||||||
|
|
||||||
|
val generatePaperclipPatch by target.tasks.registering<GeneratePaperclipPatch>()
|
||||||
|
|
||||||
// Setup the server jar
|
// Setup the server jar
|
||||||
target.afterEvaluate {
|
target.afterEvaluate {
|
||||||
target.ext.serverProject.forUseAtConfigurationTime().orNull?.setupServerProject(target, tasks.spigotTasks)?.let { reobfJar ->
|
target.ext.serverProject.forUseAtConfigurationTime().orNull?.setupServerProject(target, tasks.spigotTasks)?.let { reobfJar ->
|
||||||
val generatePaperclipPatch by target.tasks.registering<GeneratePaperclipPatch> {
|
generatePaperclipPatch.configure {
|
||||||
originalJar.set(tasks.generalTasks.downloadServerJar.flatMap { it.outputJar })
|
originalJar.set(tasks.generalTasks.downloadServerJar.flatMap { it.outputJar })
|
||||||
patchedJar.set(reobfJar.flatMap { it.outputJar })
|
patchedJar.set(reobfJar.flatMap { it.outputJar })
|
||||||
mcVersion.set(target.ext.minecraftVersion)
|
mcVersion.set(target.ext.minecraftVersion)
|
||||||
|
@ -103,13 +105,13 @@ class Paperweight : Plugin<Project> {
|
||||||
|
|
||||||
target.tasks.named("jar", Jar::class) {
|
target.tasks.named("jar", Jar::class) {
|
||||||
val paperclipConfig = target.configurations.named(Constants.PAPERCLIP_CONFIG)
|
val paperclipConfig = target.configurations.named(Constants.PAPERCLIP_CONFIG)
|
||||||
dependsOn(paperclipConfig)
|
dependsOn(paperclipConfig, generatePaperclipPatch)
|
||||||
|
|
||||||
val paperclipZip = target.zipTree(paperclipConfig.map { it.singleFile })
|
val paperclipZip = target.zipTree(paperclipConfig.map { it.singleFile })
|
||||||
from(paperclipZip) {
|
from(paperclipZip) {
|
||||||
exclude("META-INF/MANIFEST.MF")
|
exclude("META-INF/MANIFEST.MF")
|
||||||
}
|
}
|
||||||
from(target.zipTree(generatePaperclipPatch.flatMap { it.outputZip }.get()))
|
from(target.zipTree(generatePaperclipPatch.flatMap { it.outputZip }))
|
||||||
|
|
||||||
manifest.from(paperclipZip.matching { include("META-INF/MANIFEST.MF") }.singleFile)
|
manifest.from(paperclipZip.matching { include("META-INF/MANIFEST.MF") }.singleFile)
|
||||||
}
|
}
|
||||||
|
@ -150,7 +152,7 @@ class Paperweight : Plugin<Project> {
|
||||||
val initialTasks = createInitialTasks()
|
val initialTasks = createInitialTasks()
|
||||||
val generalTasks = createGeneralTasks()
|
val generalTasks = createGeneralTasks()
|
||||||
val vanillaTasks = createVanillaTasks(initialTasks, generalTasks)
|
val vanillaTasks = createVanillaTasks(initialTasks, generalTasks)
|
||||||
val spigotTasks = createSpigotTasks(initialTasks, generalTasks, vanillaTasks)
|
val spigotTasks = createSpigotTasks(generalTasks, vanillaTasks)
|
||||||
|
|
||||||
val applyMergedAt by tasks.registering<ApplyAccessTransform> {
|
val applyMergedAt by tasks.registering<ApplyAccessTransform> {
|
||||||
inputJar.set(vanillaTasks.fixJar.flatMap { it.outputJar })
|
inputJar.set(vanillaTasks.fixJar.flatMap { it.outputJar })
|
||||||
|
@ -236,6 +238,7 @@ class Paperweight : Plugin<Project> {
|
||||||
)
|
)
|
||||||
|
|
||||||
data class VanillaTasks(
|
data class VanillaTasks(
|
||||||
|
val inspectVanillaJar: TaskProvider<InspectVanillaJar>,
|
||||||
val generateMappings: TaskProvider<GenerateMappings>,
|
val generateMappings: TaskProvider<GenerateMappings>,
|
||||||
val fixJar: TaskProvider<FixJar>,
|
val fixJar: TaskProvider<FixJar>,
|
||||||
val downloadMcLibraries: TaskProvider<DownloadMcLibraries>
|
val downloadMcLibraries: TaskProvider<DownloadMcLibraries>
|
||||||
|
@ -334,11 +337,29 @@ class Paperweight : Plugin<Project> {
|
||||||
val cache: File = layout.cache
|
val cache: File = layout.cache
|
||||||
val downloadService = download
|
val downloadService = download
|
||||||
|
|
||||||
|
val downloadMcLibraries by tasks.registering<DownloadMcLibraries> {
|
||||||
|
mcLibrariesFile.set(initialTasks.setupMcLibraries.flatMap { it.outputFile })
|
||||||
|
mcRepo.set(Constants.MC_LIBRARY_URL)
|
||||||
|
outputDir.set(cache.resolve(Constants.MINECRAFT_JARS_PATH))
|
||||||
|
sourcesOutputDir.set(cache.resolve(Constants.MINECRAFT_SOURCES_PATH))
|
||||||
|
|
||||||
|
downloader.set(downloadService)
|
||||||
|
}
|
||||||
|
|
||||||
|
val inspectVanillaJar by tasks.registering<InspectVanillaJar> {
|
||||||
|
inputJar.set(generalTasks.downloadServerJar.flatMap { it.outputJar })
|
||||||
|
librariesDir.set(downloadMcLibraries.flatMap { it.outputDir })
|
||||||
|
mcLibraries.set(initialTasks.setupMcLibraries.flatMap { it.outputFile })
|
||||||
|
|
||||||
|
serverLibraries.set(cache.resolve(Constants.SERVER_LIBRARIES))
|
||||||
|
}
|
||||||
|
|
||||||
val generateMappings by tasks.registering<GenerateMappings> {
|
val generateMappings by tasks.registering<GenerateMappings> {
|
||||||
vanillaJar.set(generalTasks.filterVanillaJar.flatMap { it.outputJar })
|
vanillaJar.set(generalTasks.filterVanillaJar.flatMap { it.outputJar })
|
||||||
|
|
||||||
vanillaMappings.set(initialTasks.downloadMappings.flatMap { it.outputFile })
|
vanillaMappings.set(initialTasks.downloadMappings.flatMap { it.outputFile })
|
||||||
paramMappings.fileProvider(configurations.named(Constants.PARAM_MAPPINGS_CONFIG).map { it.singleFile })
|
paramMappings.fileProvider(configurations.named(Constants.PARAM_MAPPINGS_CONFIG).map { it.singleFile })
|
||||||
|
methodOverrides.set(inspectVanillaJar.flatMap { it.methodOverrides })
|
||||||
|
|
||||||
outputMappings.set(cache.resolve(Constants.MOJANG_YARN_MAPPINGS))
|
outputMappings.set(cache.resolve(Constants.MOJANG_YARN_MAPPINGS))
|
||||||
}
|
}
|
||||||
|
@ -356,25 +377,16 @@ class Paperweight : Plugin<Project> {
|
||||||
vanillaJar.set(generalTasks.downloadServerJar.flatMap { it.outputJar })
|
vanillaJar.set(generalTasks.downloadServerJar.flatMap { it.outputJar })
|
||||||
}
|
}
|
||||||
|
|
||||||
val downloadMcLibraries by tasks.registering<DownloadMcLibraries> {
|
return VanillaTasks(inspectVanillaJar, generateMappings, fixJar, downloadMcLibraries)
|
||||||
mcLibrariesFile.set(initialTasks.setupMcLibraries.flatMap { it.outputFile })
|
|
||||||
mcRepo.set(Constants.MC_LIBRARY_URL)
|
|
||||||
outputDir.set(cache.resolve(Constants.MINECRAFT_JARS_PATH))
|
|
||||||
sourcesOutputDir.set(cache.resolve(Constants.MINECRAFT_SOURCES_PATH))
|
|
||||||
|
|
||||||
downloader.set(downloadService)
|
|
||||||
}
|
|
||||||
|
|
||||||
return VanillaTasks(generateMappings, fixJar, downloadMcLibraries)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun Project.createSpigotTasks(initialTasks: InitialTasks, generalTasks: GeneralTasks, vanillaTasks: VanillaTasks): SpigotTasks {
|
private fun Project.createSpigotTasks(generalTasks: GeneralTasks, vanillaTasks: VanillaTasks): SpigotTasks {
|
||||||
val cache: File = layout.cache
|
val cache: File = layout.cache
|
||||||
val extension: PaperweightExtension = ext
|
val extension: PaperweightExtension = ext
|
||||||
val downloadService = download
|
val downloadService = download
|
||||||
|
|
||||||
val (buildDataInfo, downloadServerJar, filterVanillaJar) = generalTasks
|
val (buildDataInfo, downloadServerJar, filterVanillaJar) = generalTasks
|
||||||
val (generateMappings, _, _) = vanillaTasks
|
val (inspectVanillaJar, generateMappings, _, _) = vanillaTasks
|
||||||
|
|
||||||
val addAdditionalSpigotMappings by tasks.registering<AddAdditionalSpigotMappings> {
|
val addAdditionalSpigotMappings by tasks.registering<AddAdditionalSpigotMappings> {
|
||||||
classSrg.set(extension.craftBukkit.mappingsDir.file(buildDataInfo.map { it.classMappings }))
|
classSrg.set(extension.craftBukkit.mappingsDir.file(buildDataInfo.map { it.classMappings }))
|
||||||
|
@ -383,14 +395,6 @@ class Paperweight : Plugin<Project> {
|
||||||
additionalMemberEntriesSrg.set(extension.paper.additionalSpigotMemberMappings)
|
additionalMemberEntriesSrg.set(extension.paper.additionalSpigotMemberMappings)
|
||||||
}
|
}
|
||||||
|
|
||||||
val inspectVanillaJar by tasks.registering<InspectVanillaJar> {
|
|
||||||
inputJar.set(downloadServerJar.flatMap { it.outputJar })
|
|
||||||
librariesDir.set(vanillaTasks.downloadMcLibraries.flatMap { it.outputDir })
|
|
||||||
mcLibraries.set(initialTasks.setupMcLibraries.flatMap { it.outputFile })
|
|
||||||
|
|
||||||
serverLibraries.set(cache.resolve(Constants.SERVER_LIBRARIES))
|
|
||||||
}
|
|
||||||
|
|
||||||
val generateSpigotMappings by tasks.registering<GenerateSpigotMappings> {
|
val generateSpigotMappings by tasks.registering<GenerateSpigotMappings> {
|
||||||
classMappings.set(addAdditionalSpigotMappings.flatMap { it.outputClassSrg })
|
classMappings.set(addAdditionalSpigotMappings.flatMap { it.outputClassSrg })
|
||||||
memberMappings.set(addAdditionalSpigotMappings.flatMap { it.outputMemberSrg })
|
memberMappings.set(addAdditionalSpigotMappings.flatMap { it.outputMemberSrg })
|
||||||
|
@ -399,6 +403,7 @@ class Paperweight : Plugin<Project> {
|
||||||
loggerFields.set(inspectVanillaJar.flatMap { it.loggerFile })
|
loggerFields.set(inspectVanillaJar.flatMap { it.loggerFile })
|
||||||
paramIndexes.set(inspectVanillaJar.flatMap { it.paramIndexes })
|
paramIndexes.set(inspectVanillaJar.flatMap { it.paramIndexes })
|
||||||
syntheticMethods.set(inspectVanillaJar.flatMap { it.syntheticMethods })
|
syntheticMethods.set(inspectVanillaJar.flatMap { it.syntheticMethods })
|
||||||
|
methodOverrides.set(inspectVanillaJar.flatMap { it.methodOverrides })
|
||||||
|
|
||||||
sourceMappings.set(generateMappings.flatMap { it.outputMappings })
|
sourceMappings.set(generateMappings.flatMap { it.outputMappings })
|
||||||
|
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
package io.papermc.paperweight.tasks
|
package io.papermc.paperweight.tasks
|
||||||
|
|
||||||
import io.papermc.paperweight.util.AsmUtil
|
import io.papermc.paperweight.util.AsmUtil
|
||||||
|
import io.papermc.paperweight.util.ClassNodeCache
|
||||||
import io.papermc.paperweight.util.SyntheticUtil
|
import io.papermc.paperweight.util.SyntheticUtil
|
||||||
import io.papermc.paperweight.util.defaultOutput
|
import io.papermc.paperweight.util.defaultOutput
|
||||||
import io.papermc.paperweight.util.file
|
import io.papermc.paperweight.util.file
|
||||||
|
@ -33,7 +34,6 @@ import org.gradle.api.file.RegularFileProperty
|
||||||
import org.gradle.api.tasks.InputFile
|
import org.gradle.api.tasks.InputFile
|
||||||
import org.gradle.api.tasks.OutputFile
|
import org.gradle.api.tasks.OutputFile
|
||||||
import org.gradle.api.tasks.TaskAction
|
import org.gradle.api.tasks.TaskAction
|
||||||
import org.objectweb.asm.ClassReader
|
|
||||||
import org.objectweb.asm.ClassWriter
|
import org.objectweb.asm.ClassWriter
|
||||||
import org.objectweb.asm.Opcodes
|
import org.objectweb.asm.Opcodes
|
||||||
import org.objectweb.asm.Type
|
import org.objectweb.asm.Type
|
||||||
|
@ -75,8 +75,7 @@ abstract class FixJar : BaseTask(), AsmUtil {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
val node =
|
val node = classNodeCache.findClass(entry.name) ?: error("No ClassNode found for known entry")
|
||||||
classNodeCache.findClass(entry.name) ?: error("No ClassNode found for known entry")
|
|
||||||
|
|
||||||
ParameterAnnotationFixer(node).visitNode()
|
ParameterAnnotationFixer(node).visitNode()
|
||||||
OverrideAnnotationAdder(node, classNodeCache).visitNode()
|
OverrideAnnotationAdder(node, classNodeCache).visitNode()
|
||||||
|
@ -230,49 +229,3 @@ class OverrideAnnotationAdder(private val node: ClassNode, private val classNode
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ClassNodeCache(private val jarFile: JarFile, private val fallbackJar: JarFile) {
|
|
||||||
|
|
||||||
private val classNodeMap = hashMapOf<String, ClassNode?>()
|
|
||||||
|
|
||||||
fun findClass(name: String): ClassNode? {
|
|
||||||
return classNodeMap.computeIfAbsent(normalize(name)) { fileName ->
|
|
||||||
val classData = findClassData(fileName) ?: return@computeIfAbsent null
|
|
||||||
val classReader = ClassReader(classData)
|
|
||||||
val node = ClassNode(Opcodes.ASM9)
|
|
||||||
classReader.accept(node, 0)
|
|
||||||
return@computeIfAbsent node
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun findClassData(className: String): ByteArray? {
|
|
||||||
val entry = ZipEntry(className)
|
|
||||||
return (
|
|
||||||
jarFile.getInputStream(entry) // remapped class
|
|
||||||
?: fallbackJar.getInputStream(entry) // library class
|
|
||||||
?: ClassLoader.getSystemResourceAsStream(className)
|
|
||||||
)?.use { it.readBytes() } // JDK class
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun normalize(name: String): String {
|
|
||||||
var workingName = name
|
|
||||||
if (workingName.endsWith(".class")) {
|
|
||||||
workingName = workingName.substring(0, workingName.length - 6)
|
|
||||||
}
|
|
||||||
|
|
||||||
var startIndex = 0
|
|
||||||
var endIndex = workingName.length
|
|
||||||
if (workingName.startsWith('L')) {
|
|
||||||
startIndex = 1
|
|
||||||
}
|
|
||||||
if (workingName.endsWith(';')) {
|
|
||||||
endIndex--
|
|
||||||
}
|
|
||||||
|
|
||||||
return workingName.substring(startIndex, endIndex).replace('.', '/') + ".class"
|
|
||||||
}
|
|
||||||
|
|
||||||
fun clear() {
|
|
||||||
classNodeMap.clear()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -30,6 +30,7 @@ import io.papermc.paperweight.util.file
|
||||||
import io.papermc.paperweight.util.path
|
import io.papermc.paperweight.util.path
|
||||||
import java.nio.file.FileSystems
|
import java.nio.file.FileSystems
|
||||||
import java.nio.file.Files
|
import java.nio.file.Files
|
||||||
|
import java.util.zip.ZipFile
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import org.cadixdev.atlas.Atlas
|
import org.cadixdev.atlas.Atlas
|
||||||
import org.cadixdev.bombe.asm.jar.JarEntryRemappingTransformer
|
import org.cadixdev.bombe.asm.jar.JarEntryRemappingTransformer
|
||||||
|
@ -41,6 +42,7 @@ import org.cadixdev.lorenz.merge.MappingSetMergerHandler
|
||||||
import org.cadixdev.lorenz.merge.MergeConfig
|
import org.cadixdev.lorenz.merge.MergeConfig
|
||||||
import org.cadixdev.lorenz.merge.MergeContext
|
import org.cadixdev.lorenz.merge.MergeContext
|
||||||
import org.cadixdev.lorenz.merge.MergeResult
|
import org.cadixdev.lorenz.merge.MergeResult
|
||||||
|
import org.cadixdev.lorenz.merge.MethodMergeStrategy
|
||||||
import org.cadixdev.lorenz.model.ClassMapping
|
import org.cadixdev.lorenz.model.ClassMapping
|
||||||
import org.cadixdev.lorenz.model.FieldMapping
|
import org.cadixdev.lorenz.model.FieldMapping
|
||||||
import org.cadixdev.lorenz.model.InnerClassMapping
|
import org.cadixdev.lorenz.model.InnerClassMapping
|
||||||
|
@ -66,6 +68,8 @@ abstract class GenerateMappings : DefaultTask() {
|
||||||
abstract val vanillaMappings: RegularFileProperty
|
abstract val vanillaMappings: RegularFileProperty
|
||||||
@get:InputFile
|
@get:InputFile
|
||||||
abstract val paramMappings: RegularFileProperty
|
abstract val paramMappings: RegularFileProperty
|
||||||
|
@get:InputFile
|
||||||
|
abstract val methodOverrides: RegularFileProperty
|
||||||
|
|
||||||
@get:OutputFile
|
@get:OutputFile
|
||||||
abstract val outputMappings: RegularFileProperty
|
abstract val outputMappings: RegularFileProperty
|
||||||
|
@ -86,16 +90,17 @@ abstract class GenerateMappings : DefaultTask() {
|
||||||
vanillaMappings,
|
vanillaMappings,
|
||||||
paramMappings,
|
paramMappings,
|
||||||
MergeConfig.builder()
|
MergeConfig.builder()
|
||||||
|
.withMethodMergeStrategy(MethodMergeStrategy.STRICT)
|
||||||
.withFieldMergeStrategy(FieldMergeStrategy.STRICT)
|
.withFieldMergeStrategy(FieldMergeStrategy.STRICT)
|
||||||
.withMergeHandler(ParamsMergeHandler())
|
.withMergeHandler(ParamsMergeHandler())
|
||||||
.build()
|
.build()
|
||||||
).merge()
|
).merge()
|
||||||
|
|
||||||
|
ensureParentExists(outputMappings)
|
||||||
|
|
||||||
// Fill out any missing inheritance info in the mappings
|
// Fill out any missing inheritance info in the mappings
|
||||||
val tempMappingsFile = Files.createTempFile("mappings", "tiny")
|
val tempMappingsFile = Files.createTempFile("mappings", "tiny")
|
||||||
val tempMappingsOutputFile = Files.createTempFile("mappings-out", "tiny")
|
try {
|
||||||
|
|
||||||
val filledMerged = try {
|
|
||||||
MappingFormats.TINY.write(merged, tempMappingsFile, Constants.OBF_NAMESPACE, Constants.DEOBF_NAMESPACE)
|
MappingFormats.TINY.write(merged, tempMappingsFile, Constants.OBF_NAMESPACE, Constants.DEOBF_NAMESPACE)
|
||||||
|
|
||||||
val queue = workerExecutor.processIsolation {
|
val queue = workerExecutor.processIsolation {
|
||||||
|
@ -105,21 +110,65 @@ abstract class GenerateMappings : DefaultTask() {
|
||||||
queue.submit(AtlasAction::class) {
|
queue.submit(AtlasAction::class) {
|
||||||
inputJar.set(vanillaJar.file)
|
inputJar.set(vanillaJar.file)
|
||||||
mappingsFile.set(tempMappingsFile.toFile())
|
mappingsFile.set(tempMappingsFile.toFile())
|
||||||
outputMappingsFile.set(tempMappingsOutputFile.toFile())
|
outputMappingsFile.set(outputMappings.file)
|
||||||
}
|
}
|
||||||
|
|
||||||
queue.await()
|
queue.await()
|
||||||
|
|
||||||
MappingFormats.TINY.read(tempMappingsOutputFile, Constants.OBF_NAMESPACE, Constants.DEOBF_NAMESPACE)
|
|
||||||
} finally {
|
} finally {
|
||||||
Files.deleteIfExists(tempMappingsFile)
|
Files.deleteIfExists(tempMappingsFile)
|
||||||
Files.deleteIfExists(tempMappingsOutputFile)
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
private fun copyOverridenParamMappings(mappings: MappingSet) {
|
||||||
|
val methods = hashMapOf<String, String>()
|
||||||
|
methodOverrides.file.reader(Charsets.UTF_8).useLines { lines ->
|
||||||
|
lines.map { line ->
|
||||||
|
val (method, superMethod) = line.split("|")
|
||||||
|
methods[method] = superMethod
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ensureParentExists(outputMappings)
|
for (classMapping in mappings.topLevelClassMappings) {
|
||||||
MappingFormats.TINY.write(filledMerged, outputMappings.path, Constants.OBF_NAMESPACE, Constants.DEOBF_NAMESPACE)
|
copyOverridenParamMappings(mappings, classMapping, methods)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun copyOverridenParamMappings(mappingSet: MappingSet, classMapping: ClassMapping<*, *>, methods: Map<String, String>) {
|
||||||
|
methodLoop@for (mapping in classMapping.methodMappings) {
|
||||||
|
if (mapping.parameterMappings.isNotEmpty()) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
var baseClassName = classMapping.obfuscatedName
|
||||||
|
var baseMethod = mapping.obfuscatedName
|
||||||
|
var baseDesc = mapping.obfuscatedDescriptor
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
val superMethod = methods["$baseClassName,$baseMethod,$baseDesc"] ?: continue@methodLoop
|
||||||
|
val (className, methodName, methodDesc) = superMethod.split(",")
|
||||||
|
|
||||||
|
val superMethodMapping = mappingSet.getClassMapping(className).orNull?.getMethodMapping(methodName, methodDesc)?.orNull
|
||||||
|
if (superMethodMapping != null) {
|
||||||
|
superMethodMapping.parameterMappings.forEach { it.copy(mapping) }
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
// prevent possible infinite loops
|
||||||
|
if (baseClassName == className) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
baseClassName = className
|
||||||
|
baseMethod = methodName
|
||||||
|
baseDesc = methodDesc
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
classMapping.innerClassMappings.forEach { copyOverridenParamMappings(mappingSet, it, methods) }
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
abstract class AtlasAction : WorkAction<AtlasParameters> {
|
abstract class AtlasAction : WorkAction<AtlasParameters> {
|
||||||
override fun execute() {
|
override fun execute() {
|
||||||
val mappings = MappingFormats.TINY.read(parameters.mappingsFile.path, Constants.OBF_NAMESPACE, Constants.DEOBF_NAMESPACE)
|
val mappings = MappingFormats.TINY.read(parameters.mappingsFile.path, Constants.OBF_NAMESPACE, Constants.DEOBF_NAMESPACE)
|
||||||
|
@ -237,7 +286,7 @@ class ParamsMergeHandler : MappingSetMergerHandler {
|
||||||
): MergeResult<MethodMapping?> {
|
): MergeResult<MethodMapping?> {
|
||||||
return MergeResult(
|
return MergeResult(
|
||||||
target.createMethodMapping(left.signature, left.deobfuscatedName),
|
target.createMethodMapping(left.signature, left.deobfuscatedName),
|
||||||
listOfNotNull(standardRightDuplicate, wiggledRightDuplicate)
|
listOfNotNull(standardRightDuplicate)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -36,11 +36,16 @@ import java.nio.file.StandardCopyOption
|
||||||
import java.security.MessageDigest
|
import java.security.MessageDigest
|
||||||
import java.security.NoSuchAlgorithmException
|
import java.security.NoSuchAlgorithmException
|
||||||
import java.util.Properties
|
import java.util.Properties
|
||||||
|
import javax.inject.Inject
|
||||||
import kotlin.experimental.and
|
import kotlin.experimental.and
|
||||||
import org.gradle.api.file.RegularFileProperty
|
import org.gradle.api.file.RegularFileProperty
|
||||||
import org.gradle.api.provider.Property
|
import org.gradle.api.provider.Property
|
||||||
import org.gradle.api.tasks.Input
|
import org.gradle.api.tasks.Input
|
||||||
import org.gradle.api.tasks.InputFile
|
import org.gradle.api.tasks.InputFile
|
||||||
|
import org.gradle.kotlin.dsl.submit
|
||||||
|
import org.gradle.workers.WorkAction
|
||||||
|
import org.gradle.workers.WorkParameters
|
||||||
|
import org.gradle.workers.WorkerExecutor
|
||||||
|
|
||||||
abstract class GeneratePaperclipPatch : ZippedTask() {
|
abstract class GeneratePaperclipPatch : ZippedTask() {
|
||||||
|
|
||||||
|
@ -51,88 +56,122 @@ abstract class GeneratePaperclipPatch : ZippedTask() {
|
||||||
@get:Input
|
@get:Input
|
||||||
abstract val mcVersion: Property<String>
|
abstract val mcVersion: Property<String>
|
||||||
|
|
||||||
|
@get:Inject
|
||||||
|
abstract val workerExecutor: WorkerExecutor
|
||||||
|
|
||||||
override fun run(rootDir: File) {
|
override fun run(rootDir: File) {
|
||||||
val patchFile = rootDir.resolve("paperMC.patch").toPath()
|
val queue = workerExecutor.processIsolation {
|
||||||
val propFile = rootDir.resolve("patch.properties").toPath()
|
forkOptions.jvmArgs("-Xmx2G")
|
||||||
val protocol = rootDir.resolve("META-INF/$PROTOCOL_FILE").toPath()
|
|
||||||
|
|
||||||
val zipUri = try {
|
|
||||||
val jarUri = patchedJar.file.toURI()
|
|
||||||
URI("jar:${jarUri.scheme}", jarUri.path, null)
|
|
||||||
} catch (e: URISyntaxException) {
|
|
||||||
throw PaperweightException("Failed to create jar URI for $patchedJar", e)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
queue.submit(PaperclipAction::class) {
|
||||||
FileSystems.newFileSystem(zipUri, mapOf<String, Any>()).use { zipFs ->
|
zipRootDir.set(rootDir)
|
||||||
val protocolPath = zipFs.getPath("META-INF", PROTOCOL_FILE)
|
originalJar.set(this@GeneratePaperclipPatch.originalJar.file)
|
||||||
if (Files.notExists(protocolPath)) {
|
patchedJar.set(this@GeneratePaperclipPatch.patchedJar.file)
|
||||||
Files.deleteIfExists(protocol)
|
mcVersion.set(this@GeneratePaperclipPatch.mcVersion.get())
|
||||||
return@use
|
}
|
||||||
|
|
||||||
|
queue.await()
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class PaperclipAction : WorkAction<PaperclipParameters> {
|
||||||
|
override fun execute() {
|
||||||
|
val rootDir = parameters.zipRootDir.path
|
||||||
|
val originalJarPath = parameters.originalJar.path
|
||||||
|
val patchedJarPath = parameters.patchedJar.path
|
||||||
|
|
||||||
|
val patchFile = rootDir.resolve("paperMC.patch")
|
||||||
|
val propFile = rootDir.resolve("patch.properties")
|
||||||
|
val protocol = rootDir.resolve("META-INF/$PROTOCOL_FILE")
|
||||||
|
|
||||||
|
val zipUri = try {
|
||||||
|
val jarUri = patchedJarPath.toUri()
|
||||||
|
URI("jar:${jarUri.scheme}", jarUri.path, null)
|
||||||
|
} catch (e: URISyntaxException) {
|
||||||
|
throw PaperweightException("Failed to create jar URI for $patchedJarPath", e)
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
FileSystems.newFileSystem(zipUri, mapOf<String, Any>()).use { zipFs ->
|
||||||
|
val protocolPath = zipFs.getPath("META-INF", PROTOCOL_FILE)
|
||||||
|
if (Files.notExists(protocolPath)) {
|
||||||
|
Files.deleteIfExists(protocol)
|
||||||
|
return@use
|
||||||
|
}
|
||||||
|
|
||||||
|
Files.createDirectories(protocol.parent)
|
||||||
|
Files.copy(protocolPath, protocol, StandardCopyOption.REPLACE_EXISTING)
|
||||||
}
|
}
|
||||||
|
} catch (e: IOException) {
|
||||||
Files.createDirectories(protocol.parent)
|
throw PaperweightException("Failed to read $patchedJarPath contents", e)
|
||||||
Files.copy(protocolPath, protocol, StandardCopyOption.REPLACE_EXISTING)
|
|
||||||
}
|
}
|
||||||
} catch (e: IOException) {
|
|
||||||
throw PaperweightException("Failed to read $patchedJar contents", e)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read the files into memory
|
// Read the files into memory
|
||||||
println("Reading jars into memory")
|
println("Reading jars into memory")
|
||||||
val originalBytes = Files.readAllBytes(originalJar.path)
|
val originalBytes = Files.readAllBytes(originalJarPath)
|
||||||
val patchedBytes = Files.readAllBytes(patchedJar.path)
|
val patchedBytes = Files.readAllBytes(patchedJarPath)
|
||||||
|
|
||||||
println("Creating Paperclip patch")
|
println("Creating Paperclip patch")
|
||||||
try {
|
try {
|
||||||
Files.newOutputStream(patchFile).use { patchOutput ->
|
Files.newOutputStream(patchFile).use { patchOutput ->
|
||||||
Diff.diff(originalBytes, patchedBytes, patchOutput)
|
Diff.diff(originalBytes, patchedBytes, patchOutput)
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
throw PaperweightException("Error creating patch between $originalJarPath and $patchedJarPath", e)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the SHA-256 hashes for the files
|
||||||
|
val digestSha256 = try {
|
||||||
|
MessageDigest.getInstance("SHA-256")
|
||||||
|
} catch (e: NoSuchAlgorithmException) {
|
||||||
|
throw PaperweightException("Could not create SHA-256 hasher", e)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Vanilla's URL uses a SHA1 hash of the vanilla server jar
|
||||||
|
val digestSha1 = try {
|
||||||
|
MessageDigest.getInstance("SHA1")
|
||||||
|
} catch (e: NoSuchAlgorithmException) {
|
||||||
|
throw PaperweightException("Could not create SHA1 hasher", e)
|
||||||
|
}
|
||||||
|
|
||||||
|
println("Hashing files")
|
||||||
|
val originalSha1 = digestSha1.digest(originalBytes)
|
||||||
|
val originalSha256 = digestSha256.digest(originalBytes)
|
||||||
|
val patchedSha256 = digestSha256.digest(patchedBytes)
|
||||||
|
|
||||||
|
val prop = Properties()
|
||||||
|
prop["originalHash"] = toHex(originalSha256)
|
||||||
|
prop["patchedHash"] = toHex(patchedSha256)
|
||||||
|
prop["patch"] = "paperMC.patch"
|
||||||
|
prop["sourceUrl"] = "https://launcher.mojang.com/v1/objects/" + toHex(originalSha1).toLowerCase() + "/server.jar"
|
||||||
|
prop["version"] = parameters.mcVersion.get()
|
||||||
|
|
||||||
|
println("Writing properties file")
|
||||||
|
Files.newBufferedWriter(propFile).use { writer ->
|
||||||
|
prop.store(
|
||||||
|
writer,
|
||||||
|
"Default Paperclip launch values. Can be overridden by placing a paperclip.properties file in the server directory."
|
||||||
|
)
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
|
||||||
throw PaperweightException("Error creating patch between ${originalJar.path} and ${patchedJar.path}", e)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add the SHA-256 hashes for the files
|
private fun toHex(hash: ByteArray): String {
|
||||||
val digestSha256 = try {
|
val sb: StringBuilder = StringBuilder(hash.size * 2)
|
||||||
MessageDigest.getInstance("SHA-256")
|
for (aHash in hash) {
|
||||||
} catch (e: NoSuchAlgorithmException) {
|
sb.append("%02X".format(aHash and 0xFF.toByte()))
|
||||||
throw PaperweightException("Could not create SHA-256 hasher", e)
|
}
|
||||||
|
return sb.toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Vanilla's URL uses a SHA1 hash of the vanilla server jar
|
companion object {
|
||||||
val digestSha1 = try {
|
private const val PROTOCOL_FILE = "io.papermc.paper.daemon.protocol"
|
||||||
MessageDigest.getInstance("SHA1")
|
|
||||||
} catch (e: NoSuchAlgorithmException) {
|
|
||||||
throw PaperweightException("Could not create SHA1 hasher", e)
|
|
||||||
}
|
|
||||||
|
|
||||||
println("Hashing files")
|
|
||||||
val originalSha1 = digestSha1.digest(originalBytes)
|
|
||||||
val originalSha256 = digestSha256.digest(originalBytes)
|
|
||||||
val patchedSha256 = digestSha256.digest(patchedBytes)
|
|
||||||
|
|
||||||
val prop = Properties()
|
|
||||||
prop["originalHash"] = toHex(originalSha256)
|
|
||||||
prop["patchedHash"] = toHex(patchedSha256)
|
|
||||||
prop["patch"] = "paperMC.patch"
|
|
||||||
prop["sourceUrl"] = "https://launcher.mojang.com/v1/objects/" + toHex(originalSha1).toLowerCase() + "/server.jar"
|
|
||||||
prop["version"] = mcVersion.get()
|
|
||||||
|
|
||||||
println("Writing properties file")
|
|
||||||
Files.newBufferedWriter(propFile).use { writer ->
|
|
||||||
prop.store(writer, "Default Paperclip launch values. Can be overridden by placing a paperclip.properties file in the server directory.")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun toHex(hash: ByteArray): String {
|
interface PaperclipParameters : WorkParameters {
|
||||||
val sb: StringBuilder = StringBuilder(hash.size * 2)
|
val zipRootDir: RegularFileProperty
|
||||||
for (aHash in hash) {
|
val originalJar: RegularFileProperty
|
||||||
sb.append("%02X".format(aHash and 0xFF.toByte()))
|
val patchedJar: RegularFileProperty
|
||||||
}
|
val mcVersion: Property<String>
|
||||||
return sb.toString()
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
const val PROTOCOL_FILE = "io.papermc.paper.daemon.protocol"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,14 +22,16 @@
|
||||||
|
|
||||||
package io.papermc.paperweight.tasks
|
package io.papermc.paperweight.tasks
|
||||||
|
|
||||||
import io.papermc.paperweight.util.Constants
|
import io.papermc.paperweight.util.Constants.DEOBF_NAMESPACE
|
||||||
|
import io.papermc.paperweight.util.Constants.OBF_NAMESPACE
|
||||||
|
import io.papermc.paperweight.util.Constants.SPIGOT_NAMESPACE
|
||||||
import io.papermc.paperweight.util.MappingFormats
|
import io.papermc.paperweight.util.MappingFormats
|
||||||
|
import io.papermc.paperweight.util.MethodRef
|
||||||
import io.papermc.paperweight.util.emptyMergeResult
|
import io.papermc.paperweight.util.emptyMergeResult
|
||||||
import io.papermc.paperweight.util.file
|
import io.papermc.paperweight.util.file
|
||||||
import io.papermc.paperweight.util.orNull
|
import io.papermc.paperweight.util.orNull
|
||||||
import io.papermc.paperweight.util.parentClass
|
import io.papermc.paperweight.util.readOverrides
|
||||||
import io.papermc.paperweight.util.path
|
import io.papermc.paperweight.util.path
|
||||||
import org.cadixdev.bombe.type.signature.MethodSignature
|
|
||||||
import org.cadixdev.lorenz.MappingSet
|
import org.cadixdev.lorenz.MappingSet
|
||||||
import org.cadixdev.lorenz.merge.MappingSetMerger
|
import org.cadixdev.lorenz.merge.MappingSetMerger
|
||||||
import org.cadixdev.lorenz.merge.MappingSetMergerHandler
|
import org.cadixdev.lorenz.merge.MappingSetMergerHandler
|
||||||
|
@ -46,6 +48,8 @@ import org.gradle.api.file.RegularFileProperty
|
||||||
import org.gradle.api.tasks.InputFile
|
import org.gradle.api.tasks.InputFile
|
||||||
import org.gradle.api.tasks.OutputFile
|
import org.gradle.api.tasks.OutputFile
|
||||||
import org.gradle.api.tasks.TaskAction
|
import org.gradle.api.tasks.TaskAction
|
||||||
|
import org.jgrapht.graph.SimpleDirectedGraph
|
||||||
|
import org.jgrapht.traverse.DepthFirstIterator
|
||||||
|
|
||||||
abstract class GenerateSpigotMappings : DefaultTask() {
|
abstract class GenerateSpigotMappings : DefaultTask() {
|
||||||
|
|
||||||
|
@ -62,6 +66,8 @@ abstract class GenerateSpigotMappings : DefaultTask() {
|
||||||
abstract val paramIndexes: RegularFileProperty
|
abstract val paramIndexes: RegularFileProperty
|
||||||
@get:InputFile
|
@get:InputFile
|
||||||
abstract val syntheticMethods: RegularFileProperty
|
abstract val syntheticMethods: RegularFileProperty
|
||||||
|
@get:InputFile
|
||||||
|
abstract val methodOverrides: RegularFileProperty
|
||||||
|
|
||||||
@get:InputFile
|
@get:InputFile
|
||||||
abstract val sourceMappings: RegularFileProperty
|
abstract val sourceMappings: RegularFileProperty
|
||||||
|
@ -84,13 +90,10 @@ abstract class GenerateSpigotMappings : DefaultTask() {
|
||||||
// Get the new package name
|
// Get the new package name
|
||||||
val newPackage = packageMappings.asFile.get().readLines()[0].split(Regex("\\s+"))[1]
|
val newPackage = packageMappings.asFile.get().readLines()[0].split(Regex("\\s+"))[1]
|
||||||
|
|
||||||
val sourceMappings = MappingFormats.TINY.read(
|
val sourceMappings = MappingFormats.TINY.read(sourceMappings.path, OBF_NAMESPACE, DEOBF_NAMESPACE)
|
||||||
sourceMappings.path,
|
|
||||||
Constants.OBF_NAMESPACE,
|
|
||||||
Constants.DEOBF_NAMESPACE
|
|
||||||
)
|
|
||||||
|
|
||||||
val synths = hashMapOf<String, MutableMap<String, MutableMap<String, String>>>()
|
/*
|
||||||
|
val synths = newSynths()
|
||||||
syntheticMethods.file.useLines { lines ->
|
syntheticMethods.file.useLines { lines ->
|
||||||
for (line in lines) {
|
for (line in lines) {
|
||||||
val (className, desc, synthName, baseName) = line.split(" ")
|
val (className, desc, synthName, baseName) = line.split(" ")
|
||||||
|
@ -98,12 +101,15 @@ abstract class GenerateSpigotMappings : DefaultTask() {
|
||||||
.computeIfAbsent(desc) { hashMapOf() }[baseName] = synthName
|
.computeIfAbsent(desc) { hashMapOf() }[baseName] = synthName
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
val overrides = readOverrides(methodOverrides)
|
||||||
|
|
||||||
val notchToSpigotSet = MappingSetMerger.create(
|
val notchToSpigotSet = MappingSetMerger.create(
|
||||||
mergedMappingSet,
|
mergedMappingSet,
|
||||||
sourceMappings,
|
sourceMappings,
|
||||||
MergeConfig.builder()
|
MergeConfig.builder()
|
||||||
.withMergeHandler(SpigotMappingsMergerHandler(newPackage, synths))
|
.withMergeHandler(SpigotMappingsMergerHandler(newPackage, overrides))
|
||||||
.build()
|
.build()
|
||||||
).merge()
|
).merge()
|
||||||
|
|
||||||
|
@ -111,12 +117,7 @@ abstract class GenerateSpigotMappings : DefaultTask() {
|
||||||
val cleanedSourceMappings = removeLambdaMappings(adjustedSourceMappings)
|
val cleanedSourceMappings = removeLambdaMappings(adjustedSourceMappings)
|
||||||
val spigotToNamedSet = notchToSpigotSet.reverse().merge(cleanedSourceMappings)
|
val spigotToNamedSet = notchToSpigotSet.reverse().merge(cleanedSourceMappings)
|
||||||
|
|
||||||
MappingFormats.TINY.write(
|
MappingFormats.TINY.write(spigotToNamedSet, outputMappings.path, SPIGOT_NAMESPACE, DEOBF_NAMESPACE)
|
||||||
spigotToNamedSet,
|
|
||||||
outputMappings.path,
|
|
||||||
Constants.SPIGOT_NAMESPACE,
|
|
||||||
Constants.DEOBF_NAMESPACE
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun adjustParamIndexes(mappings: MappingSet): MappingSet {
|
private fun adjustParamIndexes(mappings: MappingSet): MappingSet {
|
||||||
|
@ -151,7 +152,7 @@ abstract class GenerateSpigotMappings : DefaultTask() {
|
||||||
params: Map<String, Map<String, Map<Int, Int>>>
|
params: Map<String, Map<String, Map<Int, Int>>>
|
||||||
) {
|
) {
|
||||||
for (mapping in from.fieldMappings) {
|
for (mapping in from.fieldMappings) {
|
||||||
to.createFieldMapping(mapping.signature, mapping.deobfuscatedName)
|
mapping.copy(to)
|
||||||
}
|
}
|
||||||
for (mapping in from.innerClassMappings) {
|
for (mapping in from.innerClassMappings) {
|
||||||
val newMapping = to.createInnerClassMapping(mapping.obfuscatedName, mapping.deobfuscatedName)
|
val newMapping = to.createInnerClassMapping(mapping.obfuscatedName, mapping.deobfuscatedName)
|
||||||
|
@ -169,7 +170,7 @@ abstract class GenerateSpigotMappings : DefaultTask() {
|
||||||
|
|
||||||
val methodMap = classMap[mapping.signature.toJvmsIdentifier()] ?: continue
|
val methodMap = classMap[mapping.signature.toJvmsIdentifier()] ?: continue
|
||||||
for (paramMapping in paramMappings) {
|
for (paramMapping in paramMappings) {
|
||||||
val i = methodMap[paramMapping.index] ?: paramMapping.index
|
val i = methodMap[paramMapping.index] ?: continue
|
||||||
newMapping.createParameterMapping(i, paramMapping.deobfuscatedName)
|
newMapping.createParameterMapping(i, paramMapping.deobfuscatedName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -210,11 +211,14 @@ abstract class GenerateSpigotMappings : DefaultTask() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
typealias Synths = Map<String, Map<String, Map<String, String>>>
|
typealias Synths = Map<String, Map<String, Map<String, String>>>
|
||||||
|
fun newSynths() = hashMapOf<String, MutableMap<String, MutableMap<String, String>>>()
|
||||||
|
*/
|
||||||
|
|
||||||
class SpigotMappingsMergerHandler(
|
class SpigotMappingsMergerHandler(
|
||||||
private val newPackage: String,
|
private val newPackage: String,
|
||||||
private val synths: Synths
|
private val methodOverrides: SimpleDirectedGraph<MethodRef, *>
|
||||||
) : MappingSetMergerHandler {
|
) : MappingSetMergerHandler {
|
||||||
|
|
||||||
override fun mergeTopLevelClassMappings(
|
override fun mergeTopLevelClassMappings(
|
||||||
|
@ -298,7 +302,7 @@ class SpigotMappingsMergerHandler(
|
||||||
target: ClassMapping<*, *>,
|
target: ClassMapping<*, *>,
|
||||||
context: MergeContext
|
context: MergeContext
|
||||||
): MergeResult<InnerClassMapping?> {
|
): MergeResult<InnerClassMapping?> {
|
||||||
// We want to get all of the inner classes from SRG, but not the SRG names
|
// We want to get all of the members from mojmap, but not the mojmap names
|
||||||
return MergeResult(target.createInnerClassMapping(right.obfuscatedName, right.obfuscatedName), right)
|
return MergeResult(target.createInnerClassMapping(right.obfuscatedName, right.obfuscatedName), right)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -320,6 +324,8 @@ class SpigotMappingsMergerHandler(
|
||||||
target: ClassMapping<*, *>,
|
target: ClassMapping<*, *>,
|
||||||
context: MergeContext
|
context: MergeContext
|
||||||
): FieldMapping {
|
): FieldMapping {
|
||||||
|
// Spigot mappings don't include signature, so see if we can pull the signature out of the mojmap
|
||||||
|
// Regardless, still keep Spigot's name
|
||||||
val right = strictRightDuplicate ?: looseRightDuplicate ?: strictRightContinuation ?: looseRightContinuation ?: left
|
val right = strictRightDuplicate ?: looseRightDuplicate ?: strictRightContinuation ?: looseRightContinuation ?: left
|
||||||
return target.createFieldMapping(right.signature, left.deobfuscatedName)
|
return target.createFieldMapping(right.signature, left.deobfuscatedName)
|
||||||
}
|
}
|
||||||
|
@ -342,33 +348,81 @@ class SpigotMappingsMergerHandler(
|
||||||
target: ClassMapping<*, *>,
|
target: ClassMapping<*, *>,
|
||||||
context: MergeContext
|
context: MergeContext
|
||||||
): MergeResult<MethodMapping?> {
|
): MergeResult<MethodMapping?> {
|
||||||
|
val ref = MethodRef.from(left.parent.obfuscatedName, left.signature)
|
||||||
|
val newMapping = if (methodOverrides.containsVertex(ref)) {
|
||||||
|
val methodRef = DepthFirstIterator(methodOverrides, ref).asSequence().firstOrNull() ?: ref
|
||||||
|
target.getOrCreateMethodMapping(methodRef.methodName, methodRef.methodDesc).also {
|
||||||
|
it.deobfuscatedName = left.deobfuscatedName
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
target.getOrCreateMethodMapping(left.signature).also {
|
||||||
|
it.deobfuscatedName = left.deobfuscatedName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return MergeResult(newMapping)
|
||||||
|
/*
|
||||||
|
val newMapping = target.getOrCreateMethodMapping(left.signature).also {
|
||||||
|
it.deobfuscatedName = left.deobfuscatedName
|
||||||
|
}
|
||||||
|
|
||||||
// Check if Spigot calls this mapping something else
|
// Check if Spigot calls this mapping something else
|
||||||
val synthMethods = synths[left.parent.fullObfuscatedName]?.get(left.obfuscatedDescriptor)
|
val synthMethods = synths[left.parent.fullObfuscatedName]?.get(left.obfuscatedDescriptor)
|
||||||
val newName = synthMethods?.get(left.obfuscatedName)
|
val newName = synthMethods?.get(left.obfuscatedName)
|
||||||
return if (newName != null) {
|
return if (newName != null) {
|
||||||
|
// Spigot does call this mapping something else, we need to find it first
|
||||||
val newLeftMapping = left.parentClass.getMethodMapping(MethodSignature(newName, left.descriptor)).orNull
|
val newLeftMapping = left.parentClass.getMethodMapping(MethodSignature(newName, left.descriptor)).orNull
|
||||||
val newMapping = if (newLeftMapping != null) {
|
val newMapping = if (newLeftMapping != null) {
|
||||||
|
// We found it, use it for the left side instead
|
||||||
target.getOrCreateMethodMapping(newLeftMapping.signature).also {
|
target.getOrCreateMethodMapping(newLeftMapping.signature).also {
|
||||||
it.deobfuscatedName = left.deobfuscatedName
|
it.deobfuscatedName = left.deobfuscatedName
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
// Can't find the name spigot uses, check the hierarchy
|
||||||
|
|
||||||
|
val methodRef = MethodRef(newMapping.parent.obfuscatedName, newMapping.obfuscatedName, newMapping.descriptor.toString())
|
||||||
|
|
||||||
target.getOrCreateMethodMapping(left.signature).also {
|
target.getOrCreateMethodMapping(left.signature).also {
|
||||||
it.deobfuscatedName = newName
|
it.deobfuscatedName = newName
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val methodRef = MethodRef(newMapping.parent.obfuscatedName, newMapping.obfuscatedName, newMapping.descriptor.toString())
|
||||||
|
if (methodOverrides.containsVertex(methodRef)) {
|
||||||
|
for ((superClass, superName, superDesc) in BreadthFirstIterator(methodOverrides, methodRef)) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
MergeResult(newMapping)
|
MergeResult(newMapping)
|
||||||
} else {
|
} else {
|
||||||
|
// normal mapping
|
||||||
val newMapping = target.getOrCreateMethodMapping(left.signature).also {
|
val newMapping = target.getOrCreateMethodMapping(left.signature).also {
|
||||||
it.deobfuscatedName = left.deobfuscatedName
|
it.deobfuscatedName = left.deobfuscatedName
|
||||||
}
|
}
|
||||||
return MergeResult(newMapping)
|
return MergeResult(newMapping)
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
override fun addLeftMethodMapping(
|
override fun addLeftMethodMapping(
|
||||||
left: MethodMapping,
|
left: MethodMapping,
|
||||||
target: ClassMapping<*, *>,
|
target: ClassMapping<*, *>,
|
||||||
context: MergeContext
|
context: MergeContext
|
||||||
): MergeResult<MethodMapping?> {
|
): MergeResult<MethodMapping?> {
|
||||||
|
val ref = MethodRef.from(left.parent.obfuscatedName, left.signature)
|
||||||
|
val newMapping = if (methodOverrides.containsVertex(ref)) {
|
||||||
|
val methodRef = DepthFirstIterator(methodOverrides, ref).asSequence().firstOrNull() ?: ref
|
||||||
|
target.getOrCreateMethodMapping(methodRef.methodName, methodRef.methodDesc).also {
|
||||||
|
it.deobfuscatedName = left.deobfuscatedName
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
target.getOrCreateMethodMapping(left.signature).also {
|
||||||
|
it.deobfuscatedName = left.deobfuscatedName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return MergeResult(newMapping)
|
||||||
|
|
||||||
|
/*
|
||||||
// Check if Spigot maps this from a synthetic method name
|
// Check if Spigot maps this from a synthetic method name
|
||||||
var obfName: String? = null
|
var obfName: String? = null
|
||||||
val synthMethods = synths[left.parent.fullObfuscatedName]?.get(left.obfuscatedDescriptor)
|
val synthMethods = synths[left.parent.fullObfuscatedName]?.get(left.obfuscatedDescriptor)
|
||||||
|
@ -383,12 +437,14 @@ class SpigotMappingsMergerHandler(
|
||||||
}
|
}
|
||||||
|
|
||||||
if (obfName == null) {
|
if (obfName == null) {
|
||||||
|
// This mapping doesn't actually exist, drop it
|
||||||
return emptyMergeResult()
|
return emptyMergeResult()
|
||||||
}
|
}
|
||||||
|
|
||||||
val newMapping = target.getOrCreateMethodMapping(obfName, left.descriptor)
|
val newMapping = target.getOrCreateMethodMapping(obfName, left.descriptor)
|
||||||
newMapping.deobfuscatedName = left.deobfuscatedName
|
newMapping.deobfuscatedName = left.deobfuscatedName
|
||||||
return MergeResult(newMapping)
|
return MergeResult(newMapping)
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun addLeftFieldMapping(left: FieldMapping, target: ClassMapping<*, *>, context: MergeContext): FieldMapping? {
|
override fun addLeftFieldMapping(left: FieldMapping, target: ClassMapping<*, *>, context: MergeContext): FieldMapping? {
|
||||||
|
@ -410,6 +466,20 @@ class SpigotMappingsMergerHandler(
|
||||||
target: ClassMapping<*, *>,
|
target: ClassMapping<*, *>,
|
||||||
context: MergeContext
|
context: MergeContext
|
||||||
): MergeResult<MethodMapping?> {
|
): MergeResult<MethodMapping?> {
|
||||||
|
val ref = MethodRef.from(right.parent.obfuscatedName, right.signature)
|
||||||
|
if (methodOverrides.containsVertex(ref)) {
|
||||||
|
val methodRef = DepthFirstIterator(methodOverrides, ref).asSequence().firstOrNull() ?: ref
|
||||||
|
val originalMapping = context.left.getClassMapping(methodRef.className).orNull
|
||||||
|
?.getMethodMapping(methodRef.methodName, methodRef.methodDesc)?.orNull ?: return emptyMergeResult()
|
||||||
|
val newMapping = target.getOrCreateMethodMapping(right.signature).also {
|
||||||
|
it.deobfuscatedName = originalMapping.deobfuscatedName
|
||||||
|
}
|
||||||
|
return MergeResult(newMapping)
|
||||||
|
} else {
|
||||||
|
return emptyMergeResult()
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
// Check if spigot changes this method automatically
|
// Check if spigot changes this method automatically
|
||||||
val synthMethods = synths[right.parentClass.fullObfuscatedName]?.get(right.obfuscatedDescriptor)
|
val synthMethods = synths[right.parentClass.fullObfuscatedName]?.get(right.obfuscatedDescriptor)
|
||||||
val newName = synthMethods?.get(right.obfuscatedName) ?: return emptyMergeResult()
|
val newName = synthMethods?.get(right.obfuscatedName) ?: return emptyMergeResult()
|
||||||
|
@ -423,6 +493,7 @@ class SpigotMappingsMergerHandler(
|
||||||
newMapping.deobfuscatedName = newName
|
newMapping.deobfuscatedName = newName
|
||||||
}
|
}
|
||||||
return MergeResult(newMapping)
|
return MergeResult(newMapping)
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun prependPackage(name: String): String {
|
private fun prependPackage(name: String): String {
|
||||||
|
|
|
@ -23,11 +23,16 @@
|
||||||
package io.papermc.paperweight.tasks
|
package io.papermc.paperweight.tasks
|
||||||
|
|
||||||
import io.papermc.paperweight.util.AsmUtil
|
import io.papermc.paperweight.util.AsmUtil
|
||||||
|
import io.papermc.paperweight.util.ClassNodeCache
|
||||||
import io.papermc.paperweight.util.MavenArtifact
|
import io.papermc.paperweight.util.MavenArtifact
|
||||||
|
import io.papermc.paperweight.util.MethodRef
|
||||||
import io.papermc.paperweight.util.SyntheticUtil
|
import io.papermc.paperweight.util.SyntheticUtil
|
||||||
import io.papermc.paperweight.util.defaultOutput
|
import io.papermc.paperweight.util.defaultOutput
|
||||||
import io.papermc.paperweight.util.file
|
import io.papermc.paperweight.util.file
|
||||||
import io.papermc.paperweight.util.isLibraryJar
|
import io.papermc.paperweight.util.isLibraryJar
|
||||||
|
import io.papermc.paperweight.util.writeOverrides
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger
|
||||||
|
import java.util.jar.JarFile
|
||||||
import java.util.zip.ZipFile
|
import java.util.zip.ZipFile
|
||||||
import org.gradle.api.file.DirectoryProperty
|
import org.gradle.api.file.DirectoryProperty
|
||||||
import org.gradle.api.file.RegularFileProperty
|
import org.gradle.api.file.RegularFileProperty
|
||||||
|
@ -35,13 +40,15 @@ import org.gradle.api.tasks.InputDirectory
|
||||||
import org.gradle.api.tasks.InputFile
|
import org.gradle.api.tasks.InputFile
|
||||||
import org.gradle.api.tasks.OutputFile
|
import org.gradle.api.tasks.OutputFile
|
||||||
import org.gradle.api.tasks.TaskAction
|
import org.gradle.api.tasks.TaskAction
|
||||||
import org.objectweb.asm.ClassReader
|
import org.jgrapht.graph.DefaultEdge
|
||||||
import org.objectweb.asm.ClassVisitor
|
import org.jgrapht.graph.SimpleDirectedGraph
|
||||||
import org.objectweb.asm.FieldVisitor
|
import org.jgrapht.nio.Attribute
|
||||||
import org.objectweb.asm.MethodVisitor
|
import org.jgrapht.nio.DefaultAttribute
|
||||||
|
import org.jgrapht.nio.dot.DOTExporter
|
||||||
|
import org.jgrapht.nio.dot.DOTImporter
|
||||||
import org.objectweb.asm.Opcodes
|
import org.objectweb.asm.Opcodes
|
||||||
import org.objectweb.asm.Type
|
import org.objectweb.asm.Type
|
||||||
import org.objectweb.asm.tree.MethodNode
|
import org.objectweb.asm.tree.ClassNode
|
||||||
|
|
||||||
abstract class InspectVanillaJar : BaseTask() {
|
abstract class InspectVanillaJar : BaseTask() {
|
||||||
|
|
||||||
|
@ -59,12 +66,15 @@ abstract class InspectVanillaJar : BaseTask() {
|
||||||
@get:OutputFile
|
@get:OutputFile
|
||||||
abstract val syntheticMethods: RegularFileProperty
|
abstract val syntheticMethods: RegularFileProperty
|
||||||
@get:OutputFile
|
@get:OutputFile
|
||||||
|
abstract val methodOverrides: RegularFileProperty
|
||||||
|
@get:OutputFile
|
||||||
abstract val serverLibraries: RegularFileProperty
|
abstract val serverLibraries: RegularFileProperty
|
||||||
|
|
||||||
override fun init() {
|
override fun init() {
|
||||||
loggerFile.convention(defaultOutput("$name-loggerFields", "txt"))
|
loggerFile.convention(defaultOutput("$name-loggerFields", "txt"))
|
||||||
paramIndexes.convention(defaultOutput("$name-paramIndexes", "txt"))
|
paramIndexes.convention(defaultOutput("$name-paramIndexes", "txt"))
|
||||||
syntheticMethods.convention(defaultOutput("$name-syntheticMethods", "txt"))
|
syntheticMethods.convention(defaultOutput("$name-syntheticMethods", "txt"))
|
||||||
|
methodOverrides.convention(defaultOutput("$name-methodOverrides", "json"))
|
||||||
}
|
}
|
||||||
|
|
||||||
@TaskAction
|
@TaskAction
|
||||||
|
@ -72,28 +82,32 @@ abstract class InspectVanillaJar : BaseTask() {
|
||||||
val loggers = mutableListOf<LoggerFields.Data>()
|
val loggers = mutableListOf<LoggerFields.Data>()
|
||||||
val params = mutableListOf<ParamIndexes.Data>()
|
val params = mutableListOf<ParamIndexes.Data>()
|
||||||
val synthMethods = mutableListOf<SyntheticMethods.Data>()
|
val synthMethods = mutableListOf<SyntheticMethods.Data>()
|
||||||
|
val overrides = SimpleDirectedGraph<MethodRef, DefaultEdge>(DefaultEdge::class.java)
|
||||||
|
|
||||||
var visitor: ClassVisitor
|
JarFile(inputJar.file).use { jarFile ->
|
||||||
visitor = LoggerFields.Visitor(null, loggers)
|
val classNodeCache = ClassNodeCache(jarFile)
|
||||||
visitor = ParamIndexes.Visitor(visitor, params)
|
|
||||||
visitor = SyntheticMethods.Visitor(visitor, synthMethods)
|
|
||||||
|
|
||||||
archives.zipTree(inputJar.file).matching {
|
for (entry in jarFile.entries()) {
|
||||||
include("/*.class")
|
if (!entry.name.endsWith(".class")) {
|
||||||
include("/net/minecraft/**/*.class")
|
continue
|
||||||
}.forEach { file ->
|
}
|
||||||
if (file.isDirectory) {
|
if (entry.name.count { it == '/' } > 0 && !entry.name.startsWith("net/minecraft/")) {
|
||||||
return@forEach
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
val node = classNodeCache.findClass(entry.name) ?: error("No ClassNode found for known entry")
|
||||||
|
|
||||||
|
LoggerFields.Visitor.visit(node, loggers)
|
||||||
|
ParamIndexes.Visitor.visit(node, params)
|
||||||
|
SyntheticMethods.Visitor.visit(node, synthMethods)
|
||||||
|
MethodOverrides.Visitor.visit(node, classNodeCache, overrides)
|
||||||
}
|
}
|
||||||
val classData = file.readBytes()
|
|
||||||
|
|
||||||
ClassReader(classData).accept(visitor, 0)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val serverLibs = checkLibraries()
|
val serverLibs = checkLibraries()
|
||||||
|
|
||||||
loggerFile.file.bufferedWriter(Charsets.UTF_8).use { writer ->
|
loggers.sort()
|
||||||
loggers.sort()
|
loggerFile.file.bufferedWriter().use { writer ->
|
||||||
for (loggerField in loggers) {
|
for (loggerField in loggers) {
|
||||||
writer.append(loggerField.className)
|
writer.append(loggerField.className)
|
||||||
writer.append(' ')
|
writer.append(' ')
|
||||||
|
@ -102,8 +116,8 @@ abstract class InspectVanillaJar : BaseTask() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
paramIndexes.file.bufferedWriter(Charsets.UTF_8).use { writer ->
|
params.sort()
|
||||||
params.sort()
|
paramIndexes.file.bufferedWriter().use { writer ->
|
||||||
for (methodData in params) {
|
for (methodData in params) {
|
||||||
writer.append(methodData.className)
|
writer.append(methodData.className)
|
||||||
writer.append(' ')
|
writer.append(' ')
|
||||||
|
@ -120,8 +134,8 @@ abstract class InspectVanillaJar : BaseTask() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
syntheticMethods.file.bufferedWriter(Charsets.UTF_8).use { writer ->
|
synthMethods.sort()
|
||||||
synthMethods.sort()
|
syntheticMethods.file.bufferedWriter().use { writer ->
|
||||||
for ((className, desc, synthName, baseName) in synthMethods) {
|
for ((className, desc, synthName, baseName) in synthMethods) {
|
||||||
writer.append(className)
|
writer.append(className)
|
||||||
writer.append(' ')
|
writer.append(' ')
|
||||||
|
@ -134,8 +148,11 @@ abstract class InspectVanillaJar : BaseTask() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
serverLibraries.file.bufferedWriter(Charsets.UTF_8).use { writer ->
|
writeOverrides(overrides, methodOverrides)
|
||||||
serverLibs.map { it.toString() }.sorted().forEach { artifact ->
|
|
||||||
|
val libs = serverLibs.map { it.toString() }.sorted()
|
||||||
|
serverLibraries.file.bufferedWriter().use { writer ->
|
||||||
|
libs.forEach { artifact ->
|
||||||
writer.appendln(artifact)
|
writer.appendln(artifact)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -173,50 +190,22 @@ abstract class InspectVanillaJar : BaseTask() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class BaseClassVisitor(classVisitor: ClassVisitor?) : ClassVisitor(Opcodes.ASM8, classVisitor), AsmUtil {
|
|
||||||
protected var currentClass: String? = null
|
|
||||||
|
|
||||||
override fun visit(
|
|
||||||
version: Int,
|
|
||||||
access: Int,
|
|
||||||
name: String,
|
|
||||||
signature: String?,
|
|
||||||
superName: String?,
|
|
||||||
interfaces: Array<out String>?
|
|
||||||
) {
|
|
||||||
this.currentClass = name
|
|
||||||
super.visit(version, access, name, signature, superName, interfaces)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* SpecialSource2 automatically maps all Logger fields to the name LOGGER, without needing mappings defined, so we need
|
* SpecialSource2 automatically maps all Logger fields to the name LOGGER, without needing mappings defined, so we need
|
||||||
* to make a note of all of those fields
|
* to make a note of all of those fields
|
||||||
*/
|
*/
|
||||||
object LoggerFields {
|
object LoggerFields {
|
||||||
class Visitor(
|
object Visitor : AsmUtil {
|
||||||
classVisitor: ClassVisitor?,
|
|
||||||
private val fields: MutableList<Data>
|
|
||||||
) : BaseClassVisitor(classVisitor) {
|
|
||||||
|
|
||||||
override fun visitField(
|
fun visit(node: ClassNode, fields: MutableList<Data>) {
|
||||||
access: Int,
|
for (field in node.fields) {
|
||||||
name: String,
|
if (Opcodes.ACC_STATIC !in field.access || Opcodes.ACC_FINAL !in field.access) {
|
||||||
descriptor: String,
|
continue
|
||||||
signature: String?,
|
}
|
||||||
value: Any?
|
if (field.desc == "Lorg/apache/logging/log4j/Logger;") {
|
||||||
): FieldVisitor? {
|
fields += Data(node.name, field.name)
|
||||||
val ret = super.visitField(access, name, descriptor, signature, value)
|
}
|
||||||
val className = currentClass ?: return ret
|
|
||||||
|
|
||||||
if (Opcodes.ACC_STATIC !in access || Opcodes.ACC_FINAL !in access) {
|
|
||||||
return ret
|
|
||||||
}
|
}
|
||||||
if (descriptor != "Lorg/apache/logging/log4j/Logger;") {
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
fields += Data(className, name)
|
|
||||||
return ret
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -235,45 +224,33 @@ object LoggerFields {
|
||||||
* actual bytecode to translate the LVT-based indexes back to 0-based param indexes.
|
* actual bytecode to translate the LVT-based indexes back to 0-based param indexes.
|
||||||
*/
|
*/
|
||||||
object ParamIndexes {
|
object ParamIndexes {
|
||||||
class Visitor(
|
object Visitor : AsmUtil {
|
||||||
classVisitor: ClassVisitor?,
|
|
||||||
private val methods: MutableList<Data>
|
|
||||||
) : BaseClassVisitor(classVisitor) {
|
|
||||||
|
|
||||||
override fun visitMethod(
|
fun visit(node: ClassNode, methods: MutableList<Data>) {
|
||||||
access: Int,
|
for (method in node.methods) {
|
||||||
name: String,
|
val isStatic = Opcodes.ACC_STATIC in method.access
|
||||||
descriptor: String,
|
var currentIndex = if (isStatic) 0 else 1
|
||||||
signature: String?,
|
|
||||||
exceptions: Array<out String>?
|
|
||||||
): MethodVisitor? {
|
|
||||||
val ret = super.visitMethod(access, name, descriptor, signature, exceptions)
|
|
||||||
val className = currentClass ?: return ret
|
|
||||||
|
|
||||||
val isStatic = access and Opcodes.ACC_STATIC != 0
|
val types = Type.getArgumentTypes(method.desc)
|
||||||
var currentIndex = if (isStatic) 0 else 1
|
if (types.isEmpty()) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
val types = Type.getArgumentTypes(descriptor)
|
val params = ArrayList<ParamTarget>(types.size)
|
||||||
if (types.isEmpty()) {
|
val data = Data(node.name, method.name, method.desc, params)
|
||||||
return ret
|
methods += data
|
||||||
}
|
|
||||||
|
|
||||||
val params = ArrayList<ParamTarget>(types.size)
|
for (i in types.indices) {
|
||||||
val data = Data(className, name, descriptor, params)
|
params += ParamTarget(currentIndex, i)
|
||||||
methods += data
|
|
||||||
|
|
||||||
for (i in types.indices) {
|
|
||||||
params += ParamTarget(currentIndex, i)
|
|
||||||
currentIndex++
|
|
||||||
|
|
||||||
// Figure out if we should skip the next index
|
|
||||||
val type = types[i]
|
|
||||||
if (type === Type.LONG_TYPE || type === Type.DOUBLE_TYPE) {
|
|
||||||
currentIndex++
|
currentIndex++
|
||||||
|
|
||||||
|
// Figure out if we should skip the next index
|
||||||
|
val type = types[i]
|
||||||
|
if (type === Type.LONG_TYPE || type === Type.DOUBLE_TYPE) {
|
||||||
|
currentIndex++
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -303,45 +280,20 @@ object ParamIndexes {
|
||||||
* can handle it in our generated mappings.
|
* can handle it in our generated mappings.
|
||||||
*/
|
*/
|
||||||
object SyntheticMethods {
|
object SyntheticMethods {
|
||||||
class Visitor(
|
object Visitor : AsmUtil {
|
||||||
classVisitor: ClassVisitor?,
|
|
||||||
private val methods: MutableList<Data>
|
|
||||||
) : BaseClassVisitor(classVisitor) {
|
|
||||||
|
|
||||||
override fun visitMethod(
|
fun visit(node: ClassNode, methods: MutableList<Data>) {
|
||||||
access: Int,
|
for (method in node.methods) {
|
||||||
name: String,
|
if (Opcodes.ACC_SYNTHETIC !in method.access || Opcodes.ACC_BRIDGE in method.access || method.name.contains('$')) {
|
||||||
descriptor: String,
|
continue
|
||||||
signature: String?,
|
}
|
||||||
exceptions: Array<out String>?
|
|
||||||
): MethodVisitor? {
|
|
||||||
val ret = super.visitMethod(access, name, descriptor, signature, exceptions)
|
|
||||||
val className = currentClass ?: return ret
|
|
||||||
|
|
||||||
if (Opcodes.ACC_SYNTHETIC !in access || Opcodes.ACC_BRIDGE in access || name.contains('$')) {
|
val (baseName, baseDesc) = SyntheticUtil.findBaseMethod(method, node.name)
|
||||||
return ret
|
|
||||||
}
|
|
||||||
|
|
||||||
return SynthMethodVisitor(access, name, descriptor, signature, exceptions, className, methods)
|
if (baseName != method.name || baseDesc != method.desc) {
|
||||||
}
|
// Add this method as a synthetic for baseName
|
||||||
}
|
methods += Data(node.name, baseDesc, method.name, baseName)
|
||||||
|
}
|
||||||
private class SynthMethodVisitor(
|
|
||||||
access: Int,
|
|
||||||
name: String,
|
|
||||||
descriptor: String,
|
|
||||||
signature: String?,
|
|
||||||
exceptions: Array<out String>?,
|
|
||||||
private val className: String,
|
|
||||||
private val methods: MutableList<Data>
|
|
||||||
) : MethodNode(Opcodes.ASM9, access, name, descriptor, signature, exceptions) {
|
|
||||||
|
|
||||||
override fun visitEnd() {
|
|
||||||
val (baseName, baseDesc) = SyntheticUtil.findBaseMethod(this, className)
|
|
||||||
|
|
||||||
if (baseName != name || baseDesc != desc) {
|
|
||||||
// Add this method as a synthetic for baseName
|
|
||||||
methods += Data(className, baseDesc, name, baseName)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -362,3 +314,68 @@ object SyntheticMethods {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* There's no direct marker in bytecode to know if a
|
||||||
|
*/
|
||||||
|
object MethodOverrides {
|
||||||
|
object Visitor : AsmUtil {
|
||||||
|
|
||||||
|
fun visit(
|
||||||
|
node: ClassNode,
|
||||||
|
classNodeCache: ClassNodeCache,
|
||||||
|
methodOverrides: SimpleDirectedGraph<MethodRef, DefaultEdge>
|
||||||
|
) {
|
||||||
|
val superMethods = collectSuperMethods(node, classNodeCache)
|
||||||
|
|
||||||
|
val disqualifiedMethods = Opcodes.ACC_STATIC or Opcodes.ACC_PRIVATE
|
||||||
|
for (method in node.methods) {
|
||||||
|
if (method.access in disqualifiedMethods) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if (method.name == "<init>" || method.name == "<clinit>") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
val (name, desc) = SyntheticUtil.findBaseMethod(method, node.name)
|
||||||
|
|
||||||
|
superMethods[method.name + method.desc]?.let { className ->
|
||||||
|
val targetMethod = node.methods.firstOrNull { it.name == name && it.desc == desc } ?: method
|
||||||
|
|
||||||
|
val methodRef = MethodRef(node.name, method.name, method.desc)
|
||||||
|
val targetMethodRef = MethodRef(className, targetMethod.name, targetMethod.desc)
|
||||||
|
methodOverrides.addVertex(methodRef)
|
||||||
|
methodOverrides.addVertex(targetMethodRef)
|
||||||
|
methodOverrides.addEdge(methodRef, targetMethodRef)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun collectSuperMethods(node: ClassNode, classNodeCache: ClassNodeCache): Map<String, String> {
|
||||||
|
fun collectSuperMethods(node: ClassNode, superMethods: HashMap<String, String>) {
|
||||||
|
val supers = listOfNotNull(node.superName, *node.interfaces.toTypedArray())
|
||||||
|
if (supers.isEmpty()) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val disqualifiedMethods = Opcodes.ACC_STATIC or Opcodes.ACC_PRIVATE
|
||||||
|
val superNodes = supers.mapNotNull { classNodeCache.findClass(it) }
|
||||||
|
for (superNode in superNodes) {
|
||||||
|
superNode.methods.asSequence()
|
||||||
|
.filter { method -> method.access !in disqualifiedMethods }
|
||||||
|
.filter { method -> method.name != "<init>" && method.name != "<clinit>" }
|
||||||
|
.map { method -> method.name + method.desc to superNode.name }
|
||||||
|
.toMap(superMethods)
|
||||||
|
}
|
||||||
|
|
||||||
|
for (superNode in superNodes) {
|
||||||
|
collectSuperMethods(superNode, superMethods)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val result = hashMapOf<String, String>()
|
||||||
|
collectSuperMethods(node, result)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
53
src/main/kotlin/util/ClassNodeCache.kt
Normal file
53
src/main/kotlin/util/ClassNodeCache.kt
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
package io.papermc.paperweight.util
|
||||||
|
|
||||||
|
import java.util.jar.JarFile
|
||||||
|
import java.util.zip.ZipEntry
|
||||||
|
import org.objectweb.asm.ClassReader
|
||||||
|
import org.objectweb.asm.Opcodes
|
||||||
|
import org.objectweb.asm.tree.ClassNode
|
||||||
|
|
||||||
|
class ClassNodeCache(private val jarFile: JarFile, private val fallbackJar: JarFile? = null) {
|
||||||
|
|
||||||
|
private val classNodeMap = hashMapOf<String, ClassNode?>()
|
||||||
|
|
||||||
|
fun findClass(name: String): ClassNode? {
|
||||||
|
return classNodeMap.computeIfAbsent(normalize(name)) { fileName ->
|
||||||
|
val classData = findClassData(fileName) ?: return@computeIfAbsent null
|
||||||
|
val classReader = ClassReader(classData)
|
||||||
|
val node = ClassNode(Opcodes.ASM9)
|
||||||
|
classReader.accept(node, 0)
|
||||||
|
return@computeIfAbsent node
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun findClassData(className: String): ByteArray? {
|
||||||
|
val entry = ZipEntry(className)
|
||||||
|
return (
|
||||||
|
jarFile.getInputStream(entry) // remapped class
|
||||||
|
?: fallbackJar?.getInputStream(entry) // library class
|
||||||
|
?: ClassLoader.getSystemResourceAsStream(className) // JDK class
|
||||||
|
)?.use { it.readBytes() }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun normalize(name: String): String {
|
||||||
|
var workingName = name
|
||||||
|
if (workingName.endsWith(".class")) {
|
||||||
|
workingName = workingName.substring(0, workingName.length - 6)
|
||||||
|
}
|
||||||
|
|
||||||
|
var startIndex = 0
|
||||||
|
var endIndex = workingName.length
|
||||||
|
if (workingName.startsWith('L')) {
|
||||||
|
startIndex = 1
|
||||||
|
}
|
||||||
|
if (workingName.endsWith(';')) {
|
||||||
|
endIndex--
|
||||||
|
}
|
||||||
|
|
||||||
|
return workingName.substring(startIndex, endIndex).replace('.', '/') + ".class"
|
||||||
|
}
|
||||||
|
|
||||||
|
fun clear() {
|
||||||
|
classNodeMap.clear()
|
||||||
|
}
|
||||||
|
}
|
40
src/main/kotlin/util/Methodref.kt
Normal file
40
src/main/kotlin/util/Methodref.kt
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
package io.papermc.paperweight.util
|
||||||
|
|
||||||
|
import org.cadixdev.bombe.type.signature.MethodSignature
|
||||||
|
import org.jgrapht.Graph
|
||||||
|
import org.jgrapht.graph.DefaultEdge
|
||||||
|
import org.jgrapht.graph.SimpleDirectedGraph
|
||||||
|
import org.jgrapht.nio.dot.DOTImporter
|
||||||
|
import org.jgrapht.nio.gml.GmlExporter
|
||||||
|
import org.jgrapht.nio.gml.GmlImporter
|
||||||
|
import org.jgrapht.nio.json.JSONExporter
|
||||||
|
import org.jgrapht.nio.json.JSONImporter
|
||||||
|
|
||||||
|
data class MethodRef(val className: String, val methodName: String, val methodDesc: String) {
|
||||||
|
companion object {
|
||||||
|
fun from(className: String, sig: MethodSignature): MethodRef {
|
||||||
|
return MethodRef(className, sig.name, sig.descriptor.toString())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun writeOverrides(graph: Graph<MethodRef, DefaultEdge>, file: Any) {
|
||||||
|
val exporter = JSONExporter<MethodRef, DefaultEdge> { ref -> "${ref.className} ${ref.methodName} ${ref.methodDesc}" }
|
||||||
|
file.convertToFile().bufferedWriter().use { writer ->
|
||||||
|
exporter.exportGraph(graph, writer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun readOverrides(file: Any): SimpleDirectedGraph<MethodRef, *> {
|
||||||
|
val importer = JSONImporter<MethodRef, DefaultEdge>()
|
||||||
|
importer.setVertexFactory {
|
||||||
|
val (className, methodName, methodDesc) = it.split(" ")
|
||||||
|
MethodRef(className, methodName, methodDesc)
|
||||||
|
}
|
||||||
|
val overrides = SimpleDirectedGraph<MethodRef, DefaultEdge>(DefaultEdge::class.java)
|
||||||
|
file.convertToFile().bufferedReader().use { reader ->
|
||||||
|
importer.importGraph(overrides, reader)
|
||||||
|
}
|
||||||
|
|
||||||
|
return overrides
|
||||||
|
}
|
|
@ -168,16 +168,21 @@ fun <T> emptyMergeResult(): MergeResult<T?> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// We have to create our own task delegate because the ones Gradle provides don't work in plugin dev environments
|
// We have to create our own task delegate because the ones Gradle provides don't work in plugin dev environments
|
||||||
inline fun <reified T : Task> TaskContainer.registering(noinline configure: T.() -> Unit): TaskDelegateProvider<T> {
|
inline fun <reified T : Task> TaskContainer.registering(noinline configure: (T.() -> Unit)? = null): TaskDelegateProvider<T> {
|
||||||
return TaskDelegateProvider(this, T::class, configure)
|
return TaskDelegateProvider(this, T::class, configure)
|
||||||
}
|
}
|
||||||
class TaskDelegateProvider<T : Task>(
|
class TaskDelegateProvider<T : Task>(
|
||||||
private val container: TaskContainer,
|
private val container: TaskContainer,
|
||||||
private val type: KClass<T>,
|
private val type: KClass<T>,
|
||||||
private val configure: T.() -> Unit
|
private val configure: (T.() -> Unit)?
|
||||||
) {
|
) {
|
||||||
operator fun provideDelegate(thisRef: Any?, property: KProperty<*>): TaskDelegate<T> {
|
operator fun provideDelegate(thisRef: Any?, property: KProperty<*>): TaskDelegate<T> {
|
||||||
val provider = container.register(property.name, type.java, configure)
|
val provider = if (configure != null) {
|
||||||
|
container.register(property.name, type.java, configure)
|
||||||
|
} else {
|
||||||
|
container.register(property.name, type.java) {
|
||||||
|
}
|
||||||
|
}
|
||||||
return TaskDelegate(provider)
|
return TaskDelegate(provider)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue