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 {
|
||||
// we handle publications ourselves
|
||||
// we handle publications manually
|
||||
isAutomatedPublishing = false
|
||||
}
|
||||
|
||||
|
@ -79,6 +79,10 @@ dependencies {
|
|||
shade("net.fabricmc:lorenz-tiny:3.0.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 {
|
||||
|
@ -124,13 +128,17 @@ tasks.shadowJar {
|
|||
"io.sigpipe",
|
||||
"me.jamiemansfield",
|
||||
"net.fabricmc",
|
||||
"org.antlr",
|
||||
"org.apache.commons.codec",
|
||||
"org.apache.commons.compress",
|
||||
"org.apache.commons.logging",
|
||||
"org.apache.commons.text",
|
||||
"org.apache.felix",
|
||||
"org.apache.http",
|
||||
"org.cadixdev",
|
||||
"org.eclipse",
|
||||
"org.jgrapht",
|
||||
"org.jheaps",
|
||||
"org.objectweb",
|
||||
"org.osgi",
|
||||
"org.tukaani"
|
||||
|
|
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
|
@ -1,5 +1,5 @@
|
|||
distributionBase=GRADLE_USER_HOME
|
||||
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
|
||||
zipStorePath=wrapper/dists
|
||||
|
|
|
@ -92,10 +92,12 @@ class Paperweight : Plugin<Project> {
|
|||
|
||||
val tasks = target.createTasks()
|
||||
|
||||
val generatePaperclipPatch by target.tasks.registering<GeneratePaperclipPatch>()
|
||||
|
||||
// Setup the server jar
|
||||
target.afterEvaluate {
|
||||
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 })
|
||||
patchedJar.set(reobfJar.flatMap { it.outputJar })
|
||||
mcVersion.set(target.ext.minecraftVersion)
|
||||
|
@ -103,13 +105,13 @@ class Paperweight : Plugin<Project> {
|
|||
|
||||
target.tasks.named("jar", Jar::class) {
|
||||
val paperclipConfig = target.configurations.named(Constants.PAPERCLIP_CONFIG)
|
||||
dependsOn(paperclipConfig)
|
||||
dependsOn(paperclipConfig, generatePaperclipPatch)
|
||||
|
||||
val paperclipZip = target.zipTree(paperclipConfig.map { it.singleFile })
|
||||
from(paperclipZip) {
|
||||
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)
|
||||
}
|
||||
|
@ -150,7 +152,7 @@ class Paperweight : Plugin<Project> {
|
|||
val initialTasks = createInitialTasks()
|
||||
val generalTasks = createGeneralTasks()
|
||||
val vanillaTasks = createVanillaTasks(initialTasks, generalTasks)
|
||||
val spigotTasks = createSpigotTasks(initialTasks, generalTasks, vanillaTasks)
|
||||
val spigotTasks = createSpigotTasks(generalTasks, vanillaTasks)
|
||||
|
||||
val applyMergedAt by tasks.registering<ApplyAccessTransform> {
|
||||
inputJar.set(vanillaTasks.fixJar.flatMap { it.outputJar })
|
||||
|
@ -236,6 +238,7 @@ class Paperweight : Plugin<Project> {
|
|||
)
|
||||
|
||||
data class VanillaTasks(
|
||||
val inspectVanillaJar: TaskProvider<InspectVanillaJar>,
|
||||
val generateMappings: TaskProvider<GenerateMappings>,
|
||||
val fixJar: TaskProvider<FixJar>,
|
||||
val downloadMcLibraries: TaskProvider<DownloadMcLibraries>
|
||||
|
@ -334,11 +337,29 @@ class Paperweight : Plugin<Project> {
|
|||
val cache: File = layout.cache
|
||||
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> {
|
||||
vanillaJar.set(generalTasks.filterVanillaJar.flatMap { it.outputJar })
|
||||
|
||||
vanillaMappings.set(initialTasks.downloadMappings.flatMap { it.outputFile })
|
||||
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))
|
||||
}
|
||||
|
@ -356,25 +377,16 @@ class Paperweight : Plugin<Project> {
|
|||
vanillaJar.set(generalTasks.downloadServerJar.flatMap { it.outputJar })
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
return VanillaTasks(generateMappings, fixJar, downloadMcLibraries)
|
||||
return VanillaTasks(inspectVanillaJar, 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 extension: PaperweightExtension = ext
|
||||
val downloadService = download
|
||||
|
||||
val (buildDataInfo, downloadServerJar, filterVanillaJar) = generalTasks
|
||||
val (generateMappings, _, _) = vanillaTasks
|
||||
val (inspectVanillaJar, generateMappings, _, _) = vanillaTasks
|
||||
|
||||
val addAdditionalSpigotMappings by tasks.registering<AddAdditionalSpigotMappings> {
|
||||
classSrg.set(extension.craftBukkit.mappingsDir.file(buildDataInfo.map { it.classMappings }))
|
||||
|
@ -383,14 +395,6 @@ class Paperweight : Plugin<Project> {
|
|||
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> {
|
||||
classMappings.set(addAdditionalSpigotMappings.flatMap { it.outputClassSrg })
|
||||
memberMappings.set(addAdditionalSpigotMappings.flatMap { it.outputMemberSrg })
|
||||
|
@ -399,6 +403,7 @@ class Paperweight : Plugin<Project> {
|
|||
loggerFields.set(inspectVanillaJar.flatMap { it.loggerFile })
|
||||
paramIndexes.set(inspectVanillaJar.flatMap { it.paramIndexes })
|
||||
syntheticMethods.set(inspectVanillaJar.flatMap { it.syntheticMethods })
|
||||
methodOverrides.set(inspectVanillaJar.flatMap { it.methodOverrides })
|
||||
|
||||
sourceMappings.set(generateMappings.flatMap { it.outputMappings })
|
||||
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
package io.papermc.paperweight.tasks
|
||||
|
||||
import io.papermc.paperweight.util.AsmUtil
|
||||
import io.papermc.paperweight.util.ClassNodeCache
|
||||
import io.papermc.paperweight.util.SyntheticUtil
|
||||
import io.papermc.paperweight.util.defaultOutput
|
||||
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.OutputFile
|
||||
import org.gradle.api.tasks.TaskAction
|
||||
import org.objectweb.asm.ClassReader
|
||||
import org.objectweb.asm.ClassWriter
|
||||
import org.objectweb.asm.Opcodes
|
||||
import org.objectweb.asm.Type
|
||||
|
@ -75,8 +75,7 @@ abstract class FixJar : BaseTask(), AsmUtil {
|
|||
}
|
||||
|
||||
try {
|
||||
val node =
|
||||
classNodeCache.findClass(entry.name) ?: error("No ClassNode found for known entry")
|
||||
val node = classNodeCache.findClass(entry.name) ?: error("No ClassNode found for known entry")
|
||||
|
||||
ParameterAnnotationFixer(node).visitNode()
|
||||
OverrideAnnotationAdder(node, classNodeCache).visitNode()
|
||||
|
@ -230,49 +229,3 @@ class OverrideAnnotationAdder(private val node: ClassNode, private val classNode
|
|||
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 java.nio.file.FileSystems
|
||||
import java.nio.file.Files
|
||||
import java.util.zip.ZipFile
|
||||
import javax.inject.Inject
|
||||
import org.cadixdev.atlas.Atlas
|
||||
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.MergeContext
|
||||
import org.cadixdev.lorenz.merge.MergeResult
|
||||
import org.cadixdev.lorenz.merge.MethodMergeStrategy
|
||||
import org.cadixdev.lorenz.model.ClassMapping
|
||||
import org.cadixdev.lorenz.model.FieldMapping
|
||||
import org.cadixdev.lorenz.model.InnerClassMapping
|
||||
|
@ -66,6 +68,8 @@ abstract class GenerateMappings : DefaultTask() {
|
|||
abstract val vanillaMappings: RegularFileProperty
|
||||
@get:InputFile
|
||||
abstract val paramMappings: RegularFileProperty
|
||||
@get:InputFile
|
||||
abstract val methodOverrides: RegularFileProperty
|
||||
|
||||
@get:OutputFile
|
||||
abstract val outputMappings: RegularFileProperty
|
||||
|
@ -86,16 +90,17 @@ abstract class GenerateMappings : DefaultTask() {
|
|||
vanillaMappings,
|
||||
paramMappings,
|
||||
MergeConfig.builder()
|
||||
.withMethodMergeStrategy(MethodMergeStrategy.STRICT)
|
||||
.withFieldMergeStrategy(FieldMergeStrategy.STRICT)
|
||||
.withMergeHandler(ParamsMergeHandler())
|
||||
.build()
|
||||
).merge()
|
||||
|
||||
ensureParentExists(outputMappings)
|
||||
|
||||
// Fill out any missing inheritance info in the mappings
|
||||
val tempMappingsFile = Files.createTempFile("mappings", "tiny")
|
||||
val tempMappingsOutputFile = Files.createTempFile("mappings-out", "tiny")
|
||||
|
||||
val filledMerged = try {
|
||||
try {
|
||||
MappingFormats.TINY.write(merged, tempMappingsFile, Constants.OBF_NAMESPACE, Constants.DEOBF_NAMESPACE)
|
||||
|
||||
val queue = workerExecutor.processIsolation {
|
||||
|
@ -105,21 +110,65 @@ abstract class GenerateMappings : DefaultTask() {
|
|||
queue.submit(AtlasAction::class) {
|
||||
inputJar.set(vanillaJar.file)
|
||||
mappingsFile.set(tempMappingsFile.toFile())
|
||||
outputMappingsFile.set(tempMappingsOutputFile.toFile())
|
||||
outputMappingsFile.set(outputMappings.file)
|
||||
}
|
||||
|
||||
queue.await()
|
||||
|
||||
MappingFormats.TINY.read(tempMappingsOutputFile, Constants.OBF_NAMESPACE, Constants.DEOBF_NAMESPACE)
|
||||
} finally {
|
||||
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)
|
||||
MappingFormats.TINY.write(filledMerged, outputMappings.path, Constants.OBF_NAMESPACE, Constants.DEOBF_NAMESPACE)
|
||||
for (classMapping in mappings.topLevelClassMappings) {
|
||||
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> {
|
||||
override fun execute() {
|
||||
val mappings = MappingFormats.TINY.read(parameters.mappingsFile.path, Constants.OBF_NAMESPACE, Constants.DEOBF_NAMESPACE)
|
||||
|
@ -237,7 +286,7 @@ class ParamsMergeHandler : MappingSetMergerHandler {
|
|||
): MergeResult<MethodMapping?> {
|
||||
return MergeResult(
|
||||
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.NoSuchAlgorithmException
|
||||
import java.util.Properties
|
||||
import javax.inject.Inject
|
||||
import kotlin.experimental.and
|
||||
import org.gradle.api.file.RegularFileProperty
|
||||
import org.gradle.api.provider.Property
|
||||
import org.gradle.api.tasks.Input
|
||||
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() {
|
||||
|
||||
|
@ -51,88 +56,122 @@ abstract class GeneratePaperclipPatch : ZippedTask() {
|
|||
@get:Input
|
||||
abstract val mcVersion: Property<String>
|
||||
|
||||
@get:Inject
|
||||
abstract val workerExecutor: WorkerExecutor
|
||||
|
||||
override fun run(rootDir: File) {
|
||||
val patchFile = rootDir.resolve("paperMC.patch").toPath()
|
||||
val propFile = rootDir.resolve("patch.properties").toPath()
|
||||
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)
|
||||
val queue = workerExecutor.processIsolation {
|
||||
forkOptions.jvmArgs("-Xmx2G")
|
||||
}
|
||||
|
||||
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
|
||||
queue.submit(PaperclipAction::class) {
|
||||
zipRootDir.set(rootDir)
|
||||
originalJar.set(this@GeneratePaperclipPatch.originalJar.file)
|
||||
patchedJar.set(this@GeneratePaperclipPatch.patchedJar.file)
|
||||
mcVersion.set(this@GeneratePaperclipPatch.mcVersion.get())
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
Files.createDirectories(protocol.parent)
|
||||
Files.copy(protocolPath, protocol, StandardCopyOption.REPLACE_EXISTING)
|
||||
} catch (e: IOException) {
|
||||
throw PaperweightException("Failed to read $patchedJarPath contents", e)
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
throw PaperweightException("Failed to read $patchedJar contents", e)
|
||||
}
|
||||
|
||||
// Read the files into memory
|
||||
println("Reading jars into memory")
|
||||
val originalBytes = Files.readAllBytes(originalJar.path)
|
||||
val patchedBytes = Files.readAllBytes(patchedJar.path)
|
||||
// Read the files into memory
|
||||
println("Reading jars into memory")
|
||||
val originalBytes = Files.readAllBytes(originalJarPath)
|
||||
val patchedBytes = Files.readAllBytes(patchedJarPath)
|
||||
|
||||
println("Creating Paperclip patch")
|
||||
try {
|
||||
Files.newOutputStream(patchFile).use { patchOutput ->
|
||||
Diff.diff(originalBytes, patchedBytes, patchOutput)
|
||||
println("Creating Paperclip patch")
|
||||
try {
|
||||
Files.newOutputStream(patchFile).use { 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
|
||||
val digestSha256 = try {
|
||||
MessageDigest.getInstance("SHA-256")
|
||||
} catch (e: NoSuchAlgorithmException) {
|
||||
throw PaperweightException("Could not create SHA-256 hasher", e)
|
||||
private fun toHex(hash: ByteArray): String {
|
||||
val sb: StringBuilder = StringBuilder(hash.size * 2)
|
||||
for (aHash in hash) {
|
||||
sb.append("%02X".format(aHash and 0xFF.toByte()))
|
||||
}
|
||||
return sb.toString()
|
||||
}
|
||||
|
||||
// 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"] = 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.")
|
||||
companion object {
|
||||
private const val PROTOCOL_FILE = "io.papermc.paper.daemon.protocol"
|
||||
}
|
||||
}
|
||||
|
||||
private fun toHex(hash: ByteArray): String {
|
||||
val sb: StringBuilder = StringBuilder(hash.size * 2)
|
||||
for (aHash in hash) {
|
||||
sb.append("%02X".format(aHash and 0xFF.toByte()))
|
||||
}
|
||||
return sb.toString()
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val PROTOCOL_FILE = "io.papermc.paper.daemon.protocol"
|
||||
interface PaperclipParameters : WorkParameters {
|
||||
val zipRootDir: RegularFileProperty
|
||||
val originalJar: RegularFileProperty
|
||||
val patchedJar: RegularFileProperty
|
||||
val mcVersion: Property<String>
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,14 +22,16 @@
|
|||
|
||||
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.MethodRef
|
||||
import io.papermc.paperweight.util.emptyMergeResult
|
||||
import io.papermc.paperweight.util.file
|
||||
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 org.cadixdev.bombe.type.signature.MethodSignature
|
||||
import org.cadixdev.lorenz.MappingSet
|
||||
import org.cadixdev.lorenz.merge.MappingSetMerger
|
||||
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.OutputFile
|
||||
import org.gradle.api.tasks.TaskAction
|
||||
import org.jgrapht.graph.SimpleDirectedGraph
|
||||
import org.jgrapht.traverse.DepthFirstIterator
|
||||
|
||||
abstract class GenerateSpigotMappings : DefaultTask() {
|
||||
|
||||
|
@ -62,6 +66,8 @@ abstract class GenerateSpigotMappings : DefaultTask() {
|
|||
abstract val paramIndexes: RegularFileProperty
|
||||
@get:InputFile
|
||||
abstract val syntheticMethods: RegularFileProperty
|
||||
@get:InputFile
|
||||
abstract val methodOverrides: RegularFileProperty
|
||||
|
||||
@get:InputFile
|
||||
abstract val sourceMappings: RegularFileProperty
|
||||
|
@ -84,13 +90,10 @@ abstract class GenerateSpigotMappings : DefaultTask() {
|
|||
// Get the new package name
|
||||
val newPackage = packageMappings.asFile.get().readLines()[0].split(Regex("\\s+"))[1]
|
||||
|
||||
val sourceMappings = MappingFormats.TINY.read(
|
||||
sourceMappings.path,
|
||||
Constants.OBF_NAMESPACE,
|
||||
Constants.DEOBF_NAMESPACE
|
||||
)
|
||||
val sourceMappings = MappingFormats.TINY.read(sourceMappings.path, OBF_NAMESPACE, DEOBF_NAMESPACE)
|
||||
|
||||
val synths = hashMapOf<String, MutableMap<String, MutableMap<String, String>>>()
|
||||
/*
|
||||
val synths = newSynths()
|
||||
syntheticMethods.file.useLines { lines ->
|
||||
for (line in lines) {
|
||||
val (className, desc, synthName, baseName) = line.split(" ")
|
||||
|
@ -98,12 +101,15 @@ abstract class GenerateSpigotMappings : DefaultTask() {
|
|||
.computeIfAbsent(desc) { hashMapOf() }[baseName] = synthName
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
val overrides = readOverrides(methodOverrides)
|
||||
|
||||
val notchToSpigotSet = MappingSetMerger.create(
|
||||
mergedMappingSet,
|
||||
sourceMappings,
|
||||
MergeConfig.builder()
|
||||
.withMergeHandler(SpigotMappingsMergerHandler(newPackage, synths))
|
||||
.withMergeHandler(SpigotMappingsMergerHandler(newPackage, overrides))
|
||||
.build()
|
||||
).merge()
|
||||
|
||||
|
@ -111,12 +117,7 @@ abstract class GenerateSpigotMappings : DefaultTask() {
|
|||
val cleanedSourceMappings = removeLambdaMappings(adjustedSourceMappings)
|
||||
val spigotToNamedSet = notchToSpigotSet.reverse().merge(cleanedSourceMappings)
|
||||
|
||||
MappingFormats.TINY.write(
|
||||
spigotToNamedSet,
|
||||
outputMappings.path,
|
||||
Constants.SPIGOT_NAMESPACE,
|
||||
Constants.DEOBF_NAMESPACE
|
||||
)
|
||||
MappingFormats.TINY.write(spigotToNamedSet, outputMappings.path, SPIGOT_NAMESPACE, DEOBF_NAMESPACE)
|
||||
}
|
||||
|
||||
private fun adjustParamIndexes(mappings: MappingSet): MappingSet {
|
||||
|
@ -151,7 +152,7 @@ abstract class GenerateSpigotMappings : DefaultTask() {
|
|||
params: Map<String, Map<String, Map<Int, Int>>>
|
||||
) {
|
||||
for (mapping in from.fieldMappings) {
|
||||
to.createFieldMapping(mapping.signature, mapping.deobfuscatedName)
|
||||
mapping.copy(to)
|
||||
}
|
||||
for (mapping in from.innerClassMappings) {
|
||||
val newMapping = to.createInnerClassMapping(mapping.obfuscatedName, mapping.deobfuscatedName)
|
||||
|
@ -169,7 +170,7 @@ abstract class GenerateSpigotMappings : DefaultTask() {
|
|||
|
||||
val methodMap = classMap[mapping.signature.toJvmsIdentifier()] ?: continue
|
||||
for (paramMapping in paramMappings) {
|
||||
val i = methodMap[paramMapping.index] ?: paramMapping.index
|
||||
val i = methodMap[paramMapping.index] ?: continue
|
||||
newMapping.createParameterMapping(i, paramMapping.deobfuscatedName)
|
||||
}
|
||||
}
|
||||
|
@ -210,11 +211,14 @@ abstract class GenerateSpigotMappings : DefaultTask() {
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
typealias Synths = Map<String, Map<String, Map<String, String>>>
|
||||
fun newSynths() = hashMapOf<String, MutableMap<String, MutableMap<String, String>>>()
|
||||
*/
|
||||
|
||||
class SpigotMappingsMergerHandler(
|
||||
private val newPackage: String,
|
||||
private val synths: Synths
|
||||
private val methodOverrides: SimpleDirectedGraph<MethodRef, *>
|
||||
) : MappingSetMergerHandler {
|
||||
|
||||
override fun mergeTopLevelClassMappings(
|
||||
|
@ -298,7 +302,7 @@ class SpigotMappingsMergerHandler(
|
|||
target: ClassMapping<*, *>,
|
||||
context: MergeContext
|
||||
): 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)
|
||||
}
|
||||
|
||||
|
@ -320,6 +324,8 @@ class SpigotMappingsMergerHandler(
|
|||
target: ClassMapping<*, *>,
|
||||
context: MergeContext
|
||||
): 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
|
||||
return target.createFieldMapping(right.signature, left.deobfuscatedName)
|
||||
}
|
||||
|
@ -342,33 +348,81 @@ class SpigotMappingsMergerHandler(
|
|||
target: ClassMapping<*, *>,
|
||||
context: MergeContext
|
||||
): 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
|
||||
val synthMethods = synths[left.parent.fullObfuscatedName]?.get(left.obfuscatedDescriptor)
|
||||
val newName = synthMethods?.get(left.obfuscatedName)
|
||||
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 newMapping = if (newLeftMapping != null) {
|
||||
// We found it, use it for the left side instead
|
||||
target.getOrCreateMethodMapping(newLeftMapping.signature).also {
|
||||
it.deobfuscatedName = left.deobfuscatedName
|
||||
}
|
||||
} 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 {
|
||||
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)
|
||||
} else {
|
||||
// normal mapping
|
||||
val newMapping = target.getOrCreateMethodMapping(left.signature).also {
|
||||
it.deobfuscatedName = left.deobfuscatedName
|
||||
}
|
||||
return MergeResult(newMapping)
|
||||
}
|
||||
*/
|
||||
}
|
||||
override fun addLeftMethodMapping(
|
||||
left: MethodMapping,
|
||||
target: ClassMapping<*, *>,
|
||||
context: MergeContext
|
||||
): 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
|
||||
var obfName: String? = null
|
||||
val synthMethods = synths[left.parent.fullObfuscatedName]?.get(left.obfuscatedDescriptor)
|
||||
|
@ -383,12 +437,14 @@ class SpigotMappingsMergerHandler(
|
|||
}
|
||||
|
||||
if (obfName == null) {
|
||||
// This mapping doesn't actually exist, drop it
|
||||
return emptyMergeResult()
|
||||
}
|
||||
|
||||
val newMapping = target.getOrCreateMethodMapping(obfName, left.descriptor)
|
||||
newMapping.deobfuscatedName = left.deobfuscatedName
|
||||
return MergeResult(newMapping)
|
||||
*/
|
||||
}
|
||||
|
||||
override fun addLeftFieldMapping(left: FieldMapping, target: ClassMapping<*, *>, context: MergeContext): FieldMapping? {
|
||||
|
@ -410,6 +466,20 @@ class SpigotMappingsMergerHandler(
|
|||
target: ClassMapping<*, *>,
|
||||
context: MergeContext
|
||||
): 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
|
||||
val synthMethods = synths[right.parentClass.fullObfuscatedName]?.get(right.obfuscatedDescriptor)
|
||||
val newName = synthMethods?.get(right.obfuscatedName) ?: return emptyMergeResult()
|
||||
|
@ -423,6 +493,7 @@ class SpigotMappingsMergerHandler(
|
|||
newMapping.deobfuscatedName = newName
|
||||
}
|
||||
return MergeResult(newMapping)
|
||||
*/
|
||||
}
|
||||
|
||||
private fun prependPackage(name: String): String {
|
||||
|
|
|
@ -23,11 +23,16 @@
|
|||
package io.papermc.paperweight.tasks
|
||||
|
||||
import io.papermc.paperweight.util.AsmUtil
|
||||
import io.papermc.paperweight.util.ClassNodeCache
|
||||
import io.papermc.paperweight.util.MavenArtifact
|
||||
import io.papermc.paperweight.util.MethodRef
|
||||
import io.papermc.paperweight.util.SyntheticUtil
|
||||
import io.papermc.paperweight.util.defaultOutput
|
||||
import io.papermc.paperweight.util.file
|
||||
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 org.gradle.api.file.DirectoryProperty
|
||||
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.OutputFile
|
||||
import org.gradle.api.tasks.TaskAction
|
||||
import org.objectweb.asm.ClassReader
|
||||
import org.objectweb.asm.ClassVisitor
|
||||
import org.objectweb.asm.FieldVisitor
|
||||
import org.objectweb.asm.MethodVisitor
|
||||
import org.jgrapht.graph.DefaultEdge
|
||||
import org.jgrapht.graph.SimpleDirectedGraph
|
||||
import org.jgrapht.nio.Attribute
|
||||
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.Type
|
||||
import org.objectweb.asm.tree.MethodNode
|
||||
import org.objectweb.asm.tree.ClassNode
|
||||
|
||||
abstract class InspectVanillaJar : BaseTask() {
|
||||
|
||||
|
@ -59,12 +66,15 @@ abstract class InspectVanillaJar : BaseTask() {
|
|||
@get:OutputFile
|
||||
abstract val syntheticMethods: RegularFileProperty
|
||||
@get:OutputFile
|
||||
abstract val methodOverrides: RegularFileProperty
|
||||
@get:OutputFile
|
||||
abstract val serverLibraries: RegularFileProperty
|
||||
|
||||
override fun init() {
|
||||
loggerFile.convention(defaultOutput("$name-loggerFields", "txt"))
|
||||
paramIndexes.convention(defaultOutput("$name-paramIndexes", "txt"))
|
||||
syntheticMethods.convention(defaultOutput("$name-syntheticMethods", "txt"))
|
||||
methodOverrides.convention(defaultOutput("$name-methodOverrides", "json"))
|
||||
}
|
||||
|
||||
@TaskAction
|
||||
|
@ -72,28 +82,32 @@ abstract class InspectVanillaJar : BaseTask() {
|
|||
val loggers = mutableListOf<LoggerFields.Data>()
|
||||
val params = mutableListOf<ParamIndexes.Data>()
|
||||
val synthMethods = mutableListOf<SyntheticMethods.Data>()
|
||||
val overrides = SimpleDirectedGraph<MethodRef, DefaultEdge>(DefaultEdge::class.java)
|
||||
|
||||
var visitor: ClassVisitor
|
||||
visitor = LoggerFields.Visitor(null, loggers)
|
||||
visitor = ParamIndexes.Visitor(visitor, params)
|
||||
visitor = SyntheticMethods.Visitor(visitor, synthMethods)
|
||||
JarFile(inputJar.file).use { jarFile ->
|
||||
val classNodeCache = ClassNodeCache(jarFile)
|
||||
|
||||
archives.zipTree(inputJar.file).matching {
|
||||
include("/*.class")
|
||||
include("/net/minecraft/**/*.class")
|
||||
}.forEach { file ->
|
||||
if (file.isDirectory) {
|
||||
return@forEach
|
||||
for (entry in jarFile.entries()) {
|
||||
if (!entry.name.endsWith(".class")) {
|
||||
continue
|
||||
}
|
||||
if (entry.name.count { it == '/' } > 0 && !entry.name.startsWith("net/minecraft/")) {
|
||||
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()
|
||||
|
||||
loggerFile.file.bufferedWriter(Charsets.UTF_8).use { writer ->
|
||||
loggers.sort()
|
||||
loggers.sort()
|
||||
loggerFile.file.bufferedWriter().use { writer ->
|
||||
for (loggerField in loggers) {
|
||||
writer.append(loggerField.className)
|
||||
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) {
|
||||
writer.append(methodData.className)
|
||||
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) {
|
||||
writer.append(className)
|
||||
writer.append(' ')
|
||||
|
@ -134,8 +148,11 @@ abstract class InspectVanillaJar : BaseTask() {
|
|||
}
|
||||
}
|
||||
|
||||
serverLibraries.file.bufferedWriter(Charsets.UTF_8).use { writer ->
|
||||
serverLibs.map { it.toString() }.sorted().forEach { artifact ->
|
||||
writeOverrides(overrides, methodOverrides)
|
||||
|
||||
val libs = serverLibs.map { it.toString() }.sorted()
|
||||
serverLibraries.file.bufferedWriter().use { writer ->
|
||||
libs.forEach { 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
|
||||
* to make a note of all of those fields
|
||||
*/
|
||||
object LoggerFields {
|
||||
class Visitor(
|
||||
classVisitor: ClassVisitor?,
|
||||
private val fields: MutableList<Data>
|
||||
) : BaseClassVisitor(classVisitor) {
|
||||
object Visitor : AsmUtil {
|
||||
|
||||
override fun visitField(
|
||||
access: Int,
|
||||
name: String,
|
||||
descriptor: String,
|
||||
signature: String?,
|
||||
value: Any?
|
||||
): FieldVisitor? {
|
||||
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
|
||||
fun visit(node: ClassNode, fields: MutableList<Data>) {
|
||||
for (field in node.fields) {
|
||||
if (Opcodes.ACC_STATIC !in field.access || Opcodes.ACC_FINAL !in field.access) {
|
||||
continue
|
||||
}
|
||||
if (field.desc == "Lorg/apache/logging/log4j/Logger;") {
|
||||
fields += Data(node.name, field.name)
|
||||
}
|
||||
}
|
||||
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.
|
||||
*/
|
||||
object ParamIndexes {
|
||||
class Visitor(
|
||||
classVisitor: ClassVisitor?,
|
||||
private val methods: MutableList<Data>
|
||||
) : BaseClassVisitor(classVisitor) {
|
||||
object Visitor : AsmUtil {
|
||||
|
||||
override fun visitMethod(
|
||||
access: Int,
|
||||
name: String,
|
||||
descriptor: String,
|
||||
signature: String?,
|
||||
exceptions: Array<out String>?
|
||||
): MethodVisitor? {
|
||||
val ret = super.visitMethod(access, name, descriptor, signature, exceptions)
|
||||
val className = currentClass ?: return ret
|
||||
fun visit(node: ClassNode, methods: MutableList<Data>) {
|
||||
for (method in node.methods) {
|
||||
val isStatic = Opcodes.ACC_STATIC in method.access
|
||||
var currentIndex = if (isStatic) 0 else 1
|
||||
|
||||
val isStatic = access and Opcodes.ACC_STATIC != 0
|
||||
var currentIndex = if (isStatic) 0 else 1
|
||||
val types = Type.getArgumentTypes(method.desc)
|
||||
if (types.isEmpty()) {
|
||||
continue
|
||||
}
|
||||
|
||||
val types = Type.getArgumentTypes(descriptor)
|
||||
if (types.isEmpty()) {
|
||||
return ret
|
||||
}
|
||||
val params = ArrayList<ParamTarget>(types.size)
|
||||
val data = Data(node.name, method.name, method.desc, params)
|
||||
methods += data
|
||||
|
||||
val params = ArrayList<ParamTarget>(types.size)
|
||||
val data = Data(className, name, descriptor, params)
|
||||
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) {
|
||||
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++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -303,45 +280,20 @@ object ParamIndexes {
|
|||
* can handle it in our generated mappings.
|
||||
*/
|
||||
object SyntheticMethods {
|
||||
class Visitor(
|
||||
classVisitor: ClassVisitor?,
|
||||
private val methods: MutableList<Data>
|
||||
) : BaseClassVisitor(classVisitor) {
|
||||
object Visitor : AsmUtil {
|
||||
|
||||
override fun visitMethod(
|
||||
access: Int,
|
||||
name: String,
|
||||
descriptor: String,
|
||||
signature: String?,
|
||||
exceptions: Array<out String>?
|
||||
): MethodVisitor? {
|
||||
val ret = super.visitMethod(access, name, descriptor, signature, exceptions)
|
||||
val className = currentClass ?: return ret
|
||||
fun visit(node: ClassNode, methods: MutableList<Data>) {
|
||||
for (method in node.methods) {
|
||||
if (Opcodes.ACC_SYNTHETIC !in method.access || Opcodes.ACC_BRIDGE in method.access || method.name.contains('$')) {
|
||||
continue
|
||||
}
|
||||
|
||||
if (Opcodes.ACC_SYNTHETIC !in access || Opcodes.ACC_BRIDGE in access || name.contains('$')) {
|
||||
return ret
|
||||
}
|
||||
val (baseName, baseDesc) = SyntheticUtil.findBaseMethod(method, node.name)
|
||||
|
||||
return SynthMethodVisitor(access, name, descriptor, signature, exceptions, className, methods)
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
if (baseName != method.name || baseDesc != method.desc) {
|
||||
// Add this method as a synthetic for baseName
|
||||
methods += Data(node.name, baseDesc, method.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
|
||||
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)
|
||||
}
|
||||
class TaskDelegateProvider<T : Task>(
|
||||
private val container: TaskContainer,
|
||||
private val type: KClass<T>,
|
||||
private val configure: T.() -> Unit
|
||||
private val configure: (T.() -> Unit)?
|
||||
) {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue