diff --git a/buildSrc/src/main/kotlin/config-kotlin.gradle.kts b/buildSrc/src/main/kotlin/config-kotlin.gradle.kts index c8eb9f7a..57aec4f4 100644 --- a/buildSrc/src/main/kotlin/config-kotlin.gradle.kts +++ b/buildSrc/src/main/kotlin/config-kotlin.gradle.kts @@ -18,6 +18,7 @@ kotlin { } repositories { + mavenLocal() maven("https://repo.papermc.io/repository/maven-snapshots/") { mavenContent { includeModule("org.cadixdev", "mercury") diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index ea043b9b..39e6cb62 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -3,6 +3,7 @@ asm = "9.5" lorenz = "0.5.8" hypo = "2.3.0" serialize = "1.5.1" +feather = "1.1.0" [libraries] asm-core = { module = "org.ow2.asm:asm", version.ref = "asm" } @@ -19,7 +20,7 @@ cadix-lorenz-asm = { module = "org.cadixdev:lorenz-asm", version.ref = "lorenz" cadix-lorenz-proguard = { module = "org.cadixdev:lorenz-io-proguard", version.ref = "lorenz" } cadix-atlas = "org.cadixdev:atlas:0.2.1" cadix-at = "org.cadixdev:at:0.1.0-rc1" -cadix-mercury = "org.cadixdev:mercury:0.1.1-paperweight-SNAPSHOT" +cadix-mercury = "org.cadixdev:mercury:0.1.1-paperweight-local-SNAPSHOT" cadix-bombe-jar = "org.cadixdev:bombe-jar:0.4.4" hypo-model = { module = "dev.denwav.hypo:hypo-model", version.ref = "hypo" } @@ -33,6 +34,9 @@ slf4j-jdk14 = "org.slf4j:slf4j-jdk14:1.7.32" lorenzTiny = "net.fabricmc:lorenz-tiny:3.0.0" jbsdiff = "io.sigpipe:jbsdiff:1.0" +feather-core = { module = "org.parchmentmc:feather", version.ref = "feather" } +feather-gson = { module = "org.parchmentmc.feather:io-gson", version.ref = "feather" } + serialize-core = { module = "org.jetbrains.kotlinx:kotlinx-serialization-core", version.ref = "serialize" } serialize-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "serialize" } diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/SoftSpoonTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/SoftSpoonTasks.kt index cfe1392f..5b0f937f 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/SoftSpoonTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/SoftSpoonTasks.kt @@ -4,6 +4,7 @@ import io.papermc.paperweight.core.ext import io.papermc.paperweight.tasks.* import io.papermc.paperweight.tasks.mache.* import io.papermc.paperweight.tasks.mache.RemapJar +import io.papermc.paperweight.tasks.patchremapv2.GeneratePatchRemapMappings import io.papermc.paperweight.tasks.patchremapv2.RemapCBPatches import io.papermc.paperweight.tasks.softspoon.ApplyPatches import io.papermc.paperweight.tasks.softspoon.ApplyPatchesFuzzy @@ -41,7 +42,7 @@ open class SoftSpoonTasks( val macheRemapJar by tasks.registering(RemapJar::class) { group = "mache" serverJar.set(layout.cache.resolve(SERVER_JAR_PATH)) - serverMappings.set(layout.cache.resolve(SERVER_MAPPINGS)) + serverMappings.set(allTasks.downloadMappings.flatMap { it.outputFile }) codebookClasspath.from(macheCodebook) minecraftClasspath.from(macheMinecraft) @@ -158,20 +159,32 @@ open class SoftSpoonTasks( // patch remap stuff val macheSpigotDecompileJar by tasks.registering { - group = "mache" + group = "patchremap" inputJar.set(macheRemapJar.flatMap { it.outputJar }) fernFlowerJar.set(project.ext.craftBukkit.fernFlowerJar) decompileCommand.set(allTasks.buildDataInfo.map { it.decompileCommand }) } + val generatePatchRemapMappings by tasks.registering(GeneratePatchRemapMappings::class) { + group = "patchremap" + + minecraftClasspath.from(macheMinecraft) + serverJar.set(macheRemapJar.flatMap { it.outputJar }) + paramMappings.from(macheParamMappings) + vanillaMappings.set(allTasks.downloadMappings.flatMap { it.outputFile }) + spigotMappings.set(allTasks.generateSpigotMappings.flatMap { it.notchToSpigotMappings }) + + patchRemapMappings.set(layout.cache.resolve(SPIGOT_MOJANG_PARCHMENT_MAPPINGS)) + } + val remapCBPatches by tasks.registering(RemapCBPatches::class) { - group = "paperweight" + group = "patchremap" base.set(layout.cache.resolve(BASE_PROJECT).resolve("sources")) //craftBukkit.set(allTasks.patchCraftBukkit.flatMap { it.outputDir }) craftBukkit.set(project.layout.cache.resolve("paperweight/taskCache/patchCraftBukkit.repo")) outputPatchDir.set(project.layout.projectDirectory.dir("patches/remapped-cb")) //mappingsFile.set(allTasks.patchMappings.flatMap { it.outputMappings }) - mappingsFile.set(layout.cache.resolve(PATCHED_SPIGOT_MOJANG_YARN_MAPPINGS)) + mappingsFile.set(layout.cache.resolve(SPIGOT_MOJANG_PARCHMENT_MAPPINGS)) } fun afterEvaluate() { diff --git a/paperweight-lib/build.gradle.kts b/paperweight-lib/build.gradle.kts index 5171d098..1cc8a457 100644 --- a/paperweight-lib/build.gradle.kts +++ b/paperweight-lib/build.gradle.kts @@ -18,6 +18,9 @@ dependencies { implementation(libs.lorenzTiny) + implementation(libs.feather.core) + implementation(libs.feather.gson) + implementation(libs.jbsdiff) implementation(libs.codebook) diff --git a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/FilterProjectDir.kt b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/FilterProjectDir.kt index 559b5811..7c2d0fed 100644 --- a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/FilterProjectDir.kt +++ b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/FilterProjectDir.kt @@ -87,7 +87,7 @@ abstract class FilterProjectDir : BaseTask() { private fun collectFiles(dir: Path): Set { return Files.walk(dir).use { stream -> stream.filter { it.isRegularFile() } - .map { it.relativeTo(dir).toString() } + .map { it.relativeTo(dir).invariantSeparatorsPathString } .collect(Collectors.toUnmodifiableSet()) } } diff --git a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/patchremapv2/GeneratePatchRemapMappings.kt b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/patchremapv2/GeneratePatchRemapMappings.kt new file mode 100644 index 00000000..7988c476 --- /dev/null +++ b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/patchremapv2/GeneratePatchRemapMappings.kt @@ -0,0 +1,241 @@ +package io.papermc.paperweight.tasks.patchremapv2 + +import com.google.gson.GsonBuilder +import dev.denwav.hypo.asm.AsmClassDataProvider +import dev.denwav.hypo.asm.hydrate.BridgeMethodHydrator +import dev.denwav.hypo.asm.hydrate.LambdaCallHydrator +import dev.denwav.hypo.asm.hydrate.LocalClassHydrator +import dev.denwav.hypo.asm.hydrate.SuperConstructorHydrator +import dev.denwav.hypo.core.HypoContext +import dev.denwav.hypo.hydrate.HydrationManager +import dev.denwav.hypo.mappings.ChangeChain +import dev.denwav.hypo.mappings.MappingsCompletionManager +import dev.denwav.hypo.mappings.contributors.CopyLambdaParametersDown +import dev.denwav.hypo.mappings.contributors.CopyMappingsDown +import dev.denwav.hypo.mappings.contributors.CopyRecordParameters +import dev.denwav.hypo.mappings.contributors.PropagateMappingsUp +import dev.denwav.hypo.model.ClassProviderRoot +import io.papermc.codebook.exceptions.UnexpectedException +import io.papermc.paperweight.tasks.* +import io.papermc.paperweight.util.* +import io.papermc.paperweight.util.constants.* +import java.io.IOException +import java.nio.file.FileSystems +import java.nio.file.Files +import java.nio.file.Path +import org.cadixdev.bombe.type.signature.FieldSignature +import org.cadixdev.lorenz.MappingSet +import org.cadixdev.lorenz.model.ClassMapping +import org.cadixdev.lorenz.model.TopLevelClassMapping +import org.gradle.api.file.ConfigurableFileCollection +import org.gradle.api.file.RegularFileProperty +import org.gradle.api.tasks.* +import org.parchmentmc.feather.io.gson.MDCGsonAdapterFactory +import org.parchmentmc.feather.io.gson.SimpleVersionAdapter +import org.parchmentmc.feather.mapping.MappingDataContainer +import org.parchmentmc.feather.mapping.VersionedMappingDataContainer +import org.parchmentmc.feather.util.SimpleVersion + +@CacheableTask +abstract class GeneratePatchRemapMappings : BaseTask() { + + @get:CompileClasspath + abstract val minecraftClasspath: ConfigurableFileCollection + + @get:PathSensitive(PathSensitivity.NONE) + @get:InputFiles + abstract val paramMappings: ConfigurableFileCollection + + @get:PathSensitive(PathSensitivity.NONE) + @get:InputFile + abstract val serverJar: RegularFileProperty + + @get:InputFile + @get:PathSensitive(PathSensitivity.NONE) + abstract val vanillaMappings: RegularFileProperty + + @get:InputFile + @get:PathSensitive(PathSensitivity.NONE) + abstract val spigotMappings: RegularFileProperty + + @get:OutputFile + abstract val patchRemapMappings: RegularFileProperty + + @TaskAction + fun run() { + val mergedMappings = merge(vanillaMappings.path, spigotMappings.path, paramMappings.singleFile.toPath()) + + // run hypo + val ctx: HypoContext + + try { + ctx = HypoContext.builder() + .withProvider(AsmClassDataProvider.of(ClassProviderRoot.fromJar(serverJar.convertToPath()))) + .withContextProvider(AsmClassDataProvider.of(ClassProviderRoot.fromJars(*minecraftClasspath.files.map { it.toPath() } + .toTypedArray()))) + .withContextProvider(AsmClassDataProvider.of(ClassProviderRoot.ofJdk())) + .build() + } catch (e: IOException) { + throw UnexpectedException("Failed to open jar files", e) + } + + try { + HydrationManager.createDefault() + .register(BridgeMethodHydrator.create()) + .register(SuperConstructorHydrator.create()) + .register(LambdaCallHydrator.create()) + .register(LocalClassHydrator.create()) + .hydrate(ctx) + } catch (e: IOException) { + throw UnexpectedException("Failed to hydrate data model", e) + } + + // Fill in any missing mapping information + val completedParamMappings = ChangeChain.create() + .addLink( + CopyMappingsDown.createWithoutOverwrite(), + CopyLambdaParametersDown.createWithoutOverwrite(), + CopyRecordParameters.create(), + PropagateMappingsUp.create() + ) + .applyChain(mergedMappings, MappingsCompletionManager.create(ctx)) + + try { + MappingFormats.TINY.write(completedParamMappings, patchRemapMappings.convertToPath(), SPIGOT_NAMESPACE, NEW_DEOBF_NAMESPACE) + } catch (e: IOException) { + throw UnexpectedException("Failed to write mappings", e) + } + } + + private fun merge(mojangPath: Path, spigotPath: Path, parchmentPath: Path): MappingSet { + // mojang -> obf + val mojangMappings = MappingFormats.PROGUARD.createReader(mojangPath).use { it.read() } + // obf -> spigot + val spigotMappings = MappingFormats.TINY.read(spigotPath, OBF_NAMESPACE, SPIGOT_NAMESPACE) + + val gson = GsonBuilder() + .registerTypeAdapterFactory(MDCGsonAdapterFactory()) + .registerTypeAdapter(SimpleVersion::class.java, SimpleVersionAdapter()) + .create() + + val mappings: MappingDataContainer + try { + FileSystems.newFileSystem(parchmentPath).use { fs -> + val jsonFile = fs.getPath("/parchment.json") + Files.newBufferedReader(jsonFile).use { reader -> + mappings = gson.fromJson(reader, VersionedMappingDataContainer::class.java) + } + } + } catch (e: IOException) { + throw UnexpectedException("Failed to read param mappings file", e) + } + + // mojang -> mojang+parchment + val parchmentMappings = this.toLorenz(mappings) + + // result: spigot -> mojang+parchment + val result = MappingSet.create() + + // merge + mojangMappings.topLevelClassMappings.filterNot { it.obfuscatedName.endsWith("package-info") }.forEach { mojangClass -> + val spigotClass = spigotMappings.getTopLevelClassMapping(mojangClass.deobfuscatedName).orElse(null) + if (spigotClass == null) { + //println("cant find spigot class for ${mojangClass.deobfuscatedName} - ${mojangClass.obfuscatedName}") + return@forEach + } + val parchmentClass = parchmentMappings.getTopLevelClassMapping(mojangClass.obfuscatedName).orElse(null) + if (parchmentClass == null) { + println("cant find parchmentClass class for ${mojangClass.obfuscatedName} - ${mojangClass.deobfuscatedName}") + return@forEach + } + val resultClass = result.createTopLevelClassMapping(spigotClass.deobfuscatedName, mojangClass.obfuscatedName) + + mergeMethodsAndFields(resultClass, mojangClass, spigotClass, parchmentClass) + + mergeInnerClass(mojangClass, spigotClass, resultClass, parchmentClass) + } + + return result + } + + private fun mergeInnerClass( + mojangClass: ClassMapping<*, *>, + spigotClass: ClassMapping<*, *>, + resultClass: ClassMapping<*, *>, + parchmentClass: TopLevelClassMapping + ) { + mojangClass.innerClassMappings.forEach { innerMojangClass -> + val innerSpigotClass = spigotClass.getInnerClassMapping(innerMojangClass.deobfuscatedName).orElse(null) + if (innerSpigotClass == null) { + //println("cant find inner spigot class for ${innerMojangClass.deobfuscatedName} - ${innerMojangClass.obfuscatedName}") + return@forEach + } + val innerParchmentClass = parchmentClass.getInnerClassMapping(innerMojangClass.obfuscatedName).orElse(null) + if (innerParchmentClass == null) { + println("cant find innerParchmentClass for ${innerMojangClass.obfuscatedName} - ${innerMojangClass.deobfuscatedName}") + return@forEach + } + val innerResultClass = resultClass.createInnerClassMapping(innerSpigotClass.deobfuscatedName, innerMojangClass.obfuscatedName) + + mergeMethodsAndFields(innerResultClass, innerMojangClass, innerSpigotClass, innerParchmentClass) + + mergeInnerClass(innerMojangClass, innerSpigotClass, innerResultClass, parchmentClass) + } + } + + private fun mergeMethodsAndFields( + resultClass: ClassMapping<*, *>, + mojangClass: ClassMapping<*, *>, + spigotClass: ClassMapping<*, *>, + parchmentClass: ClassMapping<*, *> + ) { + mojangClass.fieldMappings.forEach { mojangField -> + val spigotField = spigotClass.getFieldMapping(mojangField.deobfuscatedName).orElse(null) + if (spigotField == null) { + //println("cant find spigot field for ${mojangField.deobfuscatedName} - ${mojangField.obfuscatedName}") + return@forEach + } + val resultField = resultClass.createFieldMapping(spigotField.deobfuscatedSignature, mojangField.obfuscatedName) + resultField.deobfuscatedName = mojangField.obfuscatedName + } + + mojangClass.methodMappings.forEach { mojangMethod -> + val spigotMethod = spigotClass.getMethodMapping(mojangMethod.deobfuscatedName, mojangMethod.deobfuscatedDescriptor).orElse(null) + if (spigotMethod == null) { + //println("cant find spigot method for ${mojangMethod.deobfuscatedName} - ${mojangMethod.obfuscatedName}") + return@forEach + } + val parchmentMethod = parchmentClass.getMethodMapping(mojangMethod.obfuscatedName, mojangMethod.obfuscatedDescriptor).orElse(null) + if (parchmentMethod == null) { + //println("cant find parchmentMethod for ${mojangMethod.deobfuscatedName} - ${mojangMethod.obfuscatedName}") + return@forEach + } + val resultMethod = resultClass.createMethodMapping(spigotMethod.deobfuscatedName, spigotMethod.deobfuscatedDescriptor) + resultMethod.setDeobfuscatedName(mojangMethod.obfuscatedName) + + parchmentMethod.parameterMappings.forEach { + resultMethod.createParameterMapping(it.index, it.deobfuscatedName) + } + } + } + + private fun toLorenz(container: MappingDataContainer): MappingSet { + val mappings = MappingSet.create() + + for (aClass in container.classes) { + val classMapping = mappings.getOrCreateClassMapping(aClass.name) + for (method in aClass.methods) { + val methodMapping = classMapping.getOrCreateMethodMapping(method.name, method.descriptor) + for (param in method.parameters) { + methodMapping.getOrCreateParameterMapping(param.index.toInt()).setDeobfuscatedName(param.name) + } + } + for (field in aClass.fields) { + classMapping.getOrCreateFieldMapping(FieldSignature.of(field.name, field.descriptor)) + } + } + + return mappings + } +} + diff --git a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/patchremapv2/RemapCBPatches.kt b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/patchremapv2/RemapCBPatches.kt index e7de832a..e0fd7112 100644 --- a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/patchremapv2/RemapCBPatches.kt +++ b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/patchremapv2/RemapCBPatches.kt @@ -37,7 +37,7 @@ abstract class RemapCBPatches : BaseTask() { fun run() { val workDir = layout.cache.resolve(REMAPPED_CB).ensureClean() val patches = outputPatchDir.convertToPath().ensureClean() - val mappings = MappingFormats.TINY.read(mappingsFile.convertToPath(), SPIGOT_NAMESPACE, DEOBF_NAMESPACE) + val mappings = MappingFormats.TINY.read(mappingsFile.convertToPath(), SPIGOT_NAMESPACE, NEW_DEOBF_NAMESPACE) val configFiles = project.project(":paper-server").configurations["runtimeClasspath"].resolve().map { it.toPath() } val classpath = configFiles + listOf( diff --git a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/constants/constants.kt b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/constants/constants.kt index c5528eff..243d5467 100644 --- a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/constants/constants.kt +++ b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/constants/constants.kt @@ -91,10 +91,12 @@ const val PATCHED_SPIGOT_MOJANG_YARN_MAPPINGS = "$MAPPINGS_DIR/spigot-mojang+yar const val PATCHED_SPIGOT_MOJANG_YARN_SOURCE_MAPPINGS = "$MAPPINGS_DIR/spigot-mojang+yarn-patched-source.tiny" const val REOBF_MOJANG_SPIGOT_MAPPINGS = "$MAPPINGS_DIR/mojang+yarn-spigot-reobf.tiny" const val PATCHED_REOBF_MOJANG_SPIGOT_MAPPINGS = "$MAPPINGS_DIR/mojang+yarn-spigot-reobf-patched.tiny" +const val SPIGOT_MOJANG_PARCHMENT_MAPPINGS = "$MAPPINGS_DIR/spigot-mojang+parchment.tiny" const val OBF_NAMESPACE = "official" const val SPIGOT_NAMESPACE = "spigot" const val DEOBF_NAMESPACE = "mojang+yarn" +const val NEW_DEOBF_NAMESPACE = "mojang+parchment" private const val DATA_PATH = "$PAPER_PATH/data" const val MC_MANIFEST = "$DATA_PATH/McManifest.json"