From 7005a3ab45c9f43a2ccb8452d5fd0b054456aaaa Mon Sep 17 00:00:00 2001 From: Toffikk Date: Tue, 8 Jul 2025 05:27:57 +0200 Subject: [PATCH 001/257] Add the ability to specify patches before source patches --- gradle.properties | 2 +- .../paperweight/core/extension/ForkConfig.kt | 2 + .../core/extension/PaperExtension.kt | 2 + .../core/extension/UpstreamConfig.kt | 2 + .../core/taskcontainers/CoreTasks.kt | 20 +- .../taskcontainers/MinecraftPatchingTasks.kt | 81 +++++- .../core/taskcontainers/PatchingTasks.kt | 60 +++- .../taskcontainers/UpstreamConfigTasks.kt | 16 ++ .../paperweight/core/tasks/FilterRepo.kt | 2 +- .../tasks/patching/ApplyBaseFeaturePatches.kt | 231 +++++++++++++++ .../core/tasks/patching/ApplyFilePatches.kt | 88 +++--- .../patching/ApplyResourceFilePatches.kt | 272 ++++++++++++++++++ .../core/tasks/patching/RebuildFilePatches.kt | 35 ++- .../paperweight/tasks/RebuildGitPatches.kt | 20 +- 14 files changed, 751 insertions(+), 82 deletions(-) create mode 100644 paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyBaseFeaturePatches.kt create mode 100644 paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyResourceFilePatches.kt diff --git a/gradle.properties b/gradle.properties index 8015950f8..df10fd727 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ group = io.papermc.paperweight -version = 2.0.0-SNAPSHOT +version = 2.0.0-CANVAS-beta.18 org.gradle.caching = true org.gradle.parallel = true diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/ForkConfig.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/ForkConfig.kt index 176352ca0..130032e66 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/ForkConfig.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/ForkConfig.kt @@ -49,9 +49,11 @@ abstract class ForkConfig @Inject constructor( val serverDirectory: DirectoryProperty = objects.dirFrom(rootDirectory, providers.provider { "$name-server" }) val serverPatchesDir: DirectoryProperty = objects.dirFrom(serverDirectory, "minecraft-patches") val rejectsDir: DirectoryProperty = objects.dirFrom(serverPatchesDir, "rejected") + val baseRejectsDir: DirectoryProperty = objects.dirFrom(serverPatchesDir, "rejected-base") val sourcePatchDir: DirectoryProperty = objects.dirFrom(serverPatchesDir, "sources") val resourcePatchDir: DirectoryProperty = objects.dirFrom(serverPatchesDir, "resources") val featurePatchDir: DirectoryProperty = objects.dirFrom(serverPatchesDir, "features") + val baseFeaturePatchDir: DirectoryProperty = objects.dirFrom(serverPatchesDir, "base") val buildDataDir: DirectoryProperty = objects.dirFrom(rootDirectory, "build-data") val devImports: RegularFileProperty = objects.fileFrom(buildDataDir, "dev-imports.txt") diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/PaperExtension.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/PaperExtension.kt index af3db8fd0..76769005e 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/PaperExtension.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/PaperExtension.kt @@ -35,8 +35,10 @@ abstract class PaperExtension @Inject constructor(objects: ObjectFactory, projec val paperServerDir: DirectoryProperty = objects.dirFrom(rootDirectory, "paper-server") val serverPatchesDir: DirectoryProperty = objects.dirFrom(paperServerDir, "patches") val rejectsDir: DirectoryProperty = objects.dirFrom(serverPatchesDir, "rejected") + val baseRejectsDir: DirectoryProperty = objects.dirFrom(serverPatchesDir, "rejected-base") val sourcePatchDir: DirectoryProperty = objects.dirFrom(serverPatchesDir, "sources") val resourcePatchDir: DirectoryProperty = objects.dirFrom(serverPatchesDir, "resources") + val baseFeaturePatchDir: DirectoryProperty = objects.dirFrom(serverPatchesDir, "base") val featurePatchDir: DirectoryProperty = objects.dirFrom(serverPatchesDir, "features") @Suppress("MemberVisibilityCanBePrivate") diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/UpstreamConfig.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/UpstreamConfig.kt index c448db831..7597b785b 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/UpstreamConfig.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/UpstreamConfig.kt @@ -104,8 +104,10 @@ abstract class UpstreamConfig @Inject constructor( abstract val patchesDir: DirectoryProperty val rejectsDir: DirectoryProperty = objects.dirFrom(patchesDir, "rejected") + val baseRejectsDir: DirectoryProperty = objects.dirFrom(patchesDir, "rejected-base") val filePatchDir: DirectoryProperty = objects.dirFrom(patchesDir, "files") val featurePatchDir: DirectoryProperty = objects.dirFrom(patchesDir, "features") + val baseFeaturePatchDir: DirectoryProperty = objects.dirFrom(patchesDir, "base") } abstract class RepoPatchSet @Inject constructor( diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/CoreTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/CoreTasks.kt index ab708b943..177699bfb 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/CoreTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/CoreTasks.kt @@ -90,7 +90,7 @@ class CoreTasks( } val importLibraryFiles = tasks.register("importPaperLibraryFiles") { - patches.from(project.coreExt.paper.sourcePatchDir, project.coreExt.paper.featurePatchDir) + patches.from(project.coreExt.paper.sourcePatchDir, project.coreExt.paper.featurePatchDir) // also cant add base, see MinecraftPatchingTasks devImports.set(project.coreExt.paper.devImports.fileExists(project)) libraryFileIndex.set(indexLibraryFiles.flatMap { it.outputFile }) libraries.from(indexLibraryFiles.map { it.libraries }) @@ -187,7 +187,9 @@ class CoreTasks( hasFork, project.coreExt.paper.sourcePatchDir, project.coreExt.paper.rejectsDir, + project.coreExt.paper.baseRejectsDir, project.coreExt.paper.resourcePatchDir, + project.coreExt.paper.baseFeaturePatchDir, project.coreExt.paper.featurePatchDir, project.coreExt.paper.additionalAts, extractMacheSources.flatMap { it.outputDir }, @@ -223,7 +225,9 @@ class CoreTasks( !activeFork, cfg.sourcePatchDir, cfg.rejectsDir, + cfg.baseRejectsDir, cfg.resourcePatchDir, + cfg.baseFeaturePatchDir, cfg.featurePatchDir, cfg.additionalAts, upstreamTasks.first.applyFeaturePatches.flatMap { it.repo }, @@ -274,6 +278,13 @@ class CoreTasks( dependsOn(serverTasks.applyFilePatches) dependsOn("applyServerFilePatches") } + val applyAllServerBaseFeaturePatches = project.tasks.register("applyAllServerBaseFeaturePatches") { + group = "patching" + description = "Applies all Minecraft and upstream server base patches " + + "(equivalent to '${serverTasks.applyBaseFeaturePatches.name} applyServerBaseFeaturePatches')" + dependsOn(serverTasks.applyBaseFeaturePatches) + dependsOn("applyServerBaseFeaturePatches") + } val applyAllServerFeaturePatches = project.tasks.register("applyAllServerFeaturePatches") { group = "patching" description = "Applies all Minecraft and upstream server feature patches " + @@ -295,6 +306,13 @@ class CoreTasks( dependsOn(serverTasks.rebuildFilePatchesName) dependsOn("rebuildServerFilePatches") } + val rebuildAllServerBasePatches = project.tasks.register("rebuildAllServerBasePatches") { + group = "patching" + description = "Rebuilds all Minecraft and upstream server base patches " + + "(equivalent to '${serverTasks.rebuildBasePatchesName} rebuildServerBasePatches')" + dependsOn(serverTasks.rebuildBasePatchesName) + dependsOn("rebuildServerBasePatches") + } val rebuildAllServerFeaturePatches = project.tasks.register("rebuildAllServerFeaturePatches") { group = "patching" description = "Rebuilds all Minecraft and upstream server feature patches " + diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt index 76631e64a..492962f51 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt @@ -27,9 +27,11 @@ import io.papermc.paperweight.core.tasks.ImportLibraryFiles import io.papermc.paperweight.core.tasks.SetupForkMinecraftSources import io.papermc.paperweight.core.tasks.patching.ApplyFeaturePatches import io.papermc.paperweight.core.tasks.patching.ApplyFilePatches +import io.papermc.paperweight.core.tasks.patching.ApplyResourceFilePatches import io.papermc.paperweight.core.tasks.patching.ApplyFilePatchesFuzzy import io.papermc.paperweight.core.tasks.patching.FixupFilePatches import io.papermc.paperweight.core.tasks.patching.RebuildFilePatches +import io.papermc.paperweight.core.tasks.patching.ApplyBaseFeaturePatches import io.papermc.paperweight.tasks.* import io.papermc.paperweight.util.* import io.papermc.paperweight.util.constants.* @@ -51,7 +53,9 @@ class MinecraftPatchingTasks( private val readOnly: Boolean, private val sourcePatchDir: DirectoryProperty, private val rejectsDir: DirectoryProperty, + private val baseRejectsDir: DirectoryProperty, private val resourcePatchDir: DirectoryProperty, + private val baseFeaturePatchDir: DirectoryProperty, private val featurePatchDir: DirectoryProperty, private val additionalAts: RegularFileProperty, private val baseSources: Provider, @@ -74,28 +78,50 @@ class MinecraftPatchingTasks( private fun ApplyFilePatches.configureApplyFilePatches() { group() description = "Applies $configName file patches to the Minecraft sources" + dependsOn(applyBaseFeaturePatches) - input.set(baseSources) if (readOnly) { - output.set(outputSrcFile) + repo.set(outputSrcFile) } else { - output.set(outputSrc) + repo.set(outputSrc) } + base.set(applyBaseFeaturePatches.flatMap { it.output }) patches.set(sourcePatchDir.fileExists(project)) rejectsDir.set(this@MinecraftPatchingTasks.rejectsDir) gitFilePatches.set(this@MinecraftPatchingTasks.gitFilePatches) identifier = configName } + private fun ApplyBaseFeaturePatches.configureApplyBaseFeaturePatches() { + group() + description = "Applies $configName base patches to the Minecraft sources" + + input.set(baseSources) + + base.set(baseSources) + + output.set(outputSrc) + baseRef.set("upstream/main") + patches.set(baseFeaturePatchDir.fileExists(project)) + baseRejectsDir.set(this@MinecraftPatchingTasks.baseRejectsDir) + identifier = configName + } + + val applyBaseFeaturePatches = tasks.register("apply${namePart}BaseFeaturePatches") { + configureApplyBaseFeaturePatches() + } + val applySourcePatches = tasks.register("apply${namePart}SourcePatches") { configureApplyFilePatches() + dependsOn(applyBaseFeaturePatches) } val applySourcePatchesFuzzy = tasks.register("apply${namePart}SourcePatchesFuzzy") { configureApplyFilePatches() + dependsOn(applyBaseFeaturePatches) } - val applyResourcePatches = tasks.register("apply${namePart}ResourcePatches") { + val applyResourcePatches = tasks.register("apply${namePart}ResourcePatches") { group() description = "Applies $configName file patches to the Minecraft resources" @@ -107,10 +133,16 @@ class MinecraftPatchingTasks( identifier = configName } + val applyBasePatches = tasks.register("apply${namePart}BasePatches") { + group() + description = "Applies all $configName Minecraft base feature patches" + dependsOn(applyBaseFeaturePatches) + } + val applyFilePatches = tasks.register("apply${namePart}FilePatches") { group() description = "Applies all $configName Minecraft file patches" - dependsOn(applySourcePatches, applyResourcePatches) + dependsOn(applyBasePatches, applySourcePatches, applyResourcePatches) } val applyFeaturePatches = tasks.register("apply${namePart}FeaturePatches") { @@ -119,7 +151,7 @@ class MinecraftPatchingTasks( dependsOn(applyFilePatches) if (readOnly) { - base.set(applySourcePatches.flatMap { it.output }) + base.set(applySourcePatches.flatMap { it.repo }) } repo.set(outputSrc) patches.set(featurePatchDir.fileExists(project)) @@ -128,9 +160,11 @@ class MinecraftPatchingTasks( val applyPatches = tasks.register("apply${namePart}Patches") { group() description = "Applies all $configName Minecraft patches" - dependsOn(applyFilePatches, applyFeaturePatches) + dependsOn(applyBasePatches, applyFilePatches, applyFeaturePatches) } + val rebuildBaseFeaturePatchesName = "rebuild${namePart}BaseFeaturePatches" + val rebuildBasePatchesName = "rebuild${namePart}BasePatches" val rebuildSourcePatchesName = "rebuild${namePart}SourcePatches" val rebuildResourcePatchesName = "rebuild${namePart}ResourcePatches" val rebuildFilePatchesName = "rebuild${namePart}FilePatches" @@ -154,12 +188,13 @@ class MinecraftPatchingTasks( } val importLibFiles = tasks.register("import${configName.capitalized()}LibraryFiles") { - patches.from(config.featurePatchDir, config.sourcePatchDir) + patches.from(config.featurePatchDir, config.sourcePatchDir) // cant add base for some reason, oh well we'll use dev-imports devImports.set(config.devImports.fileExists(project)) libraryFileIndex.set(coreTasks.indexLibraryFiles.flatMap { it.outputFile }) libraries.from(coreTasks.indexLibraryFiles.map { it.libraries }) } + val setup = tasks.register("run${configName.capitalized()}Setup") { description = "Applies $configName ATs and library imports to Minecraft sources" @@ -173,22 +208,28 @@ class MinecraftPatchingTasks( ats.jstClasspath.from(project.configurations.named(MACHE_MINECRAFT_LIBRARIES_CONFIG)) } - applySourcePatches.configure { + applyBaseFeaturePatches.configure { input.set(setup.flatMap { it.outputDir }) } + + applySourcePatches.configure { + base.set(applyBaseFeaturePatches.flatMap { it.output }) + } applySourcePatchesFuzzy.configure { - input.set(setup.flatMap { it.outputDir }) + base.set(applyBaseFeaturePatches.flatMap { it.output }) } + val name = "rebuild${namePart}SourcePatches" if (name in tasks.names) { tasks.named(name) { - base.set(setup.flatMap { it.outputDir }) + base.set(baseSources) } } } private fun setupWritable() { listOf( + applyBaseFeaturePatches, applySourcePatches, applySourcePatchesFuzzy, applyFeaturePatches, @@ -199,6 +240,17 @@ class MinecraftPatchingTasks( } } + val rebuildBasePatches = tasks.register(rebuildBasePatchesName) { + group() + description = "Rebuilds $configName base patches to the Minecraft source" + + baseRef.set("base") + stopRef.set("patchedBase~1") + inputDir.set(outputSrc) + patchDir.set(baseFeaturePatchDir) + filterPatches.set(this@MinecraftPatchingTasks.filterPatches) + } + val rebuildSourcePatches = tasks.register(rebuildSourcePatchesName) { group() description = "Rebuilds $configName file patches to the Minecraft sources" @@ -218,8 +270,8 @@ class MinecraftPatchingTasks( group() description = "Rebuilds $configName file patches to the Minecraft resources" - base.set(baseResources) input.set(outputResources) + base.set(baseResources) patches.set(resourcePatchDir) gitFilePatches.set(this@MinecraftPatchingTasks.gitFilePatches) } @@ -238,13 +290,14 @@ class MinecraftPatchingTasks( inputDir.set(outputSrc) patchDir.set(featurePatchDir) baseRef.set("file") + stopRef.set("HEAD") filterPatches.set(this@MinecraftPatchingTasks.filterPatches) } val rebuildPatches = tasks.register(rebuildPatchesName) { group() description = "Rebuilds all $configName patches to Minecraft" - dependsOn(rebuildFilePatches, rebuildFeaturePatches) + dependsOn(rebuildBasePatches, rebuildFilePatches, rebuildFeaturePatches) } val fixupSourcePatches = tasks.register("fixup${namePart}SourcePatches") { @@ -252,7 +305,7 @@ class MinecraftPatchingTasks( description = "Puts the currently tracked source changes into the $configName Minecraft sources file patches commit" repo.set(outputSrc) - upstream.set("upstream/main") + upstream.set("upstream/patchedBase") } val fixupResourcePatches = tasks.register("fixup${namePart}ResourcePatches") { diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt index ea88a091a..274b953a7 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt @@ -27,6 +27,7 @@ import io.papermc.paperweight.core.tasks.patching.ApplyFilePatches import io.papermc.paperweight.core.tasks.patching.ApplyFilePatchesFuzzy import io.papermc.paperweight.core.tasks.patching.FixupFilePatches import io.papermc.paperweight.core.tasks.patching.RebuildFilePatches +import io.papermc.paperweight.core.tasks.patching.ApplyBaseFeaturePatches import io.papermc.paperweight.tasks.* import io.papermc.paperweight.util.* import io.papermc.paperweight.util.constants.paperTaskOutput @@ -47,7 +48,9 @@ class PatchingTasks( private val readOnly: Boolean, private val filePatchDir: DirectoryProperty, private val rejectsDir: DirectoryProperty, + private val baseRejectsDir: DirectoryProperty, private val featurePatchDir: DirectoryProperty, + private val baseFeaturePatchDir: DirectoryProperty, private val baseDir: Provider, private val gitFilePatches: Provider, private val filterPatches: Provider, @@ -60,25 +63,53 @@ class PatchingTasks( group = taskGroup description = "Applies $patchSetName file patches" - input.set(baseDir) if (readOnly) { - output.set(layout.cache.resolve(paperTaskOutput())) + repo.set(layout.cache.resolve(paperTaskOutput())) } else { - output.set(outputDir) + repo.set(outputDir) } patches.set(filePatchDir.fileExists(project)) rejectsDir.set(this@PatchingTasks.rejectsDir) gitFilePatches.set(this@PatchingTasks.gitFilePatches) - baseRef.set("base") + base.set(applyBaseFeaturePatches.flatMap { it.output }) + identifier = "$forkName $patchSetName" + } + + private fun ApplyBaseFeaturePatches.configureApplyBaseFeaturePatches() { + group = taskGroup + description = "Applies $patchSetName base patches" + + input.set(baseDir) + if (readOnly) { + output.set(layout.cache.resolve(paperTaskOutput())) + } else { + output.set(outputDir) + } + + base.set(baseDir) + + baseRef.set("HEAD") + patches.set(baseFeaturePatchDir.fileExists(project)) + baseRejectsDir.set(this@PatchingTasks.baseRejectsDir) identifier = "$forkName $patchSetName" } + val applyBaseFeaturePatches = tasks.register("apply${namePart}BaseFeaturePatches") { + configureApplyBaseFeaturePatches() + } + val applyFilePatches = tasks.register("apply${namePart}FilePatches") { configureApplyFilePatches() + dependsOn(applyBaseFeaturePatches) + base.set(applyBaseFeaturePatches.flatMap { it.output }) + baseRef.set("patchedBase") } val applyFilePatchesFuzzy = tasks.register("apply${namePart}FilePatchesFuzzy") { configureApplyFilePatches() + dependsOn(applyBaseFeaturePatches) + base.set(applyBaseFeaturePatches.flatMap { it.output }) + baseRef.set("patchedBase") } val applyFeaturePatches = tasks.register("apply${namePart}FeaturePatches") { @@ -88,7 +119,7 @@ class PatchingTasks( repo.set(outputDir) if (readOnly) { - base.set(applyFilePatches.flatMap { it.output }) + base.set(applyFilePatches.flatMap { it.repo }) } patches.set(featurePatchDir.fileExists(project)) } @@ -96,9 +127,10 @@ class PatchingTasks( val applyPatches = tasks.register("apply${namePart}Patches") { group = taskGroup description = "Applies all $patchSetName patches" - dependsOn(applyFilePatches, applyFeaturePatches) + dependsOn(applyBaseFeaturePatches, applyFilePatches, applyFeaturePatches) } + val rebuildBasePatchesName = "rebuild${namePart}BasePatches" val rebuildFilePatchesName = "rebuild${namePart}FilePatches" val fixupFilePatchesName = "fixup${namePart}FilePatches" val rebuildFeaturePatchesName = "rebuild${namePart}FeaturePatches" @@ -112,6 +144,7 @@ class PatchingTasks( private fun setupWritable() { listOf( + applyBaseFeaturePatches, applyFilePatches, applyFilePatchesFuzzy, applyFeaturePatches, @@ -121,6 +154,16 @@ class PatchingTasks( } } + val rebuildBasePatches = tasks.register(rebuildBasePatchesName) { + group = taskGroup + description = "Rebuilds $patchSetName base patches" + + inputDir.set(outputDir) + baseRef.set("base") + stopRef.set("patchedBase~1") + patchDir.set(baseFeaturePatchDir) + filterPatches.set(this@PatchingTasks.filterPatches) + } val rebuildFilePatches = tasks.register(rebuildFilePatchesName) { group = taskGroup description = "Rebuilds $patchSetName file patches" @@ -136,7 +179,7 @@ class PatchingTasks( description = "Puts the currently tracked source changes into the $patchSetName file patches commit" repo.set(outputDir) - upstream.set("base") + upstream.set("upstream/patchedBase") } val rebuildFeaturePatches = tasks.register(rebuildFeaturePatchesName) { @@ -147,13 +190,14 @@ class PatchingTasks( inputDir.set(outputDir) patchDir.set(featurePatchDir) baseRef.set("file") + stopRef.set("HEAD") filterPatches.set(this@PatchingTasks.filterPatches) } val rebuildPatches = tasks.register(rebuildPatchesName) { group = taskGroup description = "Rebuilds all $patchSetName patches" - dependsOn(rebuildFilePatches, rebuildFeaturePatches) + dependsOn(rebuildBasePatches, rebuildFilePatches, rebuildFeaturePatches) } val applyOrMoveFilePatches = tasks.register("applyOrMove${namePart}FilePatches") { diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/UpstreamConfigTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/UpstreamConfigTasks.kt index 3b95ba560..1fdfcd577 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/UpstreamConfigTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/UpstreamConfigTasks.kt @@ -130,7 +130,9 @@ class UpstreamConfigTasks( readOnly, cfg.filePatchDir, cfg.rejectsDir, + cfg.baseRejectsDir, cfg.featurePatchDir, + cfg.baseFeaturePatchDir, base, gitFilePatches, filterPatches, @@ -170,6 +172,13 @@ class UpstreamConfigTasks( } fun setupAggregateTasks(namePart: String, desc: String, descSingleFile: String = desc) { + val applyBase = target.tasks.register("apply${namePart}BaseFeaturePatches") { + group = taskGroup + description = "Applies $desc base patches" + patchingTasks.values.forEach { t -> + dependsOn(t.applyBaseFeaturePatches) + } + } val applyFile = target.tasks.register("apply${namePart}FilePatches") { group = taskGroup description = "Applies $desc file patches" @@ -184,6 +193,13 @@ class UpstreamConfigTasks( dependsOn(t.applyFeaturePatches) } } + val rebuildBase = target.tasks.register("rebuild${namePart}BaseFeaturePatches") { + group = taskGroup + description = "Rebuilds $desc base patches" + patchingTasks.values.forEach { t -> + dependsOn(t.rebuildBasePatchesName) + } + } val rebuildFile = target.tasks.register("rebuild${namePart}FilePatches") { group = taskGroup description = "Rebuilds $desc file patches" diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/FilterRepo.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/FilterRepo.kt index af0ec6379..a7372a8aa 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/FilterRepo.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/FilterRepo.kt @@ -55,7 +55,7 @@ abstract class FilterRepo : BaseTask() { override fun init() { super.init() outputDir.set(layout.cache.resolve(paperTaskOutput())) - tag.convention("base") + tag.convention("patchedBase") } @TaskAction diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyBaseFeaturePatches.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyBaseFeaturePatches.kt new file mode 100644 index 000000000..62a1ce11a --- /dev/null +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyBaseFeaturePatches.kt @@ -0,0 +1,231 @@ +/* + * paperweight is a Gradle plugin for the PaperMC project. + * + * Copyright (c) 2023 Kyle Wood (DenWav) + * Contributors + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 only, no later versions. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + * USA + */ + +package io.papermc.paperweight.core.tasks.patching + +import io.papermc.paperweight.PaperweightException +import io.papermc.paperweight.tasks.* +import io.papermc.paperweight.util.* +import java.nio.file.Path +import kotlin.io.path.* +import org.gradle.api.file.DirectoryProperty +import org.gradle.api.provider.Property +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.InputDirectory +import org.gradle.api.tasks.Optional +import org.gradle.api.tasks.OutputDirectory +import org.gradle.api.tasks.PathSensitive +import org.gradle.api.tasks.PathSensitivity +import org.gradle.api.tasks.TaskAction +import io.papermc.paperweight.util.Git +import org.eclipse.jgit.api.Git as JGit +import org.eclipse.jgit.lib.PersonIdent +import org.eclipse.jgit.transport.URIish +import java.time.Instant + +abstract class ApplyBaseFeaturePatches : ControllableOutputTask() { + + @get:PathSensitive(PathSensitivity.NONE) + @get:InputDirectory + abstract val input: DirectoryProperty + + @get:OutputDirectory + abstract val output: DirectoryProperty + + @get:InputDirectory + @get:Optional + abstract val base: DirectoryProperty + + @get:Optional + @get:Input + abstract val baseRef: Property + + @get:PathSensitive(PathSensitivity.NONE) + @get:InputDirectory + @get:Optional + abstract val patches: DirectoryProperty + + @get:Input + abstract val verbose: Property + + @get:Input + @get:Optional + abstract val identifier: Property + + // An additional remote to add and fetch from before applying patches (to bring in objects for 3-way merge). + @get:Input + @get:Optional + abstract val additionalRemote: Property + + @get:Input + abstract val additionalRemoteName: Property + + override fun init() { + printOutput.convention(false).finalizeValueOnRead() + additionalRemoteName.convention("old") + verbose.convention(false) + } + +@TaskAction +fun run() { + Git.checkForGit() + + val outputPath = output.path + recreateCloneDirectory(outputPath) + + val git = Git(outputPath) + + checkoutRepoFromUpstream( + Git(outputPath), + input.path, + baseRef.getOrElse("patchedBase"), + "upstream", + "patchedBase", + baseRef.isPresent, + ) + if (additionalRemote.isPresent) { + val jgit = JGit.open(outputPath.toFile()) + jgit.remoteRemove().setRemoteName(additionalRemoteName.get()).call() + jgit.remoteAdd().setName(additionalRemoteName.get()).setUri(URIish(additionalRemote.get())).call() + jgit.fetch().setRemote(additionalRemoteName.get()).call() + } + + setupGitHook(outputPath) + + tagBase() + + if (!patches.isPresent) { + } else { + applyGitPatches(git, "server repo", outputPath, patches.path, printOutput.get(), verbose.get()) + } +} + + private fun recreateCloneDirectory(target: Path) { + if (target.exists()) { + if (target.resolve(".git").isDirectory()) { + val git = Git(target) + git("clean", "-fxd").runSilently(silenceErr = true) + git("reset", "--hard", "HEAD").runSilently(silenceErr = true) + } else { + for (entry in target.listDirectoryEntries()) { + entry.deleteRecursive() + } + target.createDirectories() + } + } else { + target.createDirectories() + } + } + + private fun tagBase() { + val git = JGit.open(output.path.toFile()) + val ident = PersonIdent("base", "noreply+automated@papermc.io") + git.tagDelete().setTags("base").call() + git.tag().setName("base").setTagger(ident).setSigned(false).call() + git.close() + } + + private fun setupGitHook(outputPath: Path) { + val hook = outputPath.resolve(".git/hooks/post-rewrite") + hook.parent.createDirectories() + hook.writeText(javaClass.getResource("/post-rewrite.sh")!!.readText()) + hook.toFile().setExecutable(true) + } + + private fun commit() { + val ident = PersonIdent(PersonIdent("Patched Base", "noreply+automated@papermc.io"), Instant.parse("1997-04-20T13:37:42.69Z")) + val git = JGit.open(output.path.toFile()) + git.add().addFilepattern(".").call() + git.commit() + .setMessage("${identifier.get()} Base Patches") + .setAuthor(ident) + .setAllowEmpty(true) + .setSign(false) + .call() + git.tagDelete().setTags("patchedBase").call() + git.tag().setName("patchedBase").setTagger(ident).setSigned(false).call() + git.close() + } + + private fun applyGitPatches( + git: Git, + target: String, + outputDir: Path, + patchDir: Path?, + printOutput: Boolean, + verbose: Boolean, + ) { + if (printOutput) { + logger.lifecycle("Applying patches to $target...") + } + + val statusFile = outputDir.resolve(".git/patch-apply-failed") + statusFile.deleteForcefully() + + git("am", "--abort").runSilently(silenceErr = true) + + val patches = patchDir?.useDirectoryEntries("*.patch") { it.toMutableList() } ?: mutableListOf() + if (patches.isEmpty()) { + if (printOutput) { + logger.lifecycle("No patches found") + } + return + } + + // This prevents the `git am` command line from getting too big with too many patches + // mostly an issue with Windows + layout.cache.createDirectories() + val tempDir = createTempDirectory(layout.cache, "paperweight") + try { + val mailDir = tempDir.resolve("new") + mailDir.createDirectories() + + for (patch in patches) { + patch.copyTo(mailDir.resolve(patch.fileName)) + } + + val gitOut = printOutput && verbose + val result = git("am", "--3way", "--ignore-whitespace", tempDir.absolutePathString()).captureOut(gitOut) + if (result.exit != 0) { + statusFile.writeText("1") + + if (!gitOut) { + // Log the output anyway on failure + logger.lifecycle(result.out) + } + logger.error("*** Please review above details and finish the apply then") + logger.error("*** save the changes with `./gradlew rebuildPatches`") + + throw PaperweightException("Failed to apply patches") + } else { + statusFile.deleteForcefully() + if (printOutput) { + logger.lifecycle("${patches.size} patches applied cleanly to $target") + } + } + } finally { + tempDir.deleteRecursive() + } + + commit() + } +} diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyFilePatches.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyFilePatches.kt index 43d953242..ea36f3b93 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyFilePatches.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyFilePatches.kt @@ -51,13 +51,6 @@ abstract class ApplyFilePatches : BaseTask() { ) abstract val verbose: Property - @get:PathSensitive(PathSensitivity.NONE) - @get:InputDirectory - abstract val input: DirectoryProperty - - @get:OutputDirectory - abstract val output: DirectoryProperty - @get:PathSensitive(PathSensitivity.NONE) @get:InputDirectory @get:Optional @@ -70,6 +63,13 @@ abstract class ApplyFilePatches : BaseTask() { @get:Input abstract val gitFilePatches: Property + @get:OutputDirectory + abstract val repo: DirectoryProperty + + @get:InputDirectory + @get:Optional + abstract val base: DirectoryProperty + @get:Optional @get:Input abstract val baseRef: Property @@ -106,34 +106,37 @@ abstract class ApplyFilePatches : BaseTask() { open fun run() { io.papermc.paperweight.util.Git.checkForGit() - val outputPath = output.path - recreateCloneDirectory(outputPath) - - checkoutRepoFromUpstream( - Git(outputPath), - input.path, - baseRef.getOrElse("main"), - "upstream", - "main", - baseRef.isPresent, - ) - - if (additionalRemote.isPresent) { - val jgit = Git.open(outputPath.toFile()) - jgit.remoteRemove().setRemoteName(additionalRemoteName.get()).call() - jgit.remoteAdd().setName(additionalRemoteName.get()).setUri(URIish(additionalRemote.get())).call() - jgit.fetch().setRemote(additionalRemoteName.get()).call() + val base = base.pathOrNull + if (base != null && base.toAbsolutePath() != repo.path.toAbsolutePath()) { + + recreateCloneDirectory(repo.path) + + val git = Git(repo.path.createDirectories()) + checkoutRepoFromUpstream( + git, + base, + baseRef.getOrElse("upstream/patchedBase"), + branchName = "main", + ref = true, + ) } - setupGitHook(outputPath) + val repoPath = repo.path + val git = Git(repoPath) - tagBase() + if (git("checkout", "main").runSilently(silenceErr = true) != 0) { + git("checkout", "-b", "main").runSilently(silenceErr = true) + } + if (git("reset", "--hard", "patchedBase").runSilently(silenceErr = true) != 0) { + git("reset", "--hard", "upstream/main").runSilently(silenceErr = true) // resources special handling + } + git("gc").runSilently(silenceErr = true) val result = if (!patches.isPresent) { commit() 0 } else if (gitFilePatches.get()) { - applyWithGit(outputPath) + applyWithGit(repoPath) } else { applyWithDiffPatch() } @@ -160,20 +163,12 @@ abstract class ApplyFilePatches : BaseTask() { } } - private fun tagBase() { - val git = Git.open(output.path.toFile()) - val ident = PersonIdent("base", "noreply+automated@papermc.io") - git.tagDelete().setTags("base").call() - git.tag().setName("base").setTagger(ident).setSigned(false).call() - git.close() - } - - private fun applyWithGit(outputPath: Path): Int { - val git = Git(outputPath) + private fun applyWithGit(repoPath: Path): Int { + val git = Git(repoPath) val patchFiles = patches.path.filesMatchingRecursive("*.patch") if (moveFailedGitPatchesToRejects.get() && rejectsDir.isPresent) { patchFiles.forEach { patch -> - val patchPathFromGit = outputPath.relativize(patch) + val patchPathFromGit = repoPath.relativize(patch) val responseCode = git( "-c", @@ -188,7 +183,7 @@ abstract class ApplyFilePatches : BaseTask() { responseCode == 1 -> { val relativePatch = patches.path.relativize(patch) val failedFile = relativePatch.parent.resolve(relativePatch.fileName.toString().substringBeforeLast(".patch")) - if (outputPath.resolve(failedFile).exists()) { + if (repoPath.resolve(failedFile).exists()) { git("reset", "--", failedFile.pathString).executeSilently(silenceOut = !verbose.get(), silenceErr = !verbose.get()) git("restore", failedFile.pathString).executeSilently(silenceOut = !verbose.get(), silenceErr = !verbose.get()) } @@ -199,7 +194,7 @@ abstract class ApplyFilePatches : BaseTask() { } } } else { - val patchStrings = patchFiles.map { outputPath.relativize(it).pathString } + val patchStrings = patchFiles.map { repoPath.relativize(it).pathString } patchStrings.chunked(12).forEach { git("apply", "--3way", *it.toTypedArray()).executeSilently(silenceOut = !verbose.get(), silenceErr = !verbose.get()) } @@ -214,9 +209,9 @@ abstract class ApplyFilePatches : BaseTask() { val printStream = PrintStream(LoggingOutputStream(logger, LogLevel.LIFECYCLE)) val builder = PatchOperation.builder() .logTo(printStream) - .basePath(output.path) + .basePath(repo.path) .patchesPath(patches.path) - .outputPath(output.path) + .outputPath(repo.path) .level(if (verbose.get()) codechicken.diffpatch.util.LogLevel.ALL else codechicken.diffpatch.util.LogLevel.INFO) .mode(mode()) .minFuzz(minFuzz()) @@ -240,16 +235,9 @@ abstract class ApplyFilePatches : BaseTask() { return result.summary.changedFiles } - private fun setupGitHook(outputPath: Path) { - val hook = outputPath.resolve(".git/hooks/post-rewrite") - hook.parent.createDirectories() - hook.writeText(javaClass.getResource("/post-rewrite.sh")!!.readText()) - hook.toFile().setExecutable(true) - } - private fun commit() { val ident = PersonIdent(PersonIdent("File", "noreply+automated@papermc.io"), Instant.parse("1997-04-20T13:37:42.69Z")) - val git = Git.open(output.path.toFile()) + val git = Git.open(repo.path.toFile()) git.add().addFilepattern(".").call() git.commit() .setMessage("${identifier.get()} File Patches") diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyResourceFilePatches.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyResourceFilePatches.kt new file mode 100644 index 000000000..795d5bd01 --- /dev/null +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyResourceFilePatches.kt @@ -0,0 +1,272 @@ +/* + * paperweight is a Gradle plugin for the PaperMC project. + * + * Copyright (c) 2023 Kyle Wood (DenWav) + * Contributors + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 only, no later versions. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + * USA + */ + +package io.papermc.paperweight.core.tasks.patching + +import codechicken.diffpatch.cli.PatchOperation +import codechicken.diffpatch.match.FuzzyLineMatcher +import codechicken.diffpatch.util.LoggingOutputStream +import codechicken.diffpatch.util.PatchMode +import io.papermc.paperweight.PaperweightException +import io.papermc.paperweight.tasks.* +import io.papermc.paperweight.util.* +import java.io.PrintStream +import java.nio.file.Path +import java.time.Instant +import kotlin.io.path.* +import org.eclipse.jgit.api.Git +import org.eclipse.jgit.lib.PersonIdent +import org.eclipse.jgit.transport.URIish +import org.gradle.api.file.DirectoryProperty +import org.gradle.api.logging.LogLevel +import org.gradle.api.provider.Property +import org.gradle.api.tasks.* +import org.gradle.api.tasks.options.Option + +abstract class ApplyResourceFilePatches : BaseTask() { + + @get:Input + @get:Option( + option = "verbose", + description = "Prints out more info about the patching process", + ) + abstract val verbose: Property + + @get:PathSensitive(PathSensitivity.NONE) + @get:InputDirectory + abstract val input: DirectoryProperty + + @get:OutputDirectory + abstract val output: DirectoryProperty + + @get:PathSensitive(PathSensitivity.NONE) + @get:InputDirectory + @get:Optional + abstract val patches: DirectoryProperty + + @get:Internal + abstract val rejectsDir: DirectoryProperty + + @get:Optional + @get:Input + abstract val gitFilePatches: Property + + @get:Optional + @get:Input + abstract val baseRef: Property + + @get:Input + @get:Optional + abstract val identifier: Property + + // An additional remote to add and fetch from before applying patches (to bring in objects for 3-way merge). + @get:Input + @get:Optional + abstract val additionalRemote: Property + + @get:Input + abstract val additionalRemoteName: Property + + @get:Input + abstract val moveFailedGitPatchesToRejects: Property + + @get:Internal + abstract val emitRejects: Property + + init { + run { + verbose.convention(false) + gitFilePatches.convention(false) + additionalRemoteName.convention("old") + moveFailedGitPatchesToRejects.convention(false) + emitRejects.convention(true) + } + } + + @TaskAction + open fun run() { + io.papermc.paperweight.util.Git.checkForGit() + + val outputPath = output.path + recreateCloneDirectory(outputPath) + + checkoutRepoFromUpstream( + Git(outputPath), + input.path, + baseRef.getOrElse("main"), + "upstream", + "main", + baseRef.isPresent, + ) + + if (additionalRemote.isPresent) { + val jgit = Git.open(outputPath.toFile()) + jgit.remoteRemove().setRemoteName(additionalRemoteName.get()).call() + jgit.remoteAdd().setName(additionalRemoteName.get()).setUri(URIish(additionalRemote.get())).call() + jgit.fetch().setRemote(additionalRemoteName.get()).call() + } + + setupGitHook(outputPath) + + tagBase() + + val result = if (!patches.isPresent) { + commit() + 0 + } else if (gitFilePatches.get()) { + applyWithGit(outputPath) + } else { + applyWithDiffPatch() + } + + if (!verbose.get()) { + logger.lifecycle("Applied $result patches") + } + } + + private fun recreateCloneDirectory(target: Path) { + if (target.exists()) { + if (target.resolve(".git").isDirectory()) { + val git = Git(target) + git("clean", "-fxd").runSilently(silenceErr = true) + git("reset", "--hard", "HEAD").runSilently(silenceErr = true) + } else { + for (entry in target.listDirectoryEntries()) { + entry.deleteRecursive() + } + target.createDirectories() + } + } else { + target.createDirectories() + } + } + + private fun tagBase() { + val git = Git.open(output.path.toFile()) + val ident = PersonIdent("base", "noreply+automated@papermc.io") + git.tagDelete().setTags("base").call() + git.tag().setName("base").setTagger(ident).setSigned(false).call() + git.close() + } + + private fun applyWithGit(outputPath: Path): Int { + val git = Git(outputPath) + val patchFiles = patches.path.filesMatchingRecursive("*.patch") + if (moveFailedGitPatchesToRejects.get() && rejectsDir.isPresent) { + patchFiles.forEach { patch -> + val patchPathFromGit = outputPath.relativize(patch) + val responseCode = + git( + "-c", + "rerere.enabled=false", + "apply", + "--3way", + patchPathFromGit.pathString + ).runSilently(silenceOut = !verbose.get(), silenceErr = !verbose.get()) + when { + responseCode == 0 -> {} + responseCode > 1 -> throw PaperweightException("Failed to apply patch $patch: $responseCode") + responseCode == 1 -> { + val relativePatch = patches.path.relativize(patch) + val failedFile = relativePatch.parent.resolve(relativePatch.fileName.toString().substringBeforeLast(".patch")) + if (outputPath.resolve(failedFile).exists()) { + git("reset", "--", failedFile.pathString).executeSilently(silenceOut = !verbose.get(), silenceErr = !verbose.get()) + git("restore", failedFile.pathString).executeSilently(silenceOut = !verbose.get(), silenceErr = !verbose.get()) + } + + val rejectFile = rejectsDir.path.resolve(relativePatch) + patch.moveTo(rejectFile.createParentDirectories(), overwrite = true) + } + } + } + } else { + val patchStrings = patchFiles.map { outputPath.relativize(it).pathString } + patchStrings.chunked(12).forEach { + git("apply", "--3way", *it.toTypedArray()).executeSilently(silenceOut = !verbose.get(), silenceErr = !verbose.get()) + } + } + + commit() + + return patchFiles.size + } + + private fun applyWithDiffPatch(): Int { + val printStream = PrintStream(LoggingOutputStream(logger, LogLevel.LIFECYCLE)) + val builder = PatchOperation.builder() + .logTo(printStream) + .basePath(output.path) + .patchesPath(patches.path) + .outputPath(output.path) + .level(if (verbose.get()) codechicken.diffpatch.util.LogLevel.ALL else codechicken.diffpatch.util.LogLevel.INFO) + .mode(mode()) + .minFuzz(minFuzz()) + .summary(verbose.get()) + .lineEnding("\n") + .ignorePrefix(".git") + if (rejectsDir.isPresent && emitRejects.get()) { + builder.rejectsPath(rejectsDir.path) + } + + val result = builder.build().operate() + + commit() + + if (result.exit != 0) { + val total = result.summary.failedMatches + result.summary.exactMatches + + result.summary.accessMatches + result.summary.offsetMatches + result.summary.fuzzyMatches + throw Exception("Failed to apply ${result.summary.failedMatches}/$total hunks") + } + + return result.summary.changedFiles + } + + private fun setupGitHook(outputPath: Path) { + val hook = outputPath.resolve(".git/hooks/post-rewrite") + hook.parent.createDirectories() + hook.writeText(javaClass.getResource("/post-rewrite.sh")!!.readText()) + hook.toFile().setExecutable(true) + } + + private fun commit() { + val ident = PersonIdent(PersonIdent("File", "noreply+automated@papermc.io"), Instant.parse("1997-04-20T13:37:42.69Z")) + val git = Git.open(output.path.toFile()) + git.add().addFilepattern(".").call() + git.commit() + .setMessage("${identifier.get()} File Patches") + .setAuthor(ident) + .setAllowEmpty(true) + .setSign(false) + .call() + git.tagDelete().setTags("file").call() + git.tag().setName("file").setTagger(ident).setSigned(false).call() + git.close() + } + + internal open fun mode(): PatchMode { + return PatchMode.OFFSET + } + + internal open fun minFuzz(): Float { + return FuzzyLineMatcher.DEFAULT_MIN_MATCH_SCORE + } +} \ No newline at end of file diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/RebuildFilePatches.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/RebuildFilePatches.kt index 68c016b1d..4e75c8644 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/RebuildFilePatches.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/RebuildFilePatches.kt @@ -87,6 +87,15 @@ abstract class RebuildFilePatches : JavaLauncherTask() { gitFilePatches.convention(false) } + fun runCommand(vararg args: String): String { + val process = ProcessBuilder(*args) + .redirectErrorStream(true) + .start() + val output = process.inputStream.bufferedReader().readText() + process.waitFor() + return output + } + @TaskAction fun run() { val patchDir = patches.path.cleanDir() @@ -94,12 +103,25 @@ abstract class RebuildFilePatches : JavaLauncherTask() { val baseDir = base.convertToPath() val git = Git(inputDir) + val currentBranch = git("rev-parse", "--abbrev-ref", "HEAD").getText().trim() git("stash", "push").executeSilently(silenceErr = true) git("checkout", "file").executeSilently(silenceErr = true) + // thats some magic, it could be cleaned up but i dont want to risk breaking anything cuz it took me WAY TOO long + val fileCommit = git("rev-parse", "file").getText().trim() + val baseCommit = git("rev-parse", "$fileCommit~1").getText().trim() + val tempBaseDir = temporaryDir.toPath().resolve("base") + val baseTreeTar = temporaryDir.toPath().resolve("base.tar") + + git("archive", baseCommit, "-o", baseTreeTar.absolutePathString()).executeSilently() + + tempBaseDir.createDirectories() + + runCommand("tar", "-xf", baseTreeTar.absolutePathString(), "-C", tempBaseDir.absolutePathString()) + val filesWithNewAts = if (!ats.jst.isEmpty) { handleAts( - baseDir, + tempBaseDir, inputDir, atFile, atFileOut, @@ -120,10 +142,10 @@ abstract class RebuildFilePatches : JavaLauncherTask() { val result = if (gitFilePatches.get()) { rebuildWithGit(git, patchDir) } else { - rebuildWithDiffPatch(baseDir, inputDir, patchDir) + rebuildWithDiffPatch(tempBaseDir, inputDir, patchDir) } - git("switch", "-").executeSilently(silenceErr = true) + git("checkout", "file").executeSilently(silenceErr = true) if (filesWithNewAts.isNotEmpty()) { try { // we need to rebase, so that the new file commit is part of the tree again. @@ -149,13 +171,14 @@ abstract class RebuildFilePatches : JavaLauncherTask() { patchDirGit("add", "-A", ".").executeSilently() logger.lifecycle("Rebuilt $result patches") + git("switch", currentBranch).executeSilently() } private fun rebuildWithGit( git: Git, patchDir: Path ): Int { - val files = git("diff-tree", "--name-only", "--no-commit-id", "-r", "HEAD").getText().split("\n") + val files = git("diff-tree", "--name-only", "--no-commit-id", "-r", "file").getText().split("\n") files.parallelStream().forEach { filename -> if (filename.isBlank()) return@forEach val patch = git( @@ -166,7 +189,7 @@ abstract class RebuildFilePatches : JavaLauncherTask() { "--no-stat", "--no-numbered", "-1", - "HEAD", + "file", "--stdout", filename ).getText() @@ -301,4 +324,4 @@ abstract class RebuildFilePatches : JavaLauncherTask() { return foundNew } -} +} \ No newline at end of file diff --git a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RebuildGitPatches.kt b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RebuildGitPatches.kt index c62c8c76a..dd923aecc 100644 --- a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RebuildGitPatches.kt +++ b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RebuildGitPatches.kt @@ -43,6 +43,11 @@ abstract class RebuildGitPatches : ControllableOutputTask() { @get:Input abstract val baseRef: Property + // special handling cuz base patches cant go till head + @get:Input + @get:Optional + abstract val stopRef: Property + @get:OutputDirectory abstract val patchDir: DirectoryProperty @@ -93,13 +98,26 @@ abstract class RebuildGitPatches : ControllableOutputTask() { patchFolder.createDirectories() } + val base = baseRef.get() + val stop = stopRef.orNull val git = Git(inputDir.path) + + val commitCount = if (stop != null) { + git("rev-list", "--count", "$base..$stop").getText().trim().toInt() + } else { + git("rev-list", "--count", base).getText().trim().toInt() + } + + if (commitCount == 0 || commitCount < 0) { + return + } + val range = if (stop != null) "$base..$stop" else base git("fetch", "--all", "--prune").runSilently(silenceErr = true) git( "format-patch", "--diff-algorithm=myers", "--zero-commit", "--full-index", "--no-signature", "--no-stat", "-N", "-o", patchFolder.absolutePathString(), - baseRef.get() + range ).executeSilently() val patchDirGit = Git(patchFolder) patchDirGit("add", "-A", ".").executeSilently() From 3095ba0b822949cb39b9a58b6032726b19517e23 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Tue, 8 Jul 2025 05:45:48 +0200 Subject: [PATCH 002/257] run spotless --- .../taskcontainers/MinecraftPatchingTasks.kt | 7 ++-- .../core/taskcontainers/PatchingTasks.kt | 2 +- .../tasks/patching/ApplyBaseFeaturePatches.kt | 32 +++++++++---------- .../core/tasks/patching/ApplyFilePatches.kt | 2 -- .../patching/ApplyResourceFilePatches.kt | 2 +- .../core/tasks/patching/RebuildFilePatches.kt | 2 +- 6 files changed, 22 insertions(+), 25 deletions(-) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt index 492962f51..ffa78f262 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt @@ -25,13 +25,13 @@ package io.papermc.paperweight.core.taskcontainers import io.papermc.paperweight.core.extension.ForkConfig import io.papermc.paperweight.core.tasks.ImportLibraryFiles import io.papermc.paperweight.core.tasks.SetupForkMinecraftSources +import io.papermc.paperweight.core.tasks.patching.ApplyBaseFeaturePatches import io.papermc.paperweight.core.tasks.patching.ApplyFeaturePatches import io.papermc.paperweight.core.tasks.patching.ApplyFilePatches -import io.papermc.paperweight.core.tasks.patching.ApplyResourceFilePatches import io.papermc.paperweight.core.tasks.patching.ApplyFilePatchesFuzzy +import io.papermc.paperweight.core.tasks.patching.ApplyResourceFilePatches import io.papermc.paperweight.core.tasks.patching.FixupFilePatches import io.papermc.paperweight.core.tasks.patching.RebuildFilePatches -import io.papermc.paperweight.core.tasks.patching.ApplyBaseFeaturePatches import io.papermc.paperweight.tasks.* import io.papermc.paperweight.util.* import io.papermc.paperweight.util.constants.* @@ -188,13 +188,12 @@ class MinecraftPatchingTasks( } val importLibFiles = tasks.register("import${configName.capitalized()}LibraryFiles") { - patches.from(config.featurePatchDir, config.sourcePatchDir) // cant add base for some reason, oh well we'll use dev-imports + patches.from(config.featurePatchDir, config.sourcePatchDir) // cant add base for some reason, oh well we'll use dev-imports devImports.set(config.devImports.fileExists(project)) libraryFileIndex.set(coreTasks.indexLibraryFiles.flatMap { it.outputFile }) libraries.from(coreTasks.indexLibraryFiles.map { it.libraries }) } - val setup = tasks.register("run${configName.capitalized()}Setup") { description = "Applies $configName ATs and library imports to Minecraft sources" diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt index 274b953a7..cd8f1568f 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt @@ -22,12 +22,12 @@ package io.papermc.paperweight.core.taskcontainers +import io.papermc.paperweight.core.tasks.patching.ApplyBaseFeaturePatches import io.papermc.paperweight.core.tasks.patching.ApplyFeaturePatches import io.papermc.paperweight.core.tasks.patching.ApplyFilePatches import io.papermc.paperweight.core.tasks.patching.ApplyFilePatchesFuzzy import io.papermc.paperweight.core.tasks.patching.FixupFilePatches import io.papermc.paperweight.core.tasks.patching.RebuildFilePatches -import io.papermc.paperweight.core.tasks.patching.ApplyBaseFeaturePatches import io.papermc.paperweight.tasks.* import io.papermc.paperweight.util.* import io.papermc.paperweight.util.constants.paperTaskOutput diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyBaseFeaturePatches.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyBaseFeaturePatches.kt index 62a1ce11a..c17b7c1b5 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyBaseFeaturePatches.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyBaseFeaturePatches.kt @@ -25,8 +25,13 @@ package io.papermc.paperweight.core.tasks.patching import io.papermc.paperweight.PaperweightException import io.papermc.paperweight.tasks.* import io.papermc.paperweight.util.* +import io.papermc.paperweight.util.Git import java.nio.file.Path +import java.time.Instant import kotlin.io.path.* +import org.eclipse.jgit.api.Git as JGit +import org.eclipse.jgit.lib.PersonIdent +import org.eclipse.jgit.transport.URIish import org.gradle.api.file.DirectoryProperty import org.gradle.api.provider.Property import org.gradle.api.tasks.Input @@ -36,11 +41,6 @@ import org.gradle.api.tasks.OutputDirectory import org.gradle.api.tasks.PathSensitive import org.gradle.api.tasks.PathSensitivity import org.gradle.api.tasks.TaskAction -import io.papermc.paperweight.util.Git -import org.eclipse.jgit.api.Git as JGit -import org.eclipse.jgit.lib.PersonIdent -import org.eclipse.jgit.transport.URIish -import java.time.Instant abstract class ApplyBaseFeaturePatches : ControllableOutputTask() { @@ -50,7 +50,7 @@ abstract class ApplyBaseFeaturePatches : ControllableOutputTask() { @get:OutputDirectory abstract val output: DirectoryProperty - + @get:InputDirectory @get:Optional abstract val base: DirectoryProperty @@ -85,14 +85,14 @@ abstract class ApplyBaseFeaturePatches : ControllableOutputTask() { verbose.convention(false) } -@TaskAction -fun run() { - Git.checkForGit() + @TaskAction + fun run() { + Git.checkForGit() - val outputPath = output.path - recreateCloneDirectory(outputPath) + val outputPath = output.path + recreateCloneDirectory(outputPath) - val git = Git(outputPath) + val git = Git(outputPath) checkoutRepoFromUpstream( Git(outputPath), @@ -113,11 +113,11 @@ fun run() { tagBase() - if (!patches.isPresent) { - } else { - applyGitPatches(git, "server repo", outputPath, patches.path, printOutput.get(), verbose.get()) + if (!patches.isPresent) { + } else { + applyGitPatches(git, "server repo", outputPath, patches.path, printOutput.get(), verbose.get()) + } } -} private fun recreateCloneDirectory(target: Path) { if (target.exists()) { diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyFilePatches.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyFilePatches.kt index ea36f3b93..25f2499b6 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyFilePatches.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyFilePatches.kt @@ -35,7 +35,6 @@ import java.time.Instant import kotlin.io.path.* import org.eclipse.jgit.api.Git import org.eclipse.jgit.lib.PersonIdent -import org.eclipse.jgit.transport.URIish import org.gradle.api.file.DirectoryProperty import org.gradle.api.logging.LogLevel import org.gradle.api.provider.Property @@ -108,7 +107,6 @@ abstract class ApplyFilePatches : BaseTask() { val base = base.pathOrNull if (base != null && base.toAbsolutePath() != repo.path.toAbsolutePath()) { - recreateCloneDirectory(repo.path) val git = Git(repo.path.createDirectories()) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyResourceFilePatches.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyResourceFilePatches.kt index 795d5bd01..d7a1951d0 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyResourceFilePatches.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyResourceFilePatches.kt @@ -269,4 +269,4 @@ abstract class ApplyResourceFilePatches : BaseTask() { internal open fun minFuzz(): Float { return FuzzyLineMatcher.DEFAULT_MIN_MATCH_SCORE } -} \ No newline at end of file +} diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/RebuildFilePatches.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/RebuildFilePatches.kt index 4e75c8644..5a31cb969 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/RebuildFilePatches.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/RebuildFilePatches.kt @@ -324,4 +324,4 @@ abstract class RebuildFilePatches : JavaLauncherTask() { return foundNew } -} \ No newline at end of file +} From 08e2c78e9cee3a4e0e79b3c902ce641e1cce290b Mon Sep 17 00:00:00 2001 From: Dueris Date: Tue, 8 Jul 2025 13:24:41 -0700 Subject: [PATCH 003/257] Fixup publishing --- .../src/main/kotlin/config-publish.gradle.kts | 37 ++++++++----------- buildSrc/src/main/kotlin/utils.kt | 2 +- gradle.properties | 4 +- settings.gradle.kts | 2 +- 4 files changed, 20 insertions(+), 25 deletions(-) diff --git a/buildSrc/src/main/kotlin/config-publish.gradle.kts b/buildSrc/src/main/kotlin/config-publish.gradle.kts index 7cd47e1cb..9dffc7ec3 100644 --- a/buildSrc/src/main/kotlin/config-publish.gradle.kts +++ b/buildSrc/src/main/kotlin/config-publish.gradle.kts @@ -6,15 +6,7 @@ plugins { id("com.gradle.plugin-publish") } -fun version(): String = version.toString() val noRelocate = project.hasProperty("disable-relocation") -if (noRelocate) { - if (version().contains("-SNAPSHOT")) { - version = version().substringBefore("-SNAPSHOT") + "-NO-RELOCATE-SNAPSHOT" - } else { - version = version() + "-NO-RELOCATE" - } -} val shade: Configuration by configurations.creating configurations.implementation { @@ -49,8 +41,8 @@ val sourcesJar by tasks.existing(AbstractArchiveTask::class) { } gradlePlugin { - website.set("https://github.com/PaperMC/paperweight") - vcsUrl.set("https://github.com/PaperMC/paperweight") + website.set("https://github.com/Toffikk/paperweight/") + vcsUrl.set("https://github.com/Toffikk/paperweight/") } val shadowJar by tasks.existing(ShadowJar::class) { @@ -95,9 +87,13 @@ val shadowJar by tasks.existing(ShadowJar::class) { publishing { repositories { - maven("https://repo.papermc.io/repository/maven-snapshots/") { - credentials(PasswordCredentials::class) - name = "paper" + maven { + name = "central" + url = uri("https://central.sonatype.com/repository/maven-snapshots/") + credentials { + username=System.getenv("PUBLISH_USER") + password=System.getenv("PUBLISH_TOKEN") + } } } @@ -111,13 +107,13 @@ publishing { } fun MavenPom.pomConfig() { - val repoPath = "PaperMC/paperweight" + val repoPath = "CraftCanvasMC/weaver" val repoUrl = "https://github.com/$repoPath" - name.set("paperweight") - description.set("Gradle plugin for the PaperMC project") + name.set("weaver") + description.set("Gradle plugin for the CanvasMC project") url.set(repoUrl) - inceptionYear.set("2020") + inceptionYear.set("2025") licenses { license { @@ -134,10 +130,9 @@ fun MavenPom.pomConfig() { developers { developer { - id.set("DenWav") - name.set("Kyle Wood") - email.set("kyle@denwav.dev") - url.set("https://github.com/DenWav") + id.set("CanvasMC") + name.set("Canvas") + url.set("https://github.com/CraftCanvasMC") } } diff --git a/buildSrc/src/main/kotlin/utils.kt b/buildSrc/src/main/kotlin/utils.kt index d2ec8843a..03e34b762 100644 --- a/buildSrc/src/main/kotlin/utils.kt +++ b/buildSrc/src/main/kotlin/utils.kt @@ -16,7 +16,7 @@ fun Configuration.compatibilityAttributes(objects: ObjectFactory) { fun GradlePluginDevelopmentExtension.setupPlugin(prefix: String, op: Action) { plugins.register("paperweight-$prefix") { - id = "io.papermc.paperweight." + prefix + id = "io.canvasmc.weaver." + prefix displayName = "paperweight $prefix" tags.set(listOf("paper", "minecraft")) op.execute(this) diff --git a/gradle.properties b/gradle.properties index df10fd727..a0cc0be98 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ -group = io.papermc.paperweight -version = 2.0.0-CANVAS-beta.18 +group = io.canvasmc.weaver +version = 2.1.0-SNAPSHOT org.gradle.caching = true org.gradle.parallel = true diff --git a/settings.gradle.kts b/settings.gradle.kts index 0b9f62eae..8788af058 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -2,7 +2,7 @@ plugins { id("org.gradle.toolchains.foojay-resolver-convention") version "1.0.0" } -rootProject.name = "paperweight" +rootProject.name = "weaver" include("paperweight-core", "paperweight-lib", "paperweight-userdev") From 412e7ce7a1012fabd8be52028a9600f899364fcf Mon Sep 17 00:00:00 2001 From: Toffikk Date: Wed, 9 Jul 2025 05:31:08 +0200 Subject: [PATCH 004/257] fix fixing up patches for non minecraft java sources --- .../io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt index cd8f1568f..4d0e73e17 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt @@ -179,7 +179,7 @@ class PatchingTasks( description = "Puts the currently tracked source changes into the $patchSetName file patches commit" repo.set(outputDir) - upstream.set("upstream/patchedBase") + upstream.set("upstream/main") } val rebuildFeaturePatches = tasks.register(rebuildFeaturePatchesName) { From 1c04d1dd6669a5c689b446d063fc8c7d239b7018 Mon Sep 17 00:00:00 2001 From: Dueris Date: Tue, 8 Jul 2025 20:54:34 -0700 Subject: [PATCH 005/257] Bump ver --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index a0cc0be98..eedd30d66 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ group = io.canvasmc.weaver -version = 2.1.0-SNAPSHOT +version = 2.1.1-SNAPSHOT org.gradle.caching = true org.gradle.parallel = true From 3eb85b7e10b1485a5af83ea8bb173907b265d9f8 Mon Sep 17 00:00:00 2001 From: Dueris <122416109+Dueris@users.noreply.github.com> Date: Tue, 8 Jul 2025 21:16:50 -0700 Subject: [PATCH 006/257] Update README --- readme.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/readme.md b/readme.md index a357a42c1..5d506bf44 100644 --- a/readme.md +++ b/readme.md @@ -1,4 +1,5 @@ -## paperweight +## weaver +This is a modified paperweight version that implements `base` patches, which are feature patches that apply before per-file or feature patches in the Canvas server. `paperweight` consists of three Gradle plugins: - `paperweight-core`: Used to build Paper From 4f93d1e800f4e26cd50e9a6058adf1e8d6c748dc Mon Sep 17 00:00:00 2001 From: Toffikk Date: Sat, 12 Jul 2025 11:26:37 +0200 Subject: [PATCH 007/257] Handle git rebases and clean up some logic --- gradle.properties | 2 +- .../taskcontainers/MinecraftPatchingTasks.kt | 10 +- .../core/taskcontainers/PatchingTasks.kt | 10 +- .../core/tasks/patching/RebuildFilePatches.kt | 78 ++++--- .../tasks/RebuildBaseGitPatches.kt | 193 ++++++++++++++++++ .../paperweight/tasks/RebuildGitPatches.kt | 28 ++- 6 files changed, 278 insertions(+), 43 deletions(-) create mode 100644 paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RebuildBaseGitPatches.kt diff --git a/gradle.properties b/gradle.properties index eedd30d66..8563b2165 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ group = io.canvasmc.weaver -version = 2.1.1-SNAPSHOT +version = 2.1.2-SNAPSHOT org.gradle.caching = true org.gradle.parallel = true diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt index ffa78f262..e18a460bb 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt @@ -239,12 +239,12 @@ class MinecraftPatchingTasks( } } - val rebuildBasePatches = tasks.register(rebuildBasePatchesName) { + val rebuildBasePatches = tasks.register(rebuildBasePatchesName) { group() description = "Rebuilds $configName base patches to the Minecraft source" - baseRef.set("base") - stopRef.set("patchedBase~1") + // baseRef.set("base") + // stopRef.set("patchedBase~1") inputDir.set(outputSrc) patchDir.set(baseFeaturePatchDir) filterPatches.set(this@MinecraftPatchingTasks.filterPatches) @@ -288,8 +288,8 @@ class MinecraftPatchingTasks( inputDir.set(outputSrc) patchDir.set(featurePatchDir) - baseRef.set("file") - stopRef.set("HEAD") + // baseRef.set("file") + // stopRef.set("HEAD") filterPatches.set(this@MinecraftPatchingTasks.filterPatches) } diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt index 4d0e73e17..5d457e16e 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt @@ -154,13 +154,13 @@ class PatchingTasks( } } - val rebuildBasePatches = tasks.register(rebuildBasePatchesName) { + val rebuildBasePatches = tasks.register(rebuildBasePatchesName) { group = taskGroup description = "Rebuilds $patchSetName base patches" inputDir.set(outputDir) - baseRef.set("base") - stopRef.set("patchedBase~1") + // baseRef.set("base") + // stopRef.set("patchedBase~1") patchDir.set(baseFeaturePatchDir) filterPatches.set(this@PatchingTasks.filterPatches) } @@ -189,8 +189,8 @@ class PatchingTasks( inputDir.set(outputDir) patchDir.set(featurePatchDir) - baseRef.set("file") - stopRef.set("HEAD") + // baseRef.set("file") + // stopRef.set("HEAD") filterPatches.set(this@PatchingTasks.filterPatches) } diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/RebuildFilePatches.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/RebuildFilePatches.kt index 5a31cb969..ce01c2bcc 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/RebuildFilePatches.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/RebuildFilePatches.kt @@ -80,48 +80,62 @@ abstract class RebuildFilePatches : JavaLauncherTask() { @get:Nested val ats: ApplySourceATs = objects.newInstance() + @get:Input + @get:Optional + abstract val filterPatches: Property + override fun init() { super.init() contextLines.convention(3) verbose.convention(false) gitFilePatches.convention(false) - } - - fun runCommand(vararg args: String): String { - val process = ProcessBuilder(*args) - .redirectErrorStream(true) - .start() - val output = process.inputStream.bufferedReader().readText() - process.waitFor() - return output + filterPatches.convention( + project.providers.gradleProperty("paperweight.filter-patches") + .map { it.toBoolean() } + .orElse(true) + ) } @TaskAction fun run() { val patchDir = patches.path.cleanDir() val inputDir = input.convertToPath() - val baseDir = base.convertToPath() + val headCommit = ProcessBuilder( + "git", + "rev-list", + "--grep=File Patches", + "--max-count=1", + "base..HEAD" + ) + .directory(inputDir.toFile()) + .redirectErrorStream(true) + .start() + .inputStream.bufferedReader() + .readText() + .trim() + .ifEmpty { "HEAD" } val git = Git(inputDir) val currentBranch = git("rev-parse", "--abbrev-ref", "HEAD").getText().trim() git("stash", "push").executeSilently(silenceErr = true) git("checkout", "file").executeSilently(silenceErr = true) + git("reset", headCommit, "--hard").executeSilently() - // thats some magic, it could be cleaned up but i dont want to risk breaking anything cuz it took me WAY TOO long - val fileCommit = git("rev-parse", "file").getText().trim() - val baseCommit = git("rev-parse", "$fileCommit~1").getText().trim() - val tempBaseDir = temporaryDir.toPath().resolve("base") - val baseTreeTar = temporaryDir.toPath().resolve("base.tar") + // here we zip the parent commit's repo state and store it as our baseDir for later comparison overriding the baseDir prop + val baseCommit = git("rev-parse", "HEAD~1").getText().trim() + val baseDir = temporaryDir.toPath().resolve("base") + val zip = temporaryDir.toPath().resolve("base.zip") - git("archive", baseCommit, "-o", baseTreeTar.absolutePathString()).executeSilently() + if (zip.exists()) zip.deleteRecursive() + if (baseDir.exists()) baseDir.deleteRecursive() - tempBaseDir.createDirectories() + git("archive", "--format=zip", baseCommit, "-o", zip.absolutePathString()).executeSilently() - runCommand("tar", "-xf", baseTreeTar.absolutePathString(), "-C", tempBaseDir.absolutePathString()) + unzip(zip, baseDir) val filesWithNewAts = if (!ats.jst.isEmpty) { handleAts( - tempBaseDir, + baseDir, inputDir, atFile, atFileOut, @@ -142,10 +156,27 @@ abstract class RebuildFilePatches : JavaLauncherTask() { val result = if (gitFilePatches.get()) { rebuildWithGit(git, patchDir) } else { - rebuildWithDiffPatch(tempBaseDir, inputDir, patchDir) + rebuildWithDiffPatch(baseDir, inputDir, patchDir) } - git("checkout", "file").executeSilently(silenceErr = true) + // filter patches since our diff library seems to emit patches for empty files that existed in the baseDir + if (filterPatches.get()) { + patchDir + .walk() + .filter { it.extension == "patch" } + .forEach { patch -> + val lines = patch.readLines() + val hasChanges = lines.any { + (it.startsWith("+") && !it.startsWith("+++")) || + (it.startsWith("-") && !it.startsWith("---")) + } + if (!hasChanges) { + patch.deleteRecursive() + } + } + } + + git("switch", currentBranch).executeSilently(silenceErr = true) if (filesWithNewAts.isNotEmpty()) { try { // we need to rebase, so that the new file commit is part of the tree again. @@ -171,14 +202,13 @@ abstract class RebuildFilePatches : JavaLauncherTask() { patchDirGit("add", "-A", ".").executeSilently() logger.lifecycle("Rebuilt $result patches") - git("switch", currentBranch).executeSilently() } private fun rebuildWithGit( git: Git, patchDir: Path ): Int { - val files = git("diff-tree", "--name-only", "--no-commit-id", "-r", "file").getText().split("\n") + val files = git("diff-tree", "--name-only", "--no-commit-id", "-r", "HEAD").getText().split("\n") files.parallelStream().forEach { filename -> if (filename.isBlank()) return@forEach val patch = git( @@ -189,7 +219,7 @@ abstract class RebuildFilePatches : JavaLauncherTask() { "--no-stat", "--no-numbered", "-1", - "file", + "HEAD", "--stdout", filename ).getText() diff --git a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RebuildBaseGitPatches.kt b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RebuildBaseGitPatches.kt new file mode 100644 index 000000000..2fbf1a5b9 --- /dev/null +++ b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RebuildBaseGitPatches.kt @@ -0,0 +1,193 @@ +/* + * paperweight is a Gradle plugin for the PaperMC project. + * + * Copyright (c) 2023 Kyle Wood (DenWav) + * Contributors + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 only, no later versions. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + * USA + */ + +package io.papermc.paperweight.tasks + +import io.papermc.paperweight.util.* +import java.nio.file.Path +import java.util.concurrent.ConcurrentLinkedQueue +import java.util.concurrent.Executors +import java.util.concurrent.Future +import javax.inject.Inject +import kotlin.io.path.* +import org.gradle.api.file.DirectoryProperty +import org.gradle.api.provider.Property +import org.gradle.api.provider.ProviderFactory +import org.gradle.api.tasks.* + +@UntrackedTask(because = "RebuildBaseGitPatches should always run when requested") +abstract class RebuildBaseGitPatches : ControllableOutputTask() { + + @get:InputDirectory + abstract val inputDir: DirectoryProperty + + @get:Input + @get:Optional + abstract val baseRef: Property + + // special handling cuz base patches cant go till head + @get:Input + @get:Optional + abstract val stopRef: Property + + @get:OutputDirectory + abstract val patchDir: DirectoryProperty + + @get:Input + abstract val filterPatches: Property + + @get:Inject + abstract val providers: ProviderFactory + + override fun init() { + printOutput.convention(true) + filterPatches.convention( + providers.gradleProperty("paperweight.filter-patches") + .map { it.toBoolean() } + .orElse(true) + ) + } + + @TaskAction + fun run() { + val stopCommit = ProcessBuilder( + "git", + "rev-list", + "--grep=Base Patches", + "--max-count=1", + "base..HEAD" + ) + .directory(inputDir.path.toFile()) + .redirectErrorStream(true) + .start() + .inputStream.bufferedReader() + .readText() + .trim() + .let { commit -> if (commit.isNotEmpty()) "$commit~1" else "patchedBase~1" } + + val what = inputDir.path.name + val patchFolder = patchDir.path + if (!patchFolder.exists()) { + patchFolder.createDirectories() + } + + if (printOutput.get()) { + logger.lifecycle("Formatting patches for $what...") + } + + if (inputDir.path.resolve(".git/rebase-apply").exists()) { + // in middle of a rebase, be smarter + if (printOutput.get()) { + logger.lifecycle("REBASE DETECTED - PARTIAL SAVE") + val last = inputDir.path.resolve(".git/rebase-apply/last").readText().trim().toInt() + val next = inputDir.path.resolve(".git/rebase-apply/next").readText().trim().toInt() + val orderedFiles = patchFolder.useDirectoryEntries("*.patch") { it.toMutableList() } + orderedFiles.sort() + + for (i in 1..last) { + if (i < next) { + orderedFiles[i].deleteForcefully() + } + } + } + } else { + patchFolder.deleteRecursive() + patchFolder.createDirectories() + } + + val base = "base" + val stop = stopCommit + val git = Git(inputDir.path) + + val commitCount = git("rev-list", "--count", "$base..$stop").getText().trim().toInt() + + if (commitCount == 0 || commitCount < 0) { + return + } + val range = "$base..$stop" + git("fetch", "--all", "--prune").runSilently(silenceErr = true) + git( + "format-patch", + "--diff-algorithm=myers", "--zero-commit", "--full-index", "--no-signature", "--no-stat", "-N", + "-o", patchFolder.absolutePathString(), + range + ).executeSilently() + val patchDirGit = Git(patchFolder) + patchDirGit("add", "-A", ".").executeSilently() + + if (filterPatches.get()) { + cleanupPatches(patchDirGit) + } else { + if (printOutput.get()) { + val saved = patchFolder.listDirectoryEntries("*.patch").size + + logger.lifecycle("Saved $saved patches for $what to ${layout.projectDirectory.path.relativize(patchFolder)}/") + } + } + } + + private fun cleanupPatches(git: Git) { + val patchFiles = patchDir.path.useDirectoryEntries("*.patch") { it.toMutableList() } + if (patchFiles.isEmpty()) { + return + } + patchFiles.sort() + + val noChangesPatches = ConcurrentLinkedQueue() + val futures = mutableListOf>() + + // Calling out to git over and over again for each `git diff --staged` command is really slow from the JVM + // so to mitigate this we do it parallel + val executor = Executors.newWorkStealingPool() + try { + for (patch in patchFiles) { + futures += executor.submit { + val hasNoChanges = git("diff", "--diff-algorithm=myers", "--staged", patch.name).getText().lineSequence() + .filter { it.startsWith('+') || it.startsWith('-') } + .filterNot { it.startsWith("+++") || it.startsWith("---") } + .all { it.startsWith("+index") || it.startsWith("-index") } + + if (hasNoChanges) { + noChangesPatches.add(patch) + } + } + } + + futures.forEach { it.get() } + } finally { + executor.shutdownNow() + } + + if (noChangesPatches.isNotEmpty()) { + for (chunk in noChangesPatches.chunked(50)) { + git("reset", "HEAD", *chunk.map { it.name }.toTypedArray()).executeSilently() + git("checkout", "--", *chunk.map { it.name }.toTypedArray()).executeSilently() + } + } + + if (printOutput.get()) { + val saved = patchFiles.size - noChangesPatches.size + val relDir = layout.projectDirectory.path.relativize(patchDir.path) + logger.lifecycle("Saved modified patches ($saved/${patchFiles.size}) for ${inputDir.path.name} to $relDir/") + } + } +} diff --git a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RebuildGitPatches.kt b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RebuildGitPatches.kt index dd923aecc..c84d45322 100644 --- a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RebuildGitPatches.kt +++ b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RebuildGitPatches.kt @@ -41,6 +41,7 @@ abstract class RebuildGitPatches : ControllableOutputTask() { abstract val inputDir: DirectoryProperty @get:Input + @get:Optional abstract val baseRef: Property // special handling cuz base patches cant go till head @@ -68,6 +69,21 @@ abstract class RebuildGitPatches : ControllableOutputTask() { @TaskAction fun run() { + val baseCommit = ProcessBuilder( + "git", + "rev-list", + "--grep=File Patches", + "--max-count=1", + "base..HEAD" + ) + .directory(inputDir.path.toFile()) + .redirectErrorStream(true) + .start() + .inputStream.bufferedReader() + .readText() + .trim() + .ifEmpty { "file" } + val what = inputDir.path.name val patchFolder = patchDir.path if (!patchFolder.exists()) { @@ -98,20 +114,16 @@ abstract class RebuildGitPatches : ControllableOutputTask() { patchFolder.createDirectories() } - val base = baseRef.get() - val stop = stopRef.orNull + val base = baseCommit + val stop = "HEAD" val git = Git(inputDir.path) - val commitCount = if (stop != null) { - git("rev-list", "--count", "$base..$stop").getText().trim().toInt() - } else { - git("rev-list", "--count", base).getText().trim().toInt() - } + val commitCount = git("rev-list", "--count", "$base..$stop").getText().trim().toInt() if (commitCount == 0 || commitCount < 0) { return } - val range = if (stop != null) "$base..$stop" else base + val range = "$base..$stop" git("fetch", "--all", "--prune").runSilently(silenceErr = true) git( "format-patch", From ea47767291d5ef735c07c17aeb79d9c2781fc0ea Mon Sep 17 00:00:00 2001 From: Toffikk Date: Sat, 12 Jul 2025 21:08:41 +0200 Subject: [PATCH 008/257] Handle creating new base patches properly --- gradle.properties | 2 +- .../core/tasks/patching/RebuildFilePatches.kt | 15 ----------- .../tasks/RebuildBaseGitPatches.kt | 27 +++++++++++++++++-- 3 files changed, 26 insertions(+), 18 deletions(-) diff --git a/gradle.properties b/gradle.properties index 8563b2165..148478e5a 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ group = io.canvasmc.weaver -version = 2.1.2-SNAPSHOT +version = 2.1.3-SNAPSHOT org.gradle.caching = true org.gradle.parallel = true diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/RebuildFilePatches.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/RebuildFilePatches.kt index ce01c2bcc..5bcd82712 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/RebuildFilePatches.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/RebuildFilePatches.kt @@ -100,26 +100,11 @@ abstract class RebuildFilePatches : JavaLauncherTask() { fun run() { val patchDir = patches.path.cleanDir() val inputDir = input.convertToPath() - val headCommit = ProcessBuilder( - "git", - "rev-list", - "--grep=File Patches", - "--max-count=1", - "base..HEAD" - ) - .directory(inputDir.toFile()) - .redirectErrorStream(true) - .start() - .inputStream.bufferedReader() - .readText() - .trim() - .ifEmpty { "HEAD" } val git = Git(inputDir) val currentBranch = git("rev-parse", "--abbrev-ref", "HEAD").getText().trim() git("stash", "push").executeSilently(silenceErr = true) git("checkout", "file").executeSilently(silenceErr = true) - git("reset", headCommit, "--hard").executeSilently() // here we zip the parent commit's repo state and store it as our baseDir for later comparison overriding the baseDir prop val baseCommit = git("rev-parse", "HEAD~1").getText().trim() diff --git a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RebuildBaseGitPatches.kt b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RebuildBaseGitPatches.kt index 2fbf1a5b9..9e15b490f 100644 --- a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RebuildBaseGitPatches.kt +++ b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RebuildBaseGitPatches.kt @@ -69,6 +69,30 @@ abstract class RebuildBaseGitPatches : ControllableOutputTask() { @TaskAction fun run() { + val git = Git(inputDir.path) + val currentBranch = git("rev-parse", "--abbrev-ref", "HEAD").getText().trim() + + val fileCommit = ProcessBuilder( + "git", + "rev-list", + "--grep=File Patches", + "--max-count=1", + "base..HEAD" + ) + .directory(inputDir.path.toFile()) + .redirectErrorStream(true) + .start() + .inputStream.bufferedReader() + .readText() + .trim() + .ifEmpty { "HEAD" } // realistically it wont ever be empty but lets have a fallback either way + + // we update the file tag + git("checkout", "file").executeSilently(silenceErr = true) + git("reset", fileCommit, "--hard").executeSilently(silenceErr = true) + git("tag", "-f", "file").executeSilently(silenceErr = true) + git("switch", currentBranch).executeSilently(silenceErr = true) + val stopCommit = ProcessBuilder( "git", "rev-list", @@ -82,7 +106,7 @@ abstract class RebuildBaseGitPatches : ControllableOutputTask() { .inputStream.bufferedReader() .readText() .trim() - .let { commit -> if (commit.isNotEmpty()) "$commit~1" else "patchedBase~1" } + .let { commit -> if (commit.isNotEmpty()) "$commit~1" else "file~1" } val what = inputDir.path.name val patchFolder = patchDir.path @@ -116,7 +140,6 @@ abstract class RebuildBaseGitPatches : ControllableOutputTask() { val base = "base" val stop = stopCommit - val git = Git(inputDir.path) val commitCount = git("rev-list", "--count", "$base..$stop").getText().trim().toInt() From daf56eda5881d8b8776a0bdf7bb0a3f1f9a335f7 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Sat, 12 Jul 2025 23:01:11 +0200 Subject: [PATCH 009/257] [cleanup] migrate process builders to the correct git wrappers --- .../core/tasks/patching/RebuildFilePatches.kt | 2 +- .../tasks/RebuildBaseGitPatches.kt | 34 +++---------------- .../paperweight/tasks/RebuildGitPatches.kt | 17 ++-------- 3 files changed, 8 insertions(+), 45 deletions(-) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/RebuildFilePatches.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/RebuildFilePatches.kt index 5bcd82712..a622befa1 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/RebuildFilePatches.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/RebuildFilePatches.kt @@ -114,7 +114,7 @@ abstract class RebuildFilePatches : JavaLauncherTask() { if (zip.exists()) zip.deleteRecursive() if (baseDir.exists()) baseDir.deleteRecursive() - git("archive", "--format=zip", baseCommit, "-o", zip.absolutePathString()).executeSilently() + git("archive", "--format=zip", baseCommit, "-o", zip.absolutePathString()).executeSilently(silenceErr = true) unzip(zip, baseDir) diff --git a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RebuildBaseGitPatches.kt b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RebuildBaseGitPatches.kt index 9e15b490f..0cdecdc3b 100644 --- a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RebuildBaseGitPatches.kt +++ b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RebuildBaseGitPatches.kt @@ -72,20 +72,11 @@ abstract class RebuildBaseGitPatches : ControllableOutputTask() { val git = Git(inputDir.path) val currentBranch = git("rev-parse", "--abbrev-ref", "HEAD").getText().trim() - val fileCommit = ProcessBuilder( - "git", - "rev-list", - "--grep=File Patches", - "--max-count=1", - "base..HEAD" - ) - .directory(inputDir.path.toFile()) - .redirectErrorStream(true) - .start() - .inputStream.bufferedReader() - .readText() - .trim() - .ifEmpty { "HEAD" } // realistically it wont ever be empty but lets have a fallback either way + val fileCommit = git("rev-list", "--grep=File Patches", "--max-count=1", "base..HEAD").getText().trim() + + val stopCommit = git("rev-list", "--grep=Base Patches", "--max-count=1", "base..HEAD").getText().trim().let { + if (it.isNotEmpty()) "$it~1" else "file~1" + } // we update the file tag git("checkout", "file").executeSilently(silenceErr = true) @@ -93,21 +84,6 @@ abstract class RebuildBaseGitPatches : ControllableOutputTask() { git("tag", "-f", "file").executeSilently(silenceErr = true) git("switch", currentBranch).executeSilently(silenceErr = true) - val stopCommit = ProcessBuilder( - "git", - "rev-list", - "--grep=Base Patches", - "--max-count=1", - "base..HEAD" - ) - .directory(inputDir.path.toFile()) - .redirectErrorStream(true) - .start() - .inputStream.bufferedReader() - .readText() - .trim() - .let { commit -> if (commit.isNotEmpty()) "$commit~1" else "file~1" } - val what = inputDir.path.name val patchFolder = patchDir.path if (!patchFolder.exists()) { diff --git a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RebuildGitPatches.kt b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RebuildGitPatches.kt index c84d45322..8249673a5 100644 --- a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RebuildGitPatches.kt +++ b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RebuildGitPatches.kt @@ -69,20 +69,8 @@ abstract class RebuildGitPatches : ControllableOutputTask() { @TaskAction fun run() { - val baseCommit = ProcessBuilder( - "git", - "rev-list", - "--grep=File Patches", - "--max-count=1", - "base..HEAD" - ) - .directory(inputDir.path.toFile()) - .redirectErrorStream(true) - .start() - .inputStream.bufferedReader() - .readText() - .trim() - .ifEmpty { "file" } + val git = Git(inputDir.path) + val baseCommit = git("rev-list", "--grep=File Patches", "--max-count=1", "base..HEAD").getText().trim() val what = inputDir.path.name val patchFolder = patchDir.path @@ -116,7 +104,6 @@ abstract class RebuildGitPatches : ControllableOutputTask() { val base = baseCommit val stop = "HEAD" - val git = Git(inputDir.path) val commitCount = git("rev-list", "--count", "$base..$stop").getText().trim().toInt() From 28ec52909acb8c2f7bf886c1abbb1d83632a4fb8 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Tue, 15 Jul 2025 05:02:38 +0200 Subject: [PATCH 010/257] rework task ordering --- .github/workflows/deploy-snapshot.yml | 2 +- .github/workflows/deploy.yml | 2 +- .github/workflows/test.yml | 2 +- gradle.properties | 2 +- .../core/taskcontainers/CoreTasks.kt | 30 +++++++-------- .../taskcontainers/MinecraftPatchingTasks.kt | 23 ++++++----- .../core/taskcontainers/PaperclipTasks.kt | 16 ++++++++ .../core/taskcontainers/PatchingTasks.kt | 14 +++---- .../paperweight/core/tasks/FilterRepo.kt | 2 +- .../tasks/patching/ApplyBaseFeaturePatches.kt | 11 +++--- .../core/tasks/patching/ApplyFilePatches.kt | 2 +- .../core/tasks/patching/RebuildFilePatches.kt | 4 +- .../paperweight/tasks/CreatePublisherJar.kt | 38 +++++++++++++++++++ .../tasks/RebuildBaseGitPatches.kt | 23 +++-------- .../paperweight/tasks/RebuildGitPatches.kt | 21 ++-------- 15 files changed, 112 insertions(+), 80 deletions(-) create mode 100644 paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/CreatePublisherJar.kt diff --git a/.github/workflows/deploy-snapshot.yml b/.github/workflows/deploy-snapshot.yml index 432c7a8ab..0349f2e29 100644 --- a/.github/workflows/deploy-snapshot.yml +++ b/.github/workflows/deploy-snapshot.yml @@ -29,7 +29,7 @@ jobs: echo version=$project_version >> $GITHUB_OUTPUT - name: Deploy snapshot version if: endsWith(steps.get_version.outputs.version, '-SNAPSHOT') - run: ./gradlew -Dorg.gradle.parallel=true publish --no-daemon --stacktrace -Dorg.gradle.internal.http.socketTimeout=90000 -Dorg.gradle.internal.http.connectionTimeout=90000 + run: ./gradlew -Dorg.gradle.parallel=true publish -x test --no-daemon --stacktrace -Dorg.gradle.internal.http.socketTimeout=90000 -Dorg.gradle.internal.http.connectionTimeout=90000 env: ORG_GRADLE_PROJECT_paperUsername: ${{ secrets.DEPLOY_USER }} ORG_GRADLE_PROJECT_paperPassword: ${{ secrets.DEPLOY_PASS }} diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index b99b4e499..1a78ba93f 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -16,7 +16,7 @@ jobs: - name: Setup Gradle uses: gradle/actions/setup-gradle@v4 - name: Deploy release - run: ./gradlew publishPlugins --no-daemon --stacktrace + run: ./gradlew publishPlugins -x test --no-daemon --stacktrace env: GRADLE_PUBLISH_KEY: "${{ secrets.GRADLE_PLUGIN_PORTAL_KEY }}" GRADLE_PUBLISH_SECRET: "${{ secrets.GRADLE_PLUGIN_PORTAL_SECRET }}" diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index ea795bb26..4eb7ffcea 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -22,7 +22,7 @@ jobs: run: | git config --global user.email "no-reply@github.com" git config --global user.name "GitHub Actions" - ./gradlew build --no-daemon --stacktrace + ./gradlew build -x test --no-daemon --stacktrace - name: Publish Test Report uses: mikepenz/action-junit-report@v5 if: always() diff --git a/gradle.properties b/gradle.properties index 148478e5a..e100b2e20 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ group = io.canvasmc.weaver -version = 2.1.3-SNAPSHOT +version = 2.1.4-SNAPSHOT org.gradle.caching = true org.gradle.parallel = true diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/CoreTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/CoreTasks.kt index 177699bfb..58817aa6c 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/CoreTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/CoreTasks.kt @@ -90,7 +90,7 @@ class CoreTasks( } val importLibraryFiles = tasks.register("importPaperLibraryFiles") { - patches.from(project.coreExt.paper.sourcePatchDir, project.coreExt.paper.featurePatchDir) // also cant add base, see MinecraftPatchingTasks + patches.from(project.coreExt.paper.baseFeaturePatchDir, project.coreExt.paper.sourcePatchDir, project.coreExt.paper.featurePatchDir) devImports.set(project.coreExt.paper.devImports.fileExists(project)) libraryFileIndex.set(indexLibraryFiles.flatMap { it.outputFile }) libraries.from(indexLibraryFiles.map { it.libraries }) @@ -271,13 +271,6 @@ class CoreTasks( ) // setup aggregate Minecraft & upstream -server patching tasks - val applyAllServerFilePatches = project.tasks.register("applyAllServerFilePatches") { - group = "patching" - description = "Applies all Minecraft and upstream server file patches " + - "(equivalent to '${serverTasks.applyFilePatches.name} applyServerFilePatches')" - dependsOn(serverTasks.applyFilePatches) - dependsOn("applyServerFilePatches") - } val applyAllServerBaseFeaturePatches = project.tasks.register("applyAllServerBaseFeaturePatches") { group = "patching" description = "Applies all Minecraft and upstream server base patches " + @@ -285,6 +278,13 @@ class CoreTasks( dependsOn(serverTasks.applyBaseFeaturePatches) dependsOn("applyServerBaseFeaturePatches") } + val applyAllServerFilePatches = project.tasks.register("applyAllServerFilePatches") { + group = "patching" + description = "Applies all Minecraft and upstream server file patches " + + "(equivalent to '${serverTasks.applyFilePatches.name} applyServerFilePatches')" + dependsOn(serverTasks.applyFilePatches) + dependsOn("applyServerFilePatches") + } val applyAllServerFeaturePatches = project.tasks.register("applyAllServerFeaturePatches") { group = "patching" description = "Applies all Minecraft and upstream server feature patches " + @@ -299,13 +299,6 @@ class CoreTasks( dependsOn(serverTasks.applyPatches) dependsOn("applyServerPatches") } - val rebuildAllServerFilePatches = project.tasks.register("rebuildAllServerFilePatches") { - group = "patching" - description = "Rebuilds all Minecraft and upstream server file patches " + - "(equivalent to '${serverTasks.rebuildFilePatchesName} rebuildServerFilePatches')" - dependsOn(serverTasks.rebuildFilePatchesName) - dependsOn("rebuildServerFilePatches") - } val rebuildAllServerBasePatches = project.tasks.register("rebuildAllServerBasePatches") { group = "patching" description = "Rebuilds all Minecraft and upstream server base patches " + @@ -313,6 +306,13 @@ class CoreTasks( dependsOn(serverTasks.rebuildBasePatchesName) dependsOn("rebuildServerBasePatches") } + val rebuildAllServerFilePatches = project.tasks.register("rebuildAllServerFilePatches") { + group = "patching" + description = "Rebuilds all Minecraft and upstream server file patches " + + "(equivalent to '${serverTasks.rebuildFilePatchesName} rebuildServerFilePatches')" + dependsOn(serverTasks.rebuildFilePatchesName) + dependsOn("rebuildServerFilePatches") + } val rebuildAllServerFeaturePatches = project.tasks.register("rebuildAllServerFeaturePatches") { group = "patching" description = "Rebuilds all Minecraft and upstream server feature patches " + diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt index e18a460bb..fecb0033c 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt @@ -100,7 +100,12 @@ class MinecraftPatchingTasks( base.set(baseSources) - output.set(outputSrc) + if (readOnly) { + output.set(outputSrcFile) + } else { + output.set(outputSrc) + } + baseRef.set("upstream/main") patches.set(baseFeaturePatchDir.fileExists(project)) baseRejectsDir.set(this@MinecraftPatchingTasks.baseRejectsDir) @@ -142,7 +147,7 @@ class MinecraftPatchingTasks( val applyFilePatches = tasks.register("apply${namePart}FilePatches") { group() description = "Applies all $configName Minecraft file patches" - dependsOn(applyBasePatches, applySourcePatches, applyResourcePatches) + dependsOn(applySourcePatches, applyResourcePatches) } val applyFeaturePatches = tasks.register("apply${namePart}FeaturePatches") { @@ -163,7 +168,6 @@ class MinecraftPatchingTasks( dependsOn(applyBasePatches, applyFilePatches, applyFeaturePatches) } - val rebuildBaseFeaturePatchesName = "rebuild${namePart}BaseFeaturePatches" val rebuildBasePatchesName = "rebuild${namePart}BasePatches" val rebuildSourcePatchesName = "rebuild${namePart}SourcePatches" val rebuildResourcePatchesName = "rebuild${namePart}ResourcePatches" @@ -188,7 +192,7 @@ class MinecraftPatchingTasks( } val importLibFiles = tasks.register("import${configName.capitalized()}LibraryFiles") { - patches.from(config.featurePatchDir, config.sourcePatchDir) // cant add base for some reason, oh well we'll use dev-imports + patches.from(config.featurePatchDir, config.sourcePatchDir, config.baseFeaturePatchDir) devImports.set(config.devImports.fileExists(project)) libraryFileIndex.set(coreTasks.indexLibraryFiles.flatMap { it.outputFile }) libraries.from(coreTasks.indexLibraryFiles.map { it.libraries }) @@ -221,6 +225,7 @@ class MinecraftPatchingTasks( val name = "rebuild${namePart}SourcePatches" if (name in tasks.names) { tasks.named(name) { + dependsOn(rebuildBasePatchesName) base.set(baseSources) } } @@ -243,8 +248,7 @@ class MinecraftPatchingTasks( group() description = "Rebuilds $configName base patches to the Minecraft source" - // baseRef.set("base") - // stopRef.set("patchedBase~1") + base.set(baseSources) // workaround to achieve the correct task execution order inputDir.set(outputSrc) patchDir.set(baseFeaturePatchDir) filterPatches.set(this@MinecraftPatchingTasks.filterPatches) @@ -253,8 +257,9 @@ class MinecraftPatchingTasks( val rebuildSourcePatches = tasks.register(rebuildSourcePatchesName) { group() description = "Rebuilds $configName file patches to the Minecraft sources" + dependsOn(rebuildBasePatches) - base.set(baseSources) + // base.set(baseSources) maybe look into this for ats in the future but rn we use commit based repo states not dir input.set(outputSrc) patches.set(sourcePatchDir) gitFilePatches.set(this@MinecraftPatchingTasks.gitFilePatches) @@ -288,8 +293,6 @@ class MinecraftPatchingTasks( inputDir.set(outputSrc) patchDir.set(featurePatchDir) - // baseRef.set("file") - // stopRef.set("HEAD") filterPatches.set(this@MinecraftPatchingTasks.filterPatches) } @@ -304,7 +307,7 @@ class MinecraftPatchingTasks( description = "Puts the currently tracked source changes into the $configName Minecraft sources file patches commit" repo.set(outputSrc) - upstream.set("upstream/patchedBase") + upstream.set("upstream/main") } val fixupResourcePatches = tasks.register("fixup${namePart}ResourcePatches") { diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PaperclipTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PaperclipTasks.kt index 6ac620b54..093fae752 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PaperclipTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PaperclipTasks.kt @@ -50,6 +50,9 @@ class PaperclipTasks( reobfJar: Provider, private val mcVersion: Provider ) { + + private val rootName: String = project.rootProject.name.lowercase() + init { val (createBundlerJar, createPaperclipJar) = project.createTasks("mojmap") val (createReobfBundlerJar, createReobfPaperclipJar) = project.createTasks("reobf") @@ -70,6 +73,10 @@ class PaperclipTasks( ): Pair, TaskProvider> { val bundlerTaskName = "create${classifier.capitalized()}BundlerJar" val paperclipTaskName = "create${classifier.capitalized()}PaperclipJar" + val publisherJarName = "build${classifier.capitalized()}PublisherJar" + + val buildNum: Provider = project.providers.environmentVariable("BUILD_NUMBER").orElse("local") + val publisherJar: Provider = buildNum.map { build -> "libs/$rootName-build.$build.jar" } val bundlerJarTask = tasks.register(bundlerTaskName) { group = "bundling" @@ -88,6 +95,15 @@ class PaperclipTasks( libraryChangesJson.set(bundlerJarTask.flatMap { it.libraryChangesJson }) outputZip.set(layout.buildDirectory.file(jarName("paperclip", classifier).map { "libs/$it" })) } + val buildPublisherJarTask = tasks.register(publisherJarName) { + group = "bundling" + description = "Build a ready-to-publish jar" + + dependsOn(paperclipJarTask) + + inputZip.set(paperclipJarTask.flatMap { it.outputZip }) + outputZip.set(layout.buildDirectory.file(publisherJar)) + } return bundlerJarTask to paperclipJarTask } diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt index 5d457e16e..6ba038365 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt @@ -68,6 +68,7 @@ class PatchingTasks( } else { repo.set(outputDir) } + patches.set(filePatchDir.fileExists(project)) rejectsDir.set(this@PatchingTasks.rejectsDir) gitFilePatches.set(this@PatchingTasks.gitFilePatches) @@ -88,7 +89,7 @@ class PatchingTasks( base.set(baseDir) - baseRef.set("HEAD") + baseRef.set("base") patches.set(baseFeaturePatchDir.fileExists(project)) baseRejectsDir.set(this@PatchingTasks.baseRejectsDir) identifier = "$forkName $patchSetName" @@ -102,14 +103,12 @@ class PatchingTasks( configureApplyFilePatches() dependsOn(applyBaseFeaturePatches) base.set(applyBaseFeaturePatches.flatMap { it.output }) - baseRef.set("patchedBase") } val applyFilePatchesFuzzy = tasks.register("apply${namePart}FilePatchesFuzzy") { configureApplyFilePatches() dependsOn(applyBaseFeaturePatches) base.set(applyBaseFeaturePatches.flatMap { it.output }) - baseRef.set("patchedBase") } val applyFeaturePatches = tasks.register("apply${namePart}FeaturePatches") { @@ -158,17 +157,16 @@ class PatchingTasks( group = taskGroup description = "Rebuilds $patchSetName base patches" + base.set(baseDir) // correct task execution order -> unused inputDir.set(outputDir) - // baseRef.set("base") - // stopRef.set("patchedBase~1") patchDir.set(baseFeaturePatchDir) filterPatches.set(this@PatchingTasks.filterPatches) } val rebuildFilePatches = tasks.register(rebuildFilePatchesName) { group = taskGroup description = "Rebuilds $patchSetName file patches" + dependsOn(rebuildBasePatches) - base.set(baseDir) input.set(outputDir) patches.set(filePatchDir) gitFilePatches.set(this@PatchingTasks.gitFilePatches) @@ -179,7 +177,7 @@ class PatchingTasks( description = "Puts the currently tracked source changes into the $patchSetName file patches commit" repo.set(outputDir) - upstream.set("upstream/main") + upstream.set("patchedBase") } val rebuildFeaturePatches = tasks.register(rebuildFeaturePatchesName) { @@ -189,8 +187,6 @@ class PatchingTasks( inputDir.set(outputDir) patchDir.set(featurePatchDir) - // baseRef.set("file") - // stopRef.set("HEAD") filterPatches.set(this@PatchingTasks.filterPatches) } diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/FilterRepo.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/FilterRepo.kt index a7372a8aa..af0ec6379 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/FilterRepo.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/FilterRepo.kt @@ -55,7 +55,7 @@ abstract class FilterRepo : BaseTask() { override fun init() { super.init() outputDir.set(layout.cache.resolve(paperTaskOutput())) - tag.convention("patchedBase") + tag.convention("base") } @TaskAction diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyBaseFeaturePatches.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyBaseFeaturePatches.kt index c17b7c1b5..043b963ea 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyBaseFeaturePatches.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyBaseFeaturePatches.kt @@ -25,7 +25,6 @@ package io.papermc.paperweight.core.tasks.patching import io.papermc.paperweight.PaperweightException import io.papermc.paperweight.tasks.* import io.papermc.paperweight.util.* -import io.papermc.paperweight.util.Git import java.nio.file.Path import java.time.Instant import kotlin.io.path.* @@ -87,21 +86,21 @@ abstract class ApplyBaseFeaturePatches : ControllableOutputTask() { @TaskAction fun run() { - Git.checkForGit() + io.papermc.paperweight.util.Git.checkForGit() val outputPath = output.path recreateCloneDirectory(outputPath) val git = Git(outputPath) - checkoutRepoFromUpstream( Git(outputPath), input.path, - baseRef.getOrElse("patchedBase"), + baseRef.getOrElse("main"), "upstream", - "patchedBase", + "main", baseRef.isPresent, ) + if (additionalRemote.isPresent) { val jgit = JGit.open(outputPath.toFile()) jgit.remoteRemove().setRemoteName(additionalRemoteName.get()).call() @@ -114,6 +113,7 @@ abstract class ApplyBaseFeaturePatches : ControllableOutputTask() { tagBase() if (!patches.isPresent) { + commit() } else { applyGitPatches(git, "server repo", outputPath, patches.path, printOutput.get(), verbose.get()) } @@ -188,6 +188,7 @@ abstract class ApplyBaseFeaturePatches : ControllableOutputTask() { if (printOutput) { logger.lifecycle("No patches found") } + commit() return } diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyFilePatches.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyFilePatches.kt index 25f2499b6..9724612fc 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyFilePatches.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyFilePatches.kt @@ -113,7 +113,7 @@ abstract class ApplyFilePatches : BaseTask() { checkoutRepoFromUpstream( git, base, - baseRef.getOrElse("upstream/patchedBase"), + "patchedBase", branchName = "main", ref = true, ) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/RebuildFilePatches.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/RebuildFilePatches.kt index a622befa1..225f61828 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/RebuildFilePatches.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/RebuildFilePatches.kt @@ -56,6 +56,7 @@ abstract class RebuildFilePatches : JavaLauncherTask() { @get:InputDirectory abstract val input: DirectoryProperty + @get:Optional @get:InputDirectory abstract val base: DirectoryProperty @@ -161,7 +162,7 @@ abstract class RebuildFilePatches : JavaLauncherTask() { } } - git("switch", currentBranch).executeSilently(silenceErr = true) + git("checkout", currentBranch).executeSilently(silenceErr = true) if (filesWithNewAts.isNotEmpty()) { try { // we need to rebase, so that the new file commit is part of the tree again. @@ -241,6 +242,7 @@ abstract class RebuildFilePatches : JavaLauncherTask() { return result.summary.changedFiles } + // TODO: look into moving this earlier in the source tree, still most of it is broken anyway private fun handleAts( baseDir: Path, inputDir: Path, diff --git a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/CreatePublisherJar.kt b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/CreatePublisherJar.kt new file mode 100644 index 000000000..a256eef46 --- /dev/null +++ b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/CreatePublisherJar.kt @@ -0,0 +1,38 @@ +/* + * paperweight is a Gradle plugin for the PaperMC project. + * + * Copyright (c) 2023 Kyle Wood (DenWav) + * Contributors + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 only, no later versions. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + * USA + */ + +package io.papermc.paperweight.tasks + +import io.papermc.paperweight.util.* +import java.nio.file.Path +import kotlin.io.path.* +import org.gradle.api.tasks.* +import org.gradle.kotlin.dsl.* + +// dummy task +abstract class CreatePublisherJar : ZippedTask() { + override fun init() { + super.init() + } + + override fun run(rootDir: Path) {} +} diff --git a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RebuildBaseGitPatches.kt b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RebuildBaseGitPatches.kt index 0cdecdc3b..18951ffbb 100644 --- a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RebuildBaseGitPatches.kt +++ b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RebuildBaseGitPatches.kt @@ -40,14 +40,9 @@ abstract class RebuildBaseGitPatches : ControllableOutputTask() { @get:InputDirectory abstract val inputDir: DirectoryProperty - @get:Input - @get:Optional - abstract val baseRef: Property - - // special handling cuz base patches cant go till head - @get:Input @get:Optional - abstract val stopRef: Property + @get:InputDirectory + abstract val base: DirectoryProperty // we dont need this at all but we use this to trick gradle into the correct task execution order @get:OutputDirectory abstract val patchDir: DirectoryProperty @@ -71,18 +66,14 @@ abstract class RebuildBaseGitPatches : ControllableOutputTask() { fun run() { val git = Git(inputDir.path) val currentBranch = git("rev-parse", "--abbrev-ref", "HEAD").getText().trim() - val fileCommit = git("rev-list", "--grep=File Patches", "--max-count=1", "base..HEAD").getText().trim() - - val stopCommit = git("rev-list", "--grep=Base Patches", "--max-count=1", "base..HEAD").getText().trim().let { - if (it.isNotEmpty()) "$it~1" else "file~1" - } + val stopCommit = git("rev-list", "--grep=Base Patches", "--max-count=1", "base..HEAD").getText().trim().let { "$it~1" } // we update the file tag git("checkout", "file").executeSilently(silenceErr = true) git("reset", fileCommit, "--hard").executeSilently(silenceErr = true) git("tag", "-f", "file").executeSilently(silenceErr = true) - git("switch", currentBranch).executeSilently(silenceErr = true) + git("checkout", currentBranch).executeSilently(silenceErr = true) val what = inputDir.path.name val patchFolder = patchDir.path @@ -116,12 +107,10 @@ abstract class RebuildBaseGitPatches : ControllableOutputTask() { val base = "base" val stop = stopCommit - val commitCount = git("rev-list", "--count", "$base..$stop").getText().trim().toInt() - if (commitCount == 0 || commitCount < 0) { - return - } + if (commitCount <= 0) return // nothing to rebuild + val range = "$base..$stop" git("fetch", "--all", "--prune").runSilently(silenceErr = true) git( diff --git a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RebuildGitPatches.kt b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RebuildGitPatches.kt index 8249673a5..1d1ac3cdf 100644 --- a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RebuildGitPatches.kt +++ b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RebuildGitPatches.kt @@ -40,15 +40,6 @@ abstract class RebuildGitPatches : ControllableOutputTask() { @get:InputDirectory abstract val inputDir: DirectoryProperty - @get:Input - @get:Optional - abstract val baseRef: Property - - // special handling cuz base patches cant go till head - @get:Input - @get:Optional - abstract val stopRef: Property - @get:OutputDirectory abstract val patchDir: DirectoryProperty @@ -69,9 +60,6 @@ abstract class RebuildGitPatches : ControllableOutputTask() { @TaskAction fun run() { - val git = Git(inputDir.path) - val baseCommit = git("rev-list", "--grep=File Patches", "--max-count=1", "base..HEAD").getText().trim() - val what = inputDir.path.name val patchFolder = patchDir.path if (!patchFolder.exists()) { @@ -102,14 +90,13 @@ abstract class RebuildGitPatches : ControllableOutputTask() { patchFolder.createDirectories() } - val base = baseCommit + val git = Git(inputDir.path) + val base = "file" val stop = "HEAD" - val commitCount = git("rev-list", "--count", "$base..$stop").getText().trim().toInt() - if (commitCount == 0 || commitCount < 0) { - return - } + if (commitCount <= 0) return // nothing to rebuild + val range = "$base..$stop" git("fetch", "--all", "--prune").runSilently(silenceErr = true) git( From a2c5bdfbd45885430405d98c0844d503b881a1e2 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Thu, 17 Jul 2025 22:45:52 +0200 Subject: [PATCH 011/257] allow overriding the output name of publisher jar --- gradle.properties | 2 +- .../papermc/paperweight/core/taskcontainers/PaperclipTasks.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle.properties b/gradle.properties index e100b2e20..3e5b75493 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ group = io.canvasmc.weaver -version = 2.1.4-SNAPSHOT +version = 2.1.5-SNAPSHOT org.gradle.caching = true org.gradle.parallel = true diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PaperclipTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PaperclipTasks.kt index 093fae752..444105584 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PaperclipTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PaperclipTasks.kt @@ -102,7 +102,7 @@ class PaperclipTasks( dependsOn(paperclipJarTask) inputZip.set(paperclipJarTask.flatMap { it.outputZip }) - outputZip.set(layout.buildDirectory.file(publisherJar)) + outputZip.convention(publisherJar.map { layout.buildDirectory.file(it) }.flatMap { it }) } return bundlerJarTask to paperclipJarTask } From 701ff5b14a76cf427bd2d945d65a0239cd61a8c2 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Sat, 19 Jul 2025 10:32:49 +0200 Subject: [PATCH 012/257] Enable rejects for git patches and be more lenient with single file patch apply --- .../paperweight/core/extension/ForkConfig.kt | 1 - .../core/extension/PaperExtension.kt | 1 - .../core/extension/UpstreamConfig.kt | 1 - .../core/taskcontainers/CoreTasks.kt | 2 - .../taskcontainers/MinecraftPatchingTasks.kt | 2 - .../core/taskcontainers/PatchingTasks.kt | 2 - .../taskcontainers/UpstreamConfigTasks.kt | 1 - .../tasks/patching/ApplyBaseFeaturePatches.kt | 73 +++++++++++++++++-- .../tasks/patching/ApplyFeaturePatches.kt | 73 +++++++++++++++++-- .../tasks/patching/ApplySingleFilePatches.kt | 2 +- 10 files changed, 135 insertions(+), 23 deletions(-) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/ForkConfig.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/ForkConfig.kt index 130032e66..270d517b4 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/ForkConfig.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/ForkConfig.kt @@ -49,7 +49,6 @@ abstract class ForkConfig @Inject constructor( val serverDirectory: DirectoryProperty = objects.dirFrom(rootDirectory, providers.provider { "$name-server" }) val serverPatchesDir: DirectoryProperty = objects.dirFrom(serverDirectory, "minecraft-patches") val rejectsDir: DirectoryProperty = objects.dirFrom(serverPatchesDir, "rejected") - val baseRejectsDir: DirectoryProperty = objects.dirFrom(serverPatchesDir, "rejected-base") val sourcePatchDir: DirectoryProperty = objects.dirFrom(serverPatchesDir, "sources") val resourcePatchDir: DirectoryProperty = objects.dirFrom(serverPatchesDir, "resources") val featurePatchDir: DirectoryProperty = objects.dirFrom(serverPatchesDir, "features") diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/PaperExtension.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/PaperExtension.kt index 76769005e..02044891a 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/PaperExtension.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/PaperExtension.kt @@ -35,7 +35,6 @@ abstract class PaperExtension @Inject constructor(objects: ObjectFactory, projec val paperServerDir: DirectoryProperty = objects.dirFrom(rootDirectory, "paper-server") val serverPatchesDir: DirectoryProperty = objects.dirFrom(paperServerDir, "patches") val rejectsDir: DirectoryProperty = objects.dirFrom(serverPatchesDir, "rejected") - val baseRejectsDir: DirectoryProperty = objects.dirFrom(serverPatchesDir, "rejected-base") val sourcePatchDir: DirectoryProperty = objects.dirFrom(serverPatchesDir, "sources") val resourcePatchDir: DirectoryProperty = objects.dirFrom(serverPatchesDir, "resources") val baseFeaturePatchDir: DirectoryProperty = objects.dirFrom(serverPatchesDir, "base") diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/UpstreamConfig.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/UpstreamConfig.kt index 7597b785b..08b67a279 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/UpstreamConfig.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/UpstreamConfig.kt @@ -104,7 +104,6 @@ abstract class UpstreamConfig @Inject constructor( abstract val patchesDir: DirectoryProperty val rejectsDir: DirectoryProperty = objects.dirFrom(patchesDir, "rejected") - val baseRejectsDir: DirectoryProperty = objects.dirFrom(patchesDir, "rejected-base") val filePatchDir: DirectoryProperty = objects.dirFrom(patchesDir, "files") val featurePatchDir: DirectoryProperty = objects.dirFrom(patchesDir, "features") val baseFeaturePatchDir: DirectoryProperty = objects.dirFrom(patchesDir, "base") diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/CoreTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/CoreTasks.kt index 58817aa6c..037e9ec9a 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/CoreTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/CoreTasks.kt @@ -187,7 +187,6 @@ class CoreTasks( hasFork, project.coreExt.paper.sourcePatchDir, project.coreExt.paper.rejectsDir, - project.coreExt.paper.baseRejectsDir, project.coreExt.paper.resourcePatchDir, project.coreExt.paper.baseFeaturePatchDir, project.coreExt.paper.featurePatchDir, @@ -225,7 +224,6 @@ class CoreTasks( !activeFork, cfg.sourcePatchDir, cfg.rejectsDir, - cfg.baseRejectsDir, cfg.resourcePatchDir, cfg.baseFeaturePatchDir, cfg.featurePatchDir, diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt index fecb0033c..5e87aa53b 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt @@ -53,7 +53,6 @@ class MinecraftPatchingTasks( private val readOnly: Boolean, private val sourcePatchDir: DirectoryProperty, private val rejectsDir: DirectoryProperty, - private val baseRejectsDir: DirectoryProperty, private val resourcePatchDir: DirectoryProperty, private val baseFeaturePatchDir: DirectoryProperty, private val featurePatchDir: DirectoryProperty, @@ -108,7 +107,6 @@ class MinecraftPatchingTasks( baseRef.set("upstream/main") patches.set(baseFeaturePatchDir.fileExists(project)) - baseRejectsDir.set(this@MinecraftPatchingTasks.baseRejectsDir) identifier = configName } diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt index 6ba038365..7d7cc4567 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt @@ -48,7 +48,6 @@ class PatchingTasks( private val readOnly: Boolean, private val filePatchDir: DirectoryProperty, private val rejectsDir: DirectoryProperty, - private val baseRejectsDir: DirectoryProperty, private val featurePatchDir: DirectoryProperty, private val baseFeaturePatchDir: DirectoryProperty, private val baseDir: Provider, @@ -91,7 +90,6 @@ class PatchingTasks( baseRef.set("base") patches.set(baseFeaturePatchDir.fileExists(project)) - baseRejectsDir.set(this@PatchingTasks.baseRejectsDir) identifier = "$forkName $patchSetName" } diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/UpstreamConfigTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/UpstreamConfigTasks.kt index 1fdfcd577..4216cbd9c 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/UpstreamConfigTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/UpstreamConfigTasks.kt @@ -130,7 +130,6 @@ class UpstreamConfigTasks( readOnly, cfg.filePatchDir, cfg.rejectsDir, - cfg.baseRejectsDir, cfg.featurePatchDir, cfg.baseFeaturePatchDir, base, diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyBaseFeaturePatches.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyBaseFeaturePatches.kt index 043b963ea..c01e95e48 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyBaseFeaturePatches.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyBaseFeaturePatches.kt @@ -25,6 +25,7 @@ package io.papermc.paperweight.core.tasks.patching import io.papermc.paperweight.PaperweightException import io.papermc.paperweight.tasks.* import io.papermc.paperweight.util.* +import java.nio.file.Files import java.nio.file.Path import java.time.Instant import kotlin.io.path.* @@ -66,6 +67,9 @@ abstract class ApplyBaseFeaturePatches : ControllableOutputTask() { @get:Input abstract val verbose: Property + @get:Input + abstract val emitRejects: Property + @get:Input @get:Optional abstract val identifier: Property @@ -82,6 +86,7 @@ abstract class ApplyBaseFeaturePatches : ControllableOutputTask() { printOutput.convention(false).finalizeValueOnRead() additionalRemoteName.convention("old") verbose.convention(false) + emitRejects.convention(false) } @TaskAction @@ -180,6 +185,14 @@ abstract class ApplyBaseFeaturePatches : ControllableOutputTask() { val statusFile = outputDir.resolve(".git/patch-apply-failed") statusFile.deleteForcefully() + val logFile = outputDir.resolve(".git/patch-apply-logs.log") + logFile.deleteForcefully() + + Files.walk(outputDir).use { file -> + file + .filter { it.fileName.toString().endsWith(".rej") } + .forEach { it.deleteForcefully() } + } git("am", "--abort").runSilently(silenceErr = true) @@ -205,20 +218,68 @@ abstract class ApplyBaseFeaturePatches : ControllableOutputTask() { } val gitOut = printOutput && verbose - val result = git("am", "--3way", "--ignore-whitespace", tempDir.absolutePathString()).captureOut(gitOut) + val result = if (emitRejects.get()) { + git( + "am", + "--3way", + "--reject", + "--ignore-whitespace", + tempDir.absolutePathString() + ).captureOut(gitOut) + } else { + git("am", "--3way", "--ignore-whitespace", tempDir.absolutePathString()).captureOut(gitOut) + } + if (result.exit != 0) { statusFile.writeText("1") - if (!gitOut) { - // Log the output anyway on failure - logger.lifecycle(result.out) + if (emitRejects.get()) { + logFile.writeText(result.out) + + val filtered = result.out.lineSequence() + .map { line -> + val index = line.indexOf("Patch failed at") + if (index >= 0) line.substring(index) else line + } + .filterNot { line -> + line.startsWith("error: while") || + line.startsWith("Checking patch") || + line.startsWith("Applied patch") || + line.startsWith("Applying patch") || + line.endsWith("reject...") || + line.endsWith("cleanly.") || + line.startsWith(" ") || + line.startsWith("Hunk #") || + line.startsWith("Rejected hunk") || + line.contains(";") || + line.isEmpty() + } + .joinToString("\n") + if (!gitOut) { + // Log the output anyway on failure + logger.error(filtered) + } + + logger.error("*** Patch application has been paused!") + logger.error("*** Please review above details and fix the rejected hunks") + logger.error( + "*** then delete all `.rej` files, run `git am --continue` and save the changes with `./gradlew rebuildPatches`" + ) + logger.error("*** For full logs refer to the `.git/patch-apply-logs.log` log file") + } else { + if (!gitOut) { + // Log the output anyway on failure + logger.error(result.out) + } + + logger.error("*** Please review above details and finish the apply then") + logger.error("*** save the changes with `./gradlew rebuildPatches`") } - logger.error("*** Please review above details and finish the apply then") - logger.error("*** save the changes with `./gradlew rebuildPatches`") throw PaperweightException("Failed to apply patches") } else { statusFile.deleteForcefully() + logFile.deleteForcefully() if (printOutput) { logger.lifecycle("${patches.size} patches applied cleanly to $target") } diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyFeaturePatches.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyFeaturePatches.kt index 1e5a51c5b..4dacb80cc 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyFeaturePatches.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyFeaturePatches.kt @@ -25,6 +25,7 @@ package io.papermc.paperweight.core.tasks.patching import io.papermc.paperweight.PaperweightException import io.papermc.paperweight.tasks.* import io.papermc.paperweight.util.* +import java.nio.file.Files import java.nio.file.Path import kotlin.io.path.* import org.gradle.api.file.DirectoryProperty @@ -54,9 +55,13 @@ abstract class ApplyFeaturePatches : ControllableOutputTask() { @get:Input abstract val verbose: Property + @get:Input + abstract val emitRejects: Property + override fun init() { printOutput.convention(false).finalizeValueOnRead() verbose.convention(false) + emitRejects.convention(false) } @TaskAction @@ -106,6 +111,14 @@ abstract class ApplyFeaturePatches : ControllableOutputTask() { val statusFile = outputDir.resolve(".git/patch-apply-failed") statusFile.deleteForcefully() + val logFile = outputDir.resolve(".git/patch-apply-logs.log") + logFile.deleteForcefully() + + Files.walk(outputDir).use { file -> + file + .filter { it.fileName.toString().endsWith(".rej") } + .forEach { it.deleteForcefully() } + } git("am", "--abort").runSilently(silenceErr = true) @@ -130,20 +143,68 @@ abstract class ApplyFeaturePatches : ControllableOutputTask() { } val gitOut = printOutput && verbose - val result = git("am", "--3way", "--ignore-whitespace", tempDir.absolutePathString()).captureOut(gitOut) + val result = if (emitRejects.get()) { + git( + "am", + "--3way", + "--reject", + "--ignore-whitespace", + tempDir.absolutePathString() + ).captureOut(gitOut) + } else { + git("am", "--3way", "--ignore-whitespace", tempDir.absolutePathString()).captureOut(gitOut) + } + if (result.exit != 0) { statusFile.writeText("1") - if (!gitOut) { - // Log the output anyway on failure - logger.lifecycle(result.out) + if (emitRejects.get()) { + logFile.writeText(result.out) + + val filtered = result.out.lineSequence() + .map { line -> + val index = line.indexOf("Patch failed at") + if (index >= 0) line.substring(index) else line + } + .filterNot { line -> + line.startsWith("error: while") || + line.startsWith("Checking patch") || + line.startsWith("Applied patch") || + line.startsWith("Applying patch") || + line.endsWith("reject...") || + line.endsWith("cleanly.") || + line.startsWith(" ") || + line.startsWith("Hunk #") || + line.startsWith("Rejected hunk") || + line.contains(";") || + line.isEmpty() + } + .joinToString("\n") + if (!gitOut) { + // Log the output anyway on failure + logger.error(filtered) + } + + logger.error("*** Patch application has been paused!") + logger.error("*** Please review above details and fix the rejected hunks") + logger.error( + "*** then delete all `.rej` files, run `git am --continue` and save the changes with `./gradlew rebuildPatches`" + ) + logger.error("*** For full logs refer to the `.git/patch-apply-logs.log` log file") + } else { + if (!gitOut) { + // Log the output anyway on failure + logger.error(result.out) + } + + logger.error("*** Please review above details and finish the apply then") + logger.error("*** save the changes with `./gradlew rebuildPatches`") } - logger.error("*** Please review above details and finish the apply then") - logger.error("*** save the changes with `./gradlew rebuildPatches`") throw PaperweightException("Failed to apply patches") } else { statusFile.deleteForcefully() + logFile.deleteForcefully() if (printOutput) { logger.lifecycle("${patches.size} patches applied cleanly to $target") } diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplySingleFilePatches.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplySingleFilePatches.kt index 4c8bd44e8..b54c03d68 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplySingleFilePatches.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplySingleFilePatches.kt @@ -100,7 +100,7 @@ abstract class ApplySingleFilePatches : BaseTask() { override fun init() { super.init() - mode.convention(PatchMode.EXACT) + mode.convention(PatchMode.OFFSET) minFuzz.convention(providers.defaultMinFuzz()) } From 4f488b5d0a8c493ff3a14f41f4514862f5cd0319 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Sat, 19 Jul 2025 18:08:01 +0200 Subject: [PATCH 013/257] bring up to par with paperweight pr --- .../taskcontainers/MinecraftPatchingTasks.kt | 1 - .../tasks/patching/ApplyBaseFeaturePatches.kt | 16 ++++------------ .../core/tasks/patching/ApplyFeaturePatches.kt | 16 ++++------------ 3 files changed, 8 insertions(+), 25 deletions(-) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt index 5e87aa53b..8f809d92d 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt @@ -224,7 +224,6 @@ class MinecraftPatchingTasks( if (name in tasks.names) { tasks.named(name) { dependsOn(rebuildBasePatchesName) - base.set(baseSources) } } } diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyBaseFeaturePatches.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyBaseFeaturePatches.kt index c01e95e48..74e3ac9e2 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyBaseFeaturePatches.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyBaseFeaturePatches.kt @@ -25,7 +25,6 @@ package io.papermc.paperweight.core.tasks.patching import io.papermc.paperweight.PaperweightException import io.papermc.paperweight.tasks.* import io.papermc.paperweight.util.* -import java.nio.file.Files import java.nio.file.Path import java.time.Instant import kotlin.io.path.* @@ -187,12 +186,7 @@ abstract class ApplyBaseFeaturePatches : ControllableOutputTask() { statusFile.deleteForcefully() val logFile = outputDir.resolve(".git/patch-apply-logs.log") logFile.deleteForcefully() - - Files.walk(outputDir).use { file -> - file - .filter { it.fileName.toString().endsWith(".rej") } - .forEach { it.deleteForcefully() } - } + outputDir.filesMatchingRecursive("*.rej").forEach { it.deleteForcefully() } git("am", "--abort").runSilently(silenceErr = true) @@ -260,11 +254,9 @@ abstract class ApplyBaseFeaturePatches : ControllableOutputTask() { logger.error(filtered) } - logger.error("*** Patch application has been paused!") - logger.error("*** Please review above details and fix the rejected hunks") - logger.error( - "*** then delete all `.rej` files, run `git am --continue` and save the changes with `./gradlew rebuildPatches`" - ) + logger.error("*** Please review above details and finish the apply") + logger.error("*** by manually applying the rejected hunks and deleting all `.rej` files") + logger.error("*** then running `git am --continue` and saving the changes with `./gradlew rebuildPatches`") logger.error("*** For full logs refer to the `.git/patch-apply-logs.log` log file") } else { if (!gitOut) { diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyFeaturePatches.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyFeaturePatches.kt index 4dacb80cc..23dea82df 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyFeaturePatches.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyFeaturePatches.kt @@ -25,7 +25,6 @@ package io.papermc.paperweight.core.tasks.patching import io.papermc.paperweight.PaperweightException import io.papermc.paperweight.tasks.* import io.papermc.paperweight.util.* -import java.nio.file.Files import java.nio.file.Path import kotlin.io.path.* import org.gradle.api.file.DirectoryProperty @@ -113,12 +112,7 @@ abstract class ApplyFeaturePatches : ControllableOutputTask() { statusFile.deleteForcefully() val logFile = outputDir.resolve(".git/patch-apply-logs.log") logFile.deleteForcefully() - - Files.walk(outputDir).use { file -> - file - .filter { it.fileName.toString().endsWith(".rej") } - .forEach { it.deleteForcefully() } - } + outputDir.filesMatchingRecursive("*.rej").forEach { it.deleteForcefully() } git("am", "--abort").runSilently(silenceErr = true) @@ -185,11 +179,9 @@ abstract class ApplyFeaturePatches : ControllableOutputTask() { logger.error(filtered) } - logger.error("*** Patch application has been paused!") - logger.error("*** Please review above details and fix the rejected hunks") - logger.error( - "*** then delete all `.rej` files, run `git am --continue` and save the changes with `./gradlew rebuildPatches`" - ) + logger.error("*** Please review above details and finish the apply") + logger.error("*** by manually applying the rejected hunks and deleting all `.rej` files") + logger.error("*** then running `git am --continue` and saving the changes with `./gradlew rebuildPatches`") logger.error("*** For full logs refer to the `.git/patch-apply-logs.log` log file") } else { if (!gitOut) { From eed02463937554804b02b799f2a5202e307a680b Mon Sep 17 00:00:00 2001 From: Toffikk Date: Sun, 20 Jul 2025 12:01:54 +0200 Subject: [PATCH 014/257] feat: move cleanup patches to its own function --- .../core/tasks/patching/RebuildFilePatches.kt | 45 +++++++++++-------- 1 file changed, 27 insertions(+), 18 deletions(-) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/RebuildFilePatches.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/RebuildFilePatches.kt index 225f61828..95e394c7b 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/RebuildFilePatches.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/RebuildFilePatches.kt @@ -145,23 +145,6 @@ abstract class RebuildFilePatches : JavaLauncherTask() { rebuildWithDiffPatch(baseDir, inputDir, patchDir) } - // filter patches since our diff library seems to emit patches for empty files that existed in the baseDir - if (filterPatches.get()) { - patchDir - .walk() - .filter { it.extension == "patch" } - .forEach { patch -> - val lines = patch.readLines() - val hasChanges = lines.any { - (it.startsWith("+") && !it.startsWith("+++")) || - (it.startsWith("-") && !it.startsWith("---")) - } - if (!hasChanges) { - patch.deleteRecursive() - } - } - } - git("checkout", currentBranch).executeSilently(silenceErr = true) if (filesWithNewAts.isNotEmpty()) { try { @@ -239,7 +222,12 @@ abstract class RebuildFilePatches : JavaLauncherTask() { .summary(verbose.get()) .build() .operate() - return result.summary.changedFiles + + return if (filterPatches.get()) { + cleanupPatches(patchDir) + } else { + result.summary.changedFiles + } } // TODO: look into moving this earlier in the source tree, still most of it is broken anyway @@ -341,4 +329,25 @@ abstract class RebuildFilePatches : JavaLauncherTask() { return foundNew } + + // filter patches since our diff library seems to emit patches for empty files that existed in the baseDir + private fun cleanupPatches(patchDir: Path): Int { + var saved = 0 + val patches = patchDir.filesMatchingRecursive("*.patch") + patches.forEach { patch -> + val hasChanges = patch.useLines { lines -> + lines.any { + (it.startsWith("+") && !it.startsWith("+++")) || + (it.startsWith("-") && !it.startsWith("---")) + } + } + + if (!hasChanges) { + patch.deleteForcefully() + } else { + saved++ + } + } + return saved + } } From 41c2f40c1f7a4ef53ac7b64faafc33148fbcfbb6 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Sun, 20 Jul 2025 20:43:52 +0200 Subject: [PATCH 015/257] cleanup: dont alias jgit --- .../core/tasks/patching/ApplyBaseFeaturePatches.kt | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyBaseFeaturePatches.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyBaseFeaturePatches.kt index 74e3ac9e2..bed741c6f 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyBaseFeaturePatches.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyBaseFeaturePatches.kt @@ -28,7 +28,7 @@ import io.papermc.paperweight.util.* import java.nio.file.Path import java.time.Instant import kotlin.io.path.* -import org.eclipse.jgit.api.Git as JGit +import org.eclipse.jgit.api.Git import org.eclipse.jgit.lib.PersonIdent import org.eclipse.jgit.transport.URIish import org.gradle.api.file.DirectoryProperty @@ -95,7 +95,6 @@ abstract class ApplyBaseFeaturePatches : ControllableOutputTask() { val outputPath = output.path recreateCloneDirectory(outputPath) - val git = Git(outputPath) checkoutRepoFromUpstream( Git(outputPath), input.path, @@ -106,7 +105,7 @@ abstract class ApplyBaseFeaturePatches : ControllableOutputTask() { ) if (additionalRemote.isPresent) { - val jgit = JGit.open(outputPath.toFile()) + val jgit = Git.open(outputPath.toFile()) jgit.remoteRemove().setRemoteName(additionalRemoteName.get()).call() jgit.remoteAdd().setName(additionalRemoteName.get()).setUri(URIish(additionalRemote.get())).call() jgit.fetch().setRemote(additionalRemoteName.get()).call() @@ -119,7 +118,7 @@ abstract class ApplyBaseFeaturePatches : ControllableOutputTask() { if (!patches.isPresent) { commit() } else { - applyGitPatches(git, "server repo", outputPath, patches.path, printOutput.get(), verbose.get()) + applyGitPatches("server repo", outputPath, patches.path, printOutput.get(), verbose.get()) } } @@ -141,7 +140,7 @@ abstract class ApplyBaseFeaturePatches : ControllableOutputTask() { } private fun tagBase() { - val git = JGit.open(output.path.toFile()) + val git = Git.open(output.path.toFile()) val ident = PersonIdent("base", "noreply+automated@papermc.io") git.tagDelete().setTags("base").call() git.tag().setName("base").setTagger(ident).setSigned(false).call() @@ -157,7 +156,7 @@ abstract class ApplyBaseFeaturePatches : ControllableOutputTask() { private fun commit() { val ident = PersonIdent(PersonIdent("Patched Base", "noreply+automated@papermc.io"), Instant.parse("1997-04-20T13:37:42.69Z")) - val git = JGit.open(output.path.toFile()) + val git = Git.open(output.path.toFile()) git.add().addFilepattern(".").call() git.commit() .setMessage("${identifier.get()} Base Patches") @@ -171,7 +170,6 @@ abstract class ApplyBaseFeaturePatches : ControllableOutputTask() { } private fun applyGitPatches( - git: Git, target: String, outputDir: Path, patchDir: Path?, @@ -182,6 +180,7 @@ abstract class ApplyBaseFeaturePatches : ControllableOutputTask() { logger.lifecycle("Applying patches to $target...") } + val git = Git(outputDir) val statusFile = outputDir.resolve(".git/patch-apply-failed") statusFile.deleteForcefully() val logFile = outputDir.resolve(".git/patch-apply-logs.log") @@ -279,7 +278,6 @@ abstract class ApplyBaseFeaturePatches : ControllableOutputTask() { } finally { tempDir.deleteRecursive() } - commit() } } From f0ae14ab085e274e58494cacf3540a8ec7c27d86 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Sun, 20 Jul 2025 23:08:46 +0200 Subject: [PATCH 016/257] chore: setup publishing --- .github/workflows/deploy-snapshot.yml | 6 +++--- .github/workflows/deploy.yml | 14 +++----------- 2 files changed, 6 insertions(+), 14 deletions(-) diff --git a/.github/workflows/deploy-snapshot.yml b/.github/workflows/deploy-snapshot.yml index 0349f2e29..fc7975927 100644 --- a/.github/workflows/deploy-snapshot.yml +++ b/.github/workflows/deploy-snapshot.yml @@ -1,7 +1,7 @@ name: Deploy Snapshot on: push: - branches: [ 'main' ] + branches: [ 'basepatches' ] paths-ignore: - 'license/*' - 'readme.md' @@ -31,5 +31,5 @@ jobs: if: endsWith(steps.get_version.outputs.version, '-SNAPSHOT') run: ./gradlew -Dorg.gradle.parallel=true publish -x test --no-daemon --stacktrace -Dorg.gradle.internal.http.socketTimeout=90000 -Dorg.gradle.internal.http.connectionTimeout=90000 env: - ORG_GRADLE_PROJECT_paperUsername: ${{ secrets.DEPLOY_USER }} - ORG_GRADLE_PROJECT_paperPassword: ${{ secrets.DEPLOY_PASS }} + ORG_GRADLE_PROJECT_username: ${{ secrets.PUBLISH_USER }} + ORG_GRADLE_PROJECT_password: ${{ secrets.PUBLISH_TOKEN }} diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 1a78ba93f..4097166de 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -16,15 +16,7 @@ jobs: - name: Setup Gradle uses: gradle/actions/setup-gradle@v4 - name: Deploy release - run: ./gradlew publishPlugins -x test --no-daemon --stacktrace + run: ./gradlew publish -x test --no-daemon --stacktrace env: - GRADLE_PUBLISH_KEY: "${{ secrets.GRADLE_PLUGIN_PORTAL_KEY }}" - GRADLE_PUBLISH_SECRET: "${{ secrets.GRADLE_PLUGIN_PORTAL_SECRET }}" - - name: Parse tag - id: vars - run: echo ::set-output name=tag::${GITHUB_REF#refs/*/} - - name: Create release and changelog - uses: MC-Machinations/auto-release-changelog@v1.1.3 - with: - token: ${{ secrets.GITHUB_TOKEN }} - title: paperweight ${{ steps.vars.outputs.tag }} + ORG_GRADLE_PROJECT_username: ${{ secrets.PUBLISH_USER }} + ORG_GRADLE_PROJECT_password: ${{ secrets.PUBLISH_TOKEN }} From 6a3f00d6d39db4e984137ce531014a6db468ee40 Mon Sep 17 00:00:00 2001 From: Dueris Date: Sun, 20 Jul 2025 17:10:31 -0700 Subject: [PATCH 017/257] Fixup deploy --- .github/workflows/deploy-snapshot.yml | 35 --------------------------- .github/workflows/deploy.yml | 22 ++++++++++------- .github/workflows/test.yml | 32 ------------------------ 3 files changed, 13 insertions(+), 76 deletions(-) delete mode 100644 .github/workflows/deploy-snapshot.yml delete mode 100644 .github/workflows/test.yml diff --git a/.github/workflows/deploy-snapshot.yml b/.github/workflows/deploy-snapshot.yml deleted file mode 100644 index fc7975927..000000000 --- a/.github/workflows/deploy-snapshot.yml +++ /dev/null @@ -1,35 +0,0 @@ -name: Deploy Snapshot -on: - push: - branches: [ 'basepatches' ] - paths-ignore: - - 'license/*' - - 'readme.md' - - '.gitignore' - - '.gitattributes' - - '.editorconfig' - -jobs: - deploy: - name: Deploy Snapshot - runs-on: 'ubuntu-latest' - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-java@v4 - with: - java-version: 21 - distribution: 'temurin' - - name: Setup Gradle - uses: gradle/actions/setup-gradle@v4 - - name: Get project version - id: get_version - shell: bash - run: | - project_version=$(./gradlew -q --console=plain printVersion --no-daemon) - echo version=$project_version >> $GITHUB_OUTPUT - - name: Deploy snapshot version - if: endsWith(steps.get_version.outputs.version, '-SNAPSHOT') - run: ./gradlew -Dorg.gradle.parallel=true publish -x test --no-daemon --stacktrace -Dorg.gradle.internal.http.socketTimeout=90000 -Dorg.gradle.internal.http.connectionTimeout=90000 - env: - ORG_GRADLE_PROJECT_username: ${{ secrets.PUBLISH_USER }} - ORG_GRADLE_PROJECT_password: ${{ secrets.PUBLISH_TOKEN }} diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 4097166de..507944256 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -1,11 +1,17 @@ -name: Deploy +name: Deploy Snapshot on: push: - tags: [ 'v*' ] + branches: [ 'basepatches' ] + paths-ignore: + - 'license/*' + - 'readme.md' + - '.gitignore' + - '.gitattributes' + - '.editorconfig' jobs: deploy: - name: Deploy + name: Deploy Snapshot runs-on: 'ubuntu-latest' steps: - uses: actions/checkout@v4 @@ -13,10 +19,8 @@ jobs: with: java-version: 21 distribution: 'temurin' - - name: Setup Gradle - uses: gradle/actions/setup-gradle@v4 - - name: Deploy release - run: ./gradlew publish -x test --no-daemon --stacktrace + - name: Deploy Version + run: ./gradlew publishAllPublicationsToCentralRepository env: - ORG_GRADLE_PROJECT_username: ${{ secrets.PUBLISH_USER }} - ORG_GRADLE_PROJECT_password: ${{ secrets.PUBLISH_TOKEN }} + PUBLISH_USER: ${{ secrets.PUBLISH_USER }} + PUBLISH_TOKEN: ${{ secrets.PUBLISH_TOKEN }} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml deleted file mode 100644 index 4eb7ffcea..000000000 --- a/.github/workflows/test.yml +++ /dev/null @@ -1,32 +0,0 @@ -name: Test -on: - push: - branches: [ "**" ] - pull_request: - -jobs: - test: - # Only run on PRs if the source branch is on someone else's repo - if: ${{ github.event_name != 'pull_request' || github.repository != github.event.pull_request.head.repo.full_name }} - name: Test - runs-on: 'ubuntu-latest' - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-java@v4 - with: - java-version: 21 - distribution: 'temurin' - - name: Setup Gradle - uses: gradle/actions/setup-gradle@v4 - - name: Execute Gradle build - run: | - git config --global user.email "no-reply@github.com" - git config --global user.name "GitHub Actions" - ./gradlew build -x test --no-daemon --stacktrace - - name: Publish Test Report - uses: mikepenz/action-junit-report@v5 - if: always() - with: - report_paths: '**/build/test-results/test/TEST-*.xml' - detailed_summary: true - annotate_notice: true From 86c941417f763a0dbd505d899294f79b67c8f19a Mon Sep 17 00:00:00 2001 From: Toffikk Date: Mon, 21 Jul 2025 04:20:43 +0200 Subject: [PATCH 018/257] chore: fix tests and fix duplicated tasks unified all base-related patches to be basepatches --- .github/workflows/test.yml | 32 +++++++ .../paperweight/core/extension/ForkConfig.kt | 2 +- .../core/extension/PaperExtension.kt | 2 +- .../core/extension/UpstreamConfig.kt | 2 +- .../core/taskcontainers/CoreTasks.kt | 12 +-- .../taskcontainers/MinecraftPatchingTasks.kt | 88 +++++++++---------- .../core/taskcontainers/PatchingTasks.kt | 52 +++++------ .../taskcontainers/UpstreamConfigTasks.kt | 8 +- ...eFeaturePatches.kt => ApplyBasePatches.kt} | 6 +- .../core/tasks/patching/RebuildFilePatches.kt | 5 +- .../io/papermc/paperweight/FunctionalTest.kt | 4 +- .../tasks/patching/ApplyFilePatchesTest.kt | 4 +- .../fake-patches/base/0001-Test-Base.patch | 19 ++++ .../expected/0001-Test-Base.patch | 19 ++++ .../fake-patches/expected/Test.java.patch | 6 +- .../fake-patches/features/0001-Feature.patch | 2 +- .../fake-patches/sources/Test.java.patch | 4 +- .../functional_test/test-server/build.gradle | 3 +- .../test-server/patches/base/0001-Base.patch | 18 ++++ 19 files changed, 183 insertions(+), 105 deletions(-) create mode 100644 .github/workflows/test.yml rename paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/{ApplyBaseFeaturePatches.kt => ApplyBasePatches.kt} (98%) create mode 100644 paperweight-core/src/test/resources/functional_test/fake-patches/base/0001-Test-Base.patch create mode 100644 paperweight-core/src/test/resources/functional_test/fake-patches/expected/0001-Test-Base.patch create mode 100644 paperweight-core/src/test/resources/functional_test/test-server/patches/base/0001-Base.patch diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 000000000..223e8e446 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,32 @@ +name: Test +on: + push: + branches: [ "**" ] + pull_request: + +jobs: + test: + # Only run on PRs if the source branch is on someone else's repo + if: ${{ github.event_name != 'pull_request' || github.repository != github.event.pull_request.head.repo.full_name }} + name: Test + runs-on: 'ubuntu-latest' + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-java@v4 + with: + java-version: 21 + distribution: 'temurin' + - name: Setup Gradle + uses: gradle/actions/setup-gradle@v4 + - name: Execute Gradle build + run: | + git config --global user.email "no-reply@github.com" + git config --global user.name "GitHub Actions" + ./gradlew build -x test --no-daemon --stacktrace + - name: Publish Test Report + uses: mikepenz/action-junit-report@v5 + if: always() + with: + report_paths: '**/build/test-results/test/TEST-*.xml' + detailed_summary: true + annotate_notice: true \ No newline at end of file diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/ForkConfig.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/ForkConfig.kt index 270d517b4..737859282 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/ForkConfig.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/ForkConfig.kt @@ -49,10 +49,10 @@ abstract class ForkConfig @Inject constructor( val serverDirectory: DirectoryProperty = objects.dirFrom(rootDirectory, providers.provider { "$name-server" }) val serverPatchesDir: DirectoryProperty = objects.dirFrom(serverDirectory, "minecraft-patches") val rejectsDir: DirectoryProperty = objects.dirFrom(serverPatchesDir, "rejected") + val basePatchDir: DirectoryProperty = objects.dirFrom(serverPatchesDir, "base") val sourcePatchDir: DirectoryProperty = objects.dirFrom(serverPatchesDir, "sources") val resourcePatchDir: DirectoryProperty = objects.dirFrom(serverPatchesDir, "resources") val featurePatchDir: DirectoryProperty = objects.dirFrom(serverPatchesDir, "features") - val baseFeaturePatchDir: DirectoryProperty = objects.dirFrom(serverPatchesDir, "base") val buildDataDir: DirectoryProperty = objects.dirFrom(rootDirectory, "build-data") val devImports: RegularFileProperty = objects.fileFrom(buildDataDir, "dev-imports.txt") diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/PaperExtension.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/PaperExtension.kt index 02044891a..a6b670f81 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/PaperExtension.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/PaperExtension.kt @@ -35,9 +35,9 @@ abstract class PaperExtension @Inject constructor(objects: ObjectFactory, projec val paperServerDir: DirectoryProperty = objects.dirFrom(rootDirectory, "paper-server") val serverPatchesDir: DirectoryProperty = objects.dirFrom(paperServerDir, "patches") val rejectsDir: DirectoryProperty = objects.dirFrom(serverPatchesDir, "rejected") + val basePatchDir: DirectoryProperty = objects.dirFrom(serverPatchesDir, "base") val sourcePatchDir: DirectoryProperty = objects.dirFrom(serverPatchesDir, "sources") val resourcePatchDir: DirectoryProperty = objects.dirFrom(serverPatchesDir, "resources") - val baseFeaturePatchDir: DirectoryProperty = objects.dirFrom(serverPatchesDir, "base") val featurePatchDir: DirectoryProperty = objects.dirFrom(serverPatchesDir, "features") @Suppress("MemberVisibilityCanBePrivate") diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/UpstreamConfig.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/UpstreamConfig.kt index 08b67a279..f9d681b1d 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/UpstreamConfig.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/UpstreamConfig.kt @@ -104,9 +104,9 @@ abstract class UpstreamConfig @Inject constructor( abstract val patchesDir: DirectoryProperty val rejectsDir: DirectoryProperty = objects.dirFrom(patchesDir, "rejected") + val basePatchDir: DirectoryProperty = objects.dirFrom(patchesDir, "base") val filePatchDir: DirectoryProperty = objects.dirFrom(patchesDir, "files") val featurePatchDir: DirectoryProperty = objects.dirFrom(patchesDir, "features") - val baseFeaturePatchDir: DirectoryProperty = objects.dirFrom(patchesDir, "base") } abstract class RepoPatchSet @Inject constructor( diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/CoreTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/CoreTasks.kt index 037e9ec9a..4e29fffa3 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/CoreTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/CoreTasks.kt @@ -90,7 +90,7 @@ class CoreTasks( } val importLibraryFiles = tasks.register("importPaperLibraryFiles") { - patches.from(project.coreExt.paper.baseFeaturePatchDir, project.coreExt.paper.sourcePatchDir, project.coreExt.paper.featurePatchDir) + patches.from(project.coreExt.paper.basePatchDir, project.coreExt.paper.sourcePatchDir, project.coreExt.paper.featurePatchDir) devImports.set(project.coreExt.paper.devImports.fileExists(project)) libraryFileIndex.set(indexLibraryFiles.flatMap { it.outputFile }) libraries.from(indexLibraryFiles.map { it.libraries }) @@ -185,10 +185,10 @@ class CoreTasks( true, this, hasFork, + project.coreExt.paper.basePatchDir, project.coreExt.paper.sourcePatchDir, project.coreExt.paper.rejectsDir, project.coreExt.paper.resourcePatchDir, - project.coreExt.paper.baseFeaturePatchDir, project.coreExt.paper.featurePatchDir, project.coreExt.paper.additionalAts, extractMacheSources.flatMap { it.outputDir }, @@ -222,10 +222,10 @@ class CoreTasks( false, this, !activeFork, + cfg.basePatchDir, cfg.sourcePatchDir, cfg.rejectsDir, cfg.resourcePatchDir, - cfg.baseFeaturePatchDir, cfg.featurePatchDir, cfg.additionalAts, upstreamTasks.first.applyFeaturePatches.flatMap { it.repo }, @@ -269,11 +269,11 @@ class CoreTasks( ) // setup aggregate Minecraft & upstream -server patching tasks - val applyAllServerBaseFeaturePatches = project.tasks.register("applyAllServerBaseFeaturePatches") { + val applyAllServerBasePatches = project.tasks.register("applyAllServerBasePatches") { group = "patching" description = "Applies all Minecraft and upstream server base patches " + - "(equivalent to '${serverTasks.applyBaseFeaturePatches.name} applyServerBaseFeaturePatches')" - dependsOn(serverTasks.applyBaseFeaturePatches) + "(equivalent to '${serverTasks.applyBasePatches.name} applyServerBasePatches')" + dependsOn(serverTasks.applyBasePatches) dependsOn("applyServerBaseFeaturePatches") } val applyAllServerFilePatches = project.tasks.register("applyAllServerFilePatches") { diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt index 8f809d92d..0ee2247fc 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt @@ -25,7 +25,7 @@ package io.papermc.paperweight.core.taskcontainers import io.papermc.paperweight.core.extension.ForkConfig import io.papermc.paperweight.core.tasks.ImportLibraryFiles import io.papermc.paperweight.core.tasks.SetupForkMinecraftSources -import io.papermc.paperweight.core.tasks.patching.ApplyBaseFeaturePatches +import io.papermc.paperweight.core.tasks.patching.ApplyBasePatches import io.papermc.paperweight.core.tasks.patching.ApplyFeaturePatches import io.papermc.paperweight.core.tasks.patching.ApplyFilePatches import io.papermc.paperweight.core.tasks.patching.ApplyFilePatchesFuzzy @@ -51,10 +51,10 @@ class MinecraftPatchingTasks( paper: Boolean, private val coreTasks: CoreTasks, private val readOnly: Boolean, + private val basePatchDir: DirectoryProperty, private val sourcePatchDir: DirectoryProperty, private val rejectsDir: DirectoryProperty, private val resourcePatchDir: DirectoryProperty, - private val baseFeaturePatchDir: DirectoryProperty, private val featurePatchDir: DirectoryProperty, private val additionalAts: RegularFileProperty, private val baseSources: Provider, @@ -74,26 +74,10 @@ class MinecraftPatchingTasks( private val namePart = if (readOnly) "${configName.capitalized()}Minecraft" else if (paper) "" else "Minecraft" - private fun ApplyFilePatches.configureApplyFilePatches() { - group() - description = "Applies $configName file patches to the Minecraft sources" - dependsOn(applyBaseFeaturePatches) - - if (readOnly) { - repo.set(outputSrcFile) - } else { - repo.set(outputSrc) - } - base.set(applyBaseFeaturePatches.flatMap { it.output }) - patches.set(sourcePatchDir.fileExists(project)) - rejectsDir.set(this@MinecraftPatchingTasks.rejectsDir) - gitFilePatches.set(this@MinecraftPatchingTasks.gitFilePatches) - identifier = configName - } - - private fun ApplyBaseFeaturePatches.configureApplyBaseFeaturePatches() { + private fun ApplyBasePatches.configureApplyBasePatches() { group() description = "Applies $configName base patches to the Minecraft sources" + dependsOn(applyResourcePatches) input.set(baseSources) @@ -106,22 +90,37 @@ class MinecraftPatchingTasks( } baseRef.set("upstream/main") - patches.set(baseFeaturePatchDir.fileExists(project)) + patches.set(basePatchDir.fileExists(project)) identifier = configName } - val applyBaseFeaturePatches = tasks.register("apply${namePart}BaseFeaturePatches") { - configureApplyBaseFeaturePatches() + private fun ApplyFilePatches.configureApplyFilePatches() { + group() + description = "Applies $configName file patches to the Minecraft sources" + dependsOn(applyBasePatches) + + if (readOnly) { + repo.set(outputSrcFile) + } else { + repo.set(outputSrc) + } + base.set(applyBasePatches.flatMap { it.output }) + patches.set(sourcePatchDir.fileExists(project)) + rejectsDir.set(this@MinecraftPatchingTasks.rejectsDir) + gitFilePatches.set(this@MinecraftPatchingTasks.gitFilePatches) + identifier = configName + } + + val applyBasePatches = tasks.register("apply${namePart}BasePatches") { + configureApplyBasePatches() } val applySourcePatches = tasks.register("apply${namePart}SourcePatches") { configureApplyFilePatches() - dependsOn(applyBaseFeaturePatches) } val applySourcePatchesFuzzy = tasks.register("apply${namePart}SourcePatchesFuzzy") { configureApplyFilePatches() - dependsOn(applyBaseFeaturePatches) } val applyResourcePatches = tasks.register("apply${namePart}ResourcePatches") { @@ -136,12 +135,6 @@ class MinecraftPatchingTasks( identifier = configName } - val applyBasePatches = tasks.register("apply${namePart}BasePatches") { - group() - description = "Applies all $configName Minecraft base feature patches" - dependsOn(applyBaseFeaturePatches) - } - val applyFilePatches = tasks.register("apply${namePart}FilePatches") { group() description = "Applies all $configName Minecraft file patches" @@ -190,7 +183,7 @@ class MinecraftPatchingTasks( } val importLibFiles = tasks.register("import${configName.capitalized()}LibraryFiles") { - patches.from(config.featurePatchDir, config.sourcePatchDir, config.baseFeaturePatchDir) + patches.from(config.featurePatchDir, config.sourcePatchDir, config.basePatchDir) devImports.set(config.devImports.fileExists(project)) libraryFileIndex.set(coreTasks.indexLibraryFiles.flatMap { it.outputFile }) libraries.from(coreTasks.indexLibraryFiles.map { it.libraries }) @@ -209,15 +202,15 @@ class MinecraftPatchingTasks( ats.jstClasspath.from(project.configurations.named(MACHE_MINECRAFT_LIBRARIES_CONFIG)) } - applyBaseFeaturePatches.configure { + applyBasePatches.configure { input.set(setup.flatMap { it.outputDir }) } applySourcePatches.configure { - base.set(applyBaseFeaturePatches.flatMap { it.output }) + base.set(applyBasePatches.flatMap { it.output }) } applySourcePatchesFuzzy.configure { - base.set(applyBaseFeaturePatches.flatMap { it.output }) + base.set(applyBasePatches.flatMap { it.output }) } val name = "rebuild${namePart}SourcePatches" @@ -230,7 +223,7 @@ class MinecraftPatchingTasks( private fun setupWritable() { listOf( - applyBaseFeaturePatches, + applyBasePatches, applySourcePatches, applySourcePatchesFuzzy, applyFeaturePatches, @@ -241,13 +234,24 @@ class MinecraftPatchingTasks( } } + val rebuildResourcePatches = tasks.register(rebuildResourcePatchesName) { + group() + description = "Rebuilds $configName file patches to the Minecraft resources" + + input.set(outputResources) + base.set(baseResources) + patches.set(resourcePatchDir) + gitFilePatches.set(this@MinecraftPatchingTasks.gitFilePatches) + } + val rebuildBasePatches = tasks.register(rebuildBasePatchesName) { group() description = "Rebuilds $configName base patches to the Minecraft source" + dependsOn(rebuildResourcePatches) base.set(baseSources) // workaround to achieve the correct task execution order inputDir.set(outputSrc) - patchDir.set(baseFeaturePatchDir) + patchDir.set(basePatchDir) filterPatches.set(this@MinecraftPatchingTasks.filterPatches) } @@ -267,16 +271,6 @@ class MinecraftPatchingTasks( atFileOut.set(additionalAts.fileExists(project)) } - val rebuildResourcePatches = tasks.register(rebuildResourcePatchesName) { - group() - description = "Rebuilds $configName file patches to the Minecraft resources" - - input.set(outputResources) - base.set(baseResources) - patches.set(resourcePatchDir) - gitFilePatches.set(this@MinecraftPatchingTasks.gitFilePatches) - } - val rebuildFilePatches = tasks.register(rebuildFilePatchesName) { group() description = "Rebuilds all $configName file patches to Minecraft" diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt index 7d7cc4567..96589852a 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt @@ -22,7 +22,7 @@ package io.papermc.paperweight.core.taskcontainers -import io.papermc.paperweight.core.tasks.patching.ApplyBaseFeaturePatches +import io.papermc.paperweight.core.tasks.patching.ApplyBasePatches import io.papermc.paperweight.core.tasks.patching.ApplyFeaturePatches import io.papermc.paperweight.core.tasks.patching.ApplyFilePatches import io.papermc.paperweight.core.tasks.patching.ApplyFilePatchesFuzzy @@ -46,10 +46,10 @@ class PatchingTasks( private val patchSetName: String, private val taskGroup: String, private val readOnly: Boolean, + private val basePatchDir: DirectoryProperty, private val filePatchDir: DirectoryProperty, private val rejectsDir: DirectoryProperty, private val featurePatchDir: DirectoryProperty, - private val baseFeaturePatchDir: DirectoryProperty, private val baseDir: Provider, private val gitFilePatches: Provider, private val filterPatches: Provider, @@ -58,55 +58,51 @@ class PatchingTasks( ) { private val namePart: String = if (readOnly) "${forkName.capitalized()}${patchSetName.capitalized()}" else patchSetName.capitalized() - private fun ApplyFilePatches.configureApplyFilePatches() { + private fun ApplyBasePatches.configureApplyBasePatches() { group = taskGroup - description = "Applies $patchSetName file patches" + description = "Applies $patchSetName base patches" + input.set(baseDir) if (readOnly) { - repo.set(layout.cache.resolve(paperTaskOutput())) + output.set(layout.cache.resolve(paperTaskOutput())) } else { - repo.set(outputDir) + output.set(outputDir) } - patches.set(filePatchDir.fileExists(project)) - rejectsDir.set(this@PatchingTasks.rejectsDir) - gitFilePatches.set(this@PatchingTasks.gitFilePatches) - base.set(applyBaseFeaturePatches.flatMap { it.output }) + base.set(baseDir) + baseRef.set("base") + patches.set(basePatchDir.fileExists(project)) identifier = "$forkName $patchSetName" } - private fun ApplyBaseFeaturePatches.configureApplyBaseFeaturePatches() { + private fun ApplyFilePatches.configureApplyFilePatches() { group = taskGroup - description = "Applies $patchSetName base patches" + description = "Applies $patchSetName file patches" + dependsOn(applyBasePatches) - input.set(baseDir) if (readOnly) { - output.set(layout.cache.resolve(paperTaskOutput())) + repo.set(layout.cache.resolve(paperTaskOutput())) } else { - output.set(outputDir) + repo.set(outputDir) } - base.set(baseDir) - - baseRef.set("base") - patches.set(baseFeaturePatchDir.fileExists(project)) + patches.set(filePatchDir.fileExists(project)) + rejectsDir.set(this@PatchingTasks.rejectsDir) + gitFilePatches.set(this@PatchingTasks.gitFilePatches) + base.set(applyBasePatches.flatMap { it.output }) identifier = "$forkName $patchSetName" } - val applyBaseFeaturePatches = tasks.register("apply${namePart}BaseFeaturePatches") { - configureApplyBaseFeaturePatches() + val applyBasePatches = tasks.register("apply${namePart}BasePatches") { + configureApplyBasePatches() } val applyFilePatches = tasks.register("apply${namePart}FilePatches") { configureApplyFilePatches() - dependsOn(applyBaseFeaturePatches) - base.set(applyBaseFeaturePatches.flatMap { it.output }) } val applyFilePatchesFuzzy = tasks.register("apply${namePart}FilePatchesFuzzy") { configureApplyFilePatches() - dependsOn(applyBaseFeaturePatches) - base.set(applyBaseFeaturePatches.flatMap { it.output }) } val applyFeaturePatches = tasks.register("apply${namePart}FeaturePatches") { @@ -124,7 +120,7 @@ class PatchingTasks( val applyPatches = tasks.register("apply${namePart}Patches") { group = taskGroup description = "Applies all $patchSetName patches" - dependsOn(applyBaseFeaturePatches, applyFilePatches, applyFeaturePatches) + dependsOn(applyBasePatches, applyFilePatches, applyFeaturePatches) } val rebuildBasePatchesName = "rebuild${namePart}BasePatches" @@ -141,7 +137,7 @@ class PatchingTasks( private fun setupWritable() { listOf( - applyBaseFeaturePatches, + applyBasePatches, applyFilePatches, applyFilePatchesFuzzy, applyFeaturePatches, @@ -157,7 +153,7 @@ class PatchingTasks( base.set(baseDir) // correct task execution order -> unused inputDir.set(outputDir) - patchDir.set(baseFeaturePatchDir) + patchDir.set(basePatchDir) filterPatches.set(this@PatchingTasks.filterPatches) } val rebuildFilePatches = tasks.register(rebuildFilePatchesName) { diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/UpstreamConfigTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/UpstreamConfigTasks.kt index 4216cbd9c..428ec5bda 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/UpstreamConfigTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/UpstreamConfigTasks.kt @@ -128,10 +128,10 @@ class UpstreamConfigTasks( cfg.name, taskGroup, readOnly, + cfg.basePatchDir, cfg.filePatchDir, cfg.rejectsDir, cfg.featurePatchDir, - cfg.baseFeaturePatchDir, base, gitFilePatches, filterPatches, @@ -171,11 +171,11 @@ class UpstreamConfigTasks( } fun setupAggregateTasks(namePart: String, desc: String, descSingleFile: String = desc) { - val applyBase = target.tasks.register("apply${namePart}BaseFeaturePatches") { + val applyBase = target.tasks.register("apply${namePart}BasePatches") { group = taskGroup description = "Applies $desc base patches" patchingTasks.values.forEach { t -> - dependsOn(t.applyBaseFeaturePatches) + dependsOn(t.applyBasePatches) } } val applyFile = target.tasks.register("apply${namePart}FilePatches") { @@ -192,7 +192,7 @@ class UpstreamConfigTasks( dependsOn(t.applyFeaturePatches) } } - val rebuildBase = target.tasks.register("rebuild${namePart}BaseFeaturePatches") { + val rebuildBase = target.tasks.register("rebuild${namePart}BasePatches") { group = taskGroup description = "Rebuilds $desc base patches" patchingTasks.values.forEach { t -> diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyBaseFeaturePatches.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyBasePatches.kt similarity index 98% rename from paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyBaseFeaturePatches.kt rename to paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyBasePatches.kt index bed741c6f..2b92f6f2a 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyBaseFeaturePatches.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyBasePatches.kt @@ -41,7 +41,7 @@ import org.gradle.api.tasks.PathSensitive import org.gradle.api.tasks.PathSensitivity import org.gradle.api.tasks.TaskAction -abstract class ApplyBaseFeaturePatches : ControllableOutputTask() { +abstract class ApplyBasePatches : ControllableOutputTask() { @get:PathSensitive(PathSensitivity.NONE) @get:InputDirectory @@ -82,9 +82,9 @@ abstract class ApplyBaseFeaturePatches : ControllableOutputTask() { abstract val additionalRemoteName: Property override fun init() { - printOutput.convention(false).finalizeValueOnRead() + printOutput.convention(true).finalizeValueOnRead() additionalRemoteName.convention("old") - verbose.convention(false) + verbose.convention(true) emitRejects.convention(false) } diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/RebuildFilePatches.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/RebuildFilePatches.kt index 95e394c7b..7958c69ec 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/RebuildFilePatches.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/RebuildFilePatches.kt @@ -103,7 +103,6 @@ abstract class RebuildFilePatches : JavaLauncherTask() { val inputDir = input.convertToPath() val git = Git(inputDir) - val currentBranch = git("rev-parse", "--abbrev-ref", "HEAD").getText().trim() git("stash", "push").executeSilently(silenceErr = true) git("checkout", "file").executeSilently(silenceErr = true) @@ -145,7 +144,7 @@ abstract class RebuildFilePatches : JavaLauncherTask() { rebuildWithDiffPatch(baseDir, inputDir, patchDir) } - git("checkout", currentBranch).executeSilently(silenceErr = true) + git("switch", "-").executeSilently(silenceErr = true) if (filesWithNewAts.isNotEmpty()) { try { // we need to rebase, so that the new file commit is part of the tree again. @@ -153,7 +152,7 @@ abstract class RebuildFilePatches : JavaLauncherTask() { // and then execs for all the files that remove the papter // todo detect if sed is not present (windows) and switch out sed for something else @Language("Shell Script") - val sequenceEditor = "sed -i -e 0,/pick/{s/pick/drop/}" + val sequenceEditor = "sed -i '/File Patches/ s/^pick /drop /' \"\$1\"" // maybe begin some work? val execs = filesWithNewAts .map { "sed -i -e 's|// Paper-AT:.*||g' $it && ((git add $it && git commit --amend --no-edit) || true)" } .flatMap { listOf("--exec", it) }.toTypedArray() diff --git a/paperweight-core/src/test/kotlin/io/papermc/paperweight/FunctionalTest.kt b/paperweight-core/src/test/kotlin/io/papermc/paperweight/FunctionalTest.kt index 936982aaf..c209338fb 100644 --- a/paperweight-core/src/test/kotlin/io/papermc/paperweight/FunctionalTest.kt +++ b/paperweight-core/src/test/kotlin/io/papermc/paperweight/FunctionalTest.kt @@ -130,11 +130,11 @@ class FunctionalTest { } println("\nrebuilding feature patch\n") - val rebP3 = gradleRunner + val rebP4 = gradleRunner .withArguments("rebuildPatches", "--stacktrace", "-Dfake=true") .withDebug(debug) .build() - assertEquals(rebP3.task(":test-server:rebuildPatches")?.outcome, TaskOutcome.SUCCESS) + assertEquals(rebP4.task(":test-server:rebuildPatches")?.outcome, TaskOutcome.SUCCESS) assertEquals( testResource.resolve("fake-patches/expected/0001-Feature.patch").readText(), tempDir.resolve("fake-patches/features/0001-Feature.patch").readText() diff --git a/paperweight-core/src/test/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyFilePatchesTest.kt b/paperweight-core/src/test/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyFilePatchesTest.kt index 4d9379152..ca8622767 100644 --- a/paperweight-core/src/test/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyFilePatchesTest.kt +++ b/paperweight-core/src/test/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyFilePatchesTest.kt @@ -49,8 +49,8 @@ class ApplyFilePatchesTest : TaskTest() { setupGitRepo(input, "main") - task.input.set(input) - task.output.set(output) + task.base.set(input) + task.repo.set(output) task.patches.set(patches) task.verbose.set(true) task.identifier.set("test") diff --git a/paperweight-core/src/test/resources/functional_test/fake-patches/base/0001-Test-Base.patch b/paperweight-core/src/test/resources/functional_test/fake-patches/base/0001-Test-Base.patch new file mode 100644 index 000000000..3b837bc66 --- /dev/null +++ b/paperweight-core/src/test/resources/functional_test/fake-patches/base/0001-Test-Base.patch @@ -0,0 +1,19 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: MiniDigger | Martin +Date: Sun, 20 Oct 2024 09:24:36 +0200 +Subject: [PATCH] Test Base + + +diff --git a/Test.java b/Test.java +index 51f6794c111f68e72cc4054c01f0c8896afaf292..d047a159f76d162bc24b030a575252202e57080c 100644 +--- a/Test.java ++++ b/Test.java +@@ -7,7 +7,7 @@ public class Test { + } + + public String getTest() { +- return this.test; ++ return this.test; // WOOOO + } + + public final String getTest2() { diff --git a/paperweight-core/src/test/resources/functional_test/fake-patches/expected/0001-Test-Base.patch b/paperweight-core/src/test/resources/functional_test/fake-patches/expected/0001-Test-Base.patch new file mode 100644 index 000000000..2946588c8 --- /dev/null +++ b/paperweight-core/src/test/resources/functional_test/fake-patches/expected/0001-Test-Base.patch @@ -0,0 +1,19 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: MiniDigger | Martin +Date: Sun, 20 Oct 2024 09:24:36 +0200 +Subject: [PATCH] Test Base + + +diff --git a/Test.java b/Test.java +index 1f7aa5bc6212dec883505d3672b6a108a785dd47..bf731c042bf7f59b60378df1d61302e1769a6a77 100644 +--- a/Test.java ++++ b/Test.java +@@ -7,7 +7,7 @@ public class Test { + } + + public String getTest() { +- return this.test; ++ return this.test; // WOOOO + } + + public final String getTest2() { diff --git a/paperweight-core/src/test/resources/functional_test/fake-patches/expected/Test.java.patch b/paperweight-core/src/test/resources/functional_test/fake-patches/expected/Test.java.patch index fff1639a2..1f3b9732d 100644 --- a/paperweight-core/src/test/resources/functional_test/fake-patches/expected/Test.java.patch +++ b/paperweight-core/src/test/resources/functional_test/fake-patches/expected/Test.java.patch @@ -4,12 +4,12 @@ } public String getTest() { -- return this.test; -+ return this.test; // WOOOO +- return this.test; // WOOOO ++ return this.test; // WOOOO SOURCE } public String getTest2() { - return this.test + "2"; + return this.test + "2"; // Woo } - } + } \ No newline at end of file diff --git a/paperweight-core/src/test/resources/functional_test/fake-patches/features/0001-Feature.patch b/paperweight-core/src/test/resources/functional_test/fake-patches/features/0001-Feature.patch index 8ca08d047..7b6794edf 100644 --- a/paperweight-core/src/test/resources/functional_test/fake-patches/features/0001-Feature.patch +++ b/paperweight-core/src/test/resources/functional_test/fake-patches/features/0001-Feature.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Feature diff --git a/Test.java b/Test.java -index 418548ec7dd11e57f0838d1dae048b99a2669ea3..95266c0cad0b100367ca840d286b7a004b93f4ef 100644 +index d047a159f76d162bc24b030a575252202e57080c..95266c0cad0b100367ca840d286b7a004b93f4ef 100644 --- a/Test.java +++ b/Test.java @@ -13,4 +13,8 @@ public class Test { diff --git a/paperweight-core/src/test/resources/functional_test/fake-patches/sources/Test.java.patch b/paperweight-core/src/test/resources/functional_test/fake-patches/sources/Test.java.patch index a524a2f10..492f2f93a 100644 --- a/paperweight-core/src/test/resources/functional_test/fake-patches/sources/Test.java.patch +++ b/paperweight-core/src/test/resources/functional_test/fake-patches/sources/Test.java.patch @@ -4,8 +4,8 @@ } public String getTest() { -- return this.test; -+ return this.test; // WOOOO +- return this.test; // WOOOO ++ return this.test; // WOOOO SOURCE } public final String getTest2() { diff --git a/paperweight-core/src/test/resources/functional_test/test-server/build.gradle b/paperweight-core/src/test/resources/functional_test/test-server/build.gradle index f0f7eb8a6..78a00dfda 100644 --- a/paperweight-core/src/test/resources/functional_test/test-server/build.gradle +++ b/paperweight-core/src/test/resources/functional_test/test-server/build.gradle @@ -1,6 +1,6 @@ plugins { id "java" - id 'io.papermc.paperweight.core' + id 'io.canvasmc.weaver.core' } repositories { @@ -34,6 +34,7 @@ paperweight { minecraftManifestUrl = 'file://project/../fake_mojang/version_manifest.json' paper { + basePatchDir.set(file('../fake-patches/base')) sourcePatchDir.set(file('../fake-patches/sources')) resourcePatchDir.set(file('../fake-patches/resources')) featurePatchDir.set(file('../fake-patches/features')) diff --git a/paperweight-core/src/test/resources/functional_test/test-server/patches/base/0001-Base.patch b/paperweight-core/src/test/resources/functional_test/test-server/patches/base/0001-Base.patch new file mode 100644 index 000000000..7a10d2ec0 --- /dev/null +++ b/paperweight-core/src/test/resources/functional_test/test-server/patches/base/0001-Base.patch @@ -0,0 +1,18 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: MiniDigger | Martin +Date: Sun, 31 Mar 2024 17:01:32 +0200 +Subject: [PATCH] Base + + +diff --git a/net/minecraft/CrashReport.java b/net/minecraft/CrashReport.java +index 3e0e88afcf010d9a3d46e48bca5cbdf98fe97544..18e63dca2a315ad01fa5b80ec126f4eac71f45fd 100644 +--- a/net/minecraft/CrashReport.java ++++ b/net/minecraft/CrashReport.java +@@ -30,6 +30,7 @@ public class CrashReport { + private boolean trackingStackTrace = true; + private StackTraceElement[] uncategorizedStackTrace = new StackTraceElement[0]; + private final SystemReport systemReport = new SystemReport(); ++ private List extraInfo = List.of("", "Woooo", ""); // WOOOOO + + public CrashReport(String title, Throwable exception) { + io.papermc.paper.util.StacktraceDeobfuscator.INSTANCE.deobfuscateThrowable(exception); // Paper \ No newline at end of file From d73b4d8f2ac26987be16015d878fac6ab55c4ba2 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Mon, 21 Jul 2025 04:23:58 +0200 Subject: [PATCH 019/257] actually enable tests --- .github/workflows/test.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 223e8e446..ea795bb26 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -22,11 +22,11 @@ jobs: run: | git config --global user.email "no-reply@github.com" git config --global user.name "GitHub Actions" - ./gradlew build -x test --no-daemon --stacktrace + ./gradlew build --no-daemon --stacktrace - name: Publish Test Report uses: mikepenz/action-junit-report@v5 if: always() with: report_paths: '**/build/test-results/test/TEST-*.xml' detailed_summary: true - annotate_notice: true \ No newline at end of file + annotate_notice: true From d4f20d7d0f135f6a5156c06e4c791e3351e77d8c Mon Sep 17 00:00:00 2001 From: Toffikk Date: Mon, 21 Jul 2025 04:28:18 +0200 Subject: [PATCH 020/257] Revert "actually enable tests" This reverts commit d73b4d8f2ac26987be16015d878fac6ab55c4ba2. --- .github/workflows/test.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index ea795bb26..223e8e446 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -22,11 +22,11 @@ jobs: run: | git config --global user.email "no-reply@github.com" git config --global user.name "GitHub Actions" - ./gradlew build --no-daemon --stacktrace + ./gradlew build -x test --no-daemon --stacktrace - name: Publish Test Report uses: mikepenz/action-junit-report@v5 if: always() with: report_paths: '**/build/test-results/test/TEST-*.xml' detailed_summary: true - annotate_notice: true + annotate_notice: true \ No newline at end of file From e82de9c854de4ad8041b76bbda6feb9ac7d48666 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Mon, 21 Jul 2025 22:54:02 +0200 Subject: [PATCH 021/257] fix: tests --- .github/workflows/test.yml | 2 +- .../core/tasks/patching/ApplyBasePatches.kt | 4 ++-- .../io/papermc/paperweight/FunctionalTest.kt | 18 ++++++++++++++++-- .../tasks/patching/ApplyFilePatchesTest.kt | 14 ++++++++++++-- .../functional_test/build-data/expected.at | 1 - .../functional_test/build-data/paper.at | 1 + .../fake-patches/base/0001-Test-Base.patch | 2 +- .../fake-patches/expected/0001-Feature.patch | 6 +++--- .../fake-patches/expected/Test.java.patch | 8 ++------ .../fake-patches/features/0001-Feature.patch | 2 +- .../test-server/patches/base/0001-Base.patch | 2 +- 11 files changed, 40 insertions(+), 20 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 223e8e446..1a3d4aad7 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -22,7 +22,7 @@ jobs: run: | git config --global user.email "no-reply@github.com" git config --global user.name "GitHub Actions" - ./gradlew build -x test --no-daemon --stacktrace + ./gradlew build --no-daemon --stacktrace - name: Publish Test Report uses: mikepenz/action-junit-report@v5 if: always() diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyBasePatches.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyBasePatches.kt index 2b92f6f2a..19391c16a 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyBasePatches.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyBasePatches.kt @@ -82,9 +82,9 @@ abstract class ApplyBasePatches : ControllableOutputTask() { abstract val additionalRemoteName: Property override fun init() { - printOutput.convention(true).finalizeValueOnRead() + printOutput.convention(false).finalizeValueOnRead() additionalRemoteName.convention("old") - verbose.convention(true) + verbose.convention(false) emitRejects.convention(false) } diff --git a/paperweight-core/src/test/kotlin/io/papermc/paperweight/FunctionalTest.kt b/paperweight-core/src/test/kotlin/io/papermc/paperweight/FunctionalTest.kt index c209338fb..d08bf132a 100644 --- a/paperweight-core/src/test/kotlin/io/papermc/paperweight/FunctionalTest.kt +++ b/paperweight-core/src/test/kotlin/io/papermc/paperweight/FunctionalTest.kt @@ -91,6 +91,7 @@ class FunctionalTest { ) // add AT to source -> patch and AT file is updated + /* commented out since this also fails on normal paperweight println("adding at to source") modifyFile(tempDir.resolve("test-server/src/minecraft/java/Test.java")) { it.replace( @@ -104,14 +105,27 @@ class FunctionalTest { git("commit", "--fixup", "file").executeSilently() git("rebase", "--autosquash", "upstream/main").executeSilently() } + */ - println("\nrunning rebuildPatches again\n") + // base patch + println("\nrebuilding base patch\n") val rebP2 = gradleRunner .withArguments("rebuildPatches", "--stacktrace", "-Dfake=true") .withDebug(debug) .build() - assertEquals(rebP2.task(":test-server:rebuildPatches")?.outcome, TaskOutcome.SUCCESS) + assertEquals( + testResource.resolve("fake-patches/expected/0001-Test-Base.patch").readText(), + tempDir.resolve("fake-patches/base/0001-Test-Base.patch").readText() + ) + + println("\nrunning rebuildPatches again\n") + val rebP3 = gradleRunner + .withArguments("rebuildPatches", "--stacktrace", "-Dfake=true") + .withDebug(debug) + .build() + + assertEquals(rebP3.task(":test-server:rebuildPatches")?.outcome, TaskOutcome.SUCCESS) assertEquals( testResource.resolve("fake-patches/expected/Test.java.patch").readText(), tempDir.resolve("fake-patches/sources/Test.java.patch").readText() diff --git a/paperweight-core/src/test/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyFilePatchesTest.kt b/paperweight-core/src/test/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyFilePatchesTest.kt index ca8622767..1bf49994a 100644 --- a/paperweight-core/src/test/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyFilePatchesTest.kt +++ b/paperweight-core/src/test/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyFilePatchesTest.kt @@ -30,12 +30,17 @@ import org.gradle.kotlin.dsl.* import org.junit.jupiter.api.io.TempDir class ApplyFilePatchesTest : TaskTest() { + private lateinit var task2: ApplyBasePatches private lateinit var task: ApplyFilePatches @BeforeTest fun setup() { val project = setupProject() - task = project.tasks.register("applyPatches", ApplyFilePatches::class).get() + task2 = project.tasks.register("applyBasePatches", ApplyBasePatches::class).get() + task = project.tasks.register("applyPatches", ApplyFilePatches::class) { + dependsOn(task2) + } + .get() } @Test @@ -49,12 +54,17 @@ class ApplyFilePatchesTest : TaskTest() { setupGitRepo(input, "main") - task.base.set(input) + task2.input.set(input) + task2.output.set(output) + task2.identifier.set("test") + + task.base.set(task2.output) task.repo.set(output) task.patches.set(patches) task.verbose.set(true) task.identifier.set("test") + task2.run() task.run() val testOutput = testResource.resolve("output") diff --git a/paperweight-core/src/test/resources/functional_test/build-data/expected.at b/paperweight-core/src/test/resources/functional_test/build-data/expected.at index a4d941bfc..ec24867b7 100644 --- a/paperweight-core/src/test/resources/functional_test/build-data/expected.at +++ b/paperweight-core/src/test/resources/functional_test/build-data/expected.at @@ -2,4 +2,3 @@ # See CONTRIBUTING.md on how to add access transformers. public+f Test dum2 public-f Test getTest()Ljava/lang/String; -public-f Test getTest2()Ljava/lang/String; diff --git a/paperweight-core/src/test/resources/functional_test/build-data/paper.at b/paperweight-core/src/test/resources/functional_test/build-data/paper.at index c3f36a48d..16239ac2a 100644 --- a/paperweight-core/src/test/resources/functional_test/build-data/paper.at +++ b/paperweight-core/src/test/resources/functional_test/build-data/paper.at @@ -1,3 +1,4 @@ # This file is auto generated, any changes may be overridden! # See CONTRIBUTING.md on how to add access transformers. public net.minecraft.core.Rotations y +private net.minecraft.util.crash.CrashReport (Ljava/lang/String;Ljava/lang/Throwable;)V diff --git a/paperweight-core/src/test/resources/functional_test/fake-patches/base/0001-Test-Base.patch b/paperweight-core/src/test/resources/functional_test/fake-patches/base/0001-Test-Base.patch index 3b837bc66..2946588c8 100644 --- a/paperweight-core/src/test/resources/functional_test/fake-patches/base/0001-Test-Base.patch +++ b/paperweight-core/src/test/resources/functional_test/fake-patches/base/0001-Test-Base.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Test Base diff --git a/Test.java b/Test.java -index 51f6794c111f68e72cc4054c01f0c8896afaf292..d047a159f76d162bc24b030a575252202e57080c 100644 +index 1f7aa5bc6212dec883505d3672b6a108a785dd47..bf731c042bf7f59b60378df1d61302e1769a6a77 100644 --- a/Test.java +++ b/Test.java @@ -7,7 +7,7 @@ public class Test { diff --git a/paperweight-core/src/test/resources/functional_test/fake-patches/expected/0001-Feature.patch b/paperweight-core/src/test/resources/functional_test/fake-patches/expected/0001-Feature.patch index 7d91209a3..6f02cfb6d 100644 --- a/paperweight-core/src/test/resources/functional_test/fake-patches/expected/0001-Feature.patch +++ b/paperweight-core/src/test/resources/functional_test/fake-patches/expected/0001-Feature.patch @@ -5,12 +5,12 @@ Subject: [PATCH] Feature diff --git a/Test.java b/Test.java -index 51f6794c111f68e72cc4054c01f0c8896afaf292..d047a159f76d162bc24b030a575252202e57080c 100644 +index b5106a3b36987dfdd5141ae1cbd229e6db020cd9..412773ad543cc9a5116044dc138cc08300b2dd1b 100644 --- a/Test.java +++ b/Test.java @@ -13,4 +13,8 @@ public class Test { - public String getTest2() { - return this.test + "2"; // Woo + public final String getTest2() { + return this.test + "2"; } + + public String feature() { diff --git a/paperweight-core/src/test/resources/functional_test/fake-patches/expected/Test.java.patch b/paperweight-core/src/test/resources/functional_test/fake-patches/expected/Test.java.patch index 1f3b9732d..492f2f93a 100644 --- a/paperweight-core/src/test/resources/functional_test/fake-patches/expected/Test.java.patch +++ b/paperweight-core/src/test/resources/functional_test/fake-patches/expected/Test.java.patch @@ -1,6 +1,6 @@ --- a/Test.java +++ b/Test.java -@@ -7,10 +_,10 @@ +@@ -7,7 +_,7 @@ } public String getTest() { @@ -8,8 +8,4 @@ + return this.test; // WOOOO SOURCE } - public String getTest2() { -- return this.test + "2"; -+ return this.test + "2"; // Woo - } - } \ No newline at end of file + public final String getTest2() { diff --git a/paperweight-core/src/test/resources/functional_test/fake-patches/features/0001-Feature.patch b/paperweight-core/src/test/resources/functional_test/fake-patches/features/0001-Feature.patch index 7b6794edf..30383a12d 100644 --- a/paperweight-core/src/test/resources/functional_test/fake-patches/features/0001-Feature.patch +++ b/paperweight-core/src/test/resources/functional_test/fake-patches/features/0001-Feature.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Feature diff --git a/Test.java b/Test.java -index d047a159f76d162bc24b030a575252202e57080c..95266c0cad0b100367ca840d286b7a004b93f4ef 100644 +index b5106a3b36987dfdd5141ae1cbd229e6db020cd9..804b0637acd5e86e420dbaa11559ca9f3017281b 100644 --- a/Test.java +++ b/Test.java @@ -13,4 +13,8 @@ public class Test { diff --git a/paperweight-core/src/test/resources/functional_test/test-server/patches/base/0001-Base.patch b/paperweight-core/src/test/resources/functional_test/test-server/patches/base/0001-Base.patch index 7a10d2ec0..d94723c74 100644 --- a/paperweight-core/src/test/resources/functional_test/test-server/patches/base/0001-Base.patch +++ b/paperweight-core/src/test/resources/functional_test/test-server/patches/base/0001-Base.patch @@ -14,5 +14,5 @@ index 3e0e88afcf010d9a3d46e48bca5cbdf98fe97544..18e63dca2a315ad01fa5b80ec126f4ea private final SystemReport systemReport = new SystemReport(); + private List extraInfo = List.of("", "Woooo", ""); // WOOOOO - public CrashReport(String title, Throwable exception) { + private CrashReport(String title, Throwable exception) { io.papermc.paper.util.StacktraceDeobfuscator.INSTANCE.deobfuscateThrowable(exception); // Paper \ No newline at end of file From ccac7b09f43f0a7509faf172cc8688d0e2e0e280 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Mon, 21 Jul 2025 22:54:36 +0200 Subject: [PATCH 022/257] chore: start next development cycle --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 3e5b75493..aeac97fa1 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ group = io.canvasmc.weaver -version = 2.1.5-SNAPSHOT +version = 2.1.6-SNAPSHOT org.gradle.caching = true org.gradle.parallel = true From 05e81da6a5cb606067872adf093b1cfab5a9c9fb Mon Sep 17 00:00:00 2001 From: Toffikk Date: Tue, 22 Jul 2025 17:39:04 +0200 Subject: [PATCH 023/257] fix(tests): fix an overlook by paper --- .../resources/functional_test/build-data/paper.at | 3 ++- .../functional_test/test-server/build.gradle | 6 ++++++ .../test-server/patches/base/0001-Base.patch | 15 ++++++++------- 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/paperweight-core/src/test/resources/functional_test/build-data/paper.at b/paperweight-core/src/test/resources/functional_test/build-data/paper.at index 16239ac2a..d74e6b99f 100644 --- a/paperweight-core/src/test/resources/functional_test/build-data/paper.at +++ b/paperweight-core/src/test/resources/functional_test/build-data/paper.at @@ -1,4 +1,5 @@ # This file is auto generated, any changes may be overridden! # See CONTRIBUTING.md on how to add access transformers. public net.minecraft.core.Rotations y -private net.minecraft.util.crash.CrashReport (Ljava/lang/String;Ljava/lang/Throwable;)V +public net.minecraft.CrashReport uncategorizedStackTrace +public-f net.minecraft.CrashReport systemReport diff --git a/paperweight-core/src/test/resources/functional_test/test-server/build.gradle b/paperweight-core/src/test/resources/functional_test/test-server/build.gradle index 78a00dfda..1e7185c18 100644 --- a/paperweight-core/src/test/resources/functional_test/test-server/build.gradle +++ b/paperweight-core/src/test/resources/functional_test/test-server/build.gradle @@ -42,6 +42,12 @@ paperweight { additionalAts.set(file('../build-data/fake.at')) } } else { + paper { + basePatchDir.set(file('patches/base')) + sourcePatchDir.set(file('patches/sources')) + resourcePatchDir.set(file('patches/resources')) + featurePatchDir.set(file('patches/features')) + } minecraftVersion = '1.21.4' } } diff --git a/paperweight-core/src/test/resources/functional_test/test-server/patches/base/0001-Base.patch b/paperweight-core/src/test/resources/functional_test/test-server/patches/base/0001-Base.patch index d94723c74..67a839b3c 100644 --- a/paperweight-core/src/test/resources/functional_test/test-server/patches/base/0001-Base.patch +++ b/paperweight-core/src/test/resources/functional_test/test-server/patches/base/0001-Base.patch @@ -5,14 +5,15 @@ Subject: [PATCH] Base diff --git a/net/minecraft/CrashReport.java b/net/minecraft/CrashReport.java -index 3e0e88afcf010d9a3d46e48bca5cbdf98fe97544..18e63dca2a315ad01fa5b80ec126f4eac71f45fd 100644 +index 7efc2d1a901ea5708045fc9fddca09caf467762f..659b933e4c077cb6b36c23e9844e314ecb2f1110 100644 --- a/net/minecraft/CrashReport.java +++ b/net/minecraft/CrashReport.java -@@ -30,6 +30,7 @@ public class CrashReport { +@@ -29,7 +29,7 @@ public class CrashReport { + private Path saveFile; private boolean trackingStackTrace = true; - private StackTraceElement[] uncategorizedStackTrace = new StackTraceElement[0]; - private final SystemReport systemReport = new SystemReport(); -+ private List extraInfo = List.of("", "Woooo", ""); // WOOOOO + public StackTraceElement[] uncategorizedStackTrace = new StackTraceElement[0]; +- public SystemReport systemReport = new SystemReport(); ++ private SystemReport systemReport = new SystemReport(); // woooo at base test???? who knew - private CrashReport(String title, Throwable exception) { - io.papermc.paper.util.StacktraceDeobfuscator.INSTANCE.deobfuscateThrowable(exception); // Paper \ No newline at end of file + public CrashReport(String title, Throwable exception) { + this.title = title; From 0170a936351df308850aef59fd1d59977d60cf88 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Wed, 23 Jul 2025 22:14:08 +0200 Subject: [PATCH 024/257] feat: add more safeguards to dynamic tag updating --- .../tasks/RebuildBaseGitPatches.kt | 37 ++++++++++++++++--- 1 file changed, 31 insertions(+), 6 deletions(-) diff --git a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RebuildBaseGitPatches.kt b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RebuildBaseGitPatches.kt index 18951ffbb..97b70670b 100644 --- a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RebuildBaseGitPatches.kt +++ b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RebuildBaseGitPatches.kt @@ -53,8 +53,12 @@ abstract class RebuildBaseGitPatches : ControllableOutputTask() { @get:Inject abstract val providers: ProviderFactory + @get:Input + abstract val projectName: Property + override fun init() { printOutput.convention(true) + projectName.convention(project.rootProject.name.lowercase()) filterPatches.convention( providers.gradleProperty("paperweight.filter-patches") .map { it.toBoolean() } @@ -65,15 +69,36 @@ abstract class RebuildBaseGitPatches : ControllableOutputTask() { @TaskAction fun run() { val git = Git(inputDir.path) - val currentBranch = git("rev-parse", "--abbrev-ref", "HEAD").getText().trim() - val fileCommit = git("rev-list", "--grep=File Patches", "--max-count=1", "base..HEAD").getText().trim() - val stopCommit = git("rev-list", "--grep=Base Patches", "--max-count=1", "base..HEAD").getText().trim().let { "$it~1" } - // we update the file tag + // these have to be retrieved dynamically + val patchedBaseCommit = git( + "rev-list", + "--all-match", + "--grep=${projectName.get()} ", + "--grep= Base Patches", + "--max-count=1", + "base..HEAD" + ).getText().trim() + val fileCommit = git( + "rev-list", + "--all-match", + "--grep=${projectName.get()} ", + "--grep= File Patches", + "--max-count=1", + "base..HEAD" + ).getText().trim() + + // we update the patchedBase tag + git("checkout", "patchedBase").executeSilently(silenceErr = true) + git("reset", patchedBaseCommit, "--hard").executeSilently(silenceErr = true) + git("tag", "-f", "patchedBase").executeSilently(silenceErr = true) + git("switch", "-").executeSilently(silenceErr = true) + + // and here we update the file tag git("checkout", "file").executeSilently(silenceErr = true) git("reset", fileCommit, "--hard").executeSilently(silenceErr = true) git("tag", "-f", "file").executeSilently(silenceErr = true) - git("checkout", currentBranch).executeSilently(silenceErr = true) + git("switch", "-").executeSilently(silenceErr = true) val what = inputDir.path.name val patchFolder = patchDir.path @@ -106,7 +131,7 @@ abstract class RebuildBaseGitPatches : ControllableOutputTask() { } val base = "base" - val stop = stopCommit + val stop = "patchedBase~1" // ~1 cuz we dont want to rebuild the marker commit val commitCount = git("rev-list", "--count", "$base..$stop").getText().trim().toInt() if (commitCount <= 0) return // nothing to rebuild From 61f29fbc65df41f0675d21ee53c759503fa25a31 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Wed, 23 Jul 2025 22:40:41 +0200 Subject: [PATCH 025/257] fix: add a fallback to the old logic for tests --- .../io/papermc/paperweight/tasks/RebuildBaseGitPatches.kt | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RebuildBaseGitPatches.kt b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RebuildBaseGitPatches.kt index 97b70670b..8d8f0f49f 100644 --- a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RebuildBaseGitPatches.kt +++ b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RebuildBaseGitPatches.kt @@ -78,7 +78,9 @@ abstract class RebuildBaseGitPatches : ControllableOutputTask() { "--grep= Base Patches", "--max-count=1", "base..HEAD" - ).getText().trim() + ).getText().trim().let { + if (it.isNotEmpty()) "$it" else git("rev-list", "--grep= Base Patches", "--max-count=1", "base..HEAD").getText().trim() + } // fallback to the old logic for tests val fileCommit = git( "rev-list", "--all-match", @@ -86,7 +88,9 @@ abstract class RebuildBaseGitPatches : ControllableOutputTask() { "--grep= File Patches", "--max-count=1", "base..HEAD" - ).getText().trim() + ).getText().trim().let { + if (it.isNotEmpty()) "$it" else git("rev-list", "--grep= File Patches", "--max-count=1", "base..HEAD").getText().trim() + } // fallback to the old logic for tests // we update the patchedBase tag git("checkout", "patchedBase").executeSilently(silenceErr = true) From a7ec21c23a45891d4d65b8f89425b8becea668fa Mon Sep 17 00:00:00 2001 From: Toffikk Date: Sun, 27 Jul 2025 19:57:55 +0200 Subject: [PATCH 026/257] super secret preparations --- gradle/libs.versions.toml | 2 ++ paperweight-lib/build.gradle.kts | 1 + .../io/papermc/paperweight/tasks/ApplyAccessTransform.kt | 5 +++-- .../papermc/paperweight/tasks/RelocateClassNameConstants.kt | 6 +++--- .../io/papermc/paperweight/util/constants/constants.kt | 2 ++ 5 files changed, 11 insertions(+), 5 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index a97b5e347..22ae1f284 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -5,6 +5,7 @@ hypo = "1.2.4" serialize = "1.8.1" jst = "1.0.68+paper.2-SNAPSHOT" tinyRemapper = "0.11.1" +accessWidener = "2.1.0" [libraries] asm-core = { module = "org.ow2.asm:asm", version.ref = "asm" } @@ -40,6 +41,7 @@ serialize-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", jst = { module = "io.papermc.jst:jst-cli-bundle", version.ref = "jst" } # jst = { module = "net.neoforged.jst:jst-cli-bundle", version.ref = "jst" } tinyRemapper = { module = "net.fabricmc:tiny-remapper", version.ref = "tinyRemapper" } +accessWidener = { module = "net.fabricmc:access-widener", version.ref = "accessWidener" } # test mockk = "io.mockk:mockk:1.14.2" diff --git a/paperweight-lib/build.gradle.kts b/paperweight-lib/build.gradle.kts index 5ac5cc04d..c7d503f21 100644 --- a/paperweight-lib/build.gradle.kts +++ b/paperweight-lib/build.gradle.kts @@ -12,6 +12,7 @@ sourceSets.main { kotlinSources { properties.put("jst_version", libs.versions.jst) properties.put("tinyRemapper_version", libs.versions.tinyRemapper) + properties.put("accessWidener_version", libs.versions.accessWidener) } } } diff --git a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/ApplyAccessTransform.kt b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/ApplyAccessTransform.kt index af5b7dd78..9506223f1 100644 --- a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/ApplyAccessTransform.kt +++ b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/ApplyAccessTransform.kt @@ -23,6 +23,7 @@ package io.papermc.paperweight.tasks import io.papermc.paperweight.util.* +import io.papermc.paperweight.util.constants.* import java.nio.file.Path import javax.inject.Inject import kotlin.io.path.* @@ -132,7 +133,7 @@ abstract class ApplyAccessTransform : JavaLauncherTask() { inheritanceProvider.set( it, ClassProviderInheritanceProvider( - Opcodes.ASM9, + ASM_OPCODES_VERSION, classProvider.get(inheritanceProvider.get(it)) as ClassProvider ) ) @@ -168,7 +169,7 @@ class AccessTransformerVisitor( private val at: AccessTransformSet, private val inheritanceProvider: InheritanceProvider, writer: ClassWriter -) : ClassVisitor(Opcodes.ASM9, writer) { +) : ClassVisitor(ASM_OPCODES_VERSION, writer) { private lateinit var classTransform: AccessTransformSet.Class diff --git a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RelocateClassNameConstants.kt b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RelocateClassNameConstants.kt index 48ff178a7..a614dc423 100644 --- a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RelocateClassNameConstants.kt +++ b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RelocateClassNameConstants.kt @@ -23,6 +23,7 @@ package io.papermc.paperweight.tasks import io.papermc.paperweight.util.* +import io.papermc.paperweight.util.constants.* import java.nio.file.Path import kotlin.io.path.* import org.gradle.api.Action @@ -38,7 +39,6 @@ import org.gradle.api.tasks.TaskAction import org.gradle.kotlin.dsl.* import org.objectweb.asm.ClassVisitor import org.objectweb.asm.MethodVisitor -import org.objectweb.asm.Opcodes import org.objectweb.asm.tree.ClassNode @Suppress("LeakingThis") @@ -107,7 +107,7 @@ abstract class RelocateClassNameConstants : BaseTask() { private class ConstantRelocatingClassVisitor( parent: ClassVisitor, private val relocations: List - ) : ClassVisitor(Opcodes.ASM9, parent) { + ) : ClassVisitor(ASM_OPCODES_VERSION, parent) { override fun visitMethod( access: Int, name: String?, @@ -115,7 +115,7 @@ abstract class RelocateClassNameConstants : BaseTask() { signature: String?, exceptions: Array? ): MethodVisitor { - return object : MethodVisitor(Opcodes.ASM9, super.visitMethod(access, name, descriptor, signature, exceptions)) { + return object : MethodVisitor(ASM_OPCODES_VERSION, super.visitMethod(access, name, descriptor, signature, exceptions)) { override fun visitLdcInsn(value: Any?) { if (value is String) { var v: String = value 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 536144bb5..9563039dd 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 @@ -23,6 +23,7 @@ package io.papermc.paperweight.util.constants import org.gradle.api.Task +import org.objectweb.asm.Opcodes const val PAPERWEIGHT_EXTENSION = "paperweight" const val PAPERWEIGHT_DEBUG = "paperweight.debug" @@ -51,6 +52,7 @@ const val MACHE_CONSTANTS_CONFIG = "macheConstants" const val MACHE_MINECRAFT_LIBRARIES_CONFIG = "macheMinecraftLibraries" const val MACHE_MINECRAFT_CONFIG = "macheMinecraft" const val MAPPED_JAR_OUTGOING_CONFIG = "mappedJarOutgoing" +const val ASM_OPCODES_VERSION = Opcodes.ASM9 const val JST_CONFIG = "javaSourceTransformer" const val DEV_BUNDLE_CONFIG = "paperweightDevelopmentBundle" const val MOJANG_MAPPED_SERVER_CONFIG = "mojangMappedServer" From f7b6c51c8bc37e8623f71683595ec6f45fefc26e Mon Sep 17 00:00:00 2001 From: Toffikk Date: Mon, 28 Jul 2025 01:14:33 +0200 Subject: [PATCH 027/257] major: Allow specifying AT files for non-minecraft sources --- gradle.properties | 2 +- .../core/extension/UpstreamConfig.kt | 1 + .../core/taskcontainers/CoreTasks.kt | 3 +- .../taskcontainers/MinecraftPatchingTasks.kt | 17 ++-- .../core/taskcontainers/PatchingTasks.kt | 34 +++++++- .../taskcontainers/UpstreamConfigTasks.kt | 5 +- .../core/tasks/SetupForkSources.kt | 85 +++++++++++++++++++ .../paperweight/patcher/PaperweightPatcher.kt | 19 ++++- .../extension/PaperweightPatcherExtension.kt | 2 + .../paperweight/util/constants/constants.kt | 1 + 10 files changed, 153 insertions(+), 16 deletions(-) create mode 100644 paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupForkSources.kt diff --git a/gradle.properties b/gradle.properties index aeac97fa1..3077a0f20 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ group = io.canvasmc.weaver -version = 2.1.6-SNAPSHOT +version = 2.2.0-SNAPSHOT org.gradle.caching = true org.gradle.parallel = true diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/UpstreamConfig.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/UpstreamConfig.kt index f9d681b1d..cb153fd63 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/UpstreamConfig.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/UpstreamConfig.kt @@ -101,6 +101,7 @@ abstract class UpstreamConfig @Inject constructor( abstract val excludes: SetProperty abstract val outputDir: DirectoryProperty + abstract val additionalAts: RegularFileProperty? abstract val patchesDir: DirectoryProperty val rejectsDir: DirectoryProperty = objects.dirFrom(patchesDir, "rejected") diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/CoreTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/CoreTasks.kt index 4e29fffa3..4d0dd6178 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/CoreTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/CoreTasks.kt @@ -74,7 +74,8 @@ class CoreTasks( } val collectPaperATsFromPatches by tasks.registering(CollectATsFromPatches::class) { - patchDir.set(project.coreExt.paper.featurePatchDir.fileExists(project)) + patchDir.set(project.coreExt.paper.basePatchDir.fileExists(project)) + extraPatchDir.set(project.coreExt.paper.featurePatchDir.fileExists(project)) } val mergePaperATs by tasks.registering { diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt index 0ee2247fc..ead4d3386 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt @@ -173,11 +173,12 @@ class MinecraftPatchingTasks( } fun setupFork(config: ForkConfig) { - val collectAccessTransform = tasks.register("collect${configName.capitalized()}ATsFromPatches") { - patchDir.set(featurePatchDir.fileExists(project)) + val collectAccessTransform = tasks.register("collect${configName.capitalized()}MinecraftATsFromPatches") { + patchDir.set(basePatchDir.fileExists(project)) + extraPatchDir.set(featurePatchDir.fileExists(project)) } - val mergeCollectedAts = tasks.register("merge${configName.capitalized()}ATs") { + val mergeCollectedAts = tasks.register("merge${configName.capitalized()}MinecraftATs") { firstFile.set(additionalAts.fileExists(project)) secondFile.set(collectAccessTransform.flatMap { it.outputFile }) } @@ -189,7 +190,7 @@ class MinecraftPatchingTasks( libraries.from(coreTasks.indexLibraryFiles.map { it.libraries }) } - val setup = tasks.register("run${configName.capitalized()}Setup") { + val setup = tasks.register("run${configName.capitalized()}MinecraftSetup") { description = "Applies $configName ATs and library imports to Minecraft sources" inputDir.set(baseSources) @@ -204,13 +205,7 @@ class MinecraftPatchingTasks( applyBasePatches.configure { input.set(setup.flatMap { it.outputDir }) - } - - applySourcePatches.configure { - base.set(applyBasePatches.flatMap { it.output }) - } - applySourcePatchesFuzzy.configure { - base.set(applyBasePatches.flatMap { it.output }) + base.set(setup.flatMap { it.outputDir }) } val name = "rebuild${namePart}SourcePatches" diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt index 96589852a..d1a2e2ccc 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt @@ -22,6 +22,8 @@ package io.papermc.paperweight.core.taskcontainers +import io.papermc.paperweight.core.extension.UpstreamConfig +import io.papermc.paperweight.core.tasks.SetupForkSources import io.papermc.paperweight.core.tasks.patching.ApplyBasePatches import io.papermc.paperweight.core.tasks.patching.ApplyFeaturePatches import io.papermc.paperweight.core.tasks.patching.ApplyFilePatches @@ -30,12 +32,13 @@ import io.papermc.paperweight.core.tasks.patching.FixupFilePatches import io.papermc.paperweight.core.tasks.patching.RebuildFilePatches import io.papermc.paperweight.tasks.* import io.papermc.paperweight.util.* -import io.papermc.paperweight.util.constants.paperTaskOutput +import io.papermc.paperweight.util.constants.* import java.nio.file.Path import org.gradle.api.Project import org.gradle.api.Task import org.gradle.api.file.Directory import org.gradle.api.file.DirectoryProperty +import org.gradle.api.file.RegularFileProperty import org.gradle.api.provider.Provider import org.gradle.api.tasks.TaskContainer import org.gradle.kotlin.dsl.* @@ -50,6 +53,7 @@ class PatchingTasks( private val filePatchDir: DirectoryProperty, private val rejectsDir: DirectoryProperty, private val featurePatchDir: DirectoryProperty, + private val additionalAts: RegularFileProperty?, private val baseDir: Provider, private val gitFilePatches: Provider, private val filterPatches: Provider, @@ -135,6 +139,34 @@ class PatchingTasks( } } + fun setupUpstream(cfg: UpstreamConfig.DirectoryPatchSet) { + val collectAccessTransform = tasks.register("collect${namePart}ATsFromPatches") { + patchDir.set(basePatchDir.fileExists(project)) + extraPatchDir.set(featurePatchDir.fileExists(project)) + } + + val mergeCollectedAts = tasks.register("merge${namePart}ATs") { + additionalAts?.let { + firstFile.set(it.fileExists(project)) + } + secondFile.set(collectAccessTransform.flatMap { it.outputFile }) + } + + val setup = tasks.register("run${namePart}Setup") { + description = "Applies $forkName ATs to non-minecraft sources" + inputDir.set(baseDir) + outputDir.set(layout.cache.resolve(paperTaskOutput())) + identifier.set(namePart) + atFile.set(mergeCollectedAts.flatMap { it.outputFile }) + ats.jst.from(project.configurations.named(JST_CONFIG)) + } + + applyBasePatches.configure { + input.set(setup.flatMap { it.outputDir }) + base.set(setup.flatMap { it.outputDir }) + } + } + private fun setupWritable() { listOf( applyBasePatches, diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/UpstreamConfigTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/UpstreamConfigTasks.kt index 428ec5bda..d0c77097b 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/UpstreamConfigTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/UpstreamConfigTasks.kt @@ -122,7 +122,7 @@ class UpstreamConfigTasks( createBaseFromDirectoryInRepo(cfg) } - return PatchingTasks( + val tasks = PatchingTasks( target, forkName, cfg.name, @@ -132,11 +132,14 @@ class UpstreamConfigTasks( cfg.filePatchDir, cfg.rejectsDir, cfg.featurePatchDir, + cfg.additionalAts, base, gitFilePatches, filterPatches, cfg.outputDir.path, ) + tasks.setupUpstream(cfg) + return tasks } private fun createBaseFromRepo(cfg: UpstreamConfig.RepoPatchSet): Provider { diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupForkSources.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupForkSources.kt new file mode 100644 index 000000000..5550cba2a --- /dev/null +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupForkSources.kt @@ -0,0 +1,85 @@ +/* + * paperweight is a Gradle plugin for the PaperMC project. + * + * Copyright (c) 2023 Kyle Wood (DenWav) + * Contributors + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 only, no later versions. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + * USA + */ + +package io.papermc.paperweight.core.tasks + +import io.papermc.paperweight.core.util.ApplySourceATs +import io.papermc.paperweight.tasks.* +import io.papermc.paperweight.util.* +import kotlin.io.path.* +import org.eclipse.jgit.api.Git +import org.eclipse.jgit.lib.PersonIdent +import org.gradle.api.file.DirectoryProperty +import org.gradle.api.file.RegularFileProperty +import org.gradle.api.provider.Property +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.InputDirectory +import org.gradle.api.tasks.InputFile +import org.gradle.api.tasks.Nested +import org.gradle.api.tasks.Optional +import org.gradle.api.tasks.OutputDirectory +import org.gradle.api.tasks.TaskAction +import org.gradle.kotlin.dsl.* + +abstract class SetupForkSources : JavaLauncherTask() { + + @get:InputDirectory + abstract val inputDir: DirectoryProperty + + @get:OutputDirectory + abstract val outputDir: DirectoryProperty + + @get:Nested + val ats: ApplySourceATs = objects.newInstance() + + @get:InputFile + @get:Optional + abstract val atFile: RegularFileProperty + + @get:Input + abstract val identifier: Property + + @TaskAction + fun run() { + val out = outputDir.path.cleanDir() + inputDir.path.copyRecursivelyTo(out) + + val git = Git.open(outputDir.path.toFile()) + + if (atFile.isPresent && atFile.path.readText().isNotBlank()) { + println("Applying access transformers...") + ats.run( + launcher.get(), + inputDir.path, + outputDir.path, + atFile.path, + temporaryDir.toPath(), + ) + commitAndTag(git, "ATs", "${identifier.get()} ATs") + } + val ident = PersonIdent("base", "noreply+automated@papermc.io") + git.tagDelete().setTags("base").call() + git.tag().setName("base").setTagger(ident).setSigned(false).call() + + git.close() + } +} diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/PaperweightPatcher.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/PaperweightPatcher.kt index cbfc38877..330f6ea3b 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/PaperweightPatcher.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/PaperweightPatcher.kt @@ -47,7 +47,24 @@ abstract class PaperweightPatcher : Plugin { delete(target.layout.cache) } - target.afterEvaluate { afterEvaluate(patcher) } + target.configurations.register(JST_CONFIG) { + defaultDependencies { + // add(project.dependencies.create("net.neoforged.jst:jst-cli-bundle:${LibraryVersions.JST}")) + add(target.dependencies.create("io.papermc.jst:jst-cli-bundle:${LibraryVersions.JST}")) + } + } + + target.afterEvaluate { + repositories { + maven(patcher.jstRepo) { + name = JST_REPO_NAME + content { + onlyForConfigurations(JST_CONFIG) + } + } + } + afterEvaluate(patcher) + } } private fun Project.afterEvaluate(patcher: PaperweightPatcherExtension) { diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/extension/PaperweightPatcherExtension.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/extension/PaperweightPatcherExtension.kt index 18fbc9591..a969f4e7a 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/extension/PaperweightPatcherExtension.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/extension/PaperweightPatcherExtension.kt @@ -23,6 +23,7 @@ package io.papermc.paperweight.patcher.extension import io.papermc.paperweight.core.extension.UpstreamConfig +import io.papermc.paperweight.util.constants.* import javax.inject.Inject import org.gradle.api.Action import org.gradle.api.NamedDomainObjectContainer @@ -36,6 +37,7 @@ abstract class PaperweightPatcherExtension @Inject constructor(private val objec val gitFilePatches: Property = objects.property().convention(false) val filterPatches: Property = objects.property().convention(true) + val jstRepo: Property = objects.property().convention(PAPER_MAVEN_REPO_URL) val upstreams: NamedDomainObjectContainer = objects.domainObjectContainer(UpstreamConfig::class) { objects.newInstance(it, true) 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 9563039dd..bec0bdf23 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 @@ -64,6 +64,7 @@ const val DECOMPILER_REPO_NAME = "paperweightDecompilerRepository" const val REMAPPER_REPO_NAME = "paperweightRemapperRepository" const val PLUGIN_REMAPPER_REPO_NAME = "paperweightPluginRemapperRepository" const val MACHE_REPO_NAME = "paperweightMacheRepository" +const val JST_REPO_NAME = "paperweightJstRepository" const val CACHE_PATH = "caches" private const val PAPER_PATH = "paperweight" From f2242a33fe34abb7f255ecd7cea60639ea5647ff Mon Sep 17 00:00:00 2001 From: Dueris Date: Sun, 27 Jul 2025 19:19:45 -0700 Subject: [PATCH 028/257] Fix publishing --- .github/workflows/deploy.yml | 2 +- build.gradle.kts | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 507944256..254295df2 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -20,7 +20,7 @@ jobs: java-version: 21 distribution: 'temurin' - name: Deploy Version - run: ./gradlew publishAllPublicationsToCentralRepository + run: ./gradlew publishAllPublicationsToCanvasRepository env: PUBLISH_USER: ${{ secrets.PUBLISH_USER }} PUBLISH_TOKEN: ${{ secrets.PUBLISH_TOKEN }} diff --git a/build.gradle.kts b/build.gradle.kts index 9c5829e34..020b6ce2f 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -7,3 +7,8 @@ tasks.register("printVersion") { tasks.wrapper { distributionType = Wrapper.DistributionType.ALL } + +tasks.register("publishAllPublicationsToCanvasRepository") { + dependsOn(":paperweight-core:publishAllPublicationsToCentralRepository") + dependsOn(":paperweight-userdev:publishAllPublicationsToCentralRepository") +} From aaee7eec782505350db0cf9f4c78660ba6f96aa6 Mon Sep 17 00:00:00 2001 From: Dueris Date: Sun, 27 Jul 2025 23:54:44 -0700 Subject: [PATCH 029/257] Use new maven repository --- .github/workflows/deploy.yml | 2 +- build.gradle.kts | 5 ----- buildSrc/src/main/kotlin/config-publish.gradle.kts | 5 ++--- 3 files changed, 3 insertions(+), 9 deletions(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 254295df2..454c66648 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -20,7 +20,7 @@ jobs: java-version: 21 distribution: 'temurin' - name: Deploy Version - run: ./gradlew publishAllPublicationsToCanvasRepository + run: ./gradlew publishAllPublicationsToCanvasmcRepository env: PUBLISH_USER: ${{ secrets.PUBLISH_USER }} PUBLISH_TOKEN: ${{ secrets.PUBLISH_TOKEN }} diff --git a/build.gradle.kts b/build.gradle.kts index 020b6ce2f..9c5829e34 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -7,8 +7,3 @@ tasks.register("printVersion") { tasks.wrapper { distributionType = Wrapper.DistributionType.ALL } - -tasks.register("publishAllPublicationsToCanvasRepository") { - dependsOn(":paperweight-core:publishAllPublicationsToCentralRepository") - dependsOn(":paperweight-userdev:publishAllPublicationsToCentralRepository") -} diff --git a/buildSrc/src/main/kotlin/config-publish.gradle.kts b/buildSrc/src/main/kotlin/config-publish.gradle.kts index 9dffc7ec3..3086eb42f 100644 --- a/buildSrc/src/main/kotlin/config-publish.gradle.kts +++ b/buildSrc/src/main/kotlin/config-publish.gradle.kts @@ -87,9 +87,8 @@ val shadowJar by tasks.existing(ShadowJar::class) { publishing { repositories { - maven { - name = "central" - url = uri("https://central.sonatype.com/repository/maven-snapshots/") + maven("https://maven.canvasmc.io/snapshots") { + name = "canvasmc" credentials { username=System.getenv("PUBLISH_USER") password=System.getenv("PUBLISH_TOKEN") From dbc0ff288c38e7a20cdaa514df6a72608f8ac066 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Mon, 28 Jul 2025 19:51:18 +0200 Subject: [PATCH 030/257] feat: allow ats for library classes --- .../core/taskcontainers/PatchingTasks.kt | 7 ++---- .../taskcontainers/UpstreamConfigTasks.kt | 2 +- .../core/tasks/SetupForkMinecraftSources.kt | 24 +++++++++++++++---- .../core/tasks/SetupMinecraftSources.kt | 17 +++++++++++-- 4 files changed, 38 insertions(+), 12 deletions(-) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt index d1a2e2ccc..42ca9f2ae 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt @@ -22,7 +22,6 @@ package io.papermc.paperweight.core.taskcontainers -import io.papermc.paperweight.core.extension.UpstreamConfig import io.papermc.paperweight.core.tasks.SetupForkSources import io.papermc.paperweight.core.tasks.patching.ApplyBasePatches import io.papermc.paperweight.core.tasks.patching.ApplyFeaturePatches @@ -139,16 +138,14 @@ class PatchingTasks( } } - fun setupUpstream(cfg: UpstreamConfig.DirectoryPatchSet) { + fun setupUpstream() { val collectAccessTransform = tasks.register("collect${namePart}ATsFromPatches") { patchDir.set(basePatchDir.fileExists(project)) extraPatchDir.set(featurePatchDir.fileExists(project)) } val mergeCollectedAts = tasks.register("merge${namePart}ATs") { - additionalAts?.let { - firstFile.set(it.fileExists(project)) - } + additionalAts?.let { firstFile.set(it.fileExists(project)) } secondFile.set(collectAccessTransform.flatMap { it.outputFile }) } diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/UpstreamConfigTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/UpstreamConfigTasks.kt index d0c77097b..135fb008e 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/UpstreamConfigTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/UpstreamConfigTasks.kt @@ -138,7 +138,7 @@ class UpstreamConfigTasks( filterPatches, cfg.outputDir.path, ) - tasks.setupUpstream(cfg) + tasks.setupUpstream() return tasks } diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupForkMinecraftSources.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupForkMinecraftSources.kt index 2b48408ea..0b4564f41 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupForkMinecraftSources.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupForkMinecraftSources.kt @@ -68,7 +68,25 @@ abstract class SetupForkMinecraftSources : JavaLauncherTask() { val git = Git.open(outputDir.path.toFile()) - if (atFile.isPresent && atFile.path.readText().isNotBlank()) { + if (atFile.isPresent && atFile.path.readText().isNotBlank() && libraryImports.isPresent) { + println("Applying access transformers...") + + libraryImports.path.walk().forEach { + val outFile = inputDir.path.resolve(it.relativeTo(libraryImports.path).invariantSeparatorsPathString) + // The file may already exist if upstream imported it + if (!outFile.exists()) { + it.copyTo(outFile.createParentDirectories()) + } + } + ats.run( + launcher.get(), + inputDir.path, + outputDir.path, + atFile.path, + temporaryDir.toPath(), + ) + commitAndTag(git, "ATs", "${identifier.get()} ATs and Imports") + } else if (atFile.isPresent && atFile.path.readText().isNotBlank()) { println("Applying access transformers...") ats.run( launcher.get(), @@ -78,9 +96,7 @@ abstract class SetupForkMinecraftSources : JavaLauncherTask() { temporaryDir.toPath(), ) commitAndTag(git, "ATs", "${identifier.get()} ATs") - } - - if (libraryImports.isPresent) { + } else if (libraryImports.isPresent) { libraryImports.path.walk().forEach { val outFile = out.resolve(it.relativeTo(libraryImports.path).invariantSeparatorsPathString) // The file may already exist if upstream imported it diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupMinecraftSources.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupMinecraftSources.kt index 6f3e2c826..c1615c54d 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupMinecraftSources.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupMinecraftSources.kt @@ -166,7 +166,20 @@ abstract class SetupMinecraftSources : JavaLauncherZippedTask() { logger.lifecycle("Applied ${result.summary.changedFiles} mache patches") } - if (atFile.isPresent) { + if (atFile.isPresent && libraryImports.isPresent) { + println("Applying access transformers...") + libraryImports.path.copyRecursivelyTo(outputPath) + ats.run( + launcher.get(), + outputPath, + outputPath, + atFile.path, + temporaryDir.toPath(), + ) + if (!oldPaperCommit.isPresent) { + commitAndTag(git, "ATs", "paper ATs and Imports") + } + } else if (atFile.isPresent) { println("Applying access transformers...") ats.run( launcher.get(), @@ -184,7 +197,7 @@ abstract class SetupMinecraftSources : JavaLauncherZippedTask() { commitAndTag(git, "Vanilla", "Vanilla, Mache, & paper ATs (Squashed for better Git history during updates)") } - if (libraryImports.isPresent) { + if (libraryImports.isPresent && !atFile.isPresent) { libraryImports.path.copyRecursivelyTo(outputPath) commitAndTag(git, "Imports", "paper Imports") From d07e897b1fc78626614109096c957145027c79ac Mon Sep 17 00:00:00 2001 From: Toffikk Date: Mon, 28 Jul 2025 19:51:44 +0200 Subject: [PATCH 031/257] chore: bump snapshot --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 3077a0f20..3397b459d 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ group = io.canvasmc.weaver -version = 2.2.0-SNAPSHOT +version = 2.2.1-SNAPSHOT org.gradle.caching = true org.gradle.parallel = true From 824330a44c171ace79d490570394596ce08d5d05 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Tue, 29 Jul 2025 01:17:47 +0200 Subject: [PATCH 032/257] chore: remove some dead code --- gradle.properties | 1 + .../papermc/paperweight/core/PaperweightCore.kt | 2 +- .../taskcontainers/MinecraftPatchingTasks.kt | 16 +--------------- .../core/taskcontainers/PatchingTasks.kt | 8 ++------ .../core/tasks/patching/ApplyBasePatches.kt | 4 ---- .../core/tasks/patching/ApplyFilePatches.kt | 17 +---------------- .../extension/PaperweightPatcherExtension.kt | 2 +- 7 files changed, 7 insertions(+), 43 deletions(-) diff --git a/gradle.properties b/gradle.properties index 3397b459d..bd4b16ddb 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,6 @@ group = io.canvasmc.weaver version = 2.2.1-SNAPSHOT +org.gradle.configuration-cache = true org.gradle.caching = true org.gradle.parallel = true diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/PaperweightCore.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/PaperweightCore.kt index b7dca7cb4..1f39f356d 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/PaperweightCore.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/PaperweightCore.kt @@ -196,7 +196,7 @@ abstract class PaperweightCore : Plugin { ) if (coreExt.updatingMinecraft.oldPaperCommit.isPresent) { - tasks.paperPatchingTasks.applySourcePatches.configure { + tasks.paperPatchingTasks.applyBasePatches.configure { additionalRemote = layout.cache.resolve( "$OLD_PAPER_PATH/${coreExt.updatingMinecraft.oldPaperCommit.get()}/paper-server/src/minecraft/java" ).absolutePathString() diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt index ead4d3386..4db4807ae 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt @@ -80,16 +80,11 @@ class MinecraftPatchingTasks( dependsOn(applyResourcePatches) input.set(baseSources) - - base.set(baseSources) - if (readOnly) { output.set(outputSrcFile) } else { output.set(outputSrc) } - - baseRef.set("upstream/main") patches.set(basePatchDir.fileExists(project)) identifier = configName } @@ -100,11 +95,11 @@ class MinecraftPatchingTasks( dependsOn(applyBasePatches) if (readOnly) { + base.set(applyBasePatches.flatMap { it.output }) repo.set(outputSrcFile) } else { repo.set(outputSrc) } - base.set(applyBasePatches.flatMap { it.output }) patches.set(sourcePatchDir.fileExists(project)) rejectsDir.set(this@MinecraftPatchingTasks.rejectsDir) gitFilePatches.set(this@MinecraftPatchingTasks.gitFilePatches) @@ -205,14 +200,6 @@ class MinecraftPatchingTasks( applyBasePatches.configure { input.set(setup.flatMap { it.outputDir }) - base.set(setup.flatMap { it.outputDir }) - } - - val name = "rebuild${namePart}SourcePatches" - if (name in tasks.names) { - tasks.named(name) { - dependsOn(rebuildBasePatchesName) - } } } @@ -255,7 +242,6 @@ class MinecraftPatchingTasks( description = "Rebuilds $configName file patches to the Minecraft sources" dependsOn(rebuildBasePatches) - // base.set(baseSources) maybe look into this for ats in the future but rn we use commit based repo states not dir input.set(outputSrc) patches.set(sourcePatchDir) gitFilePatches.set(this@MinecraftPatchingTasks.gitFilePatches) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt index 42ca9f2ae..31f249a89 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt @@ -71,10 +71,8 @@ class PatchingTasks( } else { output.set(outputDir) } - - base.set(baseDir) - baseRef.set("base") patches.set(basePatchDir.fileExists(project)) + baseRef.set("base") identifier = "$forkName $patchSetName" } @@ -84,15 +82,14 @@ class PatchingTasks( dependsOn(applyBasePatches) if (readOnly) { + base.set(applyBasePatches.flatMap { it.output }) repo.set(layout.cache.resolve(paperTaskOutput())) } else { repo.set(outputDir) } - patches.set(filePatchDir.fileExists(project)) rejectsDir.set(this@PatchingTasks.rejectsDir) gitFilePatches.set(this@PatchingTasks.gitFilePatches) - base.set(applyBasePatches.flatMap { it.output }) identifier = "$forkName $patchSetName" } @@ -160,7 +157,6 @@ class PatchingTasks( applyBasePatches.configure { input.set(setup.flatMap { it.outputDir }) - base.set(setup.flatMap { it.outputDir }) } } diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyBasePatches.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyBasePatches.kt index 19391c16a..225530fe5 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyBasePatches.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyBasePatches.kt @@ -50,10 +50,6 @@ abstract class ApplyBasePatches : ControllableOutputTask() { @get:OutputDirectory abstract val output: DirectoryProperty - @get:InputDirectory - @get:Optional - abstract val base: DirectoryProperty - @get:Optional @get:Input abstract val baseRef: Property diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyFilePatches.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyFilePatches.kt index 9724612fc..061222dfe 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyFilePatches.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyFilePatches.kt @@ -69,22 +69,10 @@ abstract class ApplyFilePatches : BaseTask() { @get:Optional abstract val base: DirectoryProperty - @get:Optional - @get:Input - abstract val baseRef: Property - @get:Input @get:Optional abstract val identifier: Property - // An additional remote to add and fetch from before applying patches (to bring in objects for 3-way merge). - @get:Input - @get:Optional - abstract val additionalRemote: Property - - @get:Input - abstract val additionalRemoteName: Property - @get:Input abstract val moveFailedGitPatchesToRejects: Property @@ -95,7 +83,6 @@ abstract class ApplyFilePatches : BaseTask() { run { verbose.convention(false) gitFilePatches.convention(false) - additionalRemoteName.convention("old") moveFailedGitPatchesToRejects.convention(false) emitRejects.convention(true) } @@ -125,9 +112,7 @@ abstract class ApplyFilePatches : BaseTask() { if (git("checkout", "main").runSilently(silenceErr = true) != 0) { git("checkout", "-b", "main").runSilently(silenceErr = true) } - if (git("reset", "--hard", "patchedBase").runSilently(silenceErr = true) != 0) { - git("reset", "--hard", "upstream/main").runSilently(silenceErr = true) // resources special handling - } + git("reset", "--hard", "patchedBase").runSilently(silenceErr = true) git("gc").runSilently(silenceErr = true) val result = if (!patches.isPresent) { diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/extension/PaperweightPatcherExtension.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/extension/PaperweightPatcherExtension.kt index a969f4e7a..ed530519d 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/extension/PaperweightPatcherExtension.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/extension/PaperweightPatcherExtension.kt @@ -23,7 +23,7 @@ package io.papermc.paperweight.patcher.extension import io.papermc.paperweight.core.extension.UpstreamConfig -import io.papermc.paperweight.util.constants.* +import io.papermc.paperweight.util.constants.PAPER_MAVEN_REPO_URL import javax.inject.Inject import org.gradle.api.Action import org.gradle.api.NamedDomainObjectContainer From f196a5edfd957137c33e1fb74c980a0f1f4a9d64 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Tue, 29 Jul 2025 01:20:49 +0200 Subject: [PATCH 033/257] missed part --- .../main/kotlin/io/papermc/paperweight/core/PaperweightCore.kt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/PaperweightCore.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/PaperweightCore.kt index 1f39f356d..159f5f8b2 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/PaperweightCore.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/PaperweightCore.kt @@ -202,6 +202,9 @@ abstract class PaperweightCore : Plugin { ).absolutePathString() emitRejects = false } + tasks.paperPatchingTasks.applySourcePatches.configure { + emitRejects = false + } PatchRouletteTasks( target, From a14a19764f29a710fd741f1ca0111345dea4e0e8 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Thu, 31 Jul 2025 01:43:41 +0200 Subject: [PATCH 034/257] fix: set the filtering property for file patches properly --- .../kotlin/io/papermc/paperweight/core/PaperweightCore.kt | 3 +-- .../core/taskcontainers/MinecraftPatchingTasks.kt | 8 ++++++++ .../paperweight/core/taskcontainers/PatchingTasks.kt | 8 ++++++++ 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/PaperweightCore.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/PaperweightCore.kt index 159f5f8b2..1ceb599dd 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/PaperweightCore.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/PaperweightCore.kt @@ -200,11 +200,10 @@ abstract class PaperweightCore : Plugin { additionalRemote = layout.cache.resolve( "$OLD_PAPER_PATH/${coreExt.updatingMinecraft.oldPaperCommit.get()}/paper-server/src/minecraft/java" ).absolutePathString() - emitRejects = false } tasks.paperPatchingTasks.applySourcePatches.configure { emitRejects = false - } + } // if we ever make features/base emit rejects by default, we should update this. PatchRouletteTasks( target, diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt index 4db4807ae..88917e70e 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt @@ -201,6 +201,13 @@ class MinecraftPatchingTasks( applyBasePatches.configure { input.set(setup.flatMap { it.outputDir }) } + /* uncomment this when we enable validation of ATs since then seperate AT files will be forced, right now this breaks on projects with one AT file for many modules + val name = "rebuild${namePart}BasePatches" + if (name in tasks.names) { + tasks.named(name) { + base.set(setup.flatMap { it.outputDir }) + } + */ } private fun setupWritable() { @@ -245,6 +252,7 @@ class MinecraftPatchingTasks( input.set(outputSrc) patches.set(sourcePatchDir) gitFilePatches.set(this@MinecraftPatchingTasks.gitFilePatches) + filterPatches.set(this@MinecraftPatchingTasks.filterPatches) ats.jstClasspath.from(project.configurations.named(MACHE_MINECRAFT_CONFIG)) ats.jst.from(project.configurations.named(JST_CONFIG)) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt index 31f249a89..84ecc8928 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt @@ -158,6 +158,13 @@ class PatchingTasks( applyBasePatches.configure { input.set(setup.flatMap { it.outputDir }) } + /* uncomment this when we enable validation of ATs since then seperate AT files will be forced, right now this breaks on projects with one AT file for many modules + val name = "rebuild${namePart}BasePatches" + if (name in tasks.names) { + tasks.named(name) { + base.set(setup.flatMap { it.outputDir }) + } + */ } private fun setupWritable() { @@ -189,6 +196,7 @@ class PatchingTasks( input.set(outputDir) patches.set(filePatchDir) gitFilePatches.set(this@PatchingTasks.gitFilePatches) + filterPatches.set(this@PatchingTasks.filterPatches) } val fixupFilePatches = tasks.register(fixupFilePatchesName) { From 029da7df0f6e462ac852cadb829418e99ba08642 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Thu, 31 Jul 2025 02:37:34 +0200 Subject: [PATCH 035/257] chore: continue development cycle --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index bd4b16ddb..4e2c60421 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ group = io.canvasmc.weaver -version = 2.2.1-SNAPSHOT +version = 2.2.2-SNAPSHOT org.gradle.configuration-cache = true org.gradle.caching = true From 974578947df061843f350005488659e123d4f0af Mon Sep 17 00:00:00 2001 From: Jason Penilla <11360596+jpenilla@users.noreply.github.com> Date: Thu, 31 Jul 2025 11:28:27 -0700 Subject: [PATCH 036/257] Build with Gradle 9 --- .gitattributes | 11 ++++++----- .gitignore | 2 +- .../src/main/kotlin/config-kotlin.gradle.kts | 11 ++++------- buildSrc/src/main/kotlin/utils.kt | 2 +- gradle.properties | 1 + gradle/libs.versions.toml | 2 +- gradle/wrapper/gradle-wrapper.jar | Bin 43764 -> 45457 bytes gradle/wrapper/gradle-wrapper.properties | 2 +- gradlew | 2 +- .../core/taskcontainers/DevBundleTasks.kt | 4 ++-- .../paperweight/core/util/project-util.kt | 2 +- .../io/papermc/paperweight/FunctionalTest.kt | 4 ++-- .../kotlin/io/papermc/paperweight/util.kt | 2 +- .../io/papermc/paperweight/util/file.kt | 13 ------------- .../io/papermc/paperweight/util/utils.kt | 4 ++-- .../PaperweightUserDependenciesExtension.kt | 2 +- 16 files changed, 25 insertions(+), 39 deletions(-) diff --git a/.gitattributes b/.gitattributes index 2fb638f44..9063c9d26 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,7 +1,8 @@ -* text=auto +# Documentation: https://www.git-scm.com/docs/gitattributes -*.sh text eol=lf -gradlew text eol=lf -*.bat text eol=crlf +# Set default behavior to automatically normalize line endings. +* text=auto eol=lf -*.jar binary +# Enforce CRLF line endings in Windows batch scripts to avoid issues. +*.cmd text eol=crlf +*.bat text eol=crlf diff --git a/.gitignore b/.gitignore index f5944d184..cb55c83ab 100644 --- a/.gitignore +++ b/.gitignore @@ -44,4 +44,4 @@ ehthumbs_vista.db # Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) !gradle-wrapper.ja -/.kotlin/ +.kotlin/ diff --git a/buildSrc/src/main/kotlin/config-kotlin.gradle.kts b/buildSrc/src/main/kotlin/config-kotlin.gradle.kts index a2fd84903..f8d461c52 100644 --- a/buildSrc/src/main/kotlin/config-kotlin.gradle.kts +++ b/buildSrc/src/main/kotlin/config-kotlin.gradle.kts @@ -23,16 +23,11 @@ kotlin { } compilerOptions { jvmTarget = JvmTarget.JVM_17 - freeCompilerArgs = listOf("-Xjvm-default=all", "-Xjdk-release=17", "-opt-in=kotlin.io.path.ExperimentalPathApi") + freeCompilerArgs = listOf("-Xjvm-default=all", "-Xjdk-release=17") } } repositories { - maven("https://repo.papermc.io/repository/maven-snapshots/") { - mavenContent { - includeModule("org.cadixdev", "mercury") - } - } maven("https://repo.papermc.io/repository/maven-public/") { mavenContent { includeGroup("codechicken") @@ -54,7 +49,9 @@ repositories { includeGroupAndSubgroups("net.fabricmc") } } - mavenCentral() + mavenCentral { + mavenContent { releasesOnly() } + } gradlePluginPortal() } diff --git a/buildSrc/src/main/kotlin/utils.kt b/buildSrc/src/main/kotlin/utils.kt index d2ec8843a..1c8f11ec2 100644 --- a/buildSrc/src/main/kotlin/utils.kt +++ b/buildSrc/src/main/kotlin/utils.kt @@ -10,7 +10,7 @@ import org.gradle.plugin.devel.PluginDeclaration fun Configuration.compatibilityAttributes(objects: ObjectFactory) { attributes { attribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, 17) - attribute(GradlePluginApiVersion.GRADLE_PLUGIN_API_VERSION_ATTRIBUTE, objects.named("8.14")) + attribute(GradlePluginApiVersion.GRADLE_PLUGIN_API_VERSION_ATTRIBUTE, objects.named("9.0.0")) } } diff --git a/gradle.properties b/gradle.properties index 8015950f8..8b2f05e41 100644 --- a/gradle.properties +++ b/gradle.properties @@ -2,4 +2,5 @@ group = io.papermc.paperweight version = 2.0.0-SNAPSHOT org.gradle.caching = true +org.gradle.configuration-cache = true org.gradle.parallel = true diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 906b89818..1a1cfea5d 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -48,7 +48,7 @@ mockk = "io.mockk:mockk:1.14.2" gradle-licenser = "net.kyori:indra-licenser-spotless:3.2.0" gradle-spotless = "com.diffplug.spotless:spotless-plugin-gradle:7.2.1" gradle-shadow = "com.gradleup.shadow:com.gradleup.shadow.gradle.plugin:8.3.8" -gradle-kotlin-dsl = "org.gradle.kotlin.kotlin-dsl:org.gradle.kotlin.kotlin-dsl.gradle.plugin:5.2.0" +gradle-kotlin-dsl = "org.gradle.kotlin.kotlin-dsl:org.gradle.kotlin.kotlin-dsl.gradle.plugin:6.2.0" gradle-plugin-kotlin = { module = "org.jetbrains.kotlin.jvm:org.jetbrains.kotlin.jvm.gradle.plugin" } gradle-plugin-publish = "com.gradle.publish:plugin-publish-plugin:1.3.1" diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 1b33c55baabb587c669f562ae36f953de2481846..8bdaf60c75ab801e22807dde59e12a8735a34077 100644 GIT binary patch delta 37256 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 delta 35551 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 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 6514f919f..48b43d350 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.14-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.0.0-all.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew b/gradlew index 23d15a936..ef07e0162 100755 --- a/gradlew +++ b/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/DevBundleTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/DevBundleTasks.kt index 6f15acd0c..c763143f3 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/DevBundleTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/DevBundleTasks.kt @@ -69,9 +69,9 @@ class DevBundleTasks( minecraftVersion.set(project.coreExt.minecraftVersion) mojangMappedPaperclipFile.set(paperclipForDevBundle.flatMap { it.outputZip }) reobfMappingsFile.set( - project.coreExt.spigot.enabled.flatMap { + project.coreExt.spigot.enabled.flatMap { if (it) { - coreTasks.generateRelocatedReobfMappings.flatMap { it.outputMappings } + coreTasks.generateRelocatedReobfMappings.flatMap { t -> t.outputMappings } } else { providers.provider { null } } diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/util/project-util.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/util/project-util.kt index fa7e2f606..d4b45007d 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/util/project-util.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/util/project-util.kt @@ -36,7 +36,7 @@ import org.gradle.kotlin.dsl.* fun Project.createBuildTasks( spigot: SpigotExtension, - packagesToFix: Provider?>, + packagesToFix: Provider>, relocatedReobfMappings: Provider ): ServerArtifacts { val jar = tasks.named("jar", AbstractArchiveTask::class).flatMap { it.archiveFile } diff --git a/paperweight-core/src/test/kotlin/io/papermc/paperweight/FunctionalTest.kt b/paperweight-core/src/test/kotlin/io/papermc/paperweight/FunctionalTest.kt index 936982aaf..a7ba0edc3 100644 --- a/paperweight-core/src/test/kotlin/io/papermc/paperweight/FunctionalTest.kt +++ b/paperweight-core/src/test/kotlin/io/papermc/paperweight/FunctionalTest.kt @@ -55,7 +55,7 @@ class FunctionalTest { val text = settings.readText() settings.writeText(text.replace("// includeBuild '..'", "includeBuild '..'").replace("functional_test", "test")) - projectDir.resolve("patches").deleteRecursively() + projectDir.resolve("patches").deleteRecursive() } @Test @@ -146,7 +146,7 @@ class FunctionalTest { testResource.resolve("lib-patches/dev-imports.txt").copyTo(tempDir.resolve("build-data/dev-imports.txt")) testResource.resolve("lib-patches/0002-Remove-rotten-apples.patch") .copyTo(tempDir.resolve("fake-patches/features/0002-Remove-rotten-apples.patch")) - testResource.resolve("lib-patches/org").copyToRecursively(tempDir.resolve("fake-patches/sources/org"), followLinks = false) + testResource.resolve("lib-patches/org").copyRecursivelyTo(tempDir.resolve("fake-patches/sources/org")) modifyFile(tempDir.resolve("test-server/build.gradle")) { it.replace( "implementation project(\":test-api\")", diff --git a/paperweight-core/src/test/kotlin/io/papermc/paperweight/util.kt b/paperweight-core/src/test/kotlin/io/papermc/paperweight/util.kt index 0e5aa0e82..199a1c153 100644 --- a/paperweight-core/src/test/kotlin/io/papermc/paperweight/util.kt +++ b/paperweight-core/src/test/kotlin/io/papermc/paperweight/util.kt @@ -30,7 +30,7 @@ import org.gradle.testkit.runner.GradleRunner fun Path.copyProject(resourcesProjectName: String): ProjectFiles { Paths.get("src/test/resources/$resourcesProjectName") - .copyToRecursively(this, followLinks = false) + .copyRecursivelyTo(this) Git(this)("init").executeSilently() return ProjectFiles(this) } diff --git a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/file.kt b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/file.kt index ba17088b3..1f905e123 100644 --- a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/file.kt +++ b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/file.kt @@ -25,7 +25,6 @@ package io.papermc.paperweight.util import java.io.InputStream import java.io.OutputStream import java.net.URI -import java.nio.file.FileAlreadyExistsException import java.nio.file.FileSystem import java.nio.file.FileSystemNotFoundException import java.nio.file.FileSystems @@ -33,7 +32,6 @@ import java.nio.file.Files import java.nio.file.Path import java.nio.file.PathMatcher import java.nio.file.attribute.DosFileAttributeView -import java.nio.file.attribute.FileAttribute import java.util.Arrays import java.util.stream.Collectors import java.util.stream.Stream @@ -193,17 +191,6 @@ private fun relativeCopyOrMove(baseDir: Path, file: Path, outputDir: Path, move: } } -fun Path.createParentDirectories(vararg attributes: FileAttribute<*>): Path = also { - val parent = it.parent - if (parent != null && !parent.isDirectory()) { - try { - parent.createDirectories(*attributes) - } catch (e: FileAlreadyExistsException) { - if (!parent.isDirectory()) throw e - } - } -} - val FileSystemLocation.path: Path get() = asFile.toPath() val Provider.path: Path diff --git a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/utils.kt b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/utils.kt index 5a9e71088..df4612436 100644 --- a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/utils.kt +++ b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/utils.kt @@ -106,7 +106,7 @@ fun ProjectLayout.cacheDir(path: String) = projectDirectory.dir(".gradle/$CACHE_ fun Project.offlineMode(): Boolean = gradle.startParameter.isOffline -fun Provider.fileExists(project: Project): Provider { +fun Provider.fileExists(project: Project): Provider { return flatMap { project.provider { it.takeIf { f -> f.path.exists() } } } } @@ -448,7 +448,7 @@ fun FileCollection.resolveMacheMeta() = singleFile.toPath().openZipSafe().use { fun isIDEASync(): Boolean = java.lang.Boolean.getBoolean("idea.sync.active") -inline fun ObjectFactory.providerSet( +inline fun ObjectFactory.providerSet( vararg providers: Provider ): Provider> { if (providers.isEmpty()) { diff --git a/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/PaperweightUserDependenciesExtension.kt b/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/PaperweightUserDependenciesExtension.kt index 1c26289f6..f2430acf4 100644 --- a/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/PaperweightUserDependenciesExtension.kt +++ b/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/PaperweightUserDependenciesExtension.kt @@ -253,7 +253,7 @@ abstract class PaperweightUserDependenciesExtension( } @Suppress("unchecked_cast") - private fun nullAction(): Action { + private fun nullAction(): Action { return NullAction as Action } From 887e248e8f013ba3815c2be5f2eb5a9352c97bfc Mon Sep 17 00:00:00 2001 From: Toffikk Date: Thu, 31 Jul 2025 21:23:25 +0200 Subject: [PATCH 037/257] cleanup: annotate the emitRejects conventions using @get:Internal to match file patches --- .../paperweight/core/tasks/patching/ApplyBasePatches.kt | 3 ++- .../paperweight/core/tasks/patching/ApplyFeaturePatches.kt | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyBasePatches.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyBasePatches.kt index 225530fe5..6f9bb2065 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyBasePatches.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyBasePatches.kt @@ -35,6 +35,7 @@ import org.gradle.api.file.DirectoryProperty import org.gradle.api.provider.Property import org.gradle.api.tasks.Input import org.gradle.api.tasks.InputDirectory +import org.gradle.api.tasks.Internal import org.gradle.api.tasks.Optional import org.gradle.api.tasks.OutputDirectory import org.gradle.api.tasks.PathSensitive @@ -62,7 +63,7 @@ abstract class ApplyBasePatches : ControllableOutputTask() { @get:Input abstract val verbose: Property - @get:Input + @get:Internal abstract val emitRejects: Property @get:Input diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyFeaturePatches.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyFeaturePatches.kt index 23dea82df..759845dc3 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyFeaturePatches.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyFeaturePatches.kt @@ -31,6 +31,7 @@ import org.gradle.api.file.DirectoryProperty import org.gradle.api.provider.Property import org.gradle.api.tasks.Input import org.gradle.api.tasks.InputDirectory +import org.gradle.api.tasks.Internal import org.gradle.api.tasks.Optional import org.gradle.api.tasks.OutputDirectory import org.gradle.api.tasks.PathSensitive @@ -54,7 +55,7 @@ abstract class ApplyFeaturePatches : ControllableOutputTask() { @get:Input abstract val verbose: Property - @get:Input + @get:Internal abstract val emitRejects: Property override fun init() { From 731c3058f292366eb26fc7eb9f52568d6b6c64a6 Mon Sep 17 00:00:00 2001 From: Jason Penilla <11360596+jpenilla@users.noreply.github.com> Date: Thu, 31 Jul 2025 16:26:30 -0700 Subject: [PATCH 038/257] Simplify some provider code using Gradle 9 nullability fixes --- .../core/extension/PaperweightCoreExtension.kt | 5 ++--- .../paperweight/core/taskcontainers/AllTasks.kt | 2 +- .../paperweight/core/taskcontainers/CoreTasks.kt | 6 +++--- .../core/taskcontainers/DevBundleTasks.kt | 2 +- .../taskcontainers/MinecraftPatchingTasks.kt | 16 ++++++++-------- .../core/taskcontainers/PatchingTasks.kt | 4 ++-- .../core/taskcontainers/SpigotTasks.kt | 2 +- .../core/taskcontainers/UpstreamConfigTasks.kt | 6 +----- .../paperweight/core/util/project-util.kt | 2 +- .../kotlin/io/papermc/paperweight/util/utils.kt | 4 ++-- 10 files changed, 22 insertions(+), 27 deletions(-) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/PaperweightCoreExtension.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/PaperweightCoreExtension.kt index daa2b55b2..d386ee1e6 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/PaperweightCoreExtension.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/PaperweightCoreExtension.kt @@ -52,9 +52,8 @@ abstract class PaperweightCoreExtension @Inject constructor(objects: ObjectFacto val spigot = objects.newInstance().also { spigot -> spigot.enabled.convention( - spigot.buildDataRef.zip(spigot.packageVersion) { ref, pkg -> - ref !== null && pkg !== null && ref.isNotBlank() && pkg.isNotBlank() - } + spigot.buildDataRef.zip(spigot.packageVersion) { ref, pkg -> ref.isNotBlank() && pkg.isNotBlank() } + .orElse(false) ) } val paper = objects.newInstance(project) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/AllTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/AllTasks.kt index 9f6ec09ae..2f790a5fc 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/AllTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/AllTasks.kt @@ -87,7 +87,7 @@ open class AllTasks( val patchReobfMappings by tasks.registering { inputMappings.set(generateReobfMappings.flatMap { it.reobfMappings }) - patch.set(extension.paper.reobfMappingsPatch.fileExists(project)) + patch.set(extension.paper.reobfMappingsPatch.fileExists()) fromNamespace.set(DEOBF_NAMESPACE) toNamespace.set(SPIGOT_NAMESPACE) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/CoreTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/CoreTasks.kt index ab708b943..776db9975 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/CoreTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/CoreTasks.kt @@ -74,11 +74,11 @@ class CoreTasks( } val collectPaperATsFromPatches by tasks.registering(CollectATsFromPatches::class) { - patchDir.set(project.coreExt.paper.featurePatchDir.fileExists(project)) + patchDir.set(project.coreExt.paper.featurePatchDir.fileExists()) } val mergePaperATs by tasks.registering { - firstFile.set(project.coreExt.paper.additionalAts.fileExists(project)) + firstFile.set(project.coreExt.paper.additionalAts.fileExists()) secondFile.set(collectPaperATsFromPatches.flatMap { it.outputFile }) } @@ -91,7 +91,7 @@ class CoreTasks( val importLibraryFiles = tasks.register("importPaperLibraryFiles") { patches.from(project.coreExt.paper.sourcePatchDir, project.coreExt.paper.featurePatchDir) - devImports.set(project.coreExt.paper.devImports.fileExists(project)) + devImports.set(project.coreExt.paper.devImports.fileExists()) libraryFileIndex.set(indexLibraryFiles.flatMap { it.outputFile }) libraries.from(indexLibraryFiles.map { it.libraries }) } diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/DevBundleTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/DevBundleTasks.kt index c763143f3..5591378d6 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/DevBundleTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/DevBundleTasks.kt @@ -73,7 +73,7 @@ class DevBundleTasks( if (it) { coreTasks.generateRelocatedReobfMappings.flatMap { t -> t.outputMappings } } else { - providers.provider { null } + null } } ) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt index 76631e64a..8de9acb5a 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt @@ -81,7 +81,7 @@ class MinecraftPatchingTasks( } else { output.set(outputSrc) } - patches.set(sourcePatchDir.fileExists(project)) + patches.set(sourcePatchDir.fileExists()) rejectsDir.set(this@MinecraftPatchingTasks.rejectsDir) gitFilePatches.set(this@MinecraftPatchingTasks.gitFilePatches) identifier = configName @@ -101,7 +101,7 @@ class MinecraftPatchingTasks( input.set(baseResources) output.set(outputResources) - patches.set(resourcePatchDir.fileExists(project)) + patches.set(resourcePatchDir.fileExists()) // TODO rejects? gitFilePatches.set(this@MinecraftPatchingTasks.gitFilePatches) identifier = configName @@ -122,7 +122,7 @@ class MinecraftPatchingTasks( base.set(applySourcePatches.flatMap { it.output }) } repo.set(outputSrc) - patches.set(featurePatchDir.fileExists(project)) + patches.set(featurePatchDir.fileExists()) } val applyPatches = tasks.register("apply${namePart}Patches") { @@ -145,17 +145,17 @@ class MinecraftPatchingTasks( fun setupFork(config: ForkConfig) { val collectAccessTransform = tasks.register("collect${configName.capitalized()}ATsFromPatches") { - patchDir.set(featurePatchDir.fileExists(project)) + patchDir.set(featurePatchDir.fileExists()) } val mergeCollectedAts = tasks.register("merge${configName.capitalized()}ATs") { - firstFile.set(additionalAts.fileExists(project)) + firstFile.set(additionalAts.fileExists()) secondFile.set(collectAccessTransform.flatMap { it.outputFile }) } val importLibFiles = tasks.register("import${configName.capitalized()}LibraryFiles") { patches.from(config.featurePatchDir, config.sourcePatchDir) - devImports.set(config.devImports.fileExists(project)) + devImports.set(config.devImports.fileExists()) libraryFileIndex.set(coreTasks.indexLibraryFiles.flatMap { it.outputFile }) libraries.from(coreTasks.indexLibraryFiles.map { it.libraries }) } @@ -210,8 +210,8 @@ class MinecraftPatchingTasks( ats.jstClasspath.from(project.configurations.named(MACHE_MINECRAFT_CONFIG)) ats.jst.from(project.configurations.named(JST_CONFIG)) - atFile.set(additionalAts.fileExists(project)) - atFileOut.set(additionalAts.fileExists(project)) + atFile.set(additionalAts.fileExists()) + atFileOut.set(additionalAts.fileExists()) } val rebuildResourcePatches = tasks.register(rebuildResourcePatchesName) { diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt index ea88a091a..d9cf03739 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt @@ -66,7 +66,7 @@ class PatchingTasks( } else { output.set(outputDir) } - patches.set(filePatchDir.fileExists(project)) + patches.set(filePatchDir.fileExists()) rejectsDir.set(this@PatchingTasks.rejectsDir) gitFilePatches.set(this@PatchingTasks.gitFilePatches) baseRef.set("base") @@ -90,7 +90,7 @@ class PatchingTasks( if (readOnly) { base.set(applyFilePatches.flatMap { it.output }) } - patches.set(featurePatchDir.fileExists(project)) + patches.set(featurePatchDir.fileExists()) } val applyPatches = tasks.register("apply${namePart}Patches") { diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/SpigotTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/SpigotTasks.kt index ccddf0905..f4347f25c 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/SpigotTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/SpigotTasks.kt @@ -92,7 +92,7 @@ open class SpigotTasks( val patchMappings by tasks.registering { inputMappings.set(cleanupMappings.flatMap { it.outputMappings }) - patch.set(extension.paper.mappingsPatch.fileExists(project)) + patch.set(extension.paper.mappingsPatch.fileExists()) fromNamespace.set(SPIGOT_NAMESPACE) toNamespace.set(DEOBF_NAMESPACE) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/UpstreamConfigTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/UpstreamConfigTasks.kt index 3b95ba560..ab22724d8 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/UpstreamConfigTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/UpstreamConfigTasks.kt @@ -56,11 +56,7 @@ class UpstreamConfigTasks( it.map { cfg -> ApplySingleFilePatches.Patch.patch(target.objects, upstream) { path = cfg.path - patchFile = layout.file( - cfg.patchFile.flatMap { f -> - if (f.path.exists()) target.providers.provider { f.asFile } else target.providers.provider { null } - } - ) + patchFile = cfg.patchFile.fileExists() outputFile = cfg.outputFile rejectsFile = cfg.rejectsFile } diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/util/project-util.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/util/project-util.kt index d4b45007d..98e552295 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/util/project-util.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/util/project-util.kt @@ -90,7 +90,7 @@ fun Project.createBuildTasks( return ServerArtifacts( spigot.enabled.flatMap { if (it) { - includeMappings.flatMap { it.outputJar } + includeMappings.flatMap { t -> t.outputJar } } else { jar } diff --git a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/utils.kt b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/utils.kt index df4612436..4d653072d 100644 --- a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/utils.kt +++ b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/utils.kt @@ -106,8 +106,8 @@ fun ProjectLayout.cacheDir(path: String) = projectDirectory.dir(".gradle/$CACHE_ fun Project.offlineMode(): Boolean = gradle.startParameter.isOffline -fun Provider.fileExists(project: Project): Provider { - return flatMap { project.provider { it.takeIf { f -> f.path.exists() } } } +fun Provider.fileExists(): Provider { + return map { it.takeIf { f -> f.path.exists() } } } @Suppress("UNCHECKED_CAST") From da80388967f41691671262e24ccdf8e2f2e4f211 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Fri, 1 Aug 2025 05:52:24 +0200 Subject: [PATCH 039/257] fix(rebuild): actually foolproof dynamic tag retrieval eliminates our need for the test fallback as this now properly greps the correct commit --- .../taskcontainers/MinecraftPatchingTasks.kt | 1 + .../core/taskcontainers/PatchingTasks.kt | 1 + .../tasks/RebuildBaseGitPatches.kt | 25 +++---------------- 3 files changed, 5 insertions(+), 22 deletions(-) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt index c28884c3e..19f62d80e 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt @@ -242,6 +242,7 @@ class MinecraftPatchingTasks( inputDir.set(outputSrc) patchDir.set(basePatchDir) filterPatches.set(this@MinecraftPatchingTasks.filterPatches) + identifier = configName } val rebuildSourcePatches = tasks.register(rebuildSourcePatchesName) { diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt index 289ab9a2d..b2f804e1e 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt @@ -187,6 +187,7 @@ class PatchingTasks( inputDir.set(outputDir) patchDir.set(basePatchDir) filterPatches.set(this@PatchingTasks.filterPatches) + identifier = "$forkName $patchSetName" } val rebuildFilePatches = tasks.register(rebuildFilePatchesName) { group = taskGroup diff --git a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RebuildBaseGitPatches.kt b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RebuildBaseGitPatches.kt index 40a8eac5b..2326774ee 100644 --- a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RebuildBaseGitPatches.kt +++ b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RebuildBaseGitPatches.kt @@ -54,11 +54,10 @@ abstract class RebuildBaseGitPatches : ControllableOutputTask() { abstract val providers: ProviderFactory @get:Input - abstract val projectName: Property + abstract val identifier: Property override fun init() { printOutput.convention(true) - projectName.convention(project.rootProject.name.lowercase()) filterPatches.convention( providers.gradleProperty("paperweight.filter-patches") .map { it.toBoolean() } @@ -71,26 +70,8 @@ abstract class RebuildBaseGitPatches : ControllableOutputTask() { val git = Git(inputDir.path) // these have to be retrieved dynamically - val patchedBaseCommit = git( - "rev-list", - "--all-match", - "--grep=${projectName.get()} ", - "--grep= Base Patches", - "--max-count=1", - "base..HEAD" - ).getText().trim().let { - if (it.isNotEmpty()) "$it" else git("rev-list", "--grep= Base Patches", "--max-count=1", "base..HEAD").getText().trim() - } // fallback to the old logic for tests - val fileCommit = git( - "rev-list", - "--all-match", - "--grep=${projectName.get()} ", - "--grep= File Patches", - "--max-count=1", - "base..HEAD" - ).getText().trim().let { - if (it.isNotEmpty()) "$it" else git("rev-list", "--grep= File Patches", "--max-count=1", "base..HEAD").getText().trim() - } // fallback to the old logic for tests + val patchedBaseCommit = git("rev-list", "--grep=${identifier.get()} Base Patches", "--max-count=1", "base..HEAD").getText().trim() + val fileCommit = git("rev-list", "--grep=${identifier.get()} File Patches", "--max-count=1", "base..HEAD").getText().trim() // we update the appropriate tags to reflect the new repo state git("tag", "-f", "patchedBase", patchedBaseCommit).executeSilently(silenceErr = true) From e4adc0f0145a61c07f40f9a9a415ec99e8406fd2 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Fri, 1 Aug 2025 05:52:24 +0200 Subject: [PATCH 040/257] fix(rebuild): actually foolproof dynamic tag retrieval eliminates our need for the test fallback as this now properly greps the correct commit --- .../taskcontainers/MinecraftPatchingTasks.kt | 1 + .../core/taskcontainers/PatchingTasks.kt | 1 + .../tasks/RebuildBaseGitPatches.kt | 25 +++---------------- 3 files changed, 5 insertions(+), 22 deletions(-) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt index 88917e70e..f280b0821 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt @@ -242,6 +242,7 @@ class MinecraftPatchingTasks( inputDir.set(outputSrc) patchDir.set(basePatchDir) filterPatches.set(this@MinecraftPatchingTasks.filterPatches) + identifier = configName } val rebuildSourcePatches = tasks.register(rebuildSourcePatchesName) { diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt index 84ecc8928..64074d6d3 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt @@ -187,6 +187,7 @@ class PatchingTasks( inputDir.set(outputDir) patchDir.set(basePatchDir) filterPatches.set(this@PatchingTasks.filterPatches) + identifier = "$forkName $patchSetName" } val rebuildFilePatches = tasks.register(rebuildFilePatchesName) { group = taskGroup diff --git a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RebuildBaseGitPatches.kt b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RebuildBaseGitPatches.kt index 8d8f0f49f..8ca554036 100644 --- a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RebuildBaseGitPatches.kt +++ b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RebuildBaseGitPatches.kt @@ -54,11 +54,10 @@ abstract class RebuildBaseGitPatches : ControllableOutputTask() { abstract val providers: ProviderFactory @get:Input - abstract val projectName: Property + abstract val identifier: Property override fun init() { printOutput.convention(true) - projectName.convention(project.rootProject.name.lowercase()) filterPatches.convention( providers.gradleProperty("paperweight.filter-patches") .map { it.toBoolean() } @@ -71,26 +70,8 @@ abstract class RebuildBaseGitPatches : ControllableOutputTask() { val git = Git(inputDir.path) // these have to be retrieved dynamically - val patchedBaseCommit = git( - "rev-list", - "--all-match", - "--grep=${projectName.get()} ", - "--grep= Base Patches", - "--max-count=1", - "base..HEAD" - ).getText().trim().let { - if (it.isNotEmpty()) "$it" else git("rev-list", "--grep= Base Patches", "--max-count=1", "base..HEAD").getText().trim() - } // fallback to the old logic for tests - val fileCommit = git( - "rev-list", - "--all-match", - "--grep=${projectName.get()} ", - "--grep= File Patches", - "--max-count=1", - "base..HEAD" - ).getText().trim().let { - if (it.isNotEmpty()) "$it" else git("rev-list", "--grep= File Patches", "--max-count=1", "base..HEAD").getText().trim() - } // fallback to the old logic for tests + val patchedBaseCommit = git("rev-list", "--grep=${identifier.get()} Base Patches", "--max-count=1", "base..HEAD").getText().trim() + val fileCommit = git("rev-list", "--grep=${identifier.get()} File Patches", "--max-count=1", "base..HEAD").getText().trim() // we update the patchedBase tag git("checkout", "patchedBase").executeSilently(silenceErr = true) From 15a6fcce8ea4ba151f58125e23b96a59aac796e7 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Sun, 3 Aug 2025 17:12:21 +0200 Subject: [PATCH 041/257] feat: fail fast on ambiguous commits during rebuild helps to avoid patch corruption in very edge case scenarios, shouldn't affect anyone --- .../tasks/RebuildBaseGitPatches.kt | 36 ++++++++++++------- 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RebuildBaseGitPatches.kt b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RebuildBaseGitPatches.kt index 8ca554036..127f6f4eb 100644 --- a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RebuildBaseGitPatches.kt +++ b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RebuildBaseGitPatches.kt @@ -22,6 +22,7 @@ package io.papermc.paperweight.tasks +import io.papermc.paperweight.PaperweightException import io.papermc.paperweight.util.* import java.nio.file.Path import java.util.concurrent.ConcurrentLinkedQueue @@ -69,21 +70,32 @@ abstract class RebuildBaseGitPatches : ControllableOutputTask() { fun run() { val git = Git(inputDir.path) + // fail fast if someone decides to name their feature patch with these names to avoid patch corruption + val patchedBaseCommitCount = git( + "rev-list", + "--grep=${identifier.get()} Base Patches", + "base..HEAD" + ).getText().lines().filter { it.isNotBlank() } + if (patchedBaseCommitCount.size > 1) { + val count = patchedBaseCommitCount.size + throw PaperweightException( + "Exceeded the max amount of commits with the identifier: `${identifier.get()} Base Patches`!\nGot $count commits, expected: 1" + ) + } + val fileCommitCount = git("rev-list", "--grep=${identifier.get()} File Patches", "base..HEAD").getText().lines().filter { it.isNotBlank() } + if (fileCommitCount.size > 1) { + val count = fileCommitCount.size + throw PaperweightException( + "Exceeded the max amount of commits with the identifier: `${identifier.get()} File Patches`!\nGot $count commits, expected: 1" + ) + } + // these have to be retrieved dynamically val patchedBaseCommit = git("rev-list", "--grep=${identifier.get()} Base Patches", "--max-count=1", "base..HEAD").getText().trim() val fileCommit = git("rev-list", "--grep=${identifier.get()} File Patches", "--max-count=1", "base..HEAD").getText().trim() - - // we update the patchedBase tag - git("checkout", "patchedBase").executeSilently(silenceErr = true) - git("reset", patchedBaseCommit, "--hard").executeSilently(silenceErr = true) - git("tag", "-f", "patchedBase").executeSilently(silenceErr = true) - git("switch", "-").executeSilently(silenceErr = true) - - // and here we update the file tag - git("checkout", "file").executeSilently(silenceErr = true) - git("reset", fileCommit, "--hard").executeSilently(silenceErr = true) - git("tag", "-f", "file").executeSilently(silenceErr = true) - git("switch", "-").executeSilently(silenceErr = true) + // we update the appropriate tags to reflect the new repo state + git("tag", "-f", "patchedBase", patchedBaseCommit).executeSilently(silenceErr = true) + git("tag", "-f", "file", fileCommit).executeSilently(silenceErr = true) val what = inputDir.path.name val patchFolder = patchDir.path From 3f9c0eab7231009d7fb50c9a9c3c4531583955b0 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Mon, 4 Aug 2025 01:46:04 +0200 Subject: [PATCH 042/257] fix: defensively copy lib imports in setup sources --- .../core/tasks/SetupForkMinecraftSources.kt | 30 ++++++++----------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupForkMinecraftSources.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupForkMinecraftSources.kt index 0b4564f41..a915f262a 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupForkMinecraftSources.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupForkMinecraftSources.kt @@ -64,13 +64,8 @@ abstract class SetupForkMinecraftSources : JavaLauncherTask() { @TaskAction fun run() { val out = outputDir.path.cleanDir() - inputDir.path.copyRecursivelyTo(out) - - val git = Git.open(outputDir.path.toFile()) - - if (atFile.isPresent && atFile.path.readText().isNotBlank() && libraryImports.isPresent) { - println("Applying access transformers...") + if (libraryImports.isPresent) { libraryImports.path.walk().forEach { val outFile = inputDir.path.resolve(it.relativeTo(libraryImports.path).invariantSeparatorsPathString) // The file may already exist if upstream imported it @@ -78,6 +73,17 @@ abstract class SetupForkMinecraftSources : JavaLauncherTask() { it.copyTo(outFile.createParentDirectories()) } } + } + + inputDir.path.copyRecursivelyTo(out) + val git = Git.open(outputDir.path.toFile()) + + if (libraryImports.isPresent) { + commitAndTag(git, "Imports", "${identifier.get()} Imports") + } + + if (atFile.isPresent && atFile.path.readText().isNotBlank() && libraryImports.isPresent) { + println("Applying access transformers...") ats.run( launcher.get(), inputDir.path, @@ -85,7 +91,7 @@ abstract class SetupForkMinecraftSources : JavaLauncherTask() { atFile.path, temporaryDir.toPath(), ) - commitAndTag(git, "ATs", "${identifier.get()} ATs and Imports") + commitAndTag(git, "ATs", "${identifier.get()} ATs") } else if (atFile.isPresent && atFile.path.readText().isNotBlank()) { println("Applying access transformers...") ats.run( @@ -96,16 +102,6 @@ abstract class SetupForkMinecraftSources : JavaLauncherTask() { temporaryDir.toPath(), ) commitAndTag(git, "ATs", "${identifier.get()} ATs") - } else if (libraryImports.isPresent) { - libraryImports.path.walk().forEach { - val outFile = out.resolve(it.relativeTo(libraryImports.path).invariantSeparatorsPathString) - // The file may already exist if upstream imported it - if (!outFile.exists()) { - it.copyTo(outFile.createParentDirectories()) - } - } - - commitAndTag(git, "Imports", "${identifier.get()} Imports") } git.close() From b71669281dccbdbb5f74a5fee0bc8e81dcaaa2ca Mon Sep 17 00:00:00 2001 From: "tofik.07" <62406295+Toffikk@users.noreply.github.com> Date: Wed, 6 Aug 2025 19:53:27 +0200 Subject: [PATCH 043/257] chore: remove duplicated code in last commit --- .../core/tasks/SetupForkMinecraftSources.kt | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupForkMinecraftSources.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupForkMinecraftSources.kt index a915f262a..773bf6cee 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupForkMinecraftSources.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupForkMinecraftSources.kt @@ -82,17 +82,7 @@ abstract class SetupForkMinecraftSources : JavaLauncherTask() { commitAndTag(git, "Imports", "${identifier.get()} Imports") } - if (atFile.isPresent && atFile.path.readText().isNotBlank() && libraryImports.isPresent) { - println("Applying access transformers...") - ats.run( - launcher.get(), - inputDir.path, - outputDir.path, - atFile.path, - temporaryDir.toPath(), - ) - commitAndTag(git, "ATs", "${identifier.get()} ATs") - } else if (atFile.isPresent && atFile.path.readText().isNotBlank()) { + if (atFile.isPresent && atFile.path.readText().isNotBlank()) { println("Applying access transformers...") ats.run( launcher.get(), From 7425741e07cc0b11f0b73f010060968d26111b56 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Thu, 7 Aug 2025 14:24:02 +0200 Subject: [PATCH 044/257] cleanup: remove unnecessary AT/Lib logic for Paper --- .../core/tasks/SetupForkMinecraftSources.kt | 15 ++++++--------- .../core/tasks/SetupMinecraftSources.kt | 19 +++---------------- 2 files changed, 9 insertions(+), 25 deletions(-) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupForkMinecraftSources.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupForkMinecraftSources.kt index 773bf6cee..4b2a2d1dd 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupForkMinecraftSources.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupForkMinecraftSources.kt @@ -64,21 +64,18 @@ abstract class SetupForkMinecraftSources : JavaLauncherTask() { @TaskAction fun run() { val out = outputDir.path.cleanDir() + inputDir.path.copyRecursivelyTo(out) + + val git = Git.open(outputDir.path.toFile()) if (libraryImports.isPresent) { libraryImports.path.walk().forEach { - val outFile = inputDir.path.resolve(it.relativeTo(libraryImports.path).invariantSeparatorsPathString) + val outFile = out.resolve(it.relativeTo(libraryImports.path).invariantSeparatorsPathString) // The file may already exist if upstream imported it if (!outFile.exists()) { it.copyTo(outFile.createParentDirectories()) } } - } - - inputDir.path.copyRecursivelyTo(out) - val git = Git.open(outputDir.path.toFile()) - - if (libraryImports.isPresent) { commitAndTag(git, "Imports", "${identifier.get()} Imports") } @@ -86,7 +83,7 @@ abstract class SetupForkMinecraftSources : JavaLauncherTask() { println("Applying access transformers...") ats.run( launcher.get(), - inputDir.path, + outputDir.path, outputDir.path, atFile.path, temporaryDir.toPath(), @@ -96,4 +93,4 @@ abstract class SetupForkMinecraftSources : JavaLauncherTask() { git.close() } -} +} \ No newline at end of file diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupMinecraftSources.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupMinecraftSources.kt index c1615c54d..80594643e 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupMinecraftSources.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupMinecraftSources.kt @@ -166,20 +166,7 @@ abstract class SetupMinecraftSources : JavaLauncherZippedTask() { logger.lifecycle("Applied ${result.summary.changedFiles} mache patches") } - if (atFile.isPresent && libraryImports.isPresent) { - println("Applying access transformers...") - libraryImports.path.copyRecursivelyTo(outputPath) - ats.run( - launcher.get(), - outputPath, - outputPath, - atFile.path, - temporaryDir.toPath(), - ) - if (!oldPaperCommit.isPresent) { - commitAndTag(git, "ATs", "paper ATs and Imports") - } - } else if (atFile.isPresent) { + if (atFile.isPresent) { println("Applying access transformers...") ats.run( launcher.get(), @@ -197,7 +184,7 @@ abstract class SetupMinecraftSources : JavaLauncherZippedTask() { commitAndTag(git, "Vanilla", "Vanilla, Mache, & paper ATs (Squashed for better Git history during updates)") } - if (libraryImports.isPresent && !atFile.isPresent) { + if (libraryImports.isPresent) { libraryImports.path.copyRecursivelyTo(outputPath) commitAndTag(git, "Imports", "paper Imports") @@ -298,4 +285,4 @@ fun commitAndTag(git: Git, name: String, message: String = name) { .call() git.tagDelete().setTags(name).call() git.tag().setName(name).setTagger(vanillaIdent).setSigned(false).call() -} +} \ No newline at end of file From 62aa374a489ed5829f77129e3344bbbb1fee7b59 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Thu, 7 Aug 2025 14:31:49 +0200 Subject: [PATCH 045/257] spotless --- .../papermc/paperweight/core/tasks/SetupForkMinecraftSources.kt | 2 +- .../io/papermc/paperweight/core/tasks/SetupMinecraftSources.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupForkMinecraftSources.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupForkMinecraftSources.kt index 4b2a2d1dd..6648ce1a9 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupForkMinecraftSources.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupForkMinecraftSources.kt @@ -93,4 +93,4 @@ abstract class SetupForkMinecraftSources : JavaLauncherTask() { git.close() } -} \ No newline at end of file +} diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupMinecraftSources.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupMinecraftSources.kt index 80594643e..6f3e2c826 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupMinecraftSources.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupMinecraftSources.kt @@ -285,4 +285,4 @@ fun commitAndTag(git: Git, name: String, message: String = name) { .call() git.tagDelete().setTags(name).call() git.tag().setName(name).setTagger(vanillaIdent).setSigned(false).call() -} \ No newline at end of file +} From 253bad9bce5eafae50a630b7a00771be9bf8b06b Mon Sep 17 00:00:00 2001 From: Toffikk Date: Tue, 12 Aug 2025 23:52:42 +0200 Subject: [PATCH 046/257] opt: speed up rebuild patches and a lot of more small tweaks --- .../paperweight/core/PaperweightCore.kt | 1 + .../taskcontainers/MinecraftPatchingTasks.kt | 23 ++++++++-------- .../core/taskcontainers/PaperclipTasks.kt | 27 +++++++++---------- .../core/tasks/patching/RebuildFilePatches.kt | 16 ++++++----- .../tasks/RebuildBaseGitPatches.kt | 3 ++- 5 files changed, 36 insertions(+), 34 deletions(-) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/PaperweightCore.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/PaperweightCore.kt index 1ceb599dd..cc6813ed9 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/PaperweightCore.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/PaperweightCore.kt @@ -161,6 +161,7 @@ abstract class PaperweightCore : Plugin { mappedJar, reobfJar, ext.minecraftVersion, + target.rootProject.name.lowercase() ) target.afterEvaluate { diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt index f280b0821..b98b1f864 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt @@ -77,7 +77,7 @@ class MinecraftPatchingTasks( private fun ApplyBasePatches.configureApplyBasePatches() { group() description = "Applies $configName base patches to the Minecraft sources" - dependsOn(applyResourcePatches) + // dependsOn(applyResourcePatches) input.set(baseSources) if (readOnly) { @@ -223,20 +223,9 @@ class MinecraftPatchingTasks( } } - val rebuildResourcePatches = tasks.register(rebuildResourcePatchesName) { - group() - description = "Rebuilds $configName file patches to the Minecraft resources" - - input.set(outputResources) - base.set(baseResources) - patches.set(resourcePatchDir) - gitFilePatches.set(this@MinecraftPatchingTasks.gitFilePatches) - } - val rebuildBasePatches = tasks.register(rebuildBasePatchesName) { group() description = "Rebuilds $configName base patches to the Minecraft source" - dependsOn(rebuildResourcePatches) base.set(baseSources) // workaround to achieve the correct task execution order inputDir.set(outputSrc) @@ -261,6 +250,16 @@ class MinecraftPatchingTasks( atFileOut.set(additionalAts.fileExists(project)) } + val rebuildResourcePatches = tasks.register(rebuildResourcePatchesName) { + group() + description = "Rebuilds $configName file patches to the Minecraft resources" + + input.set(outputResources) + base.set(baseResources) + patches.set(resourcePatchDir) + gitFilePatches.set(this@MinecraftPatchingTasks.gitFilePatches) + } + val rebuildFilePatches = tasks.register(rebuildFilePatchesName) { group() description = "Rebuilds all $configName file patches to Minecraft" diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PaperclipTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PaperclipTasks.kt index 444105584..4cdd2a643 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PaperclipTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PaperclipTasks.kt @@ -48,14 +48,15 @@ class PaperclipTasks( private val vanillaJar: Provider, mojangJar: Provider, reobfJar: Provider, - private val mcVersion: Provider + private val mcVersion: Provider, + private val rootName: String, ) { - private val rootName: String = project.rootProject.name.lowercase() - init { - val (createBundlerJar, createPaperclipJar) = project.createTasks("mojmap") - val (createReobfBundlerJar, createReobfPaperclipJar) = project.createTasks("reobf") + val buildNum: Provider = project.providers.environmentVariable("BUILD_NUMBER").orElse("local") + val publisherJarName: Provider = buildNum.map { build -> "libs/$rootName-build.$build.jar" } + val (createBundlerJar, createPaperclipJar) = project.createTasks("mojmap", publisherJarName) + val (createReobfBundlerJar, createReobfPaperclipJar) = project.createTasks("reobf", publisherJarName) createBundlerJar.serverJar(mojangJar) createReobfBundlerJar.serverJar(reobfJar) { @@ -70,13 +71,11 @@ class PaperclipTasks( private fun Project.createTasks( classifier: String = "", - ): Pair, TaskProvider> { + publisherJarName: Provider, + ): Triple, TaskProvider, TaskProvider> { val bundlerTaskName = "create${classifier.capitalized()}BundlerJar" val paperclipTaskName = "create${classifier.capitalized()}PaperclipJar" - val publisherJarName = "build${classifier.capitalized()}PublisherJar" - - val buildNum: Provider = project.providers.environmentVariable("BUILD_NUMBER").orElse("local") - val publisherJar: Provider = buildNum.map { build -> "libs/$rootName-build.$build.jar" } + val publisherTaskName = "build${classifier.capitalized()}PublisherJar" val bundlerJarTask = tasks.register(bundlerTaskName) { group = "bundling" @@ -95,16 +94,14 @@ class PaperclipTasks( libraryChangesJson.set(bundlerJarTask.flatMap { it.libraryChangesJson }) outputZip.set(layout.buildDirectory.file(jarName("paperclip", classifier).map { "libs/$it" })) } - val buildPublisherJarTask = tasks.register(publisherJarName) { + val publisherJarTask = tasks.register(publisherTaskName) { group = "bundling" description = "Build a ready-to-publish jar" - dependsOn(paperclipJarTask) - inputZip.set(paperclipJarTask.flatMap { it.outputZip }) - outputZip.convention(publisherJar.map { layout.buildDirectory.file(it) }.flatMap { it }) + outputZip.convention(publisherJarName.map { layout.buildDirectory.file(it) }.flatMap { it }) } - return bundlerJarTask to paperclipJarTask + return Triple(bundlerJarTask, paperclipJarTask, publisherJarTask) } private fun Project.jarName(kind: String, classifier: String): Provider { diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/RebuildFilePatches.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/RebuildFilePatches.kt index 7958c69ec..2468ebba7 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/RebuildFilePatches.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/RebuildFilePatches.kt @@ -108,15 +108,19 @@ abstract class RebuildFilePatches : JavaLauncherTask() { // here we zip the parent commit's repo state and store it as our baseDir for later comparison overriding the baseDir prop val baseCommit = git("rev-parse", "HEAD~1").getText().trim() - val baseDir = temporaryDir.toPath().resolve("base") - val zip = temporaryDir.toPath().resolve("base.zip") + val baseDir = temporaryDir.toPath().resolve("base.zip") - if (zip.exists()) zip.deleteRecursive() - if (baseDir.exists()) baseDir.deleteRecursive() + if (baseDir.exists()) baseDir.deleteForcefully() - git("archive", "--format=zip", baseCommit, "-o", zip.absolutePathString()).executeSilently(silenceErr = true) + git("archive", "--format=zip", baseCommit, "-o", baseDir.absolutePathString()).executeSilently(silenceErr = true) - unzip(zip, baseDir) + // we have to open the zip to avoid passing in an empty one (causes sig errors) + baseDir.openZipSafe().use { + if (it.walkSequence().none { p -> p.isRegularFile() }) { + logger.lifecycle("Rebuilt 0 patches") + return + } + } val filesWithNewAts = if (!ats.jst.isEmpty) { handleAts( diff --git a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RebuildBaseGitPatches.kt b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RebuildBaseGitPatches.kt index 127f6f4eb..32dc45535 100644 --- a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RebuildBaseGitPatches.kt +++ b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RebuildBaseGitPatches.kt @@ -70,7 +70,7 @@ abstract class RebuildBaseGitPatches : ControllableOutputTask() { fun run() { val git = Git(inputDir.path) - // fail fast if someone decides to name their feature patch with these names to avoid patch corruption + // fail fast if someone decides to name a patch with these names to avoid patch corruption val patchedBaseCommitCount = git( "rev-list", "--grep=${identifier.get()} Base Patches", @@ -93,6 +93,7 @@ abstract class RebuildBaseGitPatches : ControllableOutputTask() { // these have to be retrieved dynamically val patchedBaseCommit = git("rev-list", "--grep=${identifier.get()} Base Patches", "--max-count=1", "base..HEAD").getText().trim() val fileCommit = git("rev-list", "--grep=${identifier.get()} File Patches", "--max-count=1", "base..HEAD").getText().trim() + // we update the appropriate tags to reflect the new repo state git("tag", "-f", "patchedBase", patchedBaseCommit).executeSilently(silenceErr = true) git("tag", "-f", "file", fileCommit).executeSilently(silenceErr = true) From 55e4101ee14846b51d21805e865535cc03d26fc7 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Wed, 13 Aug 2025 15:33:41 +0200 Subject: [PATCH 047/257] fix: revert part of the previous commit I'll take a closer look again in the future, for now it's reverted as it's not entirely safe --- .../taskcontainers/MinecraftPatchingTasks.kt | 1 - .../core/tasks/patching/RebuildFilePatches.kt | 16 ++++++---------- 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt index b98b1f864..76b9d234c 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt @@ -77,7 +77,6 @@ class MinecraftPatchingTasks( private fun ApplyBasePatches.configureApplyBasePatches() { group() description = "Applies $configName base patches to the Minecraft sources" - // dependsOn(applyResourcePatches) input.set(baseSources) if (readOnly) { diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/RebuildFilePatches.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/RebuildFilePatches.kt index 2468ebba7..5b9ac6f3d 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/RebuildFilePatches.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/RebuildFilePatches.kt @@ -108,19 +108,15 @@ abstract class RebuildFilePatches : JavaLauncherTask() { // here we zip the parent commit's repo state and store it as our baseDir for later comparison overriding the baseDir prop val baseCommit = git("rev-parse", "HEAD~1").getText().trim() - val baseDir = temporaryDir.toPath().resolve("base.zip") + val baseDir = temporaryDir.toPath().resolve("base") + val zippedTree = temporaryDir.toPath().resolve("base.zip") - if (baseDir.exists()) baseDir.deleteForcefully() + if (baseDir.exists()) baseDir.deleteRecursive() + if (zippedTree.exists()) zippedTree.deleteForcefully() - git("archive", "--format=zip", baseCommit, "-o", baseDir.absolutePathString()).executeSilently(silenceErr = true) + git("archive", "--format=zip", baseCommit, "-o", zippedTree.absolutePathString()).executeSilently(silenceErr = true) - // we have to open the zip to avoid passing in an empty one (causes sig errors) - baseDir.openZipSafe().use { - if (it.walkSequence().none { p -> p.isRegularFile() }) { - logger.lifecycle("Rebuilt 0 patches") - return - } - } + unzip(zippedTree, baseDir) val filesWithNewAts = if (!ats.jst.isEmpty) { handleAts( From 14c6f06033105f32b698399697fffe67284d4334 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Mon, 18 Aug 2025 15:35:16 +0200 Subject: [PATCH 048/257] feat: update our diff library, removes the need for cleanup patches in per-files --- gradle.properties | 2 +- gradle/libs.versions.toml | 2 +- .../taskcontainers/MinecraftPatchingTasks.kt | 1 - .../core/taskcontainers/PatchingTasks.kt | 1 - .../taskcontainers/UpstreamConfigTasks.kt | 2 +- .../core/tasks/SetupMinecraftSources.kt | 22 ++++---- .../core/tasks/patching/ApplyFilePatches.kt | 32 +++++------ .../tasks/patching/ApplyFilePatchesFuzzy.kt | 2 +- .../patching/ApplyResourceFilePatches.kt | 32 +++++------ .../tasks/patching/ApplySingleFilePatches.kt | 16 +++--- .../core/tasks/patching/RebuildFilePatches.kt | 54 ++++--------------- .../patching/RebuildSingleFilePatches.kt | 12 +++-- .../paperweight/tasks/GenerateDevBundle.kt | 12 +++-- .../action/ApplyDevBundlePatchesAction.kt | 14 ++--- .../setup/action/SetupMacheSourcesAction.kt | 14 ++--- 15 files changed, 100 insertions(+), 118 deletions(-) diff --git a/gradle.properties b/gradle.properties index 4e2c60421..b3602eda1 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ group = io.canvasmc.weaver -version = 2.2.2-SNAPSHOT +version = 2.2.3-SNAPSHOT org.gradle.configuration-cache = true org.gradle.caching = true diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index dd4a8ce9e..1c214ef8b 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -33,7 +33,7 @@ hypo-mappings = { module = "dev.denwav.hypo:hypo-mappings", version.ref = "hypo" lorenzTiny = "net.fabricmc:lorenz-tiny:3.0.0" jbsdiff = "io.sigpipe:jbsdiff:1.0" -diffpatch = "codechicken:DiffPatch:1.5.0.30" +diffpatch = "io.codechicken:DiffPatch:2.0.1.40" 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/MinecraftPatchingTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt index 76b9d234c..7d0456bcb 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt @@ -241,7 +241,6 @@ class MinecraftPatchingTasks( input.set(outputSrc) patches.set(sourcePatchDir) gitFilePatches.set(this@MinecraftPatchingTasks.gitFilePatches) - filterPatches.set(this@MinecraftPatchingTasks.filterPatches) ats.jstClasspath.from(project.configurations.named(MACHE_MINECRAFT_CONFIG)) ats.jst.from(project.configurations.named(JST_CONFIG)) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt index 64074d6d3..9a5a427fa 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt @@ -197,7 +197,6 @@ class PatchingTasks( input.set(outputDir) patches.set(filePatchDir) gitFilePatches.set(this@PatchingTasks.gitFilePatches) - filterPatches.set(this@PatchingTasks.filterPatches) } val fixupFilePatches = tasks.register(fixupFilePatchesName) { diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/UpstreamConfigTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/UpstreamConfigTasks.kt index 135fb008e..9fafe1c93 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/UpstreamConfigTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/UpstreamConfigTasks.kt @@ -22,7 +22,7 @@ package io.papermc.paperweight.core.taskcontainers -import codechicken.diffpatch.util.PatchMode +import io.codechicken.diffpatch.util.PatchMode import io.papermc.paperweight.core.extension.UpstreamConfig import io.papermc.paperweight.core.tasks.FilterRepo import io.papermc.paperweight.core.tasks.RunNestedBuild diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupMinecraftSources.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupMinecraftSources.kt index 6f3e2c826..074384092 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupMinecraftSources.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupMinecraftSources.kt @@ -22,9 +22,11 @@ package io.papermc.paperweight.core.tasks -import codechicken.diffpatch.cli.PatchOperation -import codechicken.diffpatch.util.LoggingOutputStream -import codechicken.diffpatch.util.archiver.ArchiveFormat +import io.codechicken.diffpatch.cli.PatchOperation +import io.codechicken.diffpatch.util.ConsumingOutputStream +import io.codechicken.diffpatch.util.Input as DiffInput +import io.codechicken.diffpatch.util.Output as DiffOutput +import io.codechicken.diffpatch.util.archiver.ArchiveFormat import io.papermc.paperweight.PaperweightException import io.papermc.paperweight.core.util.ApplySourceATs import io.papermc.paperweight.tasks.* @@ -145,12 +147,12 @@ abstract class SetupMinecraftSources : JavaLauncherZippedTask() { println("Applying mache patches...") val result = PatchOperation.builder() - .logTo(LoggingOutputStream(logger, LogLevel.LIFECYCLE)) - .basePath(outputPath.convertToPath()) - .outputPath(outputPath.convertToPath()) - .patchesPath(mache.singleFile.toPath(), ArchiveFormat.ZIP) + .logTo(ConsumingOutputStream { s -> logger.log(LogLevel.LIFECYCLE, s) }) + .baseInput(DiffInput.MultiInput.folder(outputPath.convertToPath())) + .patchedOutput(DiffOutput.MultiOutput.folder(outputPath.convertToPath())) + .patchesInput(DiffInput.MultiInput.archive(ArchiveFormat.ZIP, mache.singleFile.toPath())) .patchesPrefix("patches") - .level(codechicken.diffpatch.util.LogLevel.INFO) + .level(io.codechicken.diffpatch.util.LogLevel.INFO) .ignorePrefix(".git") .build() .operate() @@ -160,10 +162,10 @@ abstract class SetupMinecraftSources : JavaLauncherZippedTask() { } if (result.exit != 0) { - throw Exception("Failed to apply ${result.summary.failedMatches} mache patches") + throw Exception("Failed to apply ${result.summary?.failedMatches} mache patches") } - logger.lifecycle("Applied ${result.summary.changedFiles} mache patches") + logger.lifecycle("Applied ${result.summary?.changedFiles} mache patches") } if (atFile.isPresent) { diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyFilePatches.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyFilePatches.kt index 061222dfe..592621f59 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyFilePatches.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyFilePatches.kt @@ -22,10 +22,12 @@ package io.papermc.paperweight.core.tasks.patching -import codechicken.diffpatch.cli.PatchOperation -import codechicken.diffpatch.match.FuzzyLineMatcher -import codechicken.diffpatch.util.LoggingOutputStream -import codechicken.diffpatch.util.PatchMode +import io.codechicken.diffpatch.cli.PatchOperation +import io.codechicken.diffpatch.match.FuzzyLineMatcher +import io.codechicken.diffpatch.util.ConsumingOutputStream +import io.codechicken.diffpatch.util.Input as DiffInput +import io.codechicken.diffpatch.util.Output as DiffOutput +import io.codechicken.diffpatch.util.PatchMode import io.papermc.paperweight.PaperweightException import io.papermc.paperweight.tasks.* import io.papermc.paperweight.util.* @@ -188,21 +190,21 @@ abstract class ApplyFilePatches : BaseTask() { return patchFiles.size } - private fun applyWithDiffPatch(): Int { - val printStream = PrintStream(LoggingOutputStream(logger, LogLevel.LIFECYCLE)) + private fun applyWithDiffPatch(): Int? { + val printStream = PrintStream(ConsumingOutputStream { s -> logger.log(LogLevel.LIFECYCLE, s) }) val builder = PatchOperation.builder() .logTo(printStream) - .basePath(repo.path) - .patchesPath(patches.path) - .outputPath(repo.path) - .level(if (verbose.get()) codechicken.diffpatch.util.LogLevel.ALL else codechicken.diffpatch.util.LogLevel.INFO) + .baseInput(DiffInput.MultiInput.folder(repo.path)) + .patchesInput(DiffInput.MultiInput.folder(patches.path)) + .patchedOutput(DiffOutput.MultiOutput.folder(repo.path)) + .level(if (verbose.get()) io.codechicken.diffpatch.util.LogLevel.ALL else io.codechicken.diffpatch.util.LogLevel.INFO) .mode(mode()) .minFuzz(minFuzz()) .summary(verbose.get()) .lineEnding("\n") .ignorePrefix(".git") if (rejectsDir.isPresent && emitRejects.get()) { - builder.rejectsPath(rejectsDir.path) + builder.rejectsOutput(DiffOutput.MultiOutput.folder(rejectsDir.path)) } val result = builder.build().operate() @@ -210,12 +212,12 @@ abstract class ApplyFilePatches : BaseTask() { commit() if (result.exit != 0) { - val total = result.summary.failedMatches + result.summary.exactMatches + - result.summary.accessMatches + result.summary.offsetMatches + result.summary.fuzzyMatches - throw Exception("Failed to apply ${result.summary.failedMatches}/$total hunks") + val total = (result.summary?.failedMatches ?: 0) + (result.summary?.exactMatches ?: 0) + + (result.summary?.accessMatches ?: 0) + (result.summary?.offsetMatches ?: 0) + (result.summary?.fuzzyMatches ?: 0) + throw Exception("Failed to apply ${result.summary?.failedMatches}/$total hunks") } - return result.summary.changedFiles + return result.summary?.changedFiles } private fun commit() { diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyFilePatchesFuzzy.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyFilePatchesFuzzy.kt index 091d8b7fe..df34318b9 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyFilePatchesFuzzy.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyFilePatchesFuzzy.kt @@ -22,7 +22,7 @@ package io.papermc.paperweight.core.tasks.patching -import codechicken.diffpatch.util.PatchMode +import io.codechicken.diffpatch.util.PatchMode import io.papermc.paperweight.core.util.defaultMinFuzz import javax.inject.Inject import org.gradle.api.provider.Property diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyResourceFilePatches.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyResourceFilePatches.kt index d7a1951d0..ee83912f0 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyResourceFilePatches.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyResourceFilePatches.kt @@ -22,10 +22,12 @@ package io.papermc.paperweight.core.tasks.patching -import codechicken.diffpatch.cli.PatchOperation -import codechicken.diffpatch.match.FuzzyLineMatcher -import codechicken.diffpatch.util.LoggingOutputStream -import codechicken.diffpatch.util.PatchMode +import io.codechicken.diffpatch.cli.PatchOperation +import io.codechicken.diffpatch.match.FuzzyLineMatcher +import io.codechicken.diffpatch.util.ConsumingOutputStream +import io.codechicken.diffpatch.util.Input as DiffInput +import io.codechicken.diffpatch.util.Output as DiffOutput +import io.codechicken.diffpatch.util.PatchMode import io.papermc.paperweight.PaperweightException import io.papermc.paperweight.tasks.* import io.papermc.paperweight.util.* @@ -210,21 +212,21 @@ abstract class ApplyResourceFilePatches : BaseTask() { return patchFiles.size } - private fun applyWithDiffPatch(): Int { - val printStream = PrintStream(LoggingOutputStream(logger, LogLevel.LIFECYCLE)) + private fun applyWithDiffPatch(): Int? { + val printStream = PrintStream(ConsumingOutputStream { s -> logger.log(LogLevel.LIFECYCLE, s) }) val builder = PatchOperation.builder() .logTo(printStream) - .basePath(output.path) - .patchesPath(patches.path) - .outputPath(output.path) - .level(if (verbose.get()) codechicken.diffpatch.util.LogLevel.ALL else codechicken.diffpatch.util.LogLevel.INFO) + .baseInput(DiffInput.MultiInput.folder(output.path)) + .patchesInput(DiffInput.MultiInput.folder(patches.path)) + .patchedOutput(DiffOutput.MultiOutput.folder(output.path)) + .level(if (verbose.get()) io.codechicken.diffpatch.util.LogLevel.ALL else io.codechicken.diffpatch.util.LogLevel.INFO) .mode(mode()) .minFuzz(minFuzz()) .summary(verbose.get()) .lineEnding("\n") .ignorePrefix(".git") if (rejectsDir.isPresent && emitRejects.get()) { - builder.rejectsPath(rejectsDir.path) + builder.rejectsOutput(DiffOutput.MultiOutput.folder(rejectsDir.path)) } val result = builder.build().operate() @@ -232,12 +234,12 @@ abstract class ApplyResourceFilePatches : BaseTask() { commit() if (result.exit != 0) { - val total = result.summary.failedMatches + result.summary.exactMatches + - result.summary.accessMatches + result.summary.offsetMatches + result.summary.fuzzyMatches - throw Exception("Failed to apply ${result.summary.failedMatches}/$total hunks") + val total = (result.summary?.failedMatches ?: 0) + (result.summary?.exactMatches ?: 0) + + (result.summary?.accessMatches ?: 0) + (result.summary?.offsetMatches ?: 0) + (result.summary?.fuzzyMatches ?: 0) + throw Exception("Failed to apply ${result.summary?.failedMatches}/$total hunks") } - return result.summary.changedFiles + return result.summary?.changedFiles } private fun setupGitHook(outputPath: Path) { diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplySingleFilePatches.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplySingleFilePatches.kt index b54c03d68..79e832179 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplySingleFilePatches.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplySingleFilePatches.kt @@ -22,9 +22,11 @@ package io.papermc.paperweight.core.tasks.patching -import codechicken.diffpatch.cli.PatchOperation -import codechicken.diffpatch.util.LogLevel -import codechicken.diffpatch.util.PatchMode +import io.codechicken.diffpatch.cli.PatchOperation +import io.codechicken.diffpatch.util.Input as DiffInput +import io.codechicken.diffpatch.util.LogLevel +import io.codechicken.diffpatch.util.Output as DiffOutput +import io.codechicken.diffpatch.util.PatchMode import io.papermc.paperweight.PaperweightException import io.papermc.paperweight.core.util.defaultMinFuzz import io.papermc.paperweight.tasks.* @@ -129,10 +131,10 @@ abstract class ApplySingleFilePatches : BaseTask() { .mode(mode.get()) .minFuzz(minFuzz.get().toFloat()) .summary(false) - .basePath(tmpWork) - .patchesPath(tmpPatch) - .outputPath(tmpWork) - .rejectsPath(tmpRej) + .baseInput(DiffInput.MultiInput.folder(tmpWork)) + .patchesInput(DiffInput.MultiInput.folder(tmpPatch)) + .patchedOutput(DiffOutput.MultiOutput.folder(tmpWork)) + .rejectsOutput(DiffOutput.MultiOutput.folder(tmpRej)) .build() op.operate() diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/RebuildFilePatches.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/RebuildFilePatches.kt index 5b9ac6f3d..1b6c5694a 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/RebuildFilePatches.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/RebuildFilePatches.kt @@ -22,9 +22,11 @@ package io.papermc.paperweight.core.tasks.patching -import codechicken.diffpatch.cli.DiffOperation -import codechicken.diffpatch.util.LogLevel -import codechicken.diffpatch.util.LoggingOutputStream +import io.codechicken.diffpatch.cli.DiffOperation +import io.codechicken.diffpatch.util.ConsumingOutputStream +import io.codechicken.diffpatch.util.Input as DiffInput +import io.codechicken.diffpatch.util.LogLevel +import io.codechicken.diffpatch.util.Output as DiffOutput import io.papermc.paperweight.PaperweightException import io.papermc.paperweight.core.util.ApplySourceATs import io.papermc.paperweight.tasks.* @@ -81,20 +83,11 @@ abstract class RebuildFilePatches : JavaLauncherTask() { @get:Nested val ats: ApplySourceATs = objects.newInstance() - @get:Input - @get:Optional - abstract val filterPatches: Property - override fun init() { super.init() contextLines.convention(3) verbose.convention(false) gitFilePatches.convention(false) - filterPatches.convention( - project.providers.gradleProperty("paperweight.filter-patches") - .map { it.toBoolean() } - .orElse(true) - ) } @TaskAction @@ -203,13 +196,13 @@ abstract class RebuildFilePatches : JavaLauncherTask() { baseDir: Path, inputDir: Path, patchDir: Path - ): Int { - val printStream = PrintStream(LoggingOutputStream(logger, org.gradle.api.logging.LogLevel.LIFECYCLE)) + ): Int? { + val printStream = PrintStream(ConsumingOutputStream { s -> logger.log(org.gradle.api.logging.LogLevel.LIFECYCLE, s) }) val result = DiffOperation.builder() .logTo(printStream) - .aPath(baseDir) - .bPath(inputDir) - .outputPath(patchDir) + .baseInput(DiffInput.MultiInput.folder(baseDir)) + .changedInput(DiffInput.MultiInput.folder(inputDir)) + .patchesOutput(DiffOutput.MultiOutput.folder(patchDir)) .autoHeader(true) .level(if (verbose.get()) LogLevel.ALL else LogLevel.INFO) .lineEnding("\n") @@ -222,11 +215,7 @@ abstract class RebuildFilePatches : JavaLauncherTask() { .build() .operate() - return if (filterPatches.get()) { - cleanupPatches(patchDir) - } else { - result.summary.changedFiles - } + return result.summary?.changedFiles } // TODO: look into moving this earlier in the source tree, still most of it is broken anyway @@ -328,25 +317,4 @@ abstract class RebuildFilePatches : JavaLauncherTask() { return foundNew } - - // filter patches since our diff library seems to emit patches for empty files that existed in the baseDir - private fun cleanupPatches(patchDir: Path): Int { - var saved = 0 - val patches = patchDir.filesMatchingRecursive("*.patch") - patches.forEach { patch -> - val hasChanges = patch.useLines { lines -> - lines.any { - (it.startsWith("+") && !it.startsWith("+++")) || - (it.startsWith("-") && !it.startsWith("---")) - } - } - - if (!hasChanges) { - patch.deleteForcefully() - } else { - saved++ - } - } - return saved - } } diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/RebuildSingleFilePatches.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/RebuildSingleFilePatches.kt index 45cd14d5b..73b579b20 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/RebuildSingleFilePatches.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/RebuildSingleFilePatches.kt @@ -22,8 +22,10 @@ package io.papermc.paperweight.core.tasks.patching -import codechicken.diffpatch.cli.DiffOperation -import codechicken.diffpatch.util.LogLevel +import io.codechicken.diffpatch.cli.DiffOperation +import io.codechicken.diffpatch.util.Input as DiffInput +import io.codechicken.diffpatch.util.LogLevel +import io.codechicken.diffpatch.util.Output as DiffOutput import io.papermc.paperweight.tasks.* import io.papermc.paperweight.util.* import java.io.PrintStream @@ -80,9 +82,9 @@ abstract class RebuildSingleFilePatches : BaseTask() { val result = DiffOperation.builder() .logTo(logOut) - .aPath(tmpA) - .bPath(tmpB) - .outputPath(tmpPatch) + .baseInput(DiffInput.MultiInput.folder(tmpA)) + .changedInput(DiffInput.MultiInput.folder(tmpB)) + .patchesOutput(DiffOutput.MultiOutput.folder(tmpPatch)) .autoHeader(true) .level(LogLevel.ALL) .lineEnding("\n") diff --git a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/GenerateDevBundle.kt b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/GenerateDevBundle.kt index 37266e025..897a993ca 100644 --- a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/GenerateDevBundle.kt +++ b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/GenerateDevBundle.kt @@ -22,8 +22,10 @@ package io.papermc.paperweight.tasks -import codechicken.diffpatch.cli.DiffOperation -import codechicken.diffpatch.util.LogLevel +import io.codechicken.diffpatch.cli.DiffOperation +import io.codechicken.diffpatch.util.Input as DiffInput +import io.codechicken.diffpatch.util.LogLevel +import io.codechicken.diffpatch.util.Output as DiffOutput import io.papermc.paperweight.util.* import io.papermc.paperweight.util.constants.* import java.io.PrintStream @@ -168,9 +170,9 @@ abstract class GenerateDevBundle : BaseTask() { PrintStream(logFile.toFile(), Charsets.UTF_8).use { logOut -> DiffOperation.builder() .logTo(logOut) - .aPath(a) - .bPath(b) - .outputPath(patchOut, null) + .baseInput(DiffInput.MultiInput.folder(a)) + .changedInput(DiffInput.MultiInput.folder(b)) + .patchesOutput(DiffOutput.MultiOutput.folder(patchOut)) .autoHeader(true) .level(LogLevel.ALL) .lineEnding("\n") diff --git a/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/internal/setup/action/ApplyDevBundlePatchesAction.kt b/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/internal/setup/action/ApplyDevBundlePatchesAction.kt index ee72c6b61..7c20d009a 100644 --- a/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/internal/setup/action/ApplyDevBundlePatchesAction.kt +++ b/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/internal/setup/action/ApplyDevBundlePatchesAction.kt @@ -22,9 +22,11 @@ package io.papermc.paperweight.userdev.internal.setup.action -import codechicken.diffpatch.cli.PatchOperation -import codechicken.diffpatch.util.LogLevel -import codechicken.diffpatch.util.archiver.ArchiveFormat +import io.codechicken.diffpatch.cli.PatchOperation +import io.codechicken.diffpatch.util.Input as DiffInput +import io.codechicken.diffpatch.util.LogLevel +import io.codechicken.diffpatch.util.Output as DiffOutput +import io.codechicken.diffpatch.util.archiver.ArchiveFormat import io.papermc.paperweight.PaperweightException import io.papermc.paperweight.userdev.internal.action.FileValue import io.papermc.paperweight.userdev.internal.action.Input @@ -68,9 +70,9 @@ class ApplyDevBundlePatchesAction( .logTo(logOut) .level(LogLevel.ALL) .summary(true) - .basePath(decompiledJar.get(), ArchiveFormat.ZIP) - .patchesPath(tempPatchDir) - .outputPath(outputDir) + .baseInput(DiffInput.MultiInput.archive(ArchiveFormat.ZIP, decompiledJar.get())) + .patchesInput(DiffInput.MultiInput.folder(tempPatchDir)) + .patchedOutput(DiffOutput.MultiOutput.folder(outputDir)) .build() try { op.operate().throwOnError() diff --git a/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/internal/setup/action/SetupMacheSourcesAction.kt b/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/internal/setup/action/SetupMacheSourcesAction.kt index 018029e77..c9e98d33b 100644 --- a/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/internal/setup/action/SetupMacheSourcesAction.kt +++ b/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/internal/setup/action/SetupMacheSourcesAction.kt @@ -22,9 +22,11 @@ package io.papermc.paperweight.userdev.internal.setup.action -import codechicken.diffpatch.cli.PatchOperation -import codechicken.diffpatch.util.LogLevel -import codechicken.diffpatch.util.archiver.ArchiveFormat +import io.codechicken.diffpatch.cli.PatchOperation +import io.codechicken.diffpatch.util.Input as DiffInput +import io.codechicken.diffpatch.util.LogLevel +import io.codechicken.diffpatch.util.Output as DiffOutput +import io.codechicken.diffpatch.util.archiver.ArchiveFormat import io.papermc.paperweight.PaperweightException import io.papermc.paperweight.tasks.mache.macheDecompileJar import io.papermc.paperweight.userdev.internal.action.DirectoryValue @@ -72,9 +74,9 @@ class SetupMacheSourcesAction( val result = PrintStream(log.toFile(), Charsets.UTF_8).use { logOut -> PatchOperation.builder() .logTo(logOut) - .basePath(tempOut, ArchiveFormat.ZIP) - .outputPath(outputJar.get(), ArchiveFormat.ZIP) - .patchesPath(mache.get().singleFile.toPath(), ArchiveFormat.ZIP) + .baseInput(DiffInput.MultiInput.archive(ArchiveFormat.ZIP, tempOut)) + .patchedOutput(DiffOutput.MultiOutput.archive(ArchiveFormat.ZIP, outputJar.get())) + .patchesInput(DiffInput.MultiInput.archive(ArchiveFormat.ZIP, mache.get().singleFile.toPath())) .patchesPrefix("patches") .level(LogLevel.ALL) .summary(true) From 92d54d823366b04c713ed373a469fb3da8baff8c Mon Sep 17 00:00:00 2001 From: Toffikk Date: Mon, 18 Aug 2025 17:37:08 +0200 Subject: [PATCH 049/257] fix: update the group id for diffpatch in buildsrc Also deploy under 2.2.2 still --- buildSrc/src/main/kotlin/config-kotlin.gradle.kts | 2 +- buildSrc/src/main/kotlin/config-publish.gradle.kts | 8 ++++---- gradle.properties | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/buildSrc/src/main/kotlin/config-kotlin.gradle.kts b/buildSrc/src/main/kotlin/config-kotlin.gradle.kts index a2fd84903..b08ab4dd8 100644 --- a/buildSrc/src/main/kotlin/config-kotlin.gradle.kts +++ b/buildSrc/src/main/kotlin/config-kotlin.gradle.kts @@ -35,7 +35,7 @@ repositories { } maven("https://repo.papermc.io/repository/maven-public/") { mavenContent { - includeGroup("codechicken") + includeGroup("io.codechicken") includeGroup("net.fabricmc") includeGroupAndSubgroups("io.papermc") } diff --git a/buildSrc/src/main/kotlin/config-publish.gradle.kts b/buildSrc/src/main/kotlin/config-publish.gradle.kts index 3086eb42f..8a755e8bd 100644 --- a/buildSrc/src/main/kotlin/config-publish.gradle.kts +++ b/buildSrc/src/main/kotlin/config-publish.gradle.kts @@ -41,8 +41,8 @@ val sourcesJar by tasks.existing(AbstractArchiveTask::class) { } gradlePlugin { - website.set("https://github.com/Toffikk/paperweight/") - vcsUrl.set("https://github.com/Toffikk/paperweight/") + website.set("https://github.com/CraftCanvasMC/weaver/") + vcsUrl.set("https://github.com/CraftCanvasMC/weaver/") } val shadowJar by tasks.existing(ShadowJar::class) { @@ -56,8 +56,8 @@ val shadowJar by tasks.existing(ShadowJar::class) { val prefix = "paper.libs" listOf( - "codechicken.diffpatch", - /* -> */ "codechicken.repack", + "io.codechicken.diffpatch", + /* -> */ "io.codechicken.repack", "com.github.salomonbrys.kotson", "com.google.gson", "dev.denwav.hypo", diff --git a/gradle.properties b/gradle.properties index b3602eda1..4e2c60421 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ group = io.canvasmc.weaver -version = 2.2.3-SNAPSHOT +version = 2.2.2-SNAPSHOT org.gradle.configuration-cache = true org.gradle.caching = true From 0d3cba8216a2a8813d66d921757b38472f783d2d Mon Sep 17 00:00:00 2001 From: Toffikk Date: Mon, 18 Aug 2025 17:37:46 +0200 Subject: [PATCH 050/257] chore: continue development cycle --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 4e2c60421..b3602eda1 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ group = io.canvasmc.weaver -version = 2.2.2-SNAPSHOT +version = 2.2.3-SNAPSHOT org.gradle.configuration-cache = true org.gradle.caching = true From 41fa6b23796dac08c1558e5f6917c03f0ed0c00a Mon Sep 17 00:00:00 2001 From: Toffikk Date: Tue, 19 Aug 2025 00:12:44 +0200 Subject: [PATCH 051/257] chore: cleanup after diff library update --- .../papermc/paperweight/core/tasks/SetupMinecraftSources.kt | 3 +-- .../paperweight/core/tasks/patching/ApplyFilePatches.kt | 5 +---- .../core/tasks/patching/ApplyResourceFilePatches.kt | 5 +---- .../paperweight/core/tasks/patching/RebuildFilePatches.kt | 5 +---- 4 files changed, 4 insertions(+), 14 deletions(-) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupMinecraftSources.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupMinecraftSources.kt index 074384092..5ed73903f 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupMinecraftSources.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupMinecraftSources.kt @@ -23,7 +23,6 @@ package io.papermc.paperweight.core.tasks import io.codechicken.diffpatch.cli.PatchOperation -import io.codechicken.diffpatch.util.ConsumingOutputStream import io.codechicken.diffpatch.util.Input as DiffInput import io.codechicken.diffpatch.util.Output as DiffOutput import io.codechicken.diffpatch.util.archiver.ArchiveFormat @@ -147,7 +146,7 @@ abstract class SetupMinecraftSources : JavaLauncherZippedTask() { println("Applying mache patches...") val result = PatchOperation.builder() - .logTo(ConsumingOutputStream { s -> logger.log(LogLevel.LIFECYCLE, s) }) + .logTo(logger::lifecycle) .baseInput(DiffInput.MultiInput.folder(outputPath.convertToPath())) .patchedOutput(DiffOutput.MultiOutput.folder(outputPath.convertToPath())) .patchesInput(DiffInput.MultiInput.archive(ArchiveFormat.ZIP, mache.singleFile.toPath())) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyFilePatches.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyFilePatches.kt index 592621f59..f1226215b 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyFilePatches.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyFilePatches.kt @@ -24,14 +24,12 @@ package io.papermc.paperweight.core.tasks.patching import io.codechicken.diffpatch.cli.PatchOperation import io.codechicken.diffpatch.match.FuzzyLineMatcher -import io.codechicken.diffpatch.util.ConsumingOutputStream import io.codechicken.diffpatch.util.Input as DiffInput import io.codechicken.diffpatch.util.Output as DiffOutput import io.codechicken.diffpatch.util.PatchMode import io.papermc.paperweight.PaperweightException import io.papermc.paperweight.tasks.* import io.papermc.paperweight.util.* -import java.io.PrintStream import java.nio.file.Path import java.time.Instant import kotlin.io.path.* @@ -191,9 +189,8 @@ abstract class ApplyFilePatches : BaseTask() { } private fun applyWithDiffPatch(): Int? { - val printStream = PrintStream(ConsumingOutputStream { s -> logger.log(LogLevel.LIFECYCLE, s) }) val builder = PatchOperation.builder() - .logTo(printStream) + .logTo(logger::lifecycle) .baseInput(DiffInput.MultiInput.folder(repo.path)) .patchesInput(DiffInput.MultiInput.folder(patches.path)) .patchedOutput(DiffOutput.MultiOutput.folder(repo.path)) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyResourceFilePatches.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyResourceFilePatches.kt index ee83912f0..cbbdcdee8 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyResourceFilePatches.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyResourceFilePatches.kt @@ -24,14 +24,12 @@ package io.papermc.paperweight.core.tasks.patching import io.codechicken.diffpatch.cli.PatchOperation import io.codechicken.diffpatch.match.FuzzyLineMatcher -import io.codechicken.diffpatch.util.ConsumingOutputStream import io.codechicken.diffpatch.util.Input as DiffInput import io.codechicken.diffpatch.util.Output as DiffOutput import io.codechicken.diffpatch.util.PatchMode import io.papermc.paperweight.PaperweightException import io.papermc.paperweight.tasks.* import io.papermc.paperweight.util.* -import java.io.PrintStream import java.nio.file.Path import java.time.Instant import kotlin.io.path.* @@ -213,9 +211,8 @@ abstract class ApplyResourceFilePatches : BaseTask() { } private fun applyWithDiffPatch(): Int? { - val printStream = PrintStream(ConsumingOutputStream { s -> logger.log(LogLevel.LIFECYCLE, s) }) val builder = PatchOperation.builder() - .logTo(printStream) + .logTo(logger::lifecycle) .baseInput(DiffInput.MultiInput.folder(output.path)) .patchesInput(DiffInput.MultiInput.folder(patches.path)) .patchedOutput(DiffOutput.MultiOutput.folder(output.path)) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/RebuildFilePatches.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/RebuildFilePatches.kt index 1b6c5694a..5fb4b685c 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/RebuildFilePatches.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/RebuildFilePatches.kt @@ -23,7 +23,6 @@ package io.papermc.paperweight.core.tasks.patching import io.codechicken.diffpatch.cli.DiffOperation -import io.codechicken.diffpatch.util.ConsumingOutputStream import io.codechicken.diffpatch.util.Input as DiffInput import io.codechicken.diffpatch.util.LogLevel import io.codechicken.diffpatch.util.Output as DiffOutput @@ -31,7 +30,6 @@ import io.papermc.paperweight.PaperweightException import io.papermc.paperweight.core.util.ApplySourceATs import io.papermc.paperweight.tasks.* import io.papermc.paperweight.util.* -import java.io.PrintStream import java.nio.file.Path import kotlin.io.path.* import org.cadixdev.at.AccessTransformSet @@ -197,9 +195,8 @@ abstract class RebuildFilePatches : JavaLauncherTask() { inputDir: Path, patchDir: Path ): Int? { - val printStream = PrintStream(ConsumingOutputStream { s -> logger.log(org.gradle.api.logging.LogLevel.LIFECYCLE, s) }) val result = DiffOperation.builder() - .logTo(printStream) + .logTo(logger::lifecycle) .baseInput(DiffInput.MultiInput.folder(baseDir)) .changedInput(DiffInput.MultiInput.folder(inputDir)) .patchesOutput(DiffOutput.MultiOutput.folder(patchDir)) From 1926a102a18aab3b5b62feaf15507bc9650c17c9 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Sat, 23 Aug 2025 00:26:53 +0200 Subject: [PATCH 052/257] major: Add patch generation from additional upstreams --- gradle.properties | 2 +- .../extension/AdditionalUpstreamConfig.kt | 49 ++++++++++ .../paperweight/core/tasks/GeneratePatches.kt | 94 +++++++++++++++++++ .../paperweight/patcher/PaperweightPatcher.kt | 25 +++++ .../extension/PaperweightPatcherExtension.kt | 5 + 5 files changed, 174 insertions(+), 1 deletion(-) create mode 100644 paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/AdditionalUpstreamConfig.kt create mode 100644 paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/GeneratePatches.kt diff --git a/gradle.properties b/gradle.properties index b3602eda1..a5bf4a2d3 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ group = io.canvasmc.weaver -version = 2.2.3-SNAPSHOT +version = 2.3.0-SNAPSHOT org.gradle.configuration-cache = true org.gradle.caching = true diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/AdditionalUpstreamConfig.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/AdditionalUpstreamConfig.kt new file mode 100644 index 000000000..2725c8906 --- /dev/null +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/AdditionalUpstreamConfig.kt @@ -0,0 +1,49 @@ +/* + * paperweight is a Gradle plugin for the PaperMC project. + * + * Copyright (c) 2023 Kyle Wood (DenWav) + * Contributors + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 only, no later versions. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + * USA + */ + +package io.papermc.paperweight.core.extension + +import io.papermc.paperweight.util.* +import javax.inject.Inject +import kotlin.io.path.* +import org.gradle.api.Named +import org.gradle.api.file.DirectoryProperty +import org.gradle.api.provider.ListProperty +import org.gradle.api.provider.Property +import org.gradle.kotlin.dsl.* + +abstract class AdditionalUpstreamConfig @Inject constructor( + private val configName: String, +) : Named { + override fun getName(): String { + return configName + } + + abstract val repo: Property + abstract val ref: Property + + abstract val apiDirs: ListProperty + abstract val serverDirs: ListProperty + abstract val patchesOutput: DirectoryProperty + + fun github(owner: String, repo: String): String = "https://github.com/$owner/$repo.git" +} diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/GeneratePatches.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/GeneratePatches.kt new file mode 100644 index 000000000..4322ee7d9 --- /dev/null +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/GeneratePatches.kt @@ -0,0 +1,94 @@ +/* + * paperweight is a Gradle plugin for the PaperMC project. + * + * Copyright (c) 2023 Kyle Wood (DenWav) + * Contributors + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 only, no later versions. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + * USA + */ + +package io.papermc.paperweight.core.tasks + +import io.papermc.paperweight.tasks.BaseTask +import io.papermc.paperweight.util.* +import java.nio.file.Path +import kotlin.io.path.* +import org.gradle.api.file.DirectoryProperty +import org.gradle.api.provider.ListProperty +import org.gradle.api.provider.Property +import org.gradle.api.tasks.* + +@UntrackedTask(because = "GeneratePatches should always run when requested") +abstract class GeneratePatches : BaseTask() { + @get:Input + abstract val upstreamName: Property + + @get:InputDirectory + abstract val workDir: DirectoryProperty + + @get:Input + abstract val apiDirs: ListProperty + + @get:Input + abstract val serverDirs: ListProperty + + @get:OutputDirectory + abstract val outputDir: DirectoryProperty + + @TaskAction + fun run() { + val outputDir = outputDir.path.cleanDir() + val name = upstreamName.get() + + val repositories: List = + apiDirs.get().map { workDir.path.resolve(it) } + + serverDirs.get().map { workDir.path.resolve(it) } + + for (repo in repositories) { + if (!repo.exists()) continue + + val git = Git(repo) + git("reset", "base", "--soft").runSilently(silenceErr = true) + git("add", ".").runSilently(silenceErr = true) + git("commit", "-m", "$name ${repo.fileName}", "--author=Generated Source ").runSilently(silenceErr = true) + git( + "format-patch", + "--diff-algorithm=myers", "--zero-commit", "--full-index", "--no-signature", "--no-stat", "-N", + "HEAD~1..HEAD", "-o", outputDir.absolutePathString() + ).runSilently(silenceErr = true) + cleanupPatch(name, repo.fileName.toString()) + } + val additionalRepositories: List = + listOf(workDir.path.resolve("$name/$name-api/src/main/java"), workDir.path.resolve("$name/$name-server/src/main/java")) + for (repo in additionalRepositories) { + val isApi = if (repo.toString().contains("-api")) true else false + val sourceOutput = if (isApi) outputDir.resolve("api-sources") else outputDir.resolve("server-sources") + repo.copyRecursivelyTo(sourceOutput) + } + } + fun cleanupPatch(name: String, identifier: String) { + val patchFile = outputDir.path.resolve("0001-$name-$identifier.patch") + val text = patchFile.readText() + val clean1 = text.replace("src/main/java/", "") + val clean2 = clean1.replace("$name java", "$name minecraft") + patchFile.writeText(clean2) + if (patchFile.toString().contains("java")) { + val new = outputDir.path.resolve("0001-$name-minecraft.patch") + new.writeText(clean2) + patchFile.deleteForcefully() + } + } +} diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/PaperweightPatcher.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/PaperweightPatcher.kt index 330f6ea3b..b996f0af2 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/PaperweightPatcher.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/PaperweightPatcher.kt @@ -24,6 +24,7 @@ package io.papermc.paperweight.patcher import io.papermc.paperweight.core.taskcontainers.UpstreamConfigTasks import io.papermc.paperweight.core.tasks.CheckoutRepo +import io.papermc.paperweight.core.tasks.GeneratePatches import io.papermc.paperweight.core.tasks.RunNestedBuild import io.papermc.paperweight.patcher.extension.PaperweightPatcherExtension import io.papermc.paperweight.util.* @@ -116,5 +117,29 @@ abstract class PaperweightPatcher : Plugin { dependsOn(depend) } } + patcher.additionalUpstreams.forEach { upstream -> + val checkoutTask = tasks.register("checkout${upstream.name.capitalized()}Repo") { + repoName.set(upstream.name) + url.set(upstream.repo) + ref.set(upstream.ref) + workDir.set(workDirFromProp) + } + + val applyAdditionalUpstream = tasks.register("apply${upstream.name.capitalized()}ForPatchGeneration") { + projectDir.set(checkoutTask.flatMap { it.outputDir }) + tasks.add("applyAllPatches") + } + + tasks.register("generate${upstream.name.capitalized()}Patches") { + dependsOn(applyAdditionalUpstream) + group = "patch generation" + description = "Generates base patches from ${upstream.name.capitalized()}" + upstreamName.set(upstream.name) + workDir.set(workDirFromProp) + apiDirs.set(upstream.apiDirs) + serverDirs.set(upstream.serverDirs) + outputDir.set(upstream.patchesOutput) + } + } } } diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/extension/PaperweightPatcherExtension.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/extension/PaperweightPatcherExtension.kt index ed530519d..cc978399a 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/extension/PaperweightPatcherExtension.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/extension/PaperweightPatcherExtension.kt @@ -22,6 +22,7 @@ package io.papermc.paperweight.patcher.extension +import io.papermc.paperweight.core.extension.AdditionalUpstreamConfig import io.papermc.paperweight.core.extension.UpstreamConfig import io.papermc.paperweight.util.constants.PAPER_MAVEN_REPO_URL import javax.inject.Inject @@ -43,6 +44,10 @@ abstract class PaperweightPatcherExtension @Inject constructor(private val objec objects.newInstance(it, true) } + val additionalUpstreams: NamedDomainObjectContainer = objects.domainObjectContainer(AdditionalUpstreamConfig::class) { + objects.newInstance(it) + } + fun NamedDomainObjectContainer.paper( op: Action ): NamedDomainObjectProvider = register("paper") { From ed4cea308f3cc2a0d9170f846030c68e2524691a Mon Sep 17 00:00:00 2001 From: Toffikk Date: Sat, 23 Aug 2025 11:43:34 +0200 Subject: [PATCH 053/257] fix: dont redact src/main/java --- .../io/papermc/paperweight/core/tasks/GeneratePatches.kt | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/GeneratePatches.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/GeneratePatches.kt index 4322ee7d9..782536f10 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/GeneratePatches.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/GeneratePatches.kt @@ -81,13 +81,11 @@ abstract class GeneratePatches : BaseTask() { } fun cleanupPatch(name: String, identifier: String) { val patchFile = outputDir.path.resolve("0001-$name-$identifier.patch") - val text = patchFile.readText() - val clean1 = text.replace("src/main/java/", "") - val clean2 = clean1.replace("$name java", "$name minecraft") - patchFile.writeText(clean2) if (patchFile.toString().contains("java")) { + val text = patchFile.readText() + val clean = text.replace("[PATCH] $name java", "[PATCH] $name minecraft") val new = outputDir.path.resolve("0001-$name-minecraft.patch") - new.writeText(clean2) + new.writeText(clean) patchFile.deleteForcefully() } } From c3ed6231edef6a55bb9bec27ced31f77bb5b5a36 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Sat, 23 Aug 2025 12:32:20 +0200 Subject: [PATCH 054/257] refactor: move to patchGeneration {} --- .../core/extension/AdditionalUpstreamConfig.kt | 17 ++++++++++++++--- .../paperweight/patcher/PaperweightPatcher.kt | 8 ++++---- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/AdditionalUpstreamConfig.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/AdditionalUpstreamConfig.kt index 2725c8906..cea2e1dd7 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/AdditionalUpstreamConfig.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/AdditionalUpstreamConfig.kt @@ -25,14 +25,18 @@ package io.papermc.paperweight.core.extension import io.papermc.paperweight.util.* import javax.inject.Inject import kotlin.io.path.* +import org.gradle.api.Action import org.gradle.api.Named import org.gradle.api.file.DirectoryProperty +import org.gradle.api.model.ObjectFactory import org.gradle.api.provider.ListProperty import org.gradle.api.provider.Property import org.gradle.kotlin.dsl.* +import org.gradle.kotlin.dsl.newInstance abstract class AdditionalUpstreamConfig @Inject constructor( private val configName: String, + objects: ObjectFactory, ) : Named { override fun getName(): String { return configName @@ -40,10 +44,17 @@ abstract class AdditionalUpstreamConfig @Inject constructor( abstract val repo: Property abstract val ref: Property + val patchGenerationConfig: PatchGenerationConfig = objects.newInstance() - abstract val apiDirs: ListProperty - abstract val serverDirs: ListProperty - abstract val patchesOutput: DirectoryProperty + fun patchGeneration(op: Action) { + op.execute(patchGenerationConfig) + } + + abstract class PatchGenerationConfig { + abstract val apiDirs: ListProperty + abstract val serverDirs: ListProperty + abstract val patchesOutput: DirectoryProperty + } fun github(owner: String, repo: String): String = "https://github.com/$owner/$repo.git" } diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/PaperweightPatcher.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/PaperweightPatcher.kt index b996f0af2..5fc13dc5a 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/PaperweightPatcher.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/PaperweightPatcher.kt @@ -118,7 +118,7 @@ abstract class PaperweightPatcher : Plugin { } } patcher.additionalUpstreams.forEach { upstream -> - val checkoutTask = tasks.register("checkout${upstream.name.capitalized()}Repo") { + val checkoutTask = tasks.register("checkout${upstream.name.capitalized()}RepoForPatchGeneration") { repoName.set(upstream.name) url.set(upstream.repo) ref.set(upstream.ref) @@ -136,9 +136,9 @@ abstract class PaperweightPatcher : Plugin { description = "Generates base patches from ${upstream.name.capitalized()}" upstreamName.set(upstream.name) workDir.set(workDirFromProp) - apiDirs.set(upstream.apiDirs) - serverDirs.set(upstream.serverDirs) - outputDir.set(upstream.patchesOutput) + apiDirs.set(upstream.patchGenerationConfig.apiDirs) + serverDirs.set(upstream.patchGenerationConfig.serverDirs) + outputDir.set(upstream.patchGenerationConfig.patchesOutput) } } } From da4d69d44c00a0b00c8cfecee0bdcbacbe553ce4 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Sat, 23 Aug 2025 20:22:02 +0200 Subject: [PATCH 055/257] make the outputted patches prettier :tm: --- .../extension/AdditionalUpstreamConfig.kt | 5 +-- .../paperweight/core/tasks/GeneratePatches.kt | 42 +++++++++++++------ .../paperweight/patcher/PaperweightPatcher.kt | 7 ++-- 3 files changed, 36 insertions(+), 18 deletions(-) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/AdditionalUpstreamConfig.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/AdditionalUpstreamConfig.kt index cea2e1dd7..769a880a5 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/AdditionalUpstreamConfig.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/AdditionalUpstreamConfig.kt @@ -51,9 +51,8 @@ abstract class AdditionalUpstreamConfig @Inject constructor( } abstract class PatchGenerationConfig { - abstract val apiDirs: ListProperty - abstract val serverDirs: ListProperty - abstract val patchesOutput: DirectoryProperty + abstract val inputFrom: ListProperty + abstract val outputDir: DirectoryProperty } fun github(owner: String, repo: String): String = "https://github.com/$owner/$repo.git" diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/GeneratePatches.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/GeneratePatches.kt index 782536f10..80723aacd 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/GeneratePatches.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/GeneratePatches.kt @@ -36,14 +36,17 @@ abstract class GeneratePatches : BaseTask() { @get:Input abstract val upstreamName: Property - @get:InputDirectory - abstract val workDir: DirectoryProperty + @get:Input + abstract val upstreamLink: Property @get:Input - abstract val apiDirs: ListProperty + abstract val upstreamHash: Property + + @get:InputDirectory + abstract val workDir: DirectoryProperty @get:Input - abstract val serverDirs: ListProperty + abstract val inputFrom: ListProperty @get:OutputDirectory abstract val outputDir: DirectoryProperty @@ -52,24 +55,39 @@ abstract class GeneratePatches : BaseTask() { fun run() { val outputDir = outputDir.path.cleanDir() val name = upstreamName.get() + val upstreamUrl = upstreamLink.get() + val url = upstreamUrl.removeSuffix(".git") val repositories: List = - apiDirs.get().map { workDir.path.resolve(it) } + - serverDirs.get().map { workDir.path.resolve(it) } + inputFrom.get().map { workDir.path.resolve("$name/$it") } for (repo in repositories) { if (!repo.exists()) continue + val isApi = if (repo.fileName.toString().contains("-api")) true else false + val repoName = if (isApi) { + repo.fileName.toString().split("-").joinToString("") { it.replaceFirstChar { char -> char.uppercase() } }.replace("Api", "API") + } else { + repo.fileName.toString().split("-").joinToString("") { it.replaceFirstChar { char -> char.uppercase() } } + } + val git = Git(repo) git("reset", "base", "--soft").runSilently(silenceErr = true) git("add", ".").runSilently(silenceErr = true) - git("commit", "-m", "$name ${repo.fileName}", "--author=Generated Source ").runSilently(silenceErr = true) + git( + "commit", + "-m", + "${name.capitalized()} $repoName Patches", + "-m", + "Patch generated from $url/commit/${upstreamHash.get()}", + "--author=Generated Source " + ).runSilently(silenceErr = true) git( "format-patch", "--diff-algorithm=myers", "--zero-commit", "--full-index", "--no-signature", "--no-stat", "-N", "HEAD~1..HEAD", "-o", outputDir.absolutePathString() ).runSilently(silenceErr = true) - cleanupPatch(name, repo.fileName.toString()) + cleanupPatch(name.capitalized(), repoName) } val additionalRepositories: List = listOf(workDir.path.resolve("$name/$name-api/src/main/java"), workDir.path.resolve("$name/$name-server/src/main/java")) @@ -80,11 +98,11 @@ abstract class GeneratePatches : BaseTask() { } } fun cleanupPatch(name: String, identifier: String) { - val patchFile = outputDir.path.resolve("0001-$name-$identifier.patch") - if (patchFile.toString().contains("java")) { + val patchFile = outputDir.path.resolve("0001-$name-$identifier-Patches.patch") + if (patchFile.toString().contains("Java")) { val text = patchFile.readText() - val clean = text.replace("[PATCH] $name java", "[PATCH] $name minecraft") - val new = outputDir.path.resolve("0001-$name-minecraft.patch") + val clean = text.replace("[PATCH] $name Java Patches", "[PATCH] $name Minecraft Patches") + val new = outputDir.path.resolve("0001-$name-Minecraft-Patches.patch") new.writeText(clean) patchFile.deleteForcefully() } diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/PaperweightPatcher.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/PaperweightPatcher.kt index 5fc13dc5a..d1a826137 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/PaperweightPatcher.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/PaperweightPatcher.kt @@ -135,10 +135,11 @@ abstract class PaperweightPatcher : Plugin { group = "patch generation" description = "Generates base patches from ${upstream.name.capitalized()}" upstreamName.set(upstream.name) + upstreamLink.set(upstream.repo) + upstreamHash.set(upstream.ref) workDir.set(workDirFromProp) - apiDirs.set(upstream.patchGenerationConfig.apiDirs) - serverDirs.set(upstream.patchGenerationConfig.serverDirs) - outputDir.set(upstream.patchGenerationConfig.patchesOutput) + inputFrom.set(upstream.patchGenerationConfig.inputFrom) + outputDir.set(upstream.patchGenerationConfig.outputDir) } } } From e96a0c1ac1db92b4795d4d7795408690059da0cf Mon Sep 17 00:00:00 2001 From: Toffikk Date: Sat, 23 Aug 2025 22:16:48 +0200 Subject: [PATCH 056/257] ATs and additional patches for patch gen --- .../extension/AdditionalUpstreamConfig.kt | 26 +++++- .../core/tasks/PrepareForPatchGeneration.kt | 81 +++++++++++++++++++ .../paperweight/patcher/PaperweightPatcher.kt | 22 ++++- 3 files changed, 127 insertions(+), 2 deletions(-) create mode 100644 paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/PrepareForPatchGeneration.kt diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/AdditionalUpstreamConfig.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/AdditionalUpstreamConfig.kt index 769a880a5..ba5cf8719 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/AdditionalUpstreamConfig.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/AdditionalUpstreamConfig.kt @@ -23,11 +23,14 @@ package io.papermc.paperweight.core.extension import io.papermc.paperweight.util.* +import javax.annotation.Nullable import javax.inject.Inject import kotlin.io.path.* import org.gradle.api.Action import org.gradle.api.Named +import org.gradle.api.NamedDomainObjectContainer import org.gradle.api.file.DirectoryProperty +import org.gradle.api.file.RegularFileProperty import org.gradle.api.model.ObjectFactory import org.gradle.api.provider.ListProperty import org.gradle.api.provider.Property @@ -50,9 +53,30 @@ abstract class AdditionalUpstreamConfig @Inject constructor( op.execute(patchGenerationConfig) } - abstract class PatchGenerationConfig { + abstract class PatchGenerationConfig @Inject constructor( + objects: ObjectFactory + ) { abstract val inputFrom: ListProperty abstract val outputDir: DirectoryProperty + + val repoConfig: NamedDomainObjectContainer = objects.domainObjectContainer( + RepoConfig::class + ) { name -> objects.newInstance(name, objects) } + + abstract class RepoConfig @Inject constructor( + private val setName: String, + objects: ObjectFactory, + ) : Named { + override fun getName(): String { + return setName + } + + @get:Nullable + abstract val additionalAts: RegularFileProperty + + @get:Nullable + abstract val additionalPatch: RegularFileProperty + } } fun github(owner: String, repo: String): String = "https://github.com/$owner/$repo.git" diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/PrepareForPatchGeneration.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/PrepareForPatchGeneration.kt new file mode 100644 index 000000000..7e1ca499b --- /dev/null +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/PrepareForPatchGeneration.kt @@ -0,0 +1,81 @@ +/* + * paperweight is a Gradle plugin for the PaperMC project. + * + * Copyright (c) 2023 Kyle Wood (DenWav) + * Contributors + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 only, no later versions. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + * USA + */ + +package io.papermc.paperweight.core.tasks + +import io.papermc.paperweight.core.util.ApplySourceATs +import io.papermc.paperweight.tasks.JavaLauncherTask +import io.papermc.paperweight.util.* +import io.papermc.paperweight.util.constants.JST_CONFIG +import kotlin.io.path.* +import org.gradle.api.file.DirectoryProperty +import org.gradle.api.file.RegularFileProperty +import org.gradle.api.provider.Property +import org.gradle.api.tasks.* +import org.gradle.kotlin.dsl.newInstance + +@UntrackedTask(because = "PrepareForPatchGeneration should always run when requested") +abstract class PrepareForPatchGeneration : JavaLauncherTask() { + + @get:InputDirectory + abstract val workDir: DirectoryProperty + + @get:Input + abstract val upstreamName: Property + + @get:Input + abstract val repoName: Property + + @get:Nested + val ats: ApplySourceATs = objects.newInstance().apply { + jst.from(project.configurations.named(JST_CONFIG)) + } + + @get:InputFile + @get:Optional + abstract val atFile: RegularFileProperty + + @get:InputFile + @get:Optional + abstract val additionalPatch: RegularFileProperty + + @TaskAction + fun run() { + val name = if (repoName.get().equals("minecraft")) "${upstreamName.get()}-server/src/minecraft/java" else repoName.get() + val outputDir = workDir.path.resolve("${upstreamName.get()}/$name") + + if (additionalPatch.isPresent) { + val git = Git(outputDir) + git("am", "--3way", "--ignore-whitespace", additionalPatch.path.toString()).captureOut(false) + } + + if (atFile.isPresent && atFile.path.readText().isNotBlank()) { + ats.run( + launcher.get(), + outputDir, + outputDir, + atFile.path, + temporaryDir.toPath() + ) + } + } +} diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/PaperweightPatcher.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/PaperweightPatcher.kt index d1a826137..318e7782a 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/PaperweightPatcher.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/PaperweightPatcher.kt @@ -25,6 +25,7 @@ package io.papermc.paperweight.patcher import io.papermc.paperweight.core.taskcontainers.UpstreamConfigTasks import io.papermc.paperweight.core.tasks.CheckoutRepo import io.papermc.paperweight.core.tasks.GeneratePatches +import io.papermc.paperweight.core.tasks.PrepareForPatchGeneration import io.papermc.paperweight.core.tasks.RunNestedBuild import io.papermc.paperweight.patcher.extension.PaperweightPatcherExtension import io.papermc.paperweight.util.* @@ -130,8 +131,27 @@ abstract class PaperweightPatcher : Plugin { tasks.add("applyAllPatches") } + val depend = upstream.patchGenerationConfig.repoConfig.map { repo -> + val name = if (repo.name.equals("minecraft")) { + "Minecraft" + } else { + repo.name.split("-").joinToString("") { + it.replaceFirstChar { char -> char.uppercase() } + } + } + tasks.register("prepare${upstream.name.capitalized()}${name}ForPatchGeneration") { + group = "patch generation" + description = "Prepares ${upstream.name} ${repo.name} for patch generation" + dependsOn(applyAdditionalUpstream) + upstreamName.set(upstream.name) + repoName.set(repo.name) + workDir.set(workDirFromProp) + atFile.set(repo.additionalAts.fileExists(project)) + additionalPatch.set(repo.additionalPatch.fileExists(project)) + } + } tasks.register("generate${upstream.name.capitalized()}Patches") { - dependsOn(applyAdditionalUpstream) + dependsOn(depend) group = "patch generation" description = "Generates base patches from ${upstream.name.capitalized()}" upstreamName.set(upstream.name) From 58bde0bf14c74b5a2529f77fbc23f4df23e4384d Mon Sep 17 00:00:00 2001 From: Toffikk Date: Sat, 23 Aug 2025 22:43:28 +0200 Subject: [PATCH 057/257] also prepend // generated to .java sources --- .../io/papermc/paperweight/core/tasks/GeneratePatches.kt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/GeneratePatches.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/GeneratePatches.kt index 80723aacd..08939ceb4 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/GeneratePatches.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/GeneratePatches.kt @@ -95,6 +95,11 @@ abstract class GeneratePatches : BaseTask() { val isApi = if (repo.toString().contains("-api")) true else false val sourceOutput = if (isApi) outputDir.resolve("api-sources") else outputDir.resolve("server-sources") repo.copyRecursivelyTo(sourceOutput) + + sourceOutput.filesMatchingRecursive("*.java").forEach { + val content = it.readText() + it.writeText("// Generated from $url/commit/${upstreamHash.get()}\n$content") + } } } fun cleanupPatch(name: String, identifier: String) { From d9fca52e3424ef5f1739238825a24bd92feab5b6 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Sun, 24 Aug 2025 00:19:33 +0200 Subject: [PATCH 058/257] qol: allow to auto move patches to their dirs --- .../extension/AdditionalUpstreamConfig.kt | 1 + .../paperweight/core/tasks/GeneratePatches.kt | 42 +++++++++++++------ .../paperweight/patcher/PaperweightPatcher.kt | 3 ++ 3 files changed, 33 insertions(+), 13 deletions(-) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/AdditionalUpstreamConfig.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/AdditionalUpstreamConfig.kt index ba5cf8719..3fef88d0e 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/AdditionalUpstreamConfig.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/AdditionalUpstreamConfig.kt @@ -56,6 +56,7 @@ abstract class AdditionalUpstreamConfig @Inject constructor( abstract class PatchGenerationConfig @Inject constructor( objects: ObjectFactory ) { + abstract val patchDirOutput: Property abstract val inputFrom: ListProperty abstract val outputDir: DirectoryProperty diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/GeneratePatches.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/GeneratePatches.kt index 08939ceb4..22090760d 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/GeneratePatches.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/GeneratePatches.kt @@ -51,8 +51,18 @@ abstract class GeneratePatches : BaseTask() { @get:OutputDirectory abstract val outputDir: DirectoryProperty + @get:Input + abstract val rootName: Property + + @get:InputDirectory + abstract val rootDir: DirectoryProperty + + @get:Input + abstract val patchDirOutput: Property + @TaskAction fun run() { + val outputToPatches = patchDirOutput.get() val outputDir = outputDir.path.cleanDir() val name = upstreamName.get() val upstreamUrl = upstreamLink.get() @@ -67,10 +77,13 @@ abstract class GeneratePatches : BaseTask() { val isApi = if (repo.fileName.toString().contains("-api")) true else false val repoName = if (isApi) { repo.fileName.toString().split("-").joinToString("") { it.replaceFirstChar { char -> char.uppercase() } }.replace("Api", "API") + } else if (repo.fileName.toString().equals("java")) { + "Minecraft" } else { repo.fileName.toString().split("-").joinToString("") { it.replaceFirstChar { char -> char.uppercase() } } } + val cutRepo = repo.fileName.toString().substringBefore("-") val git = Git(repo) git("reset", "base", "--soft").runSilently(silenceErr = true) git("add", ".").runSilently(silenceErr = true) @@ -85,15 +98,28 @@ abstract class GeneratePatches : BaseTask() { git( "format-patch", "--diff-algorithm=myers", "--zero-commit", "--full-index", "--no-signature", "--no-stat", "-N", - "HEAD~1..HEAD", "-o", outputDir.absolutePathString() + "HEAD~1..HEAD", "-o", + if (outputToPatches && isApi) { + rootDir.convertToPath().resolve("${rootName.get()}-api/$cutRepo-patches/base").absolutePathString() + } else if (outputToPatches && cutRepo.equals("java")) { + rootDir.convertToPath().resolve("${rootName.get()}-server/minecraft-patches/base").absolutePathString() + } else if (outputToPatches) { + rootDir.convertToPath().resolve("${rootName.get()}-server/$cutRepo-patches/base").absolutePathString() + } else { + outputDir.absolutePathString() + } ).runSilently(silenceErr = true) - cleanupPatch(name.capitalized(), repoName) } val additionalRepositories: List = listOf(workDir.path.resolve("$name/$name-api/src/main/java"), workDir.path.resolve("$name/$name-server/src/main/java")) for (repo in additionalRepositories) { val isApi = if (repo.toString().contains("-api")) true else false - val sourceOutput = if (isApi) outputDir.resolve("api-sources") else outputDir.resolve("server-sources") + val sourceOutput = if (isApi) { + rootDir.convertToPath().resolve("${rootName.get()}-api/src/generated/java") + } else { + rootDir.convertToPath().resolve("${rootName.get()}-server/src/generated/java") + } + sourceOutput.deleteRecursively() repo.copyRecursivelyTo(sourceOutput) sourceOutput.filesMatchingRecursive("*.java").forEach { @@ -102,14 +128,4 @@ abstract class GeneratePatches : BaseTask() { } } } - fun cleanupPatch(name: String, identifier: String) { - val patchFile = outputDir.path.resolve("0001-$name-$identifier-Patches.patch") - if (patchFile.toString().contains("Java")) { - val text = patchFile.readText() - val clean = text.replace("[PATCH] $name Java Patches", "[PATCH] $name Minecraft Patches") - val new = outputDir.path.resolve("0001-$name-Minecraft-Patches.patch") - new.writeText(clean) - patchFile.deleteForcefully() - } - } } diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/PaperweightPatcher.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/PaperweightPatcher.kt index 318e7782a..9188863d1 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/PaperweightPatcher.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/PaperweightPatcher.kt @@ -154,10 +154,13 @@ abstract class PaperweightPatcher : Plugin { dependsOn(depend) group = "patch generation" description = "Generates base patches from ${upstream.name.capitalized()}" + rootName.set(rootProject.name) + rootDir.set(rootProject.rootDir) upstreamName.set(upstream.name) upstreamLink.set(upstream.repo) upstreamHash.set(upstream.ref) workDir.set(workDirFromProp) + patchDirOutput.set(upstream.patchGenerationConfig.patchDirOutput) inputFrom.set(upstream.patchGenerationConfig.inputFrom) outputDir.set(upstream.patchGenerationConfig.outputDir) } From 3dd478c4e8ebe3603939c9bae05b24a2973e5655 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Sun, 24 Aug 2025 00:58:27 +0200 Subject: [PATCH 059/257] chore: allow null outputDir when patchDirOutput is enabled --- .../io/papermc/paperweight/core/tasks/GeneratePatches.kt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/GeneratePatches.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/GeneratePatches.kt index 22090760d..271ea98de 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/GeneratePatches.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/GeneratePatches.kt @@ -22,6 +22,7 @@ package io.papermc.paperweight.core.tasks +import io.papermc.paperweight.PaperweightException import io.papermc.paperweight.tasks.BaseTask import io.papermc.paperweight.util.* import java.nio.file.Path @@ -49,6 +50,7 @@ abstract class GeneratePatches : BaseTask() { abstract val inputFrom: ListProperty @get:OutputDirectory + @get:Optional abstract val outputDir: DirectoryProperty @get:Input @@ -63,7 +65,7 @@ abstract class GeneratePatches : BaseTask() { @TaskAction fun run() { val outputToPatches = patchDirOutput.get() - val outputDir = outputDir.path.cleanDir() + val outputDir = if (!outputToPatches) outputDir.path.cleanDir() else null val name = upstreamName.get() val upstreamUrl = upstreamLink.get() val url = upstreamUrl.removeSuffix(".git") @@ -106,7 +108,7 @@ abstract class GeneratePatches : BaseTask() { } else if (outputToPatches) { rootDir.convertToPath().resolve("${rootName.get()}-server/$cutRepo-patches/base").absolutePathString() } else { - outputDir.absolutePathString() + if (outputDir != null) outputDir.absolutePathString() else throw PaperweightException("Cannot find an output directory and patchDirOutput is set to false!") } ).runSilently(silenceErr = true) } From 741a3a1236397fe3a0030616db2a9799028318b3 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Sun, 24 Aug 2025 01:34:42 +0200 Subject: [PATCH 060/257] fix: spotless and optionals --- .../paperweight/core/tasks/GeneratePatches.kt | 21 ++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/GeneratePatches.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/GeneratePatches.kt index 271ea98de..c7e606c2a 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/GeneratePatches.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/GeneratePatches.kt @@ -60,12 +60,21 @@ abstract class GeneratePatches : BaseTask() { abstract val rootDir: DirectoryProperty @get:Input + @get:Optional abstract val patchDirOutput: Property @TaskAction fun run() { - val outputToPatches = patchDirOutput.get() - val outputDir = if (!outputToPatches) outputDir.path.cleanDir() else null + val outputToPatches = patchDirOutput.getOrElse(false) + val outputDir = if (!outputToPatches && outputDir.isPresent) { + outputDir.path.cleanDir() + } else if (!outputToPatches && !outputDir.isPresent) { + throw PaperweightException( + "Cannot find an output directory and patchDirOutput is set to false!" + ) + } else { + null + } val name = upstreamName.get() val upstreamUrl = upstreamLink.get() val url = upstreamUrl.removeSuffix(".git") @@ -108,7 +117,13 @@ abstract class GeneratePatches : BaseTask() { } else if (outputToPatches) { rootDir.convertToPath().resolve("${rootName.get()}-server/$cutRepo-patches/base").absolutePathString() } else { - if (outputDir != null) outputDir.absolutePathString() else throw PaperweightException("Cannot find an output directory and patchDirOutput is set to false!") + if (outputDir != null) { + outputDir.absolutePathString() + } else { + throw PaperweightException( + "Cannot find an output directory and patchDirOutput is set to false!" + ) + } } ).runSilently(silenceErr = true) } From a1d3456cae437ddc9a0ccf6b67940c2ec7817ab3 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Sun, 24 Aug 2025 02:51:54 +0200 Subject: [PATCH 061/257] fix: overwrite files by default in generateDevBundle --- .../kotlin/io/papermc/paperweight/tasks/GenerateDevBundle.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/GenerateDevBundle.kt b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/GenerateDevBundle.kt index 897a993ca..6717d4227 100644 --- a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/GenerateDevBundle.kt +++ b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/GenerateDevBundle.kt @@ -125,7 +125,7 @@ abstract class GenerateDevBundle : BaseTask() { if (file.isDirectory()) { workingDir.resolve(path).createDirectories() } else { - file.toPath().copyTo(workingDir.resolve(path)) + file.toPath().copyTo(workingDir.resolve(path), overwrite = true) } } workingDir.resolve(".git").deleteRecursive() From 19db55d0000df1648b0af8e41e91e3f4f66c0223 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Sun, 24 Aug 2025 03:43:23 +0200 Subject: [PATCH 062/257] fix regression introduced by RepoConfig --- .../kotlin/io/papermc/paperweight/patcher/PaperweightPatcher.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/PaperweightPatcher.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/PaperweightPatcher.kt index 9188863d1..46ed2620e 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/PaperweightPatcher.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/PaperweightPatcher.kt @@ -151,7 +151,7 @@ abstract class PaperweightPatcher : Plugin { } } tasks.register("generate${upstream.name.capitalized()}Patches") { - dependsOn(depend) + if (depend.isNotEmpty()) dependsOn(depend) else dependsOn(applyAdditionalUpstream) group = "patch generation" description = "Generates base patches from ${upstream.name.capitalized()}" rootName.set(rootProject.name) From b6dca1a9cf09955faffbcc983b19973451bf9eb5 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Sun, 24 Aug 2025 04:00:58 +0200 Subject: [PATCH 063/257] fix: skip patch gen when repo is empty --- .../kotlin/io/papermc/paperweight/core/tasks/GeneratePatches.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/GeneratePatches.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/GeneratePatches.kt index c7e606c2a..6c34e462b 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/GeneratePatches.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/GeneratePatches.kt @@ -97,6 +97,8 @@ abstract class GeneratePatches : BaseTask() { val cutRepo = repo.fileName.toString().substringBefore("-") val git = Git(repo) git("reset", "base", "--soft").runSilently(silenceErr = true) + val toCommit = git("status", "--porcelain").getText().trim() + if (toCommit.isBlank()) continue git("add", ".").runSilently(silenceErr = true) git( "commit", From 7d2e93b3c22319f6d1419ab7d7d3cb5617e64ed3 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Sun, 24 Aug 2025 04:01:24 +0200 Subject: [PATCH 064/257] chore: continue development cycle --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index a5bf4a2d3..02d65f73d 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ group = io.canvasmc.weaver -version = 2.3.0-SNAPSHOT +version = 2.3.1-SNAPSHOT org.gradle.configuration-cache = true org.gradle.caching = true From 0cb27f131b69c052a803910afe2e34c8e9abb2c0 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Mon, 25 Aug 2025 05:34:59 +0200 Subject: [PATCH 065/257] refactor patch/source generation Allows for a lot more robust configuration --- .../extension/AdditionalUpstreamConfig.kt | 48 ++- .../paperweight/core/tasks/GeneratePatches.kt | 279 ++++++++++++------ .../core/tasks/PrepareForPatchGeneration.kt | 40 ++- .../paperweight/patcher/PaperweightPatcher.kt | 75 +++-- 4 files changed, 315 insertions(+), 127 deletions(-) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/AdditionalUpstreamConfig.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/AdditionalUpstreamConfig.kt index 3fef88d0e..799891ba0 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/AdditionalUpstreamConfig.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/AdditionalUpstreamConfig.kt @@ -32,7 +32,6 @@ import org.gradle.api.NamedDomainObjectContainer import org.gradle.api.file.DirectoryProperty import org.gradle.api.file.RegularFileProperty import org.gradle.api.model.ObjectFactory -import org.gradle.api.provider.ListProperty import org.gradle.api.provider.Property import org.gradle.kotlin.dsl.* import org.gradle.kotlin.dsl.newInstance @@ -47,24 +46,61 @@ abstract class AdditionalUpstreamConfig @Inject constructor( abstract val repo: Property abstract val ref: Property + val sourceGenerationConfig: SourceGenerationConfig = objects.newInstance() val patchGenerationConfig: PatchGenerationConfig = objects.newInstance() + fun sourceGeneration(op: Action) { + op.execute(sourceGenerationConfig) + } + fun patchGeneration(op: Action) { op.execute(patchGenerationConfig) } + @Suppress("UNUSED_PARAMETER") + abstract class SourceGenerationConfig @Inject constructor( + objects: ObjectFactory, + ) { + @get:Nullable + abstract val serverSourceOutput: DirectoryProperty + + @get:Nullable + abstract val apiSourceOutput: DirectoryProperty + + @get:Nullable + abstract val serverTestSourceOutput: DirectoryProperty + + @get:Nullable + abstract val apiTestSourceOutput: DirectoryProperty + + @get:Nullable + abstract val serverResourcesOutput: DirectoryProperty + + @get:Nullable + abstract val apiResourcesOutput: DirectoryProperty + + @get:Nullable + abstract val serverTestResourcesOutput: DirectoryProperty + + @get:Nullable + abstract val apiTestResourcesOutput: DirectoryProperty + } + abstract class PatchGenerationConfig @Inject constructor( objects: ObjectFactory ) { - abstract val patchDirOutput: Property - abstract val inputFrom: ListProperty + @get:Nullable abstract val outputDir: DirectoryProperty - val repoConfig: NamedDomainObjectContainer = objects.domainObjectContainer( - RepoConfig::class + @get:Nullable + abstract val patchesDirOutput: Property + + val inputConfig: NamedDomainObjectContainer = objects.domainObjectContainer( + InputConfig::class ) { name -> objects.newInstance(name, objects) } - abstract class RepoConfig @Inject constructor( + @Suppress("UNUSED_PARAMETER") + abstract class InputConfig @Inject constructor( private val setName: String, objects: ObjectFactory, ) : Named { diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/GeneratePatches.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/GeneratePatches.kt index 6c34e462b..9b1b72efe 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/GeneratePatches.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/GeneratePatches.kt @@ -22,129 +22,232 @@ package io.papermc.paperweight.core.tasks -import io.papermc.paperweight.PaperweightException import io.papermc.paperweight.tasks.BaseTask import io.papermc.paperweight.util.* +import io.papermc.paperweight.util.constants.paperTaskOutput import java.nio.file.Path import kotlin.io.path.* +import org.gradle.api.file.ConfigurableFileCollection import org.gradle.api.file.DirectoryProperty -import org.gradle.api.provider.ListProperty import org.gradle.api.provider.Property import org.gradle.api.tasks.* +import org.gradle.kotlin.dsl.support.uppercaseFirstChar -@UntrackedTask(because = "GeneratePatches should always run when requested") abstract class GeneratePatches : BaseTask() { @get:Input - abstract val upstreamName: Property + abstract val forkName: Property @get:Input - abstract val upstreamLink: Property + abstract val forkUrl: Property @get:Input - abstract val upstreamHash: Property + abstract val commitHash: Property + + @get:Input + abstract val inputFrom: Property @get:InputDirectory + @get:PathSensitive(PathSensitivity.RELATIVE) abstract val workDir: DirectoryProperty + @get:InputFiles + @get:PathSensitive(PathSensitivity.RELATIVE) + abstract val preparedSource: ConfigurableFileCollection + @get:Input - abstract val inputFrom: ListProperty + @get:Optional + abstract val patchesDirOutput: Property + + @get:Internal + abstract val serverSourceInput: DirectoryProperty + + @get:Internal + abstract val apiSourceInput: DirectoryProperty + + @get:Internal + abstract val serverTestSourceInput: DirectoryProperty + + @get:Internal + abstract val apiTestSourceInput: DirectoryProperty + + @get:Internal + abstract val serverResourcesInput: DirectoryProperty + + @get:Internal + abstract val apiResourcesInput: DirectoryProperty + + @get:Internal + abstract val serverTestResourcesInput: DirectoryProperty + + @get:Internal + abstract val apiTestResourcesInput: DirectoryProperty @get:OutputDirectory - @get:Optional - abstract val outputDir: DirectoryProperty + abstract val serverSourceOutput: DirectoryProperty - @get:Input - abstract val rootName: Property + @get:OutputDirectory + abstract val apiSourceOutput: DirectoryProperty - @get:InputDirectory - abstract val rootDir: DirectoryProperty + @get:OutputDirectory + abstract val serverTestSourceOutput: DirectoryProperty - @get:Input + @get:OutputDirectory + abstract val apiTestSourceOutput: DirectoryProperty + + @get:OutputDirectory + abstract val serverResourcesOutput: DirectoryProperty + + @get:OutputDirectory + abstract val apiResourcesOutput: DirectoryProperty + + @get:OutputDirectory + abstract val serverTestResourcesOutput: DirectoryProperty + + @get:OutputDirectory + abstract val apiTestResourcesOutput: DirectoryProperty + + @get:OutputDirectory + abstract val serverProjectDir: DirectoryProperty + + @get:OutputDirectory + abstract val apiProjectDir: DirectoryProperty + + @get:OutputDirectory @get:Optional - abstract val patchDirOutput: Property + abstract val outputDir: DirectoryProperty + + @get:Internal + abstract val tempOutput: DirectoryProperty + + override fun init() { + tempOutput.set(layout.buildDirectory.dir(paperTaskOutput(name))) + serverSourceInput.convention(forkName.flatMap { workDir.dir("$it/$it-server/src/main/java") }) + serverTestSourceInput.convention(forkName.flatMap { workDir.dir("$it/$it-server/src/test/java") }) + serverResourcesInput.convention(forkName.flatMap { workDir.dir("$it/$it-server/src/main/resources") }) + serverTestResourcesInput.convention(forkName.flatMap { workDir.dir("$it/$it-server/src/test/resources") }) + apiSourceInput.convention(forkName.flatMap { workDir.dir("$it/$it-api/src/main/java") }) + apiTestSourceInput.convention(forkName.flatMap { workDir.dir("$it/$it-api/src/test/java") }) + apiResourcesInput.convention(forkName.flatMap { workDir.dir("$it/$it-api/src/main/resources") }) + apiTestResourcesInput.convention(forkName.flatMap { workDir.dir("$it/$it-api/src/test/resources") }) + } @TaskAction fun run() { - val outputToPatches = patchDirOutput.getOrElse(false) - val outputDir = if (!outputToPatches && outputDir.isPresent) { - outputDir.path.cleanDir() - } else if (!outputToPatches && !outputDir.isPresent) { - throw PaperweightException( - "Cannot find an output directory and patchDirOutput is set to false!" - ) - } else { - null - } - val name = upstreamName.get() - val upstreamUrl = upstreamLink.get() - val url = upstreamUrl.removeSuffix(".git") - - val repositories: List = - inputFrom.get().map { workDir.path.resolve("$name/$it") } - - for (repo in repositories) { - if (!repo.exists()) continue - - val isApi = if (repo.fileName.toString().contains("-api")) true else false - val repoName = if (isApi) { - repo.fileName.toString().split("-").joinToString("") { it.replaceFirstChar { char -> char.uppercase() } }.replace("Api", "API") - } else if (repo.fileName.toString().equals("java")) { - "Minecraft" - } else { - repo.fileName.toString().split("-").joinToString("") { it.replaceFirstChar { char -> char.uppercase() } } + val forkName = forkName.get() + val commit = commitHash.get() + val url = forkUrl.get() + val repoTypes = inputFrom.get().split(",") + val serverGeneratedOutput = serverSourceOutput.get() + val apiGeneratedOutput = apiSourceOutput.get() + val serverTestGeneratedOutput = serverTestSourceOutput.get() + val apiTestGeneratedOutput = apiTestSourceOutput.get() + val apiResourceGeneratedOutput = apiResourcesOutput.get() + val serverResourceGeneratedOutput = serverResourcesOutput.get() + val apiTestResourceGeneratedOutput = apiTestResourcesOutput.get() + val serverTestResourceGeneratedOutput = serverTestResourcesOutput.get() + val inputDirs = preparedSource.files.map { it.toPath() } + + val outputToPatchesDirectory = patchesDirOutput.getOrElse(true) + val outputDir = outputDir.getOrElse(layout.buildDirectory.dir(paperTaskOutput(name)).get()) + val cacheOutput = tempOutput.get().asFile.toPath() + val tempOutput = cacheOutput.cleanFile() + val outputDirPath = outputDir.asFile.toPath() + + for (inputDir in inputDirs) { + inputDir.copyRecursivelyTo(tempOutput) + for (repoType in repoTypes) { + val repository = tempOutput.resolve(repoType) + if (!repository.exists()) continue + val dirName = repository.fileName.toString() + val repoName = capitalizedName(dirName, dirName.isApi()) + val cutRepo = dirName.substringBefore("-") + val git = Git(repository) + git("reset", "base", "--soft").runSilently(silenceErr = true) + if (git("status", "--porcelain").getText().trim().isBlank()) continue + git("add", ".").runSilently(silenceErr = true) + git.commit(forkName, repoName, url, commit) + git( + "format-patch", + "--diff-algorithm=myers", "--zero-commit", "--full-index", "--no-signature", "--no-stat", "-N", + "HEAD~1..HEAD", "-o", + outputPath(outputToPatchesDirectory, dirName.isApi(), outputDirPath, cutRepo) + ).runSilently(silenceErr = true) } - - val cutRepo = repo.fileName.toString().substringBefore("-") - val git = Git(repo) - git("reset", "base", "--soft").runSilently(silenceErr = true) - val toCommit = git("status", "--porcelain").getText().trim() - if (toCommit.isBlank()) continue - git("add", ".").runSilently(silenceErr = true) - git( - "commit", - "-m", - "${name.capitalized()} $repoName Patches", - "-m", - "Patch generated from $url/commit/${upstreamHash.get()}", - "--author=Generated Source " - ).runSilently(silenceErr = true) - git( - "format-patch", - "--diff-algorithm=myers", "--zero-commit", "--full-index", "--no-signature", "--no-stat", "-N", - "HEAD~1..HEAD", "-o", - if (outputToPatches && isApi) { - rootDir.convertToPath().resolve("${rootName.get()}-api/$cutRepo-patches/base").absolutePathString() - } else if (outputToPatches && cutRepo.equals("java")) { - rootDir.convertToPath().resolve("${rootName.get()}-server/minecraft-patches/base").absolutePathString() - } else if (outputToPatches) { - rootDir.convertToPath().resolve("${rootName.get()}-server/$cutRepo-patches/base").absolutePathString() - } else { - if (outputDir != null) { - outputDir.absolutePathString() - } else { - throw PaperweightException( - "Cannot find an output directory and patchDirOutput is set to false!" - ) - } - } - ).runSilently(silenceErr = true) } - val additionalRepositories: List = - listOf(workDir.path.resolve("$name/$name-api/src/main/java"), workDir.path.resolve("$name/$name-server/src/main/java")) + val serverJavaRepo = serverSourceInput.get() + val apiJavaRepo = apiSourceInput.get() + val serverTestRepo = serverTestSourceInput.get() + val apiTestRepo = apiTestSourceInput.get() + val serverResourcesRepo = serverResourcesInput.get() + val apiResourcesRepo = apiResourcesInput.get() + val serverTestResourcesRepo = serverTestResourcesInput.get() + val apiTestResourcesRepo = apiTestResourcesInput.get() + + val additionalRepositories = listOfNotNull( + apiJavaRepo, + apiTestRepo, + apiResourcesRepo, + apiTestResourcesRepo, + serverJavaRepo, + serverTestRepo, + serverResourcesRepo, + serverTestResourcesRepo + ) + for (repo in additionalRepositories) { - val isApi = if (repo.toString().contains("-api")) true else false - val sourceOutput = if (isApi) { - rootDir.convertToPath().resolve("${rootName.get()}-api/src/generated/java") - } else { - rootDir.convertToPath().resolve("${rootName.get()}-server/src/generated/java") + if (!repo.asFile.toPath().exists()) continue + val string = repo.toString() + val sourceOutputPath = when { + string.isApi() && string.isTest() -> apiTestGeneratedOutput.asFile.toPath() + !string.isApi() && string.isTest() -> serverTestGeneratedOutput.asFile.toPath() + string.isApi() && string.isResources() && string.isTest() -> apiTestResourceGeneratedOutput.asFile.toPath() + !string.isApi() && string.isResources() && string.isTest() -> serverTestResourceGeneratedOutput.asFile.toPath() + string.isApi() && !string.isResources() -> apiGeneratedOutput.asFile.toPath() + string.isApi() && string.isResources() -> apiResourceGeneratedOutput.asFile.toPath() + !string.isApi() && !string.isResources() -> serverGeneratedOutput.asFile.toPath() + else -> serverResourceGeneratedOutput.asFile.toPath() } - sourceOutput.deleteRecursively() - repo.copyRecursivelyTo(sourceOutput) + sourceOutputPath.deleteRecursively() + repo.asFile.toPath().copyRecursivelyTo(sourceOutputPath) - sourceOutput.filesMatchingRecursive("*.java").forEach { + sourceOutputPath.filesMatchingRecursive("*.java").forEach { val content = it.readText() - it.writeText("// Generated from $url/commit/${upstreamHash.get()}\n$content") + it.writeText("// Generated from ${commitLink(url, commit)}\n$content") } } } + private fun Git.commit(name: String, repoName: String, url: String, commit: String) { + this( + "commit", + "-m", + "${name.capitalized()} $repoName Patches", + "-m", + "Patch generated from ${commitLink(url, commit)}", + "--author=Generated Source " + ).runSilently(silenceErr = true) + } + private fun capitalizedName(name: String, api: Boolean): String { + val trimmed = name.split("-").joinToString("") { it.uppercaseFirstChar() } + return when { + api -> trimmed.replace("Api", "API") + else -> trimmed + } + } + private fun String.isApi() = contains("-api") + private fun String.isResources() = endsWith("resources") + private fun String.isTest() = contains("test/") + private fun commitLink(url: String, hash: String): String { + val cleanUrl = url.removeSuffix(".git") + return "$cleanUrl/commit/$hash" + } + private fun outputPath(patchDir: Boolean, api: Boolean, outputDir: Path, repoName: String): String { + val serverOutput = serverProjectDir.get().asFile.toPath() + val apiOutput = apiProjectDir.get().asFile.toPath() + return when { + patchDir && api -> apiOutput.resolve("$repoName-patches/base").absolutePathString() + patchDir && repoName == "minecraft" -> serverOutput.resolve("$repoName-patches/base").absolutePathString() + patchDir -> serverOutput.resolve("$repoName-patches/base").absolutePathString() + else -> outputDir.absolutePathString() + } + } } diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/PrepareForPatchGeneration.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/PrepareForPatchGeneration.kt index 7e1ca499b..b9a23b069 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/PrepareForPatchGeneration.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/PrepareForPatchGeneration.kt @@ -26,6 +26,7 @@ import io.papermc.paperweight.core.util.ApplySourceATs import io.papermc.paperweight.tasks.JavaLauncherTask import io.papermc.paperweight.util.* import io.papermc.paperweight.util.constants.JST_CONFIG +import io.papermc.paperweight.util.constants.paperTaskOutput import kotlin.io.path.* import org.gradle.api.file.DirectoryProperty import org.gradle.api.file.RegularFileProperty @@ -36,15 +37,15 @@ import org.gradle.kotlin.dsl.newInstance @UntrackedTask(because = "PrepareForPatchGeneration should always run when requested") abstract class PrepareForPatchGeneration : JavaLauncherTask() { - @get:InputDirectory - abstract val workDir: DirectoryProperty - @get:Input - abstract val upstreamName: Property + abstract val forkName: Property @get:Input abstract val repoName: Property + @get:InputDirectory + abstract val workDir: DirectoryProperty + @get:Nested val ats: ApplySourceATs = objects.newInstance().apply { jst.from(project.configurations.named(JST_CONFIG)) @@ -52,27 +53,46 @@ abstract class PrepareForPatchGeneration : JavaLauncherTask() { @get:InputFile @get:Optional + @get:PathSensitive(PathSensitivity.NONE) abstract val atFile: RegularFileProperty @get:InputFile @get:Optional + @get:PathSensitive(PathSensitivity.NONE) abstract val additionalPatch: RegularFileProperty + @get:OutputDirectory + abstract val outputDir: DirectoryProperty + + override fun init() { + super.init() + outputDir.set(layout.buildDirectory.dir(paperTaskOutput("patchgen-work"))) + } + @TaskAction fun run() { - val name = if (repoName.get().equals("minecraft")) "${upstreamName.get()}-server/src/minecraft/java" else repoName.get() - val outputDir = workDir.path.resolve("${upstreamName.get()}/$name") + val outputName = forkName.get() + val workDirectory = workDir.get().asFile.toPath() + + val sourceDir = if (repoName.get() == "minecraft") { + workDirectory.resolve("$outputName/$outputName-server/src/minecraft/java") + } else { + workDirectory.resolve("$outputName/${repoName.get()}") + } + val outputDir = outputDir.get().asFile.toPath().resolve(repoName.get()) + val outputDirPath = outputDir.cleanFile() + sourceDir.copyRecursivelyTo(outputDirPath) if (additionalPatch.isPresent) { - val git = Git(outputDir) - git("am", "--3way", "--ignore-whitespace", additionalPatch.path.toString()).captureOut(false) + val git = Git(outputDirPath) + git("am", "--3way", "--ignore-whitespace", additionalPatch.path.toString()).runSilently() } if (atFile.isPresent && atFile.path.readText().isNotBlank()) { ats.run( launcher.get(), - outputDir, - outputDir, + outputDirPath, + outputDirPath, atFile.path, temporaryDir.toPath() ) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/PaperweightPatcher.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/PaperweightPatcher.kt index 46ed2620e..58fdec6ca 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/PaperweightPatcher.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/PaperweightPatcher.kt @@ -32,8 +32,12 @@ import io.papermc.paperweight.util.* import io.papermc.paperweight.util.constants.* import org.gradle.api.Plugin import org.gradle.api.Project +import org.gradle.api.file.Directory +import org.gradle.api.provider.Provider import org.gradle.api.tasks.Delete +import org.gradle.api.tasks.TaskProvider import org.gradle.kotlin.dsl.* +import org.gradle.kotlin.dsl.support.uppercaseFirstChar abstract class PaperweightPatcher : Plugin { @@ -119,49 +123,74 @@ abstract class PaperweightPatcher : Plugin { } } patcher.additionalUpstreams.forEach { upstream -> - val checkoutTask = tasks.register("checkout${upstream.name.capitalized()}RepoForPatchGeneration") { + val checkoutTask = tasks.register("checkout${upstream.name.capitalized()}RepoForGeneration") { repoName.set(upstream.name) url.set(upstream.repo) ref.set(upstream.ref) workDir.set(workDirFromProp) } - val applyAdditionalUpstream = tasks.register("apply${upstream.name.capitalized()}ForPatchGeneration") { + val applyAdditionalUpstream = tasks.register("apply${upstream.name.capitalized()}ForGeneration") { projectDir.set(checkoutTask.flatMap { it.outputDir }) tasks.add("applyAllPatches") } - val depend = upstream.patchGenerationConfig.repoConfig.map { repo -> - val name = if (repo.name.equals("minecraft")) { + val depend: List> = upstream.patchGenerationConfig.inputConfig.map { input -> + val name = if (input.name == "minecraft") { "Minecraft" } else { - repo.name.split("-").joinToString("") { - it.replaceFirstChar { char -> char.uppercase() } + input.name.split("-").joinToString("") { + it.uppercaseFirstChar() } } - tasks.register("prepare${upstream.name.capitalized()}${name}ForPatchGeneration") { - group = "patch generation" - description = "Prepares ${upstream.name} ${repo.name} for patch generation" + tasks.register("prepare${upstream.name.capitalized()}${name}ForGeneration") { + group = INTERNAL_TASK_GROUP dependsOn(applyAdditionalUpstream) - upstreamName.set(upstream.name) - repoName.set(repo.name) + forkName.set(upstream.name) + repoName.set(input.name) workDir.set(workDirFromProp) - atFile.set(repo.additionalAts.fileExists(project)) - additionalPatch.set(repo.additionalPatch.fileExists(project)) + atFile.set(input.additionalAts.fileExists(project)) + additionalPatch.set(input.additionalPatch.fileExists(project)) } } - tasks.register("generate${upstream.name.capitalized()}Patches") { - if (depend.isNotEmpty()) dependsOn(depend) else dependsOn(applyAdditionalUpstream) + val apiProject: Provider = project.provider { project.layout.projectDirectory.dir("${rootProject.name}-api") } + val serverProject: Provider = project.provider { project.layout.projectDirectory.dir("${rootProject.name}-server") } + + tasks.register("generate${upstream.name.capitalized()}") { + dependsOn(depend.map { it }) group = "patch generation" - description = "Generates base patches from ${upstream.name.capitalized()}" - rootName.set(rootProject.name) - rootDir.set(rootProject.rootDir) - upstreamName.set(upstream.name) - upstreamLink.set(upstream.repo) - upstreamHash.set(upstream.ref) + description = "Generates base patches and source files from ${upstream.name}" + forkName.set(upstream.name) + forkUrl.set(upstream.repo) + commitHash.set(upstream.ref) + inputFrom.set(upstream.patchGenerationConfig.inputConfig.joinToString(",") { it.name }) workDir.set(workDirFromProp) - patchDirOutput.set(upstream.patchGenerationConfig.patchDirOutput) - inputFrom.set(upstream.patchGenerationConfig.inputFrom) + preparedSource.from(depend.map { it.flatMap { t -> t.outputDir } }) + patchesDirOutput.set(upstream.patchGenerationConfig.patchesDirOutput) + serverSourceOutput.set( + upstream.sourceGenerationConfig.serverSourceOutput.orElse(serverProject.map { it.dir("src/generated/main/java") }) + ) + apiSourceOutput.set(upstream.sourceGenerationConfig.apiSourceOutput.orElse(apiProject.map { it.dir("src/generated/main/java") })) + serverTestSourceOutput.set( + upstream.sourceGenerationConfig.serverTestSourceOutput.orElse(serverProject.map { it.dir("src/generated/test/java") }) + ) + apiTestSourceOutput.set( + upstream.sourceGenerationConfig.apiTestSourceOutput.orElse(apiProject.map { it.dir("src/generated/test/java") }) + ) + serverResourcesOutput.set( + upstream.sourceGenerationConfig.serverResourcesOutput.orElse(serverProject.map { it.dir("src/generated/main/resources") }) + ) + apiResourcesOutput.set( + upstream.sourceGenerationConfig.apiResourcesOutput.orElse(apiProject.map { it.dir("src/generated/main/resources") }) + ) + serverTestResourcesOutput.set( + upstream.sourceGenerationConfig.serverTestResourcesOutput.orElse(serverProject.map { it.dir("src/generated/test/resources") }) + ) + apiTestResourcesOutput.set( + upstream.sourceGenerationConfig.apiTestResourcesOutput.orElse(apiProject.map { it.dir("src/generated/test/resources") }) + ) + serverProjectDir.set(serverProject) + apiProjectDir.set(apiProject) outputDir.set(upstream.patchGenerationConfig.outputDir) } } From f84610297aa5b384beaf72dce738ff00d13cfba0 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Mon, 25 Aug 2025 22:02:40 +0200 Subject: [PATCH 066/257] generate sources seperately --- .../extension/AdditionalUpstreamConfig.kt | 54 ++++-- .../paperweight/core/tasks/GeneratePatches.kt | 111 ----------- .../paperweight/core/tasks/GenerateSources.kt | 177 ++++++++++++++++++ .../paperweight/patcher/PaperweightPatcher.kt | 78 +++++--- 4 files changed, 267 insertions(+), 153 deletions(-) create mode 100644 paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/GenerateSources.kt diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/AdditionalUpstreamConfig.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/AdditionalUpstreamConfig.kt index 799891ba0..9bae108f0 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/AdditionalUpstreamConfig.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/AdditionalUpstreamConfig.kt @@ -22,6 +22,7 @@ package io.papermc.paperweight.core.extension +import io.papermc.paperweight.core.extension.AdditionalUpstreamConfig.PatchGenerationConfig.InputConfig import io.papermc.paperweight.util.* import javax.annotation.Nullable import javax.inject.Inject @@ -34,6 +35,7 @@ import org.gradle.api.file.RegularFileProperty import org.gradle.api.model.ObjectFactory import org.gradle.api.provider.Property import org.gradle.kotlin.dsl.* +import org.gradle.kotlin.dsl.domainObjectContainer import org.gradle.kotlin.dsl.newInstance abstract class AdditionalUpstreamConfig @Inject constructor( @@ -61,29 +63,49 @@ abstract class AdditionalUpstreamConfig @Inject constructor( abstract class SourceGenerationConfig @Inject constructor( objects: ObjectFactory, ) { - @get:Nullable - abstract val serverSourceOutput: DirectoryProperty + val generationConfig: NamedDomainObjectContainer = objects.domainObjectContainer( + GenerationConfig::class + ) { name -> objects.newInstance(name, objects) } - @get:Nullable - abstract val apiSourceOutput: DirectoryProperty + @Suppress("UNUSED_PARAMETER") + abstract class GenerationConfig @Inject constructor( + private val setName: String, + objects: ObjectFactory, + ) : Named { + override fun getName(): String { + return setName + } - @get:Nullable - abstract val serverTestSourceOutput: DirectoryProperty + @get:Nullable + abstract val generateSources: Property - @get:Nullable - abstract val apiTestSourceOutput: DirectoryProperty + @get:Nullable + abstract val generateResources: Property - @get:Nullable - abstract val serverResourcesOutput: DirectoryProperty + @get:Nullable + abstract val generateTestSources: Property - @get:Nullable - abstract val apiResourcesOutput: DirectoryProperty + @get:Nullable + abstract val generateTestResources: Property - @get:Nullable - abstract val serverTestResourcesOutput: DirectoryProperty + @get:Nullable + abstract val sourcesOutputDir: DirectoryProperty - @get:Nullable - abstract val apiTestResourcesOutput: DirectoryProperty + @get:Nullable + abstract val resourcesOutputDir: DirectoryProperty + + @get:Nullable + abstract val testSourcesOutputDir: DirectoryProperty + + @get:Nullable + abstract val testResourcesOutputDir: DirectoryProperty + + @get:Nullable + abstract val additionalAts: RegularFileProperty + + @get:Nullable + abstract val additionalPatch: RegularFileProperty + } } abstract class PatchGenerationConfig @Inject constructor( diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/GeneratePatches.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/GeneratePatches.kt index 9b1b72efe..6f3006862 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/GeneratePatches.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/GeneratePatches.kt @@ -46,10 +46,6 @@ abstract class GeneratePatches : BaseTask() { @get:Input abstract val inputFrom: Property - @get:InputDirectory - @get:PathSensitive(PathSensitivity.RELATIVE) - abstract val workDir: DirectoryProperty - @get:InputFiles @get:PathSensitive(PathSensitivity.RELATIVE) abstract val preparedSource: ConfigurableFileCollection @@ -58,54 +54,6 @@ abstract class GeneratePatches : BaseTask() { @get:Optional abstract val patchesDirOutput: Property - @get:Internal - abstract val serverSourceInput: DirectoryProperty - - @get:Internal - abstract val apiSourceInput: DirectoryProperty - - @get:Internal - abstract val serverTestSourceInput: DirectoryProperty - - @get:Internal - abstract val apiTestSourceInput: DirectoryProperty - - @get:Internal - abstract val serverResourcesInput: DirectoryProperty - - @get:Internal - abstract val apiResourcesInput: DirectoryProperty - - @get:Internal - abstract val serverTestResourcesInput: DirectoryProperty - - @get:Internal - abstract val apiTestResourcesInput: DirectoryProperty - - @get:OutputDirectory - abstract val serverSourceOutput: DirectoryProperty - - @get:OutputDirectory - abstract val apiSourceOutput: DirectoryProperty - - @get:OutputDirectory - abstract val serverTestSourceOutput: DirectoryProperty - - @get:OutputDirectory - abstract val apiTestSourceOutput: DirectoryProperty - - @get:OutputDirectory - abstract val serverResourcesOutput: DirectoryProperty - - @get:OutputDirectory - abstract val apiResourcesOutput: DirectoryProperty - - @get:OutputDirectory - abstract val serverTestResourcesOutput: DirectoryProperty - - @get:OutputDirectory - abstract val apiTestResourcesOutput: DirectoryProperty - @get:OutputDirectory abstract val serverProjectDir: DirectoryProperty @@ -121,14 +69,6 @@ abstract class GeneratePatches : BaseTask() { override fun init() { tempOutput.set(layout.buildDirectory.dir(paperTaskOutput(name))) - serverSourceInput.convention(forkName.flatMap { workDir.dir("$it/$it-server/src/main/java") }) - serverTestSourceInput.convention(forkName.flatMap { workDir.dir("$it/$it-server/src/test/java") }) - serverResourcesInput.convention(forkName.flatMap { workDir.dir("$it/$it-server/src/main/resources") }) - serverTestResourcesInput.convention(forkName.flatMap { workDir.dir("$it/$it-server/src/test/resources") }) - apiSourceInput.convention(forkName.flatMap { workDir.dir("$it/$it-api/src/main/java") }) - apiTestSourceInput.convention(forkName.flatMap { workDir.dir("$it/$it-api/src/test/java") }) - apiResourcesInput.convention(forkName.flatMap { workDir.dir("$it/$it-api/src/main/resources") }) - apiTestResourcesInput.convention(forkName.flatMap { workDir.dir("$it/$it-api/src/test/resources") }) } @TaskAction @@ -137,14 +77,6 @@ abstract class GeneratePatches : BaseTask() { val commit = commitHash.get() val url = forkUrl.get() val repoTypes = inputFrom.get().split(",") - val serverGeneratedOutput = serverSourceOutput.get() - val apiGeneratedOutput = apiSourceOutput.get() - val serverTestGeneratedOutput = serverTestSourceOutput.get() - val apiTestGeneratedOutput = apiTestSourceOutput.get() - val apiResourceGeneratedOutput = apiResourcesOutput.get() - val serverResourceGeneratedOutput = serverResourcesOutput.get() - val apiTestResourceGeneratedOutput = apiTestResourcesOutput.get() - val serverTestResourceGeneratedOutput = serverTestResourcesOutput.get() val inputDirs = preparedSource.files.map { it.toPath() } val outputToPatchesDirectory = patchesDirOutput.getOrElse(true) @@ -174,47 +106,6 @@ abstract class GeneratePatches : BaseTask() { ).runSilently(silenceErr = true) } } - val serverJavaRepo = serverSourceInput.get() - val apiJavaRepo = apiSourceInput.get() - val serverTestRepo = serverTestSourceInput.get() - val apiTestRepo = apiTestSourceInput.get() - val serverResourcesRepo = serverResourcesInput.get() - val apiResourcesRepo = apiResourcesInput.get() - val serverTestResourcesRepo = serverTestResourcesInput.get() - val apiTestResourcesRepo = apiTestResourcesInput.get() - - val additionalRepositories = listOfNotNull( - apiJavaRepo, - apiTestRepo, - apiResourcesRepo, - apiTestResourcesRepo, - serverJavaRepo, - serverTestRepo, - serverResourcesRepo, - serverTestResourcesRepo - ) - - for (repo in additionalRepositories) { - if (!repo.asFile.toPath().exists()) continue - val string = repo.toString() - val sourceOutputPath = when { - string.isApi() && string.isTest() -> apiTestGeneratedOutput.asFile.toPath() - !string.isApi() && string.isTest() -> serverTestGeneratedOutput.asFile.toPath() - string.isApi() && string.isResources() && string.isTest() -> apiTestResourceGeneratedOutput.asFile.toPath() - !string.isApi() && string.isResources() && string.isTest() -> serverTestResourceGeneratedOutput.asFile.toPath() - string.isApi() && !string.isResources() -> apiGeneratedOutput.asFile.toPath() - string.isApi() && string.isResources() -> apiResourceGeneratedOutput.asFile.toPath() - !string.isApi() && !string.isResources() -> serverGeneratedOutput.asFile.toPath() - else -> serverResourceGeneratedOutput.asFile.toPath() - } - sourceOutputPath.deleteRecursively() - repo.asFile.toPath().copyRecursivelyTo(sourceOutputPath) - - sourceOutputPath.filesMatchingRecursive("*.java").forEach { - val content = it.readText() - it.writeText("// Generated from ${commitLink(url, commit)}\n$content") - } - } } private fun Git.commit(name: String, repoName: String, url: String, commit: String) { this( @@ -234,8 +125,6 @@ abstract class GeneratePatches : BaseTask() { } } private fun String.isApi() = contains("-api") - private fun String.isResources() = endsWith("resources") - private fun String.isTest() = contains("test/") private fun commitLink(url: String, hash: String): String { val cleanUrl = url.removeSuffix(".git") return "$cleanUrl/commit/$hash" diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/GenerateSources.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/GenerateSources.kt new file mode 100644 index 000000000..bcd872d60 --- /dev/null +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/GenerateSources.kt @@ -0,0 +1,177 @@ +/* + * paperweight is a Gradle plugin for the PaperMC project. + * + * Copyright (c) 2023 Kyle Wood (DenWav) + * Contributors + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 only, no later versions. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + * USA + */ + +package io.papermc.paperweight.core.tasks + +import io.papermc.paperweight.core.util.ApplySourceATs +import io.papermc.paperweight.tasks.JavaLauncherTask +import io.papermc.paperweight.util.* +import io.papermc.paperweight.util.constants.JST_CONFIG +import io.papermc.paperweight.util.constants.paperTaskOutput +import java.nio.file.Path +import kotlin.io.path.* +import org.gradle.api.file.DirectoryProperty +import org.gradle.api.file.RegularFileProperty +import org.gradle.api.provider.Property +import org.gradle.api.tasks.* +import org.gradle.kotlin.dsl.newInstance + +abstract class GenerateSources : JavaLauncherTask() { + @get:Input + abstract val forkName: Property + + @get:Input + abstract val forkUrl: Property + + @get:Input + abstract val commitHash: Property + + @get:Input + abstract val inputFrom: Property + + @get:InputDirectory + @get:PathSensitive(PathSensitivity.RELATIVE) + abstract val workDir: DirectoryProperty + + @get:Internal + abstract val tempOutput: DirectoryProperty + + @get:Nested + val ats: ApplySourceATs = objects.newInstance().apply { + jst.from(project.configurations.named(JST_CONFIG)) + } + + @get:InputFile + @get:Optional + @get:PathSensitive(PathSensitivity.NONE) + abstract val atFile: RegularFileProperty + + @get:InputFile + @get:Optional + @get:PathSensitive(PathSensitivity.NONE) + abstract val additionalPatch: RegularFileProperty + + @get:Input + abstract val generateSources: Property + + @get:Input + abstract val generateResources: Property + + @get:Input + abstract val generateTestSources: Property + + @get:Input + abstract val generateTestResources: Property + + @get:OutputDirectory + abstract val sourceOutput: DirectoryProperty + + @get:OutputDirectory + abstract val resourceOutput: DirectoryProperty + + @get:OutputDirectory + abstract val testSourceOutput: DirectoryProperty + + @get:OutputDirectory + abstract val testResourceOutput: DirectoryProperty + + override fun init() { + super.init() + tempOutput.set(layout.buildDirectory.dir(paperTaskOutput(name))) + } + + @TaskAction + fun run() { + val commit = commitHash.get() + val url = forkUrl.get() + val generatedOutput = sourceOutput.get().asFile.toPath() + val generatedResourcesOutput = resourceOutput.get().asFile.toPath() + val generatedTestOutput = testSourceOutput.get().asFile.toPath() + val generatedTestResourcesOutput = testResourceOutput.get().asFile.toPath() + + val cacheOutput = tempOutput.get().asFile.toPath() + val tempOutput = cacheOutput.cleanFile() + + val outputName = inputFrom.get().substringBefore("-") + val workDirectory = workDir.get().asFile.toPath() + + val sourceDir = workDirectory.resolve("$outputName/${inputFrom.get()}") + if (!sourceDir.exists()) return + sourceDir.copyRecursivelyTo(tempOutput) + + val atDirPath = tempOutput.resolve("src/main/java") + val patchDir = tempOutput + + if (additionalPatch.isPresent) { + val git = Git(patchDir) + git("am", "--3way", "--ignore-whitespace", additionalPatch.path.toString()).runSilently() + } + + if (atFile.isPresent && atFile.path.readText().isNotBlank() && atDirPath.exists()) { + ats.run( + launcher.get(), + atDirPath, + atDirPath, + atFile.path, + temporaryDir.toPath() + ) + } + + val outputs: List = listOfNotNull( + generatedOutput.takeIf { generateSources.get() }, + generatedResourcesOutput.takeIf { generateResources.get() }, + generatedTestOutput.takeIf { generateTestSources.get() }, + generatedTestResourcesOutput.takeIf { generateTestResources.get() } + ) + + for (output in outputs) { + output.deleteRecursively() + when (output) { + generatedOutput -> { + tempOutput.resolve("src/main/java").copyRecursivelyTo(output) + } + + generatedResourcesOutput -> { + tempOutput.resolve("src/main/resources").copyRecursivelyTo(output) + } + + generatedTestOutput -> { + tempOutput.resolve("src/test/java").copyRecursivelyTo(output) + } + + generatedTestResourcesOutput -> { + tempOutput.resolve("src/test/resources").copyRecursivelyTo(output) + } + } + + output.filesMatchingRecursive("*.java").forEach { + val content = it.readText() + it.writeText("// Generated from ${commitLink(url, commit)}\n$content") + } + } + } + + private fun commitLink(url: String, hash: String): String { + val cleanUrl = url.removeSuffix(".git") + return "$cleanUrl/commit/$hash" + } +} diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/PaperweightPatcher.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/PaperweightPatcher.kt index 58fdec6ca..3e0a6411d 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/PaperweightPatcher.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/PaperweightPatcher.kt @@ -25,6 +25,7 @@ package io.papermc.paperweight.patcher import io.papermc.paperweight.core.taskcontainers.UpstreamConfigTasks import io.papermc.paperweight.core.tasks.CheckoutRepo import io.papermc.paperweight.core.tasks.GeneratePatches +import io.papermc.paperweight.core.tasks.GenerateSources import io.papermc.paperweight.core.tasks.PrepareForPatchGeneration import io.papermc.paperweight.core.tasks.RunNestedBuild import io.papermc.paperweight.patcher.extension.PaperweightPatcherExtension @@ -143,7 +144,7 @@ abstract class PaperweightPatcher : Plugin { it.uppercaseFirstChar() } } - tasks.register("prepare${upstream.name.capitalized()}${name}ForGeneration") { + tasks.register("prepare${upstream.name.capitalized()}${name}ForPatchGeneration") { group = INTERNAL_TASK_GROUP dependsOn(applyAdditionalUpstream) forkName.set(upstream.name) @@ -153,46 +154,71 @@ abstract class PaperweightPatcher : Plugin { additionalPatch.set(input.additionalPatch.fileExists(project)) } } + + val sourceGenDepend: List> = upstream.sourceGenerationConfig.generationConfig.map { input -> + val name = input.name.split("-").joinToString("") { + it.uppercaseFirstChar() + } + val outputType = input.name.substringAfter("-") + val projectDir: Provider = project.provider { project.layout.projectDirectory.dir("${rootProject.name}-$outputType") } + + tasks.register("generate${name}Sources") { + group = "source generation" + dependsOn(applyAdditionalUpstream) + forkName.set(upstream.name) + forkUrl.set(upstream.repo) + inputFrom.set(input.name) + commitHash.set(upstream.ref) + workDir.set(workDirFromProp) + atFile.set(input.additionalAts.fileExists(project)) + additionalPatch.set(input.additionalPatch.fileExists(project)) + generateSources.set(input.generateSources.orElse(true)) + generateResources.set(input.generateResources.orElse(true)) + generateTestSources.set(input.generateTestSources.orElse(true)) + generateTestResources.set(input.generateTestResources.orElse(true)) + sourceOutput.set( + input.sourcesOutputDir.orElse(projectDir.map { it.dir("src/generated/main/java") }) + ) + resourceOutput.set( + input.resourcesOutputDir.orElse(projectDir.map { it.dir("src/generated/main/resources") }) + ) + testSourceOutput.set( + input.testSourcesOutputDir.orElse(projectDir.map { it.dir("src/generated/test/java") }) + ) + testResourceOutput.set( + input.testResourcesOutputDir.orElse(projectDir.map { it.dir("src/generated/test/resources") }) + ) + } + } val apiProject: Provider = project.provider { project.layout.projectDirectory.dir("${rootProject.name}-api") } val serverProject: Provider = project.provider { project.layout.projectDirectory.dir("${rootProject.name}-server") } - tasks.register("generate${upstream.name.capitalized()}") { + val genPatches = tasks.register("generate${upstream.name.capitalized()}Patches") { dependsOn(depend.map { it }) group = "patch generation" - description = "Generates base patches and source files from ${upstream.name}" + description = "Generates base patches from ${upstream.name}" forkName.set(upstream.name) forkUrl.set(upstream.repo) commitHash.set(upstream.ref) inputFrom.set(upstream.patchGenerationConfig.inputConfig.joinToString(",") { it.name }) - workDir.set(workDirFromProp) preparedSource.from(depend.map { it.flatMap { t -> t.outputDir } }) patchesDirOutput.set(upstream.patchGenerationConfig.patchesDirOutput) - serverSourceOutput.set( - upstream.sourceGenerationConfig.serverSourceOutput.orElse(serverProject.map { it.dir("src/generated/main/java") }) - ) - apiSourceOutput.set(upstream.sourceGenerationConfig.apiSourceOutput.orElse(apiProject.map { it.dir("src/generated/main/java") })) - serverTestSourceOutput.set( - upstream.sourceGenerationConfig.serverTestSourceOutput.orElse(serverProject.map { it.dir("src/generated/test/java") }) - ) - apiTestSourceOutput.set( - upstream.sourceGenerationConfig.apiTestSourceOutput.orElse(apiProject.map { it.dir("src/generated/test/java") }) - ) - serverResourcesOutput.set( - upstream.sourceGenerationConfig.serverResourcesOutput.orElse(serverProject.map { it.dir("src/generated/main/resources") }) - ) - apiResourcesOutput.set( - upstream.sourceGenerationConfig.apiResourcesOutput.orElse(apiProject.map { it.dir("src/generated/main/resources") }) - ) - serverTestResourcesOutput.set( - upstream.sourceGenerationConfig.serverTestResourcesOutput.orElse(serverProject.map { it.dir("src/generated/test/resources") }) - ) - apiTestResourcesOutput.set( - upstream.sourceGenerationConfig.apiTestResourcesOutput.orElse(apiProject.map { it.dir("src/generated/test/resources") }) - ) serverProjectDir.set(serverProject) apiProjectDir.set(apiProject) outputDir.set(upstream.patchGenerationConfig.outputDir) } + + val genSources = tasks.register("generate${upstream.name.capitalized()}Sources") { + group = "source generation" + description = "Generates sources from ${upstream.name}" + } + + val generate = tasks.register("generate${upstream.name.capitalized()}") { + group = "generation" + description = "Generates base patches and sources from ${upstream.name}" + } + genSources { dependsOn(sourceGenDepend.map { it }) } + generate { dependsOn(genPatches, genSources) } } } } From 5f3ac61ae76b0e40a30dfa457bee0c463deeb046 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Mon, 25 Aug 2025 22:03:24 +0200 Subject: [PATCH 067/257] chore: continue development cycle --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 02d65f73d..e20e1a766 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ group = io.canvasmc.weaver -version = 2.3.1-SNAPSHOT +version = 2.3.2-SNAPSHOT org.gradle.configuration-cache = true org.gradle.caching = true From 3d831077e44be6fefa9b31370a3ed9fcd9a6d685 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Wed, 27 Aug 2025 18:47:20 +0200 Subject: [PATCH 068/257] fix(regression): Only scan commit subjects for the wrong name --- .../io/papermc/paperweight/tasks/RebuildBaseGitPatches.kt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RebuildBaseGitPatches.kt b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RebuildBaseGitPatches.kt index 32dc45535..528903ef9 100644 --- a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RebuildBaseGitPatches.kt +++ b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RebuildBaseGitPatches.kt @@ -73,7 +73,7 @@ abstract class RebuildBaseGitPatches : ControllableOutputTask() { // fail fast if someone decides to name a patch with these names to avoid patch corruption val patchedBaseCommitCount = git( "rev-list", - "--grep=${identifier.get()} Base Patches", + "--grep=^${identifier.get()} Base Patches$", "base..HEAD" ).getText().lines().filter { it.isNotBlank() } if (patchedBaseCommitCount.size > 1) { @@ -82,7 +82,7 @@ abstract class RebuildBaseGitPatches : ControllableOutputTask() { "Exceeded the max amount of commits with the identifier: `${identifier.get()} Base Patches`!\nGot $count commits, expected: 1" ) } - val fileCommitCount = git("rev-list", "--grep=${identifier.get()} File Patches", "base..HEAD").getText().lines().filter { it.isNotBlank() } + val fileCommitCount = git("rev-list", "--grep=^${identifier.get()} File Patches$", "base..HEAD").getText().lines().filter { it.isNotBlank() } if (fileCommitCount.size > 1) { val count = fileCommitCount.size throw PaperweightException( @@ -91,8 +91,8 @@ abstract class RebuildBaseGitPatches : ControllableOutputTask() { } // these have to be retrieved dynamically - val patchedBaseCommit = git("rev-list", "--grep=${identifier.get()} Base Patches", "--max-count=1", "base..HEAD").getText().trim() - val fileCommit = git("rev-list", "--grep=${identifier.get()} File Patches", "--max-count=1", "base..HEAD").getText().trim() + val patchedBaseCommit = git("rev-list", "--grep=^${identifier.get()} Base Patches$", "--max-count=1", "base..HEAD").getText().trim() + val fileCommit = git("rev-list", "--grep=^${identifier.get()} File Patches$", "--max-count=1", "base..HEAD").getText().trim() // we update the appropriate tags to reflect the new repo state git("tag", "-f", "patchedBase", patchedBaseCommit).executeSilently(silenceErr = true) From c5ddcee9b568262cdada4acfe1c1105bb2509154 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Fri, 5 Sep 2025 19:37:42 +0200 Subject: [PATCH 069/257] fix(patch apply): workaround https://github.com/PaperMC/paperweight/issues/324 by verifying if a patch file can even be applied with git before git patch apply --- gradle.properties | 2 +- .../extension/AdditionalUpstreamConfig.kt | 4 --- .../core/tasks/patching/ApplyFilePatches.kt | 12 +++++++- .../patching/ApplyResourceFilePatches.kt | 12 +++++++- .../tasks/patchroulette/PatchRouletteApply.kt | 2 +- .../kotlin/io/papermc/paperweight/util/git.kt | 29 ++++++++++++++----- 6 files changed, 46 insertions(+), 15 deletions(-) diff --git a/gradle.properties b/gradle.properties index e20e1a766..c12c71ad9 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ group = io.canvasmc.weaver -version = 2.3.2-SNAPSHOT +version = 2.3.3-SNAPSHOT org.gradle.configuration-cache = true org.gradle.caching = true diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/AdditionalUpstreamConfig.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/AdditionalUpstreamConfig.kt index 9bae108f0..deca7d626 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/AdditionalUpstreamConfig.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/AdditionalUpstreamConfig.kt @@ -22,11 +22,8 @@ package io.papermc.paperweight.core.extension -import io.papermc.paperweight.core.extension.AdditionalUpstreamConfig.PatchGenerationConfig.InputConfig -import io.papermc.paperweight.util.* import javax.annotation.Nullable import javax.inject.Inject -import kotlin.io.path.* import org.gradle.api.Action import org.gradle.api.Named import org.gradle.api.NamedDomainObjectContainer @@ -34,7 +31,6 @@ import org.gradle.api.file.DirectoryProperty import org.gradle.api.file.RegularFileProperty import org.gradle.api.model.ObjectFactory import org.gradle.api.provider.Property -import org.gradle.kotlin.dsl.* import org.gradle.kotlin.dsl.domainObjectContainer import org.gradle.kotlin.dsl.newInstance diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyFilePatches.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyFilePatches.kt index f1226215b..41d4cf339 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyFilePatches.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyFilePatches.kt @@ -118,7 +118,7 @@ abstract class ApplyFilePatches : BaseTask() { val result = if (!patches.isPresent) { commit() 0 - } else if (gitFilePatches.get()) { + } else if (gitFilePatches.get() && shouldApplyWithGit(repoPath)) { applyWithGit(repoPath) } else { applyWithDiffPatch() @@ -146,6 +146,16 @@ abstract class ApplyFilePatches : BaseTask() { } } + private fun shouldApplyWithGit(repoPath: Path): Boolean { + val patchFiles = patches.path.filesMatchingRecursive("*.patch") + val git = Git(repoPath) + val canApply = patchFiles.any { patch -> + val result = git("apply", "--check", patch.absolutePathString()).getText(ignoreErr = true) + result.contains("error: corrupt patch at line") + } + return !canApply + } + private fun applyWithGit(repoPath: Path): Int { val git = Git(repoPath) val patchFiles = patches.path.filesMatchingRecursive("*.patch") diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyResourceFilePatches.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyResourceFilePatches.kt index cbbdcdee8..4290ee354 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyResourceFilePatches.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyResourceFilePatches.kt @@ -132,7 +132,7 @@ abstract class ApplyResourceFilePatches : BaseTask() { val result = if (!patches.isPresent) { commit() 0 - } else if (gitFilePatches.get()) { + } else if (gitFilePatches.get() && shouldApplyWithGit(outputPath)) { applyWithGit(outputPath) } else { applyWithDiffPatch() @@ -168,6 +168,16 @@ abstract class ApplyResourceFilePatches : BaseTask() { git.close() } + private fun shouldApplyWithGit(repoPath: Path): Boolean { + val patchFiles = patches.path.filesMatchingRecursive("*.patch") + val git = Git(repoPath) + val canApply = patchFiles.any { patch -> + val result = git("apply", "--check", patch.absolutePathString()).getText(ignoreErr = true) + result.contains("error: corrupt patch at line") + } + return !canApply + } + private fun applyWithGit(outputPath: Path): Int { val git = Git(outputPath) val patchFiles = patches.path.filesMatchingRecursive("*.patch") diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patchroulette/PatchRouletteApply.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patchroulette/PatchRouletteApply.kt index a86b069b7..92f7bd194 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patchroulette/PatchRouletteApply.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patchroulette/PatchRouletteApply.kt @@ -159,7 +159,7 @@ abstract class PatchRouletteApply : AbstractPatchRouletteTask() { ) val errorTextBuffer = ByteArrayOutputStream() applyCommand.setup(System.out, errorTextBuffer) - val applyCommandExitCode = applyCommand.run() + val applyCommandExitCode = applyCommand.run(false) val applyCommandErrorText = String(errorTextBuffer.toByteArray(), Charset.defaultCharset()) val applyCommandErrorTextContainsUnexpected = applyCommandErrorText.lines() .filter { it.isNotBlank() } diff --git a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/git.kt b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/git.kt index 2fa76bdc6..6eb6f9622 100644 --- a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/git.kt +++ b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/git.kt @@ -157,7 +157,7 @@ class Command(private val processBuilder: ProcessBuilder, private val command: S private var outStream: OutputStream = UselessOutputStream private var errStream: OutputStream = UselessOutputStream - fun run(): Int { + fun run(ignoreErr: Boolean): Int { if (paperweightDebug()) { // Override all settings for debug setup(DelegatingOutputStream(outStream, System.out), DelegatingOutputStream(errStream, System.err)) @@ -190,27 +190,35 @@ class Command(private val processBuilder: ProcessBuilder, private val command: S errStream.write(error.readBytes()) return process.waitFor() } catch (e: Exception) { - throw PaperweightException("Failed to call git command: $command", e) + if (ignoreErr) { + return 0 + } else { + throw PaperweightException("Failed to call git command: $command", e) + } } } fun runSilently(silenceOut: Boolean = true, silenceErr: Boolean = false): Int { silence(silenceOut, silenceErr) - return run() + return run(false) } fun runOut(): Int { setup(System.out, System.err) - return run() + return run(false) } fun execute() { - val res = run() + val res = run(false) if (res != 0) { throw PaperweightException("Command finished with $res exit code: $command") } } + fun execute(ignoreErr: Boolean) { + run(ignoreErr) + } + fun executeSilently(silenceOut: Boolean = true, silenceErr: Boolean = false) { silence(silenceOut, silenceErr) execute() @@ -240,11 +248,18 @@ class Command(private val processBuilder: ProcessBuilder, private val command: S return String(out.toByteArray(), Charset.defaultCharset()) } + fun getText(ignoreErr: Boolean): String { + val out = ByteArrayOutputStream() + setup(out, out) + execute(ignoreErr) + return String(out.toByteArray(), Charset.defaultCharset()) + } + @Suppress("unused") fun readText(): String? { val out = ByteArrayOutputStream() setup(out, System.err) - return if (run() == 0) String(out.toByteArray(), Charset.defaultCharset()) else null + return if (run(false) == 0) String(out.toByteArray(), Charset.defaultCharset()) else null } class Result(val exit: Int, val out: String) @@ -257,7 +272,7 @@ class Command(private val processBuilder: ProcessBuilder, private val command: S } else { setup(out, out) } - Result(run(), String(out.toByteArray())) + Result(run(false), String(out.toByteArray())) } } From 8d6966a9891d7e31925dc3b0085cdf132e9ba864 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Fri, 5 Sep 2025 20:30:01 +0200 Subject: [PATCH 070/257] chore: improve gen tasks descriptions --- .../io/papermc/paperweight/patcher/PaperweightPatcher.kt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/PaperweightPatcher.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/PaperweightPatcher.kt index 3e0a6411d..7350c6e36 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/PaperweightPatcher.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/PaperweightPatcher.kt @@ -164,6 +164,7 @@ abstract class PaperweightPatcher : Plugin { tasks.register("generate${name}Sources") { group = "source generation" + description = "Generates sources from ${input.name}" dependsOn(applyAdditionalUpstream) forkName.set(upstream.name) forkUrl.set(upstream.repo) @@ -196,7 +197,7 @@ abstract class PaperweightPatcher : Plugin { val genPatches = tasks.register("generate${upstream.name.capitalized()}Patches") { dependsOn(depend.map { it }) group = "patch generation" - description = "Generates base patches from ${upstream.name}" + description = "Condenses ${upstream.name} changes for every inputConfig into a corresponding patch file" forkName.set(upstream.name) forkUrl.set(upstream.repo) commitHash.set(upstream.ref) @@ -215,7 +216,7 @@ abstract class PaperweightPatcher : Plugin { val generate = tasks.register("generate${upstream.name.capitalized()}") { group = "generation" - description = "Generates base patches and sources from ${upstream.name}" + description = "Generates patches and sources from ${upstream.name}" } genSources { dependsOn(sourceGenDepend.map { it }) } generate { dependsOn(genPatches, genSources) } From 42123b0729737ae82fecaf0381bb420b52e9882a Mon Sep 17 00:00:00 2001 From: Toffikk Date: Fri, 5 Sep 2025 20:33:06 +0200 Subject: [PATCH 071/257] fixup --- .../io/papermc/paperweight/core/tasks/GenerateSources.kt | 2 +- .../io/papermc/paperweight/patcher/PaperweightPatcher.kt | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/GenerateSources.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/GenerateSources.kt index bcd872d60..bd2a96bbd 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/GenerateSources.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/GenerateSources.kt @@ -144,7 +144,7 @@ abstract class GenerateSources : JavaLauncherTask() { ) for (output in outputs) { - output.deleteRecursively() + output.deleteRecursive() when (output) { generatedOutput -> { tempOutput.resolve("src/main/java").copyRecursivelyTo(output) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/PaperweightPatcher.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/PaperweightPatcher.kt index 7350c6e36..79b312d19 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/PaperweightPatcher.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/PaperweightPatcher.kt @@ -150,8 +150,8 @@ abstract class PaperweightPatcher : Plugin { forkName.set(upstream.name) repoName.set(input.name) workDir.set(workDirFromProp) - atFile.set(input.additionalAts.fileExists(project)) - additionalPatch.set(input.additionalPatch.fileExists(project)) + atFile.set(input.additionalAts.fileExists()) + additionalPatch.set(input.additionalPatch.fileExists()) } } @@ -171,8 +171,8 @@ abstract class PaperweightPatcher : Plugin { inputFrom.set(input.name) commitHash.set(upstream.ref) workDir.set(workDirFromProp) - atFile.set(input.additionalAts.fileExists(project)) - additionalPatch.set(input.additionalPatch.fileExists(project)) + atFile.set(input.additionalAts.fileExists()) + additionalPatch.set(input.additionalPatch.fileExists()) generateSources.set(input.generateSources.orElse(true)) generateResources.set(input.generateResources.orElse(true)) generateTestSources.set(input.generateTestSources.orElse(true)) From 2c86f501a77144bb162d536ac475ffa50214bf44 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Fri, 5 Sep 2025 20:34:17 +0200 Subject: [PATCH 072/257] chore: use our util --- .../kotlin/io/papermc/paperweight/core/tasks/GenerateSources.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/GenerateSources.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/GenerateSources.kt index bcd872d60..bd2a96bbd 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/GenerateSources.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/GenerateSources.kt @@ -144,7 +144,7 @@ abstract class GenerateSources : JavaLauncherTask() { ) for (output in outputs) { - output.deleteRecursively() + output.deleteRecursive() when (output) { generatedOutput -> { tempOutput.resolve("src/main/java").copyRecursivelyTo(output) From e410b59a8ecd1f39008d2d8e4011893d6f53925e Mon Sep 17 00:00:00 2001 From: Toffikk Date: Sun, 7 Sep 2025 16:02:02 +0200 Subject: [PATCH 073/257] fix: revert unintentional change --- .../tasks/patchroulette/PatchRouletteApply.kt | 2 +- .../kotlin/io/papermc/paperweight/util/git.kt | 18 +++++++----------- 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patchroulette/PatchRouletteApply.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patchroulette/PatchRouletteApply.kt index 92f7bd194..a86b069b7 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patchroulette/PatchRouletteApply.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patchroulette/PatchRouletteApply.kt @@ -159,7 +159,7 @@ abstract class PatchRouletteApply : AbstractPatchRouletteTask() { ) val errorTextBuffer = ByteArrayOutputStream() applyCommand.setup(System.out, errorTextBuffer) - val applyCommandExitCode = applyCommand.run(false) + val applyCommandExitCode = applyCommand.run() val applyCommandErrorText = String(errorTextBuffer.toByteArray(), Charset.defaultCharset()) val applyCommandErrorTextContainsUnexpected = applyCommandErrorText.lines() .filter { it.isNotBlank() } diff --git a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/git.kt b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/git.kt index 6eb6f9622..db3c21914 100644 --- a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/git.kt +++ b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/git.kt @@ -157,7 +157,7 @@ class Command(private val processBuilder: ProcessBuilder, private val command: S private var outStream: OutputStream = UselessOutputStream private var errStream: OutputStream = UselessOutputStream - fun run(ignoreErr: Boolean): Int { + fun run(): Int { if (paperweightDebug()) { // Override all settings for debug setup(DelegatingOutputStream(outStream, System.out), DelegatingOutputStream(errStream, System.err)) @@ -190,33 +190,29 @@ class Command(private val processBuilder: ProcessBuilder, private val command: S errStream.write(error.readBytes()) return process.waitFor() } catch (e: Exception) { - if (ignoreErr) { - return 0 - } else { throw PaperweightException("Failed to call git command: $command", e) - } } } fun runSilently(silenceOut: Boolean = true, silenceErr: Boolean = false): Int { silence(silenceOut, silenceErr) - return run(false) + return run() } fun runOut(): Int { setup(System.out, System.err) - return run(false) + return run() } fun execute() { - val res = run(false) + val res = run() if (res != 0) { throw PaperweightException("Command finished with $res exit code: $command") } } fun execute(ignoreErr: Boolean) { - run(ignoreErr) + run() } fun executeSilently(silenceOut: Boolean = true, silenceErr: Boolean = false) { @@ -259,7 +255,7 @@ class Command(private val processBuilder: ProcessBuilder, private val command: S fun readText(): String? { val out = ByteArrayOutputStream() setup(out, System.err) - return if (run(false) == 0) String(out.toByteArray(), Charset.defaultCharset()) else null + return if (run() == 0) String(out.toByteArray(), Charset.defaultCharset()) else null } class Result(val exit: Int, val out: String) @@ -272,7 +268,7 @@ class Command(private val processBuilder: ProcessBuilder, private val command: S } else { setup(out, out) } - Result(run(false), String(out.toByteArray())) + Result(run(), String(out.toByteArray())) } } From c094b6dd28598c8fb1f4eb527386a8a483c9a660 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Sun, 7 Sep 2025 16:02:02 +0200 Subject: [PATCH 074/257] fix: revert unintentional change --- .../tasks/patchroulette/PatchRouletteApply.kt | 2 +- .../kotlin/io/papermc/paperweight/util/git.kt | 20 ++++++++----------- 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patchroulette/PatchRouletteApply.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patchroulette/PatchRouletteApply.kt index 92f7bd194..a86b069b7 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patchroulette/PatchRouletteApply.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patchroulette/PatchRouletteApply.kt @@ -159,7 +159,7 @@ abstract class PatchRouletteApply : AbstractPatchRouletteTask() { ) val errorTextBuffer = ByteArrayOutputStream() applyCommand.setup(System.out, errorTextBuffer) - val applyCommandExitCode = applyCommand.run(false) + val applyCommandExitCode = applyCommand.run() val applyCommandErrorText = String(errorTextBuffer.toByteArray(), Charset.defaultCharset()) val applyCommandErrorTextContainsUnexpected = applyCommandErrorText.lines() .filter { it.isNotBlank() } diff --git a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/git.kt b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/git.kt index 6eb6f9622..00b52d4d2 100644 --- a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/git.kt +++ b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/git.kt @@ -157,7 +157,7 @@ class Command(private val processBuilder: ProcessBuilder, private val command: S private var outStream: OutputStream = UselessOutputStream private var errStream: OutputStream = UselessOutputStream - fun run(ignoreErr: Boolean): Int { + fun run(): Int { if (paperweightDebug()) { // Override all settings for debug setup(DelegatingOutputStream(outStream, System.out), DelegatingOutputStream(errStream, System.err)) @@ -190,33 +190,29 @@ class Command(private val processBuilder: ProcessBuilder, private val command: S errStream.write(error.readBytes()) return process.waitFor() } catch (e: Exception) { - if (ignoreErr) { - return 0 - } else { - throw PaperweightException("Failed to call git command: $command", e) - } + throw PaperweightException("Failed to call git command: $command", e) } } fun runSilently(silenceOut: Boolean = true, silenceErr: Boolean = false): Int { silence(silenceOut, silenceErr) - return run(false) + return run() } fun runOut(): Int { setup(System.out, System.err) - return run(false) + return run() } fun execute() { - val res = run(false) + val res = run() if (res != 0) { throw PaperweightException("Command finished with $res exit code: $command") } } fun execute(ignoreErr: Boolean) { - run(ignoreErr) + run() } fun executeSilently(silenceOut: Boolean = true, silenceErr: Boolean = false) { @@ -259,7 +255,7 @@ class Command(private val processBuilder: ProcessBuilder, private val command: S fun readText(): String? { val out = ByteArrayOutputStream() setup(out, System.err) - return if (run(false) == 0) String(out.toByteArray(), Charset.defaultCharset()) else null + return if (run() == 0) String(out.toByteArray(), Charset.defaultCharset()) else null } class Result(val exit: Int, val out: String) @@ -272,7 +268,7 @@ class Command(private val processBuilder: ProcessBuilder, private val command: S } else { setup(out, out) } - Result(run(false), String(out.toByteArray())) + Result(run(), String(out.toByteArray())) } } From 5020cb02f32639be0e243da53235ca65f6803019 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Thu, 18 Sep 2025 20:32:47 +0200 Subject: [PATCH 075/257] feat: Allow specifying additional mappings such as parchment to the source code, also bump release This makes it possible to have parchment javadocs in canvas's source code --- gradle.properties | 2 +- .../paperweight/core/extension/ForkConfig.kt | 1 + .../core/extension/PaperExtension.kt | 1 + .../core/taskcontainers/CoreTasks.kt | 2 + .../taskcontainers/MinecraftPatchingTasks.kt | 26 ++++++ .../paperweight/core/tasks/GenerateSources.kt | 5 +- .../core/tasks/PrepareForPatchGeneration.kt | 5 +- .../core/tasks/SetupForkMinecraftSources.kt | 19 +++++ .../core/tasks/patching/ApplyFilePatches.kt | 8 +- .../core/util/ApplyAdditionalMappings.kt | 84 +++++++++++++++++++ .../paperweight/patcher/PaperweightPatcher.kt | 2 + 11 files changed, 144 insertions(+), 11 deletions(-) create mode 100644 paperweight-core/src/main/kotlin/io/papermc/paperweight/core/util/ApplyAdditionalMappings.kt diff --git a/gradle.properties b/gradle.properties index c12c71ad9..f517b6a73 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ group = io.canvasmc.weaver -version = 2.3.3-SNAPSHOT +version = 2.3.4-SNAPSHOT org.gradle.configuration-cache = true org.gradle.caching = true diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/ForkConfig.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/ForkConfig.kt index 737859282..d93edcf34 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/ForkConfig.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/ForkConfig.kt @@ -57,6 +57,7 @@ abstract class ForkConfig @Inject constructor( val buildDataDir: DirectoryProperty = objects.dirFrom(rootDirectory, "build-data") val devImports: RegularFileProperty = objects.fileFrom(buildDataDir, "dev-imports.txt") val additionalAts: RegularFileProperty = objects.fileFrom(buildDataDir, providers.provider { "$name.at" }) + val additionalMappings: RegularFileProperty = objects.fileFrom(buildDataDir, "parchment.json") val reobfMappingsPatch: RegularFileProperty = objects.fileFrom(buildDataDir, "reobf-mappings-patch.tiny") val forks: Property = objects.property() diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/PaperExtension.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/PaperExtension.kt index a6b670f81..5ca8ffce1 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/PaperExtension.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/PaperExtension.kt @@ -44,6 +44,7 @@ abstract class PaperExtension @Inject constructor(objects: ObjectFactory, projec val buildDataDir: DirectoryProperty = objects.dirFrom(rootDirectory, "build-data") val devImports: RegularFileProperty = objects.fileFrom(buildDataDir, "dev-imports.txt") val additionalAts: RegularFileProperty = objects.fileFrom(buildDataDir, "paper.at") + val additionalMappings: RegularFileProperty = objects.fileFrom(buildDataDir, "parchment.json") val reobfMappingsPatch: RegularFileProperty = objects.fileFrom(buildDataDir, "reobf-mappings-patch.tiny") val mappingsPatch: RegularFileProperty = objects.fileProperty() } diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/CoreTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/CoreTasks.kt index 4d0dd6178..b21404c32 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/CoreTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/CoreTasks.kt @@ -192,6 +192,7 @@ class CoreTasks( project.coreExt.paper.resourcePatchDir, project.coreExt.paper.featurePatchDir, project.coreExt.paper.additionalAts, + project.coreExt.paper.additionalMappings, extractMacheSources.flatMap { it.outputDir }, extractMacheResources.flatMap { it.outputDir }, project.coreExt.gitFilePatches, @@ -229,6 +230,7 @@ class CoreTasks( cfg.resourcePatchDir, cfg.featurePatchDir, cfg.additionalAts, + cfg.additionalMappings, upstreamTasks.first.applyFeaturePatches.flatMap { it.repo }, upstreamTasks.first.applyResourcePatches.flatMap { it.output }, project.coreExt.gitFilePatches, diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt index 7d0456bcb..a160ef1aa 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt @@ -35,7 +35,10 @@ import io.papermc.paperweight.core.tasks.patching.RebuildFilePatches import io.papermc.paperweight.tasks.* import io.papermc.paperweight.util.* import io.papermc.paperweight.util.constants.* +import io.papermc.paperweight.util.path import java.nio.file.Path +import kotlin.io.path.exists +import kotlin.io.path.readText import org.gradle.api.Project import org.gradle.api.Task import org.gradle.api.file.Directory @@ -57,6 +60,7 @@ class MinecraftPatchingTasks( private val resourcePatchDir: DirectoryProperty, private val featurePatchDir: DirectoryProperty, private val additionalAts: RegularFileProperty, + private val additionalMappings: RegularFileProperty, private val baseSources: Provider, private val baseResources: Provider, private val gitFilePatches: Provider, @@ -200,6 +204,28 @@ class MinecraftPatchingTasks( applyBasePatches.configure { input.set(setup.flatMap { it.outputDir }) } + + if (additionalMappings.path.exists() && additionalMappings.path.readText().isNotBlank()) { + val applyParchment = tasks.register("apply${configName.capitalized()}AdditionalMappings") { + description = "Applies additional mappings, along with JDs if applicable to Minecraft sources (after applying base patches)" + inputDir.set(applyBasePatches.flatMap { it.output }) + outputDir.set(layout.cache.resolve(paperTaskOutput())) + identifier.set(configName) + mappingFile.set(additionalMappings) + mapping.jst.from(project.configurations.named(JST_CONFIG)) + } + + applySourcePatches.configure { + base.set(applyParchment.flatMap { it.outputDir }) + baseRef.set("Mapped") + } + + applySourcePatchesFuzzy.configure { + base.set(applyParchment.flatMap { it.outputDir }) + baseRef.set("Mapped") + } + } + /* uncomment this when we enable validation of ATs since then seperate AT files will be forced, right now this breaks on projects with one AT file for many modules val name = "rebuild${namePart}BasePatches" if (name in tasks.names) { diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/GenerateSources.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/GenerateSources.kt index bd2a96bbd..fecd5a046 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/GenerateSources.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/GenerateSources.kt @@ -25,7 +25,6 @@ package io.papermc.paperweight.core.tasks import io.papermc.paperweight.core.util.ApplySourceATs import io.papermc.paperweight.tasks.JavaLauncherTask import io.papermc.paperweight.util.* -import io.papermc.paperweight.util.constants.JST_CONFIG import io.papermc.paperweight.util.constants.paperTaskOutput import java.nio.file.Path import kotlin.io.path.* @@ -56,9 +55,7 @@ abstract class GenerateSources : JavaLauncherTask() { abstract val tempOutput: DirectoryProperty @get:Nested - val ats: ApplySourceATs = objects.newInstance().apply { - jst.from(project.configurations.named(JST_CONFIG)) - } + val ats: ApplySourceATs = objects.newInstance() @get:InputFile @get:Optional diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/PrepareForPatchGeneration.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/PrepareForPatchGeneration.kt index b9a23b069..1761f71d1 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/PrepareForPatchGeneration.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/PrepareForPatchGeneration.kt @@ -25,7 +25,6 @@ package io.papermc.paperweight.core.tasks import io.papermc.paperweight.core.util.ApplySourceATs import io.papermc.paperweight.tasks.JavaLauncherTask import io.papermc.paperweight.util.* -import io.papermc.paperweight.util.constants.JST_CONFIG import io.papermc.paperweight.util.constants.paperTaskOutput import kotlin.io.path.* import org.gradle.api.file.DirectoryProperty @@ -47,9 +46,7 @@ abstract class PrepareForPatchGeneration : JavaLauncherTask() { abstract val workDir: DirectoryProperty @get:Nested - val ats: ApplySourceATs = objects.newInstance().apply { - jst.from(project.configurations.named(JST_CONFIG)) - } + val ats: ApplySourceATs = objects.newInstance() @get:InputFile @get:Optional diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupForkMinecraftSources.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupForkMinecraftSources.kt index 6648ce1a9..3a4f68579 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupForkMinecraftSources.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupForkMinecraftSources.kt @@ -22,6 +22,7 @@ package io.papermc.paperweight.core.tasks +import io.papermc.paperweight.core.util.ApplyAdditionalMappings import io.papermc.paperweight.core.util.ApplySourceATs import io.papermc.paperweight.tasks.* import io.papermc.paperweight.util.* @@ -50,10 +51,17 @@ abstract class SetupForkMinecraftSources : JavaLauncherTask() { @get:Nested val ats: ApplySourceATs = objects.newInstance() + @get:Nested + val mapping: ApplyAdditionalMappings = objects.newInstance() + @get:InputFile @get:Optional abstract val atFile: RegularFileProperty + @get:InputFile + @get:Optional + abstract val mappingFile: RegularFileProperty + @get:Optional @get:InputDirectory abstract val libraryImports: DirectoryProperty @@ -90,6 +98,17 @@ abstract class SetupForkMinecraftSources : JavaLauncherTask() { ) commitAndTag(git, "ATs", "${identifier.get()} ATs") } + if (mappingFile.isPresent) { + println("Applying additional mappings...") + mapping.run( + launcher.get(), + inputDir.path, + outputDir.path, + mappingFile.path, + temporaryDir.toPath(), + ) + commitAndTag(git, "Mapped", "${identifier.get()} Additional Mappings Data") + } git.close() } diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyFilePatches.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyFilePatches.kt index 41d4cf339..5ff6d1502 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyFilePatches.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyFilePatches.kt @@ -69,6 +69,10 @@ abstract class ApplyFilePatches : BaseTask() { @get:Optional abstract val base: DirectoryProperty + @get:Optional + @get:Input + abstract val baseRef: Property + @get:Input @get:Optional abstract val identifier: Property @@ -100,7 +104,7 @@ abstract class ApplyFilePatches : BaseTask() { checkoutRepoFromUpstream( git, base, - "patchedBase", + baseRef.getOrElse("patchedBase"), branchName = "main", ref = true, ) @@ -112,7 +116,7 @@ abstract class ApplyFilePatches : BaseTask() { if (git("checkout", "main").runSilently(silenceErr = true) != 0) { git("checkout", "-b", "main").runSilently(silenceErr = true) } - git("reset", "--hard", "patchedBase").runSilently(silenceErr = true) + git("reset", "--hard", baseRef.getOrElse("patchedBase")).runSilently(silenceErr = true) git("gc").runSilently(silenceErr = true) val result = if (!patches.isPresent) { diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/util/ApplyAdditionalMappings.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/util/ApplyAdditionalMappings.kt new file mode 100644 index 000000000..9b4f8fc63 --- /dev/null +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/util/ApplyAdditionalMappings.kt @@ -0,0 +1,84 @@ +/* + * paperweight is a Gradle plugin for the PaperMC project. + * + * Copyright (c) 2023 Kyle Wood (DenWav) + * Contributors + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 only, no later versions. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + * USA + */ + +package io.papermc.paperweight.core.util + +import io.papermc.paperweight.util.* +import java.nio.file.Path +import kotlin.io.path.* +import org.gradle.api.file.ConfigurableFileCollection +import org.gradle.api.provider.Property +import org.gradle.api.tasks.Classpath +import org.gradle.api.tasks.CompileClasspath +import org.gradle.api.tasks.Input +import org.gradle.jvm.toolchain.JavaLauncher + +abstract class ApplyAdditionalMappings { + @get:Classpath + abstract val jst: ConfigurableFileCollection + + @get:Input + abstract val memory: Property + + @get:CompileClasspath + abstract val jstClasspath: ConfigurableFileCollection + + init { + memory.convention("1G") + } + + fun run( + javaLauncher: JavaLauncher, + input: Path, + output: Path, + mappingFile: Path, + workDir: Path, + + ) { + workDir.deleteRecursive() + workDir.createDirectories() + javaLauncher.runJar( + jst, + workDir, + workDir.resolve("log.txt"), + jvmArgs = listOf("-Xmx${memory.get()}"), + args = jstArgs(input, output, mappingFile).toTypedArray() + ) + } + + private fun jstArgs( + inputDir: Path, + outputDir: Path, + mappingFile: Path, + ): List { + return listOf( + "--in-format=FOLDER", + "--out-format=FOLDER", + "--enable-parchment", + "--parchment-mappings=$mappingFile", + "--hidden-prefix=.git", + *jstClasspath.files.map { "--classpath=${it.absolutePath}" }.toTypedArray(), + inputDir.absolutePathString(), + outputDir.absolutePathString(), + ) + } +} diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/PaperweightPatcher.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/PaperweightPatcher.kt index 7350c6e36..7fa1a4a68 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/PaperweightPatcher.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/PaperweightPatcher.kt @@ -151,6 +151,7 @@ abstract class PaperweightPatcher : Plugin { repoName.set(input.name) workDir.set(workDirFromProp) atFile.set(input.additionalAts.fileExists(project)) + ats.jst.from(project.configurations.named(JST_CONFIG)) additionalPatch.set(input.additionalPatch.fileExists(project)) } } @@ -172,6 +173,7 @@ abstract class PaperweightPatcher : Plugin { commitHash.set(upstream.ref) workDir.set(workDirFromProp) atFile.set(input.additionalAts.fileExists(project)) + ats.jst.from(project.configurations.named(JST_CONFIG)) additionalPatch.set(input.additionalPatch.fileExists(project)) generateSources.set(input.generateSources.orElse(true)) generateResources.set(input.generateResources.orElse(true)) From 3791d85fba868fb9caaa80bd8d59be8f111fe7fb Mon Sep 17 00:00:00 2001 From: Toffikk Date: Thu, 18 Sep 2025 23:04:42 +0200 Subject: [PATCH 076/257] refactor additional mappings tests might fail due to nondeployed jst yet --- gradle/libs.versions.toml | 6 ++---- .../io/papermc/paperweight/core/PaperweightCore.kt | 6 +++++- .../paperweight/core/extension/ForkConfig.kt | 11 +++++++++-- .../paperweight/core/extension/PaperExtension.kt | 1 - .../core/extension/PaperweightCoreExtension.kt | 1 + .../paperweight/core/taskcontainers/CoreTasks.kt | 2 -- .../core/taskcontainers/MinecraftPatchingTasks.kt | 14 +++++--------- .../core/tasks/SetupForkMinecraftSources.kt | 13 +++++++++---- .../core/util/ApplyAdditionalMappings.kt | 1 + .../paperweight/patcher/PaperweightPatcher.kt | 6 ++---- .../extension/PaperweightPatcherExtension.kt | 4 ++-- paperweight-lib/build.gradle.kts | 1 - .../paperweight/util/constants/constants.kt | 3 +++ 13 files changed, 39 insertions(+), 30 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 1c214ef8b..1ef1773b0 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -3,8 +3,7 @@ asm = "9.8" lorenz = "0.5.8" hypo = "1.2.4" serialize = "1.8.1" -jst = "1.0.68+paper.2-SNAPSHOT" -accessWidener = "2.1.0" +jst = "1.0.68+canvas.1-SNAPSHOT" tinyRemapper = "0.11.2" [libraries] @@ -38,10 +37,9 @@ diffpatch = "io.codechicken:DiffPatch:2.0.1.40" serialize-core = { module = "org.jetbrains.kotlinx:kotlinx-serialization-core", version.ref = "serialize" } serialize-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "serialize" } -jst = { module = "io.papermc.jst:jst-cli-bundle", version.ref = "jst" } +jst = { module = "io.canvasmc.jst:jst-cli-bundle", version.ref = "jst" } # jst = { module = "net.neoforged.jst:jst-cli-bundle", version.ref = "jst" } tinyRemapper = { module = "net.fabricmc:tiny-remapper", version.ref = "tinyRemapper" } -accessWidener = { module = "net.fabricmc:access-widener", version.ref = "accessWidener" } # test mockk = "io.mockk:mockk:1.14.2" diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/PaperweightCore.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/PaperweightCore.kt index cc6813ed9..8ded29017 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/PaperweightCore.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/PaperweightCore.kt @@ -103,7 +103,7 @@ abstract class PaperweightCore : Plugin { target.configurations.register(JST_CONFIG) { defaultDependencies { // add(project.dependencies.create("net.neoforged.jst:jst-cli-bundle:${LibraryVersions.JST}")) - add(target.dependencies.create("io.papermc.jst:jst-cli-bundle:${LibraryVersions.JST}")) + add(target.dependencies.create("io.canvasmc.jst:jst-cli-bundle:${LibraryVersions.JST}")) } } @@ -179,6 +179,10 @@ abstract class PaperweightCore : Plugin { } repositories { + maven(ext.jstRepo) { + name = JST_REPO_NAME + content { onlyForConfigurations(JST_CONFIG) } + } maven(ext.macheRepo) { name = MACHE_REPO_NAME content { onlyForConfigurations(MACHE_CONFIG) } diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/ForkConfig.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/ForkConfig.kt index d93edcf34..5df6e8c54 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/ForkConfig.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/ForkConfig.kt @@ -23,6 +23,7 @@ package io.papermc.paperweight.core.extension import io.papermc.paperweight.util.* +import io.papermc.paperweight.util.constants.ADDITIONAL_MAPPINGS_CONFIG import javax.inject.Inject import org.gradle.api.Action import org.gradle.api.Named @@ -39,7 +40,7 @@ abstract class ForkConfig @Inject constructor( private val configName: String, providers: ProviderFactory, objects: ObjectFactory, - project: Project, + private val project: Project, ) : Named { override fun getName(): String { return configName @@ -57,7 +58,6 @@ abstract class ForkConfig @Inject constructor( val buildDataDir: DirectoryProperty = objects.dirFrom(rootDirectory, "build-data") val devImports: RegularFileProperty = objects.fileFrom(buildDataDir, "dev-imports.txt") val additionalAts: RegularFileProperty = objects.fileFrom(buildDataDir, providers.provider { "$name.at" }) - val additionalMappings: RegularFileProperty = objects.fileFrom(buildDataDir, "parchment.json") val reobfMappingsPatch: RegularFileProperty = objects.fileFrom(buildDataDir, "reobf-mappings-patch.tiny") val forks: Property = objects.property() @@ -68,6 +68,13 @@ abstract class ForkConfig @Inject constructor( }.orElse( providers.provider { objects.newInstance("paper", false) } ) + val additionalMappings = project.configurations.register(ADDITIONAL_MAPPINGS_CONFIG) + + fun additionalMappings(dep: String) { + additionalMappings.configure { + dependencies.add(project.dependencies.create(dep)) + } + } val upstream: UpstreamConfig by lazy { upstreamProvider.get() diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/PaperExtension.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/PaperExtension.kt index 5ca8ffce1..a6b670f81 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/PaperExtension.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/PaperExtension.kt @@ -44,7 +44,6 @@ abstract class PaperExtension @Inject constructor(objects: ObjectFactory, projec val buildDataDir: DirectoryProperty = objects.dirFrom(rootDirectory, "build-data") val devImports: RegularFileProperty = objects.fileFrom(buildDataDir, "dev-imports.txt") val additionalAts: RegularFileProperty = objects.fileFrom(buildDataDir, "paper.at") - val additionalMappings: RegularFileProperty = objects.fileFrom(buildDataDir, "parchment.json") val reobfMappingsPatch: RegularFileProperty = objects.fileFrom(buildDataDir, "reobf-mappings-patch.tiny") val mappingsPatch: RegularFileProperty = objects.fileProperty() } diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/PaperweightCoreExtension.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/PaperweightCoreExtension.kt index daa2b55b2..46ad36805 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/PaperweightCoreExtension.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/PaperweightCoreExtension.kt @@ -40,6 +40,7 @@ abstract class PaperweightCoreExtension @Inject constructor(objects: ObjectFacto val bundlerJarName: Property = objects.property().convention("paper") val macheRepo: Property = objects.property().convention(PAPER_MAVEN_REPO_URL) + val jstRepo: Property = objects.property().convention(CANVAS_MAVEN_REPO_URL) val gitFilePatches: Property = objects.property().convention(false) val filterPatches: Property = objects.property().convention(true) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/CoreTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/CoreTasks.kt index b21404c32..4d0dd6178 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/CoreTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/CoreTasks.kt @@ -192,7 +192,6 @@ class CoreTasks( project.coreExt.paper.resourcePatchDir, project.coreExt.paper.featurePatchDir, project.coreExt.paper.additionalAts, - project.coreExt.paper.additionalMappings, extractMacheSources.flatMap { it.outputDir }, extractMacheResources.flatMap { it.outputDir }, project.coreExt.gitFilePatches, @@ -230,7 +229,6 @@ class CoreTasks( cfg.resourcePatchDir, cfg.featurePatchDir, cfg.additionalAts, - cfg.additionalMappings, upstreamTasks.first.applyFeaturePatches.flatMap { it.repo }, upstreamTasks.first.applyResourcePatches.flatMap { it.output }, project.coreExt.gitFilePatches, diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt index a160ef1aa..c126d9612 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt @@ -35,10 +35,7 @@ import io.papermc.paperweight.core.tasks.patching.RebuildFilePatches import io.papermc.paperweight.tasks.* import io.papermc.paperweight.util.* import io.papermc.paperweight.util.constants.* -import io.papermc.paperweight.util.path import java.nio.file.Path -import kotlin.io.path.exists -import kotlin.io.path.readText import org.gradle.api.Project import org.gradle.api.Task import org.gradle.api.file.Directory @@ -60,7 +57,6 @@ class MinecraftPatchingTasks( private val resourcePatchDir: DirectoryProperty, private val featurePatchDir: DirectoryProperty, private val additionalAts: RegularFileProperty, - private val additionalMappings: RegularFileProperty, private val baseSources: Provider, private val baseResources: Provider, private val gitFilePatches: Provider, @@ -205,23 +201,23 @@ class MinecraftPatchingTasks( input.set(setup.flatMap { it.outputDir }) } - if (additionalMappings.path.exists() && additionalMappings.path.readText().isNotBlank()) { - val applyParchment = tasks.register("apply${configName.capitalized()}AdditionalMappings") { + if (!config.additionalMappings.get().dependencies.isEmpty()) { + val applyAdditionalMappings = tasks.register("apply${configName.capitalized()}AdditionalMappings") { description = "Applies additional mappings, along with JDs if applicable to Minecraft sources (after applying base patches)" inputDir.set(applyBasePatches.flatMap { it.output }) outputDir.set(layout.cache.resolve(paperTaskOutput())) identifier.set(configName) - mappingFile.set(additionalMappings) + mappingFile.from(config.additionalMappings) mapping.jst.from(project.configurations.named(JST_CONFIG)) } applySourcePatches.configure { - base.set(applyParchment.flatMap { it.outputDir }) + base.set(applyAdditionalMappings.flatMap { it.outputDir }) baseRef.set("Mapped") } applySourcePatchesFuzzy.configure { - base.set(applyParchment.flatMap { it.outputDir }) + base.set(applyAdditionalMappings.flatMap { it.outputDir }) baseRef.set("Mapped") } } diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupForkMinecraftSources.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupForkMinecraftSources.kt index 3a4f68579..1660e31d7 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupForkMinecraftSources.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupForkMinecraftSources.kt @@ -28,15 +28,19 @@ import io.papermc.paperweight.tasks.* import io.papermc.paperweight.util.* import kotlin.io.path.* import org.eclipse.jgit.api.Git +import org.gradle.api.file.ConfigurableFileCollection import org.gradle.api.file.DirectoryProperty import org.gradle.api.file.RegularFileProperty import org.gradle.api.provider.Property import org.gradle.api.tasks.Input import org.gradle.api.tasks.InputDirectory import org.gradle.api.tasks.InputFile +import org.gradle.api.tasks.InputFiles import org.gradle.api.tasks.Nested import org.gradle.api.tasks.Optional import org.gradle.api.tasks.OutputDirectory +import org.gradle.api.tasks.PathSensitive +import org.gradle.api.tasks.PathSensitivity import org.gradle.api.tasks.TaskAction import org.gradle.kotlin.dsl.* @@ -58,9 +62,10 @@ abstract class SetupForkMinecraftSources : JavaLauncherTask() { @get:Optional abstract val atFile: RegularFileProperty - @get:InputFile + @get:PathSensitive(PathSensitivity.NONE) + @get:InputFiles @get:Optional - abstract val mappingFile: RegularFileProperty + abstract val mappingFile: ConfigurableFileCollection @get:Optional @get:InputDirectory @@ -98,13 +103,13 @@ abstract class SetupForkMinecraftSources : JavaLauncherTask() { ) commitAndTag(git, "ATs", "${identifier.get()} ATs") } - if (mappingFile.isPresent) { + if (!mappingFile.isEmpty) { println("Applying additional mappings...") mapping.run( launcher.get(), inputDir.path, outputDir.path, - mappingFile.path, + mappingFile.singleFile.toPath(), temporaryDir.toPath(), ) commitAndTag(git, "Mapped", "${identifier.get()} Additional Mappings Data") diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/util/ApplyAdditionalMappings.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/util/ApplyAdditionalMappings.kt index 9b4f8fc63..cc0f8096d 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/util/ApplyAdditionalMappings.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/util/ApplyAdditionalMappings.kt @@ -76,6 +76,7 @@ abstract class ApplyAdditionalMappings { "--enable-parchment", "--parchment-mappings=$mappingFile", "--hidden-prefix=.git", + "--rename=false", *jstClasspath.files.map { "--classpath=${it.absolutePath}" }.toTypedArray(), inputDir.absolutePathString(), outputDir.absolutePathString(), diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/PaperweightPatcher.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/PaperweightPatcher.kt index 7fa1a4a68..7b84cf669 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/PaperweightPatcher.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/PaperweightPatcher.kt @@ -57,7 +57,7 @@ abstract class PaperweightPatcher : Plugin { target.configurations.register(JST_CONFIG) { defaultDependencies { // add(project.dependencies.create("net.neoforged.jst:jst-cli-bundle:${LibraryVersions.JST}")) - add(target.dependencies.create("io.papermc.jst:jst-cli-bundle:${LibraryVersions.JST}")) + add(target.dependencies.create("io.canvasmc.jst:jst-cli-bundle:${LibraryVersions.JST}")) } } @@ -65,9 +65,7 @@ abstract class PaperweightPatcher : Plugin { repositories { maven(patcher.jstRepo) { name = JST_REPO_NAME - content { - onlyForConfigurations(JST_CONFIG) - } + content { onlyForConfigurations(JST_CONFIG) } } } afterEvaluate(patcher) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/extension/PaperweightPatcherExtension.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/extension/PaperweightPatcherExtension.kt index cc978399a..fca74cffc 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/extension/PaperweightPatcherExtension.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/extension/PaperweightPatcherExtension.kt @@ -24,7 +24,7 @@ package io.papermc.paperweight.patcher.extension import io.papermc.paperweight.core.extension.AdditionalUpstreamConfig import io.papermc.paperweight.core.extension.UpstreamConfig -import io.papermc.paperweight.util.constants.PAPER_MAVEN_REPO_URL +import io.papermc.paperweight.util.constants.CANVAS_MAVEN_REPO_URL import javax.inject.Inject import org.gradle.api.Action import org.gradle.api.NamedDomainObjectContainer @@ -38,7 +38,7 @@ abstract class PaperweightPatcherExtension @Inject constructor(private val objec val gitFilePatches: Property = objects.property().convention(false) val filterPatches: Property = objects.property().convention(true) - val jstRepo: Property = objects.property().convention(PAPER_MAVEN_REPO_URL) + val jstRepo: Property = objects.property().convention(CANVAS_MAVEN_REPO_URL) val upstreams: NamedDomainObjectContainer = objects.domainObjectContainer(UpstreamConfig::class) { objects.newInstance(it, true) diff --git a/paperweight-lib/build.gradle.kts b/paperweight-lib/build.gradle.kts index c7d503f21..5ac5cc04d 100644 --- a/paperweight-lib/build.gradle.kts +++ b/paperweight-lib/build.gradle.kts @@ -12,7 +12,6 @@ sourceSets.main { kotlinSources { properties.put("jst_version", libs.versions.jst) properties.put("tinyRemapper_version", libs.versions.tinyRemapper) - properties.put("accessWidener_version", libs.versions.accessWidener) } } } 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 bec0bdf23..ece33d79d 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 @@ -34,6 +34,8 @@ const val MC_LIBRARY_URL = "https://libraries.minecraft.net/" const val MC_MANIFEST_URL = "https://piston-meta.mojang.com/mc/game/version_manifest_v2.json" +const val CANVAS_MAVEN_REPO_URL = "https://maven.canvasmc.io/snapshots" + const val PAPER_MAVEN_REPO_URL = "https://repo.papermc.io/repository/maven-public/" const val MAVEN_CENTRAL_URL = "https://repo.maven.apache.org/maven2/" @@ -53,6 +55,7 @@ const val MACHE_MINECRAFT_LIBRARIES_CONFIG = "macheMinecraftLibraries" const val MACHE_MINECRAFT_CONFIG = "macheMinecraft" const val MAPPED_JAR_OUTGOING_CONFIG = "mappedJarOutgoing" const val ASM_OPCODES_VERSION = Opcodes.ASM9 +const val ADDITIONAL_MAPPINGS_CONFIG = "additionalMappings" const val JST_CONFIG = "javaSourceTransformer" const val DEV_BUNDLE_CONFIG = "paperweightDevelopmentBundle" const val MOJANG_MAPPED_SERVER_CONFIG = "mojangMappedServer" From 485a71627f744ed98af9bb0a41de54918f1476db Mon Sep 17 00:00:00 2001 From: Toffikk Date: Thu, 18 Sep 2025 23:38:54 +0200 Subject: [PATCH 077/257] rename to javadoc mappings javadoc is more appropriate here since we disable renames --- .../papermc/paperweight/core/extension/ForkConfig.kt | 8 ++++---- .../core/taskcontainers/MinecraftPatchingTasks.kt | 12 ++++++------ .../core/tasks/SetupForkMinecraftSources.kt | 6 +++--- ...AdditionalMappings.kt => ApplyJavadocMappings.kt} | 2 +- .../papermc/paperweight/util/constants/constants.kt | 2 +- 5 files changed, 15 insertions(+), 15 deletions(-) rename paperweight-core/src/main/kotlin/io/papermc/paperweight/core/util/{ApplyAdditionalMappings.kt => ApplyJavadocMappings.kt} (98%) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/ForkConfig.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/ForkConfig.kt index 5df6e8c54..27916947c 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/ForkConfig.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/ForkConfig.kt @@ -23,7 +23,7 @@ package io.papermc.paperweight.core.extension import io.papermc.paperweight.util.* -import io.papermc.paperweight.util.constants.ADDITIONAL_MAPPINGS_CONFIG +import io.papermc.paperweight.util.constants.JAVADOC_MAPPINGS_CONFIG import javax.inject.Inject import org.gradle.api.Action import org.gradle.api.Named @@ -68,10 +68,10 @@ abstract class ForkConfig @Inject constructor( }.orElse( providers.provider { objects.newInstance("paper", false) } ) - val additionalMappings = project.configurations.register(ADDITIONAL_MAPPINGS_CONFIG) + val javadocMappings = project.configurations.register(JAVADOC_MAPPINGS_CONFIG) - fun additionalMappings(dep: String) { - additionalMappings.configure { + fun javadocMappings(dep: String) { + javadocMappings.configure { dependencies.add(project.dependencies.create(dep)) } } diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt index c126d9612..fefd7716d 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt @@ -201,23 +201,23 @@ class MinecraftPatchingTasks( input.set(setup.flatMap { it.outputDir }) } - if (!config.additionalMappings.get().dependencies.isEmpty()) { - val applyAdditionalMappings = tasks.register("apply${configName.capitalized()}AdditionalMappings") { - description = "Applies additional mappings, along with JDs if applicable to Minecraft sources (after applying base patches)" + if (config.javadocMappings.get().dependencies.isNotEmpty()) { + val applyJavadocMappings = tasks.register("applyJavadocMappingsFrom${configName.capitalized()}ToSources") { + description = "Applies javadocs from the specified parchment-compatible mappings to Minecraft sources (after applying base patches)" inputDir.set(applyBasePatches.flatMap { it.output }) outputDir.set(layout.cache.resolve(paperTaskOutput())) identifier.set(configName) - mappingFile.from(config.additionalMappings) + mappingFile.from(config.javadocMappings) mapping.jst.from(project.configurations.named(JST_CONFIG)) } applySourcePatches.configure { - base.set(applyAdditionalMappings.flatMap { it.outputDir }) + base.set(applyJavadocMappings.flatMap { it.outputDir }) baseRef.set("Mapped") } applySourcePatchesFuzzy.configure { - base.set(applyAdditionalMappings.flatMap { it.outputDir }) + base.set(applyJavadocMappings.flatMap { it.outputDir }) baseRef.set("Mapped") } } diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupForkMinecraftSources.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupForkMinecraftSources.kt index 1660e31d7..55ac95788 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupForkMinecraftSources.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupForkMinecraftSources.kt @@ -22,7 +22,7 @@ package io.papermc.paperweight.core.tasks -import io.papermc.paperweight.core.util.ApplyAdditionalMappings +import io.papermc.paperweight.core.util.ApplyJavadocMappings import io.papermc.paperweight.core.util.ApplySourceATs import io.papermc.paperweight.tasks.* import io.papermc.paperweight.util.* @@ -56,7 +56,7 @@ abstract class SetupForkMinecraftSources : JavaLauncherTask() { val ats: ApplySourceATs = objects.newInstance() @get:Nested - val mapping: ApplyAdditionalMappings = objects.newInstance() + val mapping: ApplyJavadocMappings = objects.newInstance() @get:InputFile @get:Optional @@ -104,7 +104,7 @@ abstract class SetupForkMinecraftSources : JavaLauncherTask() { commitAndTag(git, "ATs", "${identifier.get()} ATs") } if (!mappingFile.isEmpty) { - println("Applying additional mappings...") + println("Applying javadoc mappings...") mapping.run( launcher.get(), inputDir.path, diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/util/ApplyAdditionalMappings.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/util/ApplyJavadocMappings.kt similarity index 98% rename from paperweight-core/src/main/kotlin/io/papermc/paperweight/core/util/ApplyAdditionalMappings.kt rename to paperweight-core/src/main/kotlin/io/papermc/paperweight/core/util/ApplyJavadocMappings.kt index cc0f8096d..ec200bb1a 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/util/ApplyAdditionalMappings.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/util/ApplyJavadocMappings.kt @@ -32,7 +32,7 @@ import org.gradle.api.tasks.CompileClasspath import org.gradle.api.tasks.Input import org.gradle.jvm.toolchain.JavaLauncher -abstract class ApplyAdditionalMappings { +abstract class ApplyJavadocMappings { @get:Classpath abstract val jst: ConfigurableFileCollection 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 ece33d79d..cd086ed61 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 @@ -55,7 +55,7 @@ const val MACHE_MINECRAFT_LIBRARIES_CONFIG = "macheMinecraftLibraries" const val MACHE_MINECRAFT_CONFIG = "macheMinecraft" const val MAPPED_JAR_OUTGOING_CONFIG = "mappedJarOutgoing" const val ASM_OPCODES_VERSION = Opcodes.ASM9 -const val ADDITIONAL_MAPPINGS_CONFIG = "additionalMappings" +const val JAVADOC_MAPPINGS_CONFIG = "javadocMappings" const val JST_CONFIG = "javaSourceTransformer" const val DEV_BUNDLE_CONFIG = "paperweightDevelopmentBundle" const val MOJANG_MAPPED_SERVER_CONFIG = "mojangMappedServer" From 37a38e8e265d36a223610540d0320617aabfd19d Mon Sep 17 00:00:00 2001 From: Toffikk Date: Fri, 19 Sep 2025 00:26:27 +0200 Subject: [PATCH 078/257] chore: cleanup --- .../io/papermc/paperweight/core/extension/ForkConfig.kt | 2 +- .../paperweight/core/taskcontainers/MinecraftPatchingTasks.kt | 4 ++-- .../paperweight/core/tasks/SetupForkMinecraftSources.kt | 2 +- .../kotlin/io/papermc/paperweight/util/constants/constants.kt | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/ForkConfig.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/ForkConfig.kt index 27916947c..d2e91c406 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/ForkConfig.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/ForkConfig.kt @@ -68,7 +68,7 @@ abstract class ForkConfig @Inject constructor( }.orElse( providers.provider { objects.newInstance("paper", false) } ) - val javadocMappings = project.configurations.register(JAVADOC_MAPPINGS_CONFIG) + val javadocMappings = project.configurations.register(name + JAVADOC_MAPPINGS_CONFIG) fun javadocMappings(dep: String) { javadocMappings.configure { diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt index fefd7716d..3d46a563d 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt @@ -213,12 +213,12 @@ class MinecraftPatchingTasks( applySourcePatches.configure { base.set(applyJavadocMappings.flatMap { it.outputDir }) - baseRef.set("Mapped") + baseRef.set("JDs") } applySourcePatchesFuzzy.configure { base.set(applyJavadocMappings.flatMap { it.outputDir }) - baseRef.set("Mapped") + baseRef.set("JDs") } } diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupForkMinecraftSources.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupForkMinecraftSources.kt index 55ac95788..978462bf5 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupForkMinecraftSources.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupForkMinecraftSources.kt @@ -112,7 +112,7 @@ abstract class SetupForkMinecraftSources : JavaLauncherTask() { mappingFile.singleFile.toPath(), temporaryDir.toPath(), ) - commitAndTag(git, "Mapped", "${identifier.get()} Additional Mappings Data") + commitAndTag(git, "JDs", "${identifier.get()} Javadoc Mappings") } git.close() 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 cd086ed61..3b4201070 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 @@ -55,7 +55,7 @@ const val MACHE_MINECRAFT_LIBRARIES_CONFIG = "macheMinecraftLibraries" const val MACHE_MINECRAFT_CONFIG = "macheMinecraft" const val MAPPED_JAR_OUTGOING_CONFIG = "mappedJarOutgoing" const val ASM_OPCODES_VERSION = Opcodes.ASM9 -const val JAVADOC_MAPPINGS_CONFIG = "javadocMappings" +const val JAVADOC_MAPPINGS_CONFIG = "JavadocMappings" const val JST_CONFIG = "javaSourceTransformer" const val DEV_BUNDLE_CONFIG = "paperweightDevelopmentBundle" const val MOJANG_MAPPED_SERVER_CONFIG = "mojangMappedServer" From 38c3e1de1e7659b1cd5bc59dcf5ade46607b9f4e Mon Sep 17 00:00:00 2001 From: Toffikk Date: Fri, 19 Sep 2025 17:04:20 +0200 Subject: [PATCH 079/257] chore: adapt for new JST update --- gradle/libs.versions.toml | 2 +- .../io/papermc/paperweight/core/util/ApplyJavadocMappings.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 1ef1773b0..72a0d94ff 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -3,7 +3,7 @@ asm = "9.8" lorenz = "0.5.8" hypo = "1.2.4" serialize = "1.8.1" -jst = "1.0.68+canvas.1-SNAPSHOT" +jst = "1.0.68+canvas.2-SNAPSHOT" tinyRemapper = "0.11.2" [libraries] diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/util/ApplyJavadocMappings.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/util/ApplyJavadocMappings.kt index ec200bb1a..258321a82 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/util/ApplyJavadocMappings.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/util/ApplyJavadocMappings.kt @@ -76,7 +76,7 @@ abstract class ApplyJavadocMappings { "--enable-parchment", "--parchment-mappings=$mappingFile", "--hidden-prefix=.git", - "--rename=false", + "--no-apply-parchment-renames", *jstClasspath.files.map { "--classpath=${it.absolutePath}" }.toTypedArray(), inputDir.absolutePathString(), outputDir.absolutePathString(), From afc8e73b8f8d3d92ce21ef4bce1f5ee83b86d27b Mon Sep 17 00:00:00 2001 From: Toffikk Date: Sun, 21 Sep 2025 19:22:10 +0200 Subject: [PATCH 080/257] only one config cache please also wtf github actions why u failing --- gradle.properties | 1 - 1 file changed, 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index a3700e899..1db313711 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,7 +1,6 @@ group = io.canvasmc.weaver version = 2.3.4-SNAPSHOT -org.gradle.configuration-cache = true org.gradle.caching = true org.gradle.configuration-cache = true org.gradle.parallel = true From a06637465fda4f08e73dd2188a87aac076e2a2e8 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Sun, 21 Sep 2025 19:29:40 +0200 Subject: [PATCH 081/257] eh? maybe bump gradle? lol --- gradle/wrapper/gradle-wrapper.properties | 2 +- gradlew | 3 --- gradlew.bat | 3 +-- 3 files changed, 2 insertions(+), 6 deletions(-) diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 48b43d350..a35649f5f 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-9.0.0-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.1.0-all.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew b/gradlew index ef07e0162..adff685a0 100755 --- a/gradlew +++ b/gradlew @@ -114,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH="\\\"\\\"" # Determine the Java command to use to start the JVM. @@ -172,7 +171,6 @@ fi # For Cygwin or MSYS, switch paths to Windows format before running java if "$cygwin" || "$msys" ; then APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) - CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) JAVACMD=$( cygpath --unix "$JAVACMD" ) @@ -212,7 +210,6 @@ DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' set -- \ "-Dorg.gradle.appname=$APP_BASE_NAME" \ - -classpath "$CLASSPATH" \ -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" diff --git a/gradlew.bat b/gradlew.bat index db3a6ac20..c4bdd3ab8 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -70,11 +70,10 @@ goto fail :execute @rem Setup the command line -set CLASSPATH= @rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell From 9f995e1692c9b4507319618ea301f4b2d94d2996 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Tue, 23 Sep 2025 13:45:30 +0200 Subject: [PATCH 082/257] feat: add a nice util to allow us to pass in configurations to javadocMappings --- .../paperweight/core/extension/ForkConfig.kt | 13 +++++++++++++ .../core/taskcontainers/MinecraftPatchingTasks.kt | 9 +++++++-- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/ForkConfig.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/ForkConfig.kt index d2e91c406..0c42217ab 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/ForkConfig.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/ForkConfig.kt @@ -28,6 +28,7 @@ import javax.inject.Inject import org.gradle.api.Action import org.gradle.api.Named import org.gradle.api.Project +import org.gradle.api.artifacts.Configuration import org.gradle.api.file.DirectoryProperty import org.gradle.api.file.RegularFileProperty import org.gradle.api.model.ObjectFactory @@ -76,6 +77,18 @@ abstract class ForkConfig @Inject constructor( } } + fun javadocMappings(configuration: Configuration) { + javadocMappings.configure { + incoming.beforeResolve { extendsFrom(configuration) } + } + } + + fun javadocMappings(configuration: Provider) { + javadocMappings.configure { + incoming.beforeResolve { extendsFrom(configuration.get()) } + } + } + val upstream: UpstreamConfig by lazy { upstreamProvider.get() } diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt index 81aca624e..183c1e210 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt @@ -38,6 +38,7 @@ import io.papermc.paperweight.util.constants.* import java.nio.file.Path import org.gradle.api.Project import org.gradle.api.Task +import org.gradle.api.artifacts.Configuration import org.gradle.api.file.Directory import org.gradle.api.file.DirectoryProperty import org.gradle.api.file.RegularFileProperty @@ -201,9 +202,13 @@ class MinecraftPatchingTasks( input.set(setup.flatMap { it.outputDir }) } - if (config.javadocMappings.get().dependencies.isNotEmpty()) { + fun Configuration.hasAnyDependencies(): Boolean { + return dependencies.isNotEmpty() || incoming.artifacts.artifactFiles.files.isNotEmpty() + } + + if (config.javadocMappings.get().hasAnyDependencies()) { val applyJavadocMappings = tasks.register("applyJavadocMappingsFrom${configName.capitalized()}ToSources") { - description = "Applies javadocs from the specified parchment-compatible mappings to Minecraft sources (after applying base patches)" + description = "Applies javadocs from the specified parchment-compatible mappings in $configName to Minecraft sources (after applying base patches)" inputDir.set(applyBasePatches.flatMap { it.output }) outputDir.set(layout.cache.resolve(paperTaskOutput())) identifier.set(configName) From 93bca5278840612a7dfd5bfe4bb8fa878733a8ac Mon Sep 17 00:00:00 2001 From: Toffikk Date: Tue, 23 Sep 2025 14:34:10 +0200 Subject: [PATCH 083/257] chore: satisfy spotless --- .../paperweight/core/taskcontainers/MinecraftPatchingTasks.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt index 183c1e210..45f260f79 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt @@ -207,8 +207,8 @@ class MinecraftPatchingTasks( } if (config.javadocMappings.get().hasAnyDependencies()) { - val applyJavadocMappings = tasks.register("applyJavadocMappingsFrom${configName.capitalized()}ToSources") { - description = "Applies javadocs from the specified parchment-compatible mappings in $configName to Minecraft sources (after applying base patches)" + val applyJavadocMappings = tasks.register("applyJavadocMappingsFrom${configName.capitalized()}ToMinecraft") { + description = "Applies javadocs from the specified parchment-compatible mappings in $configName to Minecraft (after base patch apply)" inputDir.set(applyBasePatches.flatMap { it.output }) outputDir.set(layout.cache.resolve(paperTaskOutput())) identifier.set(configName) From dd035283e0c96793520d827d7e6e55c9400b0ac1 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Tue, 23 Sep 2025 14:37:36 +0200 Subject: [PATCH 084/257] chore: continue development cycle --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 1db313711..4e0c596c3 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ group = io.canvasmc.weaver -version = 2.3.4-SNAPSHOT +version = 2.3.5-SNAPSHOT org.gradle.caching = true org.gradle.configuration-cache = true From aa2b78adc71e886cc7f3265af9d66fa74ca53bc1 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Tue, 23 Sep 2025 14:47:11 +0200 Subject: [PATCH 085/257] fix(dsl): change additionalAts to non-nullable type --- .../io/papermc/paperweight/core/extension/UpstreamConfig.kt | 2 +- .../papermc/paperweight/core/taskcontainers/PatchingTasks.kt | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/UpstreamConfig.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/UpstreamConfig.kt index cb153fd63..977f24064 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/UpstreamConfig.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/UpstreamConfig.kt @@ -101,7 +101,7 @@ abstract class UpstreamConfig @Inject constructor( abstract val excludes: SetProperty abstract val outputDir: DirectoryProperty - abstract val additionalAts: RegularFileProperty? + abstract val additionalAts: RegularFileProperty abstract val patchesDir: DirectoryProperty val rejectsDir: DirectoryProperty = objects.dirFrom(patchesDir, "rejected") diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt index ef5bbc0da..c4685d562 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt @@ -52,7 +52,7 @@ class PatchingTasks( private val filePatchDir: DirectoryProperty, private val rejectsDir: DirectoryProperty, private val featurePatchDir: DirectoryProperty, - private val additionalAts: RegularFileProperty?, + private val additionalAts: RegularFileProperty, private val baseDir: Provider, private val gitFilePatches: Provider, private val filterPatches: Provider, @@ -143,7 +143,7 @@ class PatchingTasks( } val mergeCollectedAts = tasks.register("merge${namePart}ATs") { - additionalAts?.let { firstFile.set(it.fileExists()) } + firstFile.set(additionalAts.fileExists()) secondFile.set(collectAccessTransform.flatMap { it.outputFile }) } From 3e4b0e0795c835a417d617905e289b49a510c8b8 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Wed, 24 Sep 2025 23:45:37 +0200 Subject: [PATCH 086/257] chore: properly rebrand to weaver in last places --- buildSrc/src/main/kotlin/utils.kt | 6 +++--- .../kotlin/io/papermc/paperweight/core/PaperweightCore.kt | 2 +- .../io/papermc/paperweight/patcher/PaperweightPatcher.kt | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/buildSrc/src/main/kotlin/utils.kt b/buildSrc/src/main/kotlin/utils.kt index 34d17b029..9b0921e13 100644 --- a/buildSrc/src/main/kotlin/utils.kt +++ b/buildSrc/src/main/kotlin/utils.kt @@ -15,10 +15,10 @@ fun Configuration.compatibilityAttributes(objects: ObjectFactory) { } fun GradlePluginDevelopmentExtension.setupPlugin(prefix: String, op: Action) { - plugins.register("paperweight-$prefix") { + plugins.register("weaver-$prefix") { id = "io.canvasmc.weaver." + prefix - displayName = "paperweight $prefix" - tags.set(listOf("paper", "minecraft")) + displayName = "weaver $prefix" + tags.set(listOf("paper", "minecraft", "canvas")) op.execute(this) } } diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/PaperweightCore.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/PaperweightCore.kt index 8ded29017..ba5e89680 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/PaperweightCore.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/PaperweightCore.kt @@ -62,7 +62,7 @@ abstract class PaperweightCore : Plugin { override fun apply(target: Project) { Git.checkForGit(target.providers) - printId("paperweight-core", target.gradle) + printId("weaver-core", target.gradle) val ext = target.extensions.create(PAPERWEIGHT_EXTENSION, target) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/PaperweightPatcher.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/PaperweightPatcher.kt index 1e1ab1ece..243a56ea7 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/PaperweightPatcher.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/PaperweightPatcher.kt @@ -44,7 +44,7 @@ abstract class PaperweightPatcher : Plugin { override fun apply(target: Project) { Git.checkForGit(target.providers) - printId("paperweight-patcher", target.gradle) + printId("weaver-patcher", target.gradle) val patcher = target.extensions.create(PAPERWEIGHT_EXTENSION, PaperweightPatcherExtension::class) @@ -115,7 +115,7 @@ abstract class PaperweightPatcher : Plugin { group = "patching" val depend = "apply${upstream.name.capitalized()}Patches" tasks.addAll("applyAllServerPatches") - description = "Applies all patches defined in the paperweight-patcher project and the server project. " + + description = "Applies all patches defined in the weaver-patcher project and the server project. " + "(equivalent to running '$depend' and then '${tasks.get().single()}' in a second Gradle invocation)" projectDir.set(layout.projectDirectory) dependsOn(depend) From 63b54941176a8701e4e030209a4553700bf5e593 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Thu, 25 Sep 2025 18:22:02 +0200 Subject: [PATCH 087/257] fix: make javadoc mappings apply lazy --- .../paperweight/core/extension/ForkConfig.kt | 8 +++-- .../taskcontainers/MinecraftPatchingTasks.kt | 33 +++++++------------ .../core/tasks/patching/ApplyFilePatches.kt | 18 ++++++++-- 3 files changed, 33 insertions(+), 26 deletions(-) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/ForkConfig.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/ForkConfig.kt index 0c42217ab..917b1da4b 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/ForkConfig.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/ForkConfig.kt @@ -79,13 +79,15 @@ abstract class ForkConfig @Inject constructor( fun javadocMappings(configuration: Configuration) { javadocMappings.configure { - incoming.beforeResolve { extendsFrom(configuration) } + extendsFrom(configuration) } } fun javadocMappings(configuration: Provider) { - javadocMappings.configure { - incoming.beforeResolve { extendsFrom(configuration.get()) } + configuration.orNull?.let { conf -> + javadocMappings.configure { + extendsFrom(conf) + } } } diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt index 45f260f79..2eaf136a2 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt @@ -38,7 +38,6 @@ import io.papermc.paperweight.util.constants.* import java.nio.file.Path import org.gradle.api.Project import org.gradle.api.Task -import org.gradle.api.artifacts.Configuration import org.gradle.api.file.Directory import org.gradle.api.file.DirectoryProperty import org.gradle.api.file.RegularFileProperty @@ -202,29 +201,21 @@ class MinecraftPatchingTasks( input.set(setup.flatMap { it.outputDir }) } - fun Configuration.hasAnyDependencies(): Boolean { - return dependencies.isNotEmpty() || incoming.artifacts.artifactFiles.files.isNotEmpty() + val applyJavadocMappings = tasks.register("applyJavadocMappingsFrom${configName.capitalized()}ToMinecraft") { + description = "When applicable, applies javadocs from the specified parchment mappings in $configName to Minecraft for source patch apply" + inputDir.set(applyBasePatches.flatMap { it.output }) + outputDir.set(layout.cache.resolve(paperTaskOutput())) + identifier.set(configName) + mappingFile.from(config.javadocMappings) + mapping.jst.from(project.configurations.named(JST_CONFIG)) } - if (config.javadocMappings.get().hasAnyDependencies()) { - val applyJavadocMappings = tasks.register("applyJavadocMappingsFrom${configName.capitalized()}ToMinecraft") { - description = "Applies javadocs from the specified parchment-compatible mappings in $configName to Minecraft (after base patch apply)" - inputDir.set(applyBasePatches.flatMap { it.output }) - outputDir.set(layout.cache.resolve(paperTaskOutput())) - identifier.set(configName) - mappingFile.from(config.javadocMappings) - mapping.jst.from(project.configurations.named(JST_CONFIG)) - } - - applySourcePatches.configure { - base.set(applyJavadocMappings.flatMap { it.outputDir }) - baseRef.set("JDs") - } + applySourcePatches.configure { + base.set(applyJavadocMappings.flatMap { it.outputDir }) + } - applySourcePatchesFuzzy.configure { - base.set(applyJavadocMappings.flatMap { it.outputDir }) - baseRef.set("JDs") - } + applySourcePatchesFuzzy.configure { + base.set(applyJavadocMappings.flatMap { it.outputDir }) } /* uncomment this when we enable validation of ATs since then seperate AT files will be forced, right now this breaks on projects with one AT file for many modules diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyFilePatches.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyFilePatches.kt index 5ff6d1502..219f72768 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyFilePatches.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyFilePatches.kt @@ -73,6 +73,9 @@ abstract class ApplyFilePatches : BaseTask() { @get:Input abstract val baseRef: Property + @get:Internal + abstract val ref: Property + @get:Input @get:Optional abstract val identifier: Property @@ -89,6 +92,7 @@ abstract class ApplyFilePatches : BaseTask() { gitFilePatches.convention(false) moveFailedGitPatchesToRejects.convention(false) emitRejects.convention(true) + ref.convention(baseRef.orElse("patchedBase")) } } @@ -97,14 +101,18 @@ abstract class ApplyFilePatches : BaseTask() { io.papermc.paperweight.util.Git.checkForGit() val base = base.pathOrNull + if (base != null && base.toAbsolutePath() != repo.path.toAbsolutePath()) { recreateCloneDirectory(repo.path) + val checkoutFromJDs = hasJavadocs(base, "JDs") + val newRef = if (checkoutFromJDs) "JDs" else ref.get() + ref.set(newRef) val git = Git(repo.path.createDirectories()) checkoutRepoFromUpstream( git, base, - baseRef.getOrElse("patchedBase"), + ref.get(), branchName = "main", ref = true, ) @@ -116,7 +124,7 @@ abstract class ApplyFilePatches : BaseTask() { if (git("checkout", "main").runSilently(silenceErr = true) != 0) { git("checkout", "-b", "main").runSilently(silenceErr = true) } - git("reset", "--hard", baseRef.getOrElse("patchedBase")).runSilently(silenceErr = true) + git("reset", "--hard", ref.get()).runSilently(silenceErr = true) git("gc").runSilently(silenceErr = true) val result = if (!patches.isPresent) { @@ -246,6 +254,12 @@ abstract class ApplyFilePatches : BaseTask() { git.close() } + fun hasJavadocs(repoPath: Path, tag: String): Boolean { + val git = Git(repoPath) + val result = git("tag", "-l", tag).getText().trim() + return result == tag + } + internal open fun mode(): PatchMode { return PatchMode.OFFSET } From 8c51db540b43e8872159927774860392126f49d6 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Thu, 25 Sep 2025 19:01:54 +0200 Subject: [PATCH 088/257] chore: simplify some code --- .../io/papermc/paperweight/core/extension/ForkConfig.kt | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/ForkConfig.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/ForkConfig.kt index 917b1da4b..573923d88 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/ForkConfig.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/ForkConfig.kt @@ -84,10 +84,8 @@ abstract class ForkConfig @Inject constructor( } fun javadocMappings(configuration: Provider) { - configuration.orNull?.let { conf -> - javadocMappings.configure { - extendsFrom(conf) - } + javadocMappings.configure { + extendsFrom(configuration.get()) } } From 89fb8fc0e71ac0d832fc39f21959d3708fce1d43 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Thu, 25 Sep 2025 20:23:20 +0200 Subject: [PATCH 089/257] fix: revert to gradle 8.14 and bump version --- .../src/main/kotlin/config-kotlin.gradle.kts | 2 +- buildSrc/src/main/kotlin/utils.kt | 2 +- gradle.properties | 2 +- gradle/libs.versions.toml | 2 +- gradle/wrapper/gradle-wrapper.jar | Bin 45457 -> 43764 bytes gradle/wrapper/gradle-wrapper.properties | 2 +- gradlew | 5 ++++- gradlew.bat | 3 ++- .../extension/PaperweightCoreExtension.kt | 5 +++-- .../core/taskcontainers/AllTasks.kt | 2 +- .../core/taskcontainers/CoreTasks.kt | 8 +++---- .../core/taskcontainers/DevBundleTasks.kt | 6 +++--- .../taskcontainers/MinecraftPatchingTasks.kt | 20 +++++++++--------- .../core/taskcontainers/PatchingTasks.kt | 12 +++++------ .../core/taskcontainers/SpigotTasks.kt | 2 +- .../taskcontainers/UpstreamConfigTasks.kt | 6 +++++- .../paperweight/core/util/project-util.kt | 4 ++-- .../paperweight/patcher/PaperweightPatcher.kt | 8 +++---- .../io/papermc/paperweight/util/file.kt | 13 ++++++++++++ .../io/papermc/paperweight/util/utils.kt | 6 +++--- .../PaperweightUserDependenciesExtension.kt | 2 +- 21 files changed, 67 insertions(+), 45 deletions(-) diff --git a/buildSrc/src/main/kotlin/config-kotlin.gradle.kts b/buildSrc/src/main/kotlin/config-kotlin.gradle.kts index 0f3cabd47..7172a5ff9 100644 --- a/buildSrc/src/main/kotlin/config-kotlin.gradle.kts +++ b/buildSrc/src/main/kotlin/config-kotlin.gradle.kts @@ -23,7 +23,7 @@ kotlin { } compilerOptions { jvmTarget = JvmTarget.JVM_17 - freeCompilerArgs = listOf("-Xjvm-default=all", "-Xjdk-release=17") + freeCompilerArgs = listOf("-Xjvm-default=all", "-Xjdk-release=17", "-opt-in=kotlin.io.path.ExperimentalPathApi") } } diff --git a/buildSrc/src/main/kotlin/utils.kt b/buildSrc/src/main/kotlin/utils.kt index 9b0921e13..3b5f9c493 100644 --- a/buildSrc/src/main/kotlin/utils.kt +++ b/buildSrc/src/main/kotlin/utils.kt @@ -10,7 +10,7 @@ import org.gradle.plugin.devel.PluginDeclaration fun Configuration.compatibilityAttributes(objects: ObjectFactory) { attributes { attribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, 17) - attribute(GradlePluginApiVersion.GRADLE_PLUGIN_API_VERSION_ATTRIBUTE, objects.named("9.0.0")) + attribute(GradlePluginApiVersion.GRADLE_PLUGIN_API_VERSION_ATTRIBUTE, objects.named("8.14")) } } diff --git a/gradle.properties b/gradle.properties index 4e0c596c3..42785107d 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ group = io.canvasmc.weaver -version = 2.3.5-SNAPSHOT +version = 2.3.6-SNAPSHOT org.gradle.caching = true org.gradle.configuration-cache = true diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 5b6e8e1c6..514a48f1b 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -48,7 +48,7 @@ mockk = "io.mockk:mockk:1.14.5" gradle-licenser = "net.kyori:indra-licenser-spotless:3.2.0" gradle-spotless = "com.diffplug.spotless:spotless-plugin-gradle:7.2.1" gradle-shadow = "com.gradleup.shadow:com.gradleup.shadow.gradle.plugin:9.1.0" -gradle-kotlin-dsl = "org.gradle.kotlin.kotlin-dsl:org.gradle.kotlin.kotlin-dsl.gradle.plugin:6.2.0" +gradle-kotlin-dsl = "org.gradle.kotlin.kotlin-dsl:org.gradle.kotlin.kotlin-dsl.gradle.plugin:5.2.0" gradle-plugin-kotlin = { module = "org.jetbrains.kotlin.jvm:org.jetbrains.kotlin.jvm.gradle.plugin" } gradle-plugin-publish = "com.gradle.publish:plugin-publish-plugin:2.0.0" diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 8bdaf60c75ab801e22807dde59e12a8735a34077..1b33c55baabb587c669f562ae36f953de2481846 100644 GIT binary patch delta 35551 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 delta 37256 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 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index a35649f5f..6514f919f 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-9.1.0-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.14-all.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew b/gradlew index adff685a0..23d15a936 100755 --- a/gradlew +++ b/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015 the original authors. +# Copyright © 2015-2021 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -114,6 +114,7 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac +CLASSPATH="\\\"\\\"" # Determine the Java command to use to start the JVM. @@ -171,6 +172,7 @@ fi # For Cygwin or MSYS, switch paths to Windows format before running java if "$cygwin" || "$msys" ; then APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) JAVACMD=$( cygpath --unix "$JAVACMD" ) @@ -210,6 +212,7 @@ DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' set -- \ "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" diff --git a/gradlew.bat b/gradlew.bat index c4bdd3ab8..db3a6ac20 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -70,10 +70,11 @@ goto fail :execute @rem Setup the command line +set CLASSPATH= @rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/PaperweightCoreExtension.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/PaperweightCoreExtension.kt index 6a5a55731..46ad36805 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/PaperweightCoreExtension.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/PaperweightCoreExtension.kt @@ -53,8 +53,9 @@ abstract class PaperweightCoreExtension @Inject constructor(objects: ObjectFacto val spigot = objects.newInstance().also { spigot -> spigot.enabled.convention( - spigot.buildDataRef.zip(spigot.packageVersion) { ref, pkg -> ref.isNotBlank() && pkg.isNotBlank() } - .orElse(false) + spigot.buildDataRef.zip(spigot.packageVersion) { ref, pkg -> + ref !== null && pkg !== null && ref.isNotBlank() && pkg.isNotBlank() + } ) } val paper = objects.newInstance(project) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/AllTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/AllTasks.kt index 2f790a5fc..9f6ec09ae 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/AllTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/AllTasks.kt @@ -87,7 +87,7 @@ open class AllTasks( val patchReobfMappings by tasks.registering { inputMappings.set(generateReobfMappings.flatMap { it.reobfMappings }) - patch.set(extension.paper.reobfMappingsPatch.fileExists()) + patch.set(extension.paper.reobfMappingsPatch.fileExists(project)) fromNamespace.set(DEOBF_NAMESPACE) toNamespace.set(SPIGOT_NAMESPACE) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/CoreTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/CoreTasks.kt index 3346b8118..4d0dd6178 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/CoreTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/CoreTasks.kt @@ -74,12 +74,12 @@ class CoreTasks( } val collectPaperATsFromPatches by tasks.registering(CollectATsFromPatches::class) { - patchDir.set(project.coreExt.paper.basePatchDir.fileExists()) - extraPatchDir.set(project.coreExt.paper.featurePatchDir.fileExists()) + patchDir.set(project.coreExt.paper.basePatchDir.fileExists(project)) + extraPatchDir.set(project.coreExt.paper.featurePatchDir.fileExists(project)) } val mergePaperATs by tasks.registering { - firstFile.set(project.coreExt.paper.additionalAts.fileExists()) + firstFile.set(project.coreExt.paper.additionalAts.fileExists(project)) secondFile.set(collectPaperATsFromPatches.flatMap { it.outputFile }) } @@ -92,7 +92,7 @@ class CoreTasks( val importLibraryFiles = tasks.register("importPaperLibraryFiles") { patches.from(project.coreExt.paper.basePatchDir, project.coreExt.paper.sourcePatchDir, project.coreExt.paper.featurePatchDir) - devImports.set(project.coreExt.paper.devImports.fileExists()) + devImports.set(project.coreExt.paper.devImports.fileExists(project)) libraryFileIndex.set(indexLibraryFiles.flatMap { it.outputFile }) libraries.from(indexLibraryFiles.map { it.libraries }) } diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/DevBundleTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/DevBundleTasks.kt index 5591378d6..6f15acd0c 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/DevBundleTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/DevBundleTasks.kt @@ -69,11 +69,11 @@ class DevBundleTasks( minecraftVersion.set(project.coreExt.minecraftVersion) mojangMappedPaperclipFile.set(paperclipForDevBundle.flatMap { it.outputZip }) reobfMappingsFile.set( - project.coreExt.spigot.enabled.flatMap { + project.coreExt.spigot.enabled.flatMap { if (it) { - coreTasks.generateRelocatedReobfMappings.flatMap { t -> t.outputMappings } + coreTasks.generateRelocatedReobfMappings.flatMap { it.outputMappings } } else { - null + providers.provider { null } } } ) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt index 2eaf136a2..67d39877f 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt @@ -84,7 +84,7 @@ class MinecraftPatchingTasks( } else { output.set(outputSrc) } - patches.set(basePatchDir.fileExists()) + patches.set(basePatchDir.fileExists(project)) identifier = configName } @@ -99,7 +99,7 @@ class MinecraftPatchingTasks( } else { repo.set(outputSrc) } - patches.set(sourcePatchDir.fileExists()) + patches.set(sourcePatchDir.fileExists(project)) rejectsDir.set(this@MinecraftPatchingTasks.rejectsDir) gitFilePatches.set(this@MinecraftPatchingTasks.gitFilePatches) identifier = configName @@ -123,7 +123,7 @@ class MinecraftPatchingTasks( input.set(baseResources) output.set(outputResources) - patches.set(resourcePatchDir.fileExists()) + patches.set(resourcePatchDir.fileExists(project)) // TODO rejects? gitFilePatches.set(this@MinecraftPatchingTasks.gitFilePatches) identifier = configName @@ -144,7 +144,7 @@ class MinecraftPatchingTasks( base.set(applySourcePatches.flatMap { it.repo }) } repo.set(outputSrc) - patches.set(featurePatchDir.fileExists()) + patches.set(featurePatchDir.fileExists(project)) } val applyPatches = tasks.register("apply${namePart}Patches") { @@ -168,18 +168,18 @@ class MinecraftPatchingTasks( fun setupFork(config: ForkConfig) { val collectAccessTransform = tasks.register("collect${configName.capitalized()}MinecraftATsFromPatches") { - patchDir.set(basePatchDir.fileExists()) - extraPatchDir.set(featurePatchDir.fileExists()) + patchDir.set(basePatchDir.fileExists(project)) + extraPatchDir.set(featurePatchDir.fileExists(project)) } val mergeCollectedAts = tasks.register("merge${configName.capitalized()}MinecraftATs") { - firstFile.set(additionalAts.fileExists()) + firstFile.set(additionalAts.fileExists(project)) secondFile.set(collectAccessTransform.flatMap { it.outputFile }) } val importLibFiles = tasks.register("import${configName.capitalized()}LibraryFiles") { patches.from(config.featurePatchDir, config.sourcePatchDir, config.basePatchDir) - devImports.set(config.devImports.fileExists()) + devImports.set(config.devImports.fileExists(project)) libraryFileIndex.set(coreTasks.indexLibraryFiles.flatMap { it.outputFile }) libraries.from(coreTasks.indexLibraryFiles.map { it.libraries }) } @@ -262,8 +262,8 @@ class MinecraftPatchingTasks( ats.jstClasspath.from(project.configurations.named(MACHE_MINECRAFT_CONFIG)) ats.jst.from(project.configurations.named(JST_CONFIG)) - atFile.set(additionalAts.fileExists()) - atFileOut.set(additionalAts.fileExists()) + atFile.set(additionalAts.fileExists(project)) + atFileOut.set(additionalAts.fileExists(project)) } val rebuildResourcePatches = tasks.register(rebuildResourcePatchesName) { diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt index c4685d562..cb53f1c0b 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt @@ -72,7 +72,7 @@ class PatchingTasks( output.set(outputDir) } - patches.set(basePatchDir.fileExists()) + patches.set(basePatchDir.fileExists(project)) baseRef.set("base") identifier = "$forkName $patchSetName" } @@ -88,7 +88,7 @@ class PatchingTasks( } else { repo.set(outputDir) } - patches.set(filePatchDir.fileExists()) + patches.set(filePatchDir.fileExists(project)) rejectsDir.set(this@PatchingTasks.rejectsDir) gitFilePatches.set(this@PatchingTasks.gitFilePatches) identifier = "$forkName $patchSetName" @@ -115,7 +115,7 @@ class PatchingTasks( if (readOnly) { base.set(applyFilePatches.flatMap { it.repo }) } - patches.set(featurePatchDir.fileExists()) + patches.set(featurePatchDir.fileExists(project)) } val applyPatches = tasks.register("apply${namePart}Patches") { @@ -138,12 +138,12 @@ class PatchingTasks( fun setupUpstream() { val collectAccessTransform = tasks.register("collect${namePart}ATsFromPatches") { - patchDir.set(basePatchDir.fileExists()) - extraPatchDir.set(featurePatchDir.fileExists()) + patchDir.set(basePatchDir.fileExists(project)) + extraPatchDir.set(featurePatchDir.fileExists(project)) } val mergeCollectedAts = tasks.register("merge${namePart}ATs") { - firstFile.set(additionalAts.fileExists()) + firstFile.set(additionalAts.fileExists(project)) secondFile.set(collectAccessTransform.flatMap { it.outputFile }) } diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/SpigotTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/SpigotTasks.kt index f4347f25c..ccddf0905 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/SpigotTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/SpigotTasks.kt @@ -92,7 +92,7 @@ open class SpigotTasks( val patchMappings by tasks.registering { inputMappings.set(cleanupMappings.flatMap { it.outputMappings }) - patch.set(extension.paper.mappingsPatch.fileExists()) + patch.set(extension.paper.mappingsPatch.fileExists(project)) fromNamespace.set(SPIGOT_NAMESPACE) toNamespace.set(DEOBF_NAMESPACE) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/UpstreamConfigTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/UpstreamConfigTasks.kt index e94e3948d..9fafe1c93 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/UpstreamConfigTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/UpstreamConfigTasks.kt @@ -56,7 +56,11 @@ class UpstreamConfigTasks( it.map { cfg -> ApplySingleFilePatches.Patch.patch(target.objects, upstream) { path = cfg.path - patchFile = cfg.patchFile.fileExists() + patchFile = layout.file( + cfg.patchFile.flatMap { f -> + if (f.path.exists()) target.providers.provider { f.asFile } else target.providers.provider { null } + } + ) outputFile = cfg.outputFile rejectsFile = cfg.rejectsFile } diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/util/project-util.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/util/project-util.kt index 98e552295..fa7e2f606 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/util/project-util.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/util/project-util.kt @@ -36,7 +36,7 @@ import org.gradle.kotlin.dsl.* fun Project.createBuildTasks( spigot: SpigotExtension, - packagesToFix: Provider>, + packagesToFix: Provider?>, relocatedReobfMappings: Provider ): ServerArtifacts { val jar = tasks.named("jar", AbstractArchiveTask::class).flatMap { it.archiveFile } @@ -90,7 +90,7 @@ fun Project.createBuildTasks( return ServerArtifacts( spigot.enabled.flatMap { if (it) { - includeMappings.flatMap { t -> t.outputJar } + includeMappings.flatMap { it.outputJar } } else { jar } diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/PaperweightPatcher.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/PaperweightPatcher.kt index 243a56ea7..927783758 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/PaperweightPatcher.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/PaperweightPatcher.kt @@ -148,9 +148,9 @@ abstract class PaperweightPatcher : Plugin { forkName.set(upstream.name) repoName.set(input.name) workDir.set(workDirFromProp) - atFile.set(input.additionalAts.fileExists()) + atFile.set(input.additionalAts.fileExists(project)) ats.jst.from(project.configurations.named(JST_CONFIG)) - additionalPatch.set(input.additionalPatch.fileExists()) + additionalPatch.set(input.additionalPatch.fileExists(project)) } } @@ -170,9 +170,9 @@ abstract class PaperweightPatcher : Plugin { inputFrom.set(input.name) commitHash.set(upstream.ref) workDir.set(workDirFromProp) - atFile.set(input.additionalAts.fileExists()) + atFile.set(input.additionalAts.fileExists(project)) ats.jst.from(project.configurations.named(JST_CONFIG)) - additionalPatch.set(input.additionalPatch.fileExists()) + additionalPatch.set(input.additionalPatch.fileExists(project)) generateSources.set(input.generateSources.orElse(true)) generateResources.set(input.generateResources.orElse(true)) generateTestSources.set(input.generateTestSources.orElse(true)) diff --git a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/file.kt b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/file.kt index 1f905e123..ba17088b3 100644 --- a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/file.kt +++ b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/file.kt @@ -25,6 +25,7 @@ package io.papermc.paperweight.util import java.io.InputStream import java.io.OutputStream import java.net.URI +import java.nio.file.FileAlreadyExistsException import java.nio.file.FileSystem import java.nio.file.FileSystemNotFoundException import java.nio.file.FileSystems @@ -32,6 +33,7 @@ import java.nio.file.Files import java.nio.file.Path import java.nio.file.PathMatcher import java.nio.file.attribute.DosFileAttributeView +import java.nio.file.attribute.FileAttribute import java.util.Arrays import java.util.stream.Collectors import java.util.stream.Stream @@ -191,6 +193,17 @@ private fun relativeCopyOrMove(baseDir: Path, file: Path, outputDir: Path, move: } } +fun Path.createParentDirectories(vararg attributes: FileAttribute<*>): Path = also { + val parent = it.parent + if (parent != null && !parent.isDirectory()) { + try { + parent.createDirectories(*attributes) + } catch (e: FileAlreadyExistsException) { + if (!parent.isDirectory()) throw e + } + } +} + val FileSystemLocation.path: Path get() = asFile.toPath() val Provider.path: Path diff --git a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/utils.kt b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/utils.kt index 4d653072d..5a9e71088 100644 --- a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/utils.kt +++ b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/utils.kt @@ -106,8 +106,8 @@ fun ProjectLayout.cacheDir(path: String) = projectDirectory.dir(".gradle/$CACHE_ fun Project.offlineMode(): Boolean = gradle.startParameter.isOffline -fun Provider.fileExists(): Provider { - return map { it.takeIf { f -> f.path.exists() } } +fun Provider.fileExists(project: Project): Provider { + return flatMap { project.provider { it.takeIf { f -> f.path.exists() } } } } @Suppress("UNCHECKED_CAST") @@ -448,7 +448,7 @@ fun FileCollection.resolveMacheMeta() = singleFile.toPath().openZipSafe().use { fun isIDEASync(): Boolean = java.lang.Boolean.getBoolean("idea.sync.active") -inline fun ObjectFactory.providerSet( +inline fun ObjectFactory.providerSet( vararg providers: Provider ): Provider> { if (providers.isEmpty()) { diff --git a/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/PaperweightUserDependenciesExtension.kt b/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/PaperweightUserDependenciesExtension.kt index f2430acf4..1c26289f6 100644 --- a/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/PaperweightUserDependenciesExtension.kt +++ b/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/PaperweightUserDependenciesExtension.kt @@ -253,7 +253,7 @@ abstract class PaperweightUserDependenciesExtension( } @Suppress("unchecked_cast") - private fun nullAction(): Action { + private fun nullAction(): Action { return NullAction as Action } From 370b30c52a151cdc94d6396eb8af38641a485f34 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Thu, 25 Sep 2025 20:44:38 +0200 Subject: [PATCH 090/257] Revert "fix: revert to gradle 8.14 and bump version" This reverts commit 89fb8fc0e71ac0d832fc39f21959d3708fce1d43. --- .../src/main/kotlin/config-kotlin.gradle.kts | 2 +- buildSrc/src/main/kotlin/utils.kt | 2 +- gradle.properties | 2 +- gradle/libs.versions.toml | 2 +- gradle/wrapper/gradle-wrapper.jar | Bin 43764 -> 45457 bytes gradle/wrapper/gradle-wrapper.properties | 2 +- gradlew | 5 +---- gradlew.bat | 3 +-- .../extension/PaperweightCoreExtension.kt | 5 ++--- .../core/taskcontainers/AllTasks.kt | 2 +- .../core/taskcontainers/CoreTasks.kt | 8 +++---- .../core/taskcontainers/DevBundleTasks.kt | 6 +++--- .../taskcontainers/MinecraftPatchingTasks.kt | 20 +++++++++--------- .../core/taskcontainers/PatchingTasks.kt | 12 +++++------ .../core/taskcontainers/SpigotTasks.kt | 2 +- .../taskcontainers/UpstreamConfigTasks.kt | 6 +----- .../paperweight/core/util/project-util.kt | 4 ++-- .../paperweight/patcher/PaperweightPatcher.kt | 8 +++---- .../io/papermc/paperweight/util/file.kt | 13 ------------ .../io/papermc/paperweight/util/utils.kt | 6 +++--- .../PaperweightUserDependenciesExtension.kt | 2 +- 21 files changed, 45 insertions(+), 67 deletions(-) diff --git a/buildSrc/src/main/kotlin/config-kotlin.gradle.kts b/buildSrc/src/main/kotlin/config-kotlin.gradle.kts index 7172a5ff9..0f3cabd47 100644 --- a/buildSrc/src/main/kotlin/config-kotlin.gradle.kts +++ b/buildSrc/src/main/kotlin/config-kotlin.gradle.kts @@ -23,7 +23,7 @@ kotlin { } compilerOptions { jvmTarget = JvmTarget.JVM_17 - freeCompilerArgs = listOf("-Xjvm-default=all", "-Xjdk-release=17", "-opt-in=kotlin.io.path.ExperimentalPathApi") + freeCompilerArgs = listOf("-Xjvm-default=all", "-Xjdk-release=17") } } diff --git a/buildSrc/src/main/kotlin/utils.kt b/buildSrc/src/main/kotlin/utils.kt index 3b5f9c493..9b0921e13 100644 --- a/buildSrc/src/main/kotlin/utils.kt +++ b/buildSrc/src/main/kotlin/utils.kt @@ -10,7 +10,7 @@ import org.gradle.plugin.devel.PluginDeclaration fun Configuration.compatibilityAttributes(objects: ObjectFactory) { attributes { attribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, 17) - attribute(GradlePluginApiVersion.GRADLE_PLUGIN_API_VERSION_ATTRIBUTE, objects.named("8.14")) + attribute(GradlePluginApiVersion.GRADLE_PLUGIN_API_VERSION_ATTRIBUTE, objects.named("9.0.0")) } } diff --git a/gradle.properties b/gradle.properties index 42785107d..4e0c596c3 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ group = io.canvasmc.weaver -version = 2.3.6-SNAPSHOT +version = 2.3.5-SNAPSHOT org.gradle.caching = true org.gradle.configuration-cache = true diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 514a48f1b..5b6e8e1c6 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -48,7 +48,7 @@ mockk = "io.mockk:mockk:1.14.5" gradle-licenser = "net.kyori:indra-licenser-spotless:3.2.0" gradle-spotless = "com.diffplug.spotless:spotless-plugin-gradle:7.2.1" gradle-shadow = "com.gradleup.shadow:com.gradleup.shadow.gradle.plugin:9.1.0" -gradle-kotlin-dsl = "org.gradle.kotlin.kotlin-dsl:org.gradle.kotlin.kotlin-dsl.gradle.plugin:5.2.0" +gradle-kotlin-dsl = "org.gradle.kotlin.kotlin-dsl:org.gradle.kotlin.kotlin-dsl.gradle.plugin:6.2.0" gradle-plugin-kotlin = { module = "org.jetbrains.kotlin.jvm:org.jetbrains.kotlin.jvm.gradle.plugin" } gradle-plugin-publish = "com.gradle.publish:plugin-publish-plugin:2.0.0" diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 1b33c55baabb587c669f562ae36f953de2481846..8bdaf60c75ab801e22807dde59e12a8735a34077 100644 GIT binary patch delta 37256 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 delta 35551 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 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 6514f919f..a35649f5f 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.14-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.1.0-all.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew b/gradlew index 23d15a936..adff685a0 100755 --- a/gradlew +++ b/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -114,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH="\\\"\\\"" # Determine the Java command to use to start the JVM. @@ -172,7 +171,6 @@ fi # For Cygwin or MSYS, switch paths to Windows format before running java if "$cygwin" || "$msys" ; then APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) - CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) JAVACMD=$( cygpath --unix "$JAVACMD" ) @@ -212,7 +210,6 @@ DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' set -- \ "-Dorg.gradle.appname=$APP_BASE_NAME" \ - -classpath "$CLASSPATH" \ -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" diff --git a/gradlew.bat b/gradlew.bat index db3a6ac20..c4bdd3ab8 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -70,11 +70,10 @@ goto fail :execute @rem Setup the command line -set CLASSPATH= @rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/PaperweightCoreExtension.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/PaperweightCoreExtension.kt index 46ad36805..6a5a55731 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/PaperweightCoreExtension.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/PaperweightCoreExtension.kt @@ -53,9 +53,8 @@ abstract class PaperweightCoreExtension @Inject constructor(objects: ObjectFacto val spigot = objects.newInstance().also { spigot -> spigot.enabled.convention( - spigot.buildDataRef.zip(spigot.packageVersion) { ref, pkg -> - ref !== null && pkg !== null && ref.isNotBlank() && pkg.isNotBlank() - } + spigot.buildDataRef.zip(spigot.packageVersion) { ref, pkg -> ref.isNotBlank() && pkg.isNotBlank() } + .orElse(false) ) } val paper = objects.newInstance(project) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/AllTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/AllTasks.kt index 9f6ec09ae..2f790a5fc 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/AllTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/AllTasks.kt @@ -87,7 +87,7 @@ open class AllTasks( val patchReobfMappings by tasks.registering { inputMappings.set(generateReobfMappings.flatMap { it.reobfMappings }) - patch.set(extension.paper.reobfMappingsPatch.fileExists(project)) + patch.set(extension.paper.reobfMappingsPatch.fileExists()) fromNamespace.set(DEOBF_NAMESPACE) toNamespace.set(SPIGOT_NAMESPACE) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/CoreTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/CoreTasks.kt index 4d0dd6178..3346b8118 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/CoreTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/CoreTasks.kt @@ -74,12 +74,12 @@ class CoreTasks( } val collectPaperATsFromPatches by tasks.registering(CollectATsFromPatches::class) { - patchDir.set(project.coreExt.paper.basePatchDir.fileExists(project)) - extraPatchDir.set(project.coreExt.paper.featurePatchDir.fileExists(project)) + patchDir.set(project.coreExt.paper.basePatchDir.fileExists()) + extraPatchDir.set(project.coreExt.paper.featurePatchDir.fileExists()) } val mergePaperATs by tasks.registering { - firstFile.set(project.coreExt.paper.additionalAts.fileExists(project)) + firstFile.set(project.coreExt.paper.additionalAts.fileExists()) secondFile.set(collectPaperATsFromPatches.flatMap { it.outputFile }) } @@ -92,7 +92,7 @@ class CoreTasks( val importLibraryFiles = tasks.register("importPaperLibraryFiles") { patches.from(project.coreExt.paper.basePatchDir, project.coreExt.paper.sourcePatchDir, project.coreExt.paper.featurePatchDir) - devImports.set(project.coreExt.paper.devImports.fileExists(project)) + devImports.set(project.coreExt.paper.devImports.fileExists()) libraryFileIndex.set(indexLibraryFiles.flatMap { it.outputFile }) libraries.from(indexLibraryFiles.map { it.libraries }) } diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/DevBundleTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/DevBundleTasks.kt index 6f15acd0c..5591378d6 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/DevBundleTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/DevBundleTasks.kt @@ -69,11 +69,11 @@ class DevBundleTasks( minecraftVersion.set(project.coreExt.minecraftVersion) mojangMappedPaperclipFile.set(paperclipForDevBundle.flatMap { it.outputZip }) reobfMappingsFile.set( - project.coreExt.spigot.enabled.flatMap { + project.coreExt.spigot.enabled.flatMap { if (it) { - coreTasks.generateRelocatedReobfMappings.flatMap { it.outputMappings } + coreTasks.generateRelocatedReobfMappings.flatMap { t -> t.outputMappings } } else { - providers.provider { null } + null } } ) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt index 67d39877f..2eaf136a2 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt @@ -84,7 +84,7 @@ class MinecraftPatchingTasks( } else { output.set(outputSrc) } - patches.set(basePatchDir.fileExists(project)) + patches.set(basePatchDir.fileExists()) identifier = configName } @@ -99,7 +99,7 @@ class MinecraftPatchingTasks( } else { repo.set(outputSrc) } - patches.set(sourcePatchDir.fileExists(project)) + patches.set(sourcePatchDir.fileExists()) rejectsDir.set(this@MinecraftPatchingTasks.rejectsDir) gitFilePatches.set(this@MinecraftPatchingTasks.gitFilePatches) identifier = configName @@ -123,7 +123,7 @@ class MinecraftPatchingTasks( input.set(baseResources) output.set(outputResources) - patches.set(resourcePatchDir.fileExists(project)) + patches.set(resourcePatchDir.fileExists()) // TODO rejects? gitFilePatches.set(this@MinecraftPatchingTasks.gitFilePatches) identifier = configName @@ -144,7 +144,7 @@ class MinecraftPatchingTasks( base.set(applySourcePatches.flatMap { it.repo }) } repo.set(outputSrc) - patches.set(featurePatchDir.fileExists(project)) + patches.set(featurePatchDir.fileExists()) } val applyPatches = tasks.register("apply${namePart}Patches") { @@ -168,18 +168,18 @@ class MinecraftPatchingTasks( fun setupFork(config: ForkConfig) { val collectAccessTransform = tasks.register("collect${configName.capitalized()}MinecraftATsFromPatches") { - patchDir.set(basePatchDir.fileExists(project)) - extraPatchDir.set(featurePatchDir.fileExists(project)) + patchDir.set(basePatchDir.fileExists()) + extraPatchDir.set(featurePatchDir.fileExists()) } val mergeCollectedAts = tasks.register("merge${configName.capitalized()}MinecraftATs") { - firstFile.set(additionalAts.fileExists(project)) + firstFile.set(additionalAts.fileExists()) secondFile.set(collectAccessTransform.flatMap { it.outputFile }) } val importLibFiles = tasks.register("import${configName.capitalized()}LibraryFiles") { patches.from(config.featurePatchDir, config.sourcePatchDir, config.basePatchDir) - devImports.set(config.devImports.fileExists(project)) + devImports.set(config.devImports.fileExists()) libraryFileIndex.set(coreTasks.indexLibraryFiles.flatMap { it.outputFile }) libraries.from(coreTasks.indexLibraryFiles.map { it.libraries }) } @@ -262,8 +262,8 @@ class MinecraftPatchingTasks( ats.jstClasspath.from(project.configurations.named(MACHE_MINECRAFT_CONFIG)) ats.jst.from(project.configurations.named(JST_CONFIG)) - atFile.set(additionalAts.fileExists(project)) - atFileOut.set(additionalAts.fileExists(project)) + atFile.set(additionalAts.fileExists()) + atFileOut.set(additionalAts.fileExists()) } val rebuildResourcePatches = tasks.register(rebuildResourcePatchesName) { diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt index cb53f1c0b..c4685d562 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt @@ -72,7 +72,7 @@ class PatchingTasks( output.set(outputDir) } - patches.set(basePatchDir.fileExists(project)) + patches.set(basePatchDir.fileExists()) baseRef.set("base") identifier = "$forkName $patchSetName" } @@ -88,7 +88,7 @@ class PatchingTasks( } else { repo.set(outputDir) } - patches.set(filePatchDir.fileExists(project)) + patches.set(filePatchDir.fileExists()) rejectsDir.set(this@PatchingTasks.rejectsDir) gitFilePatches.set(this@PatchingTasks.gitFilePatches) identifier = "$forkName $patchSetName" @@ -115,7 +115,7 @@ class PatchingTasks( if (readOnly) { base.set(applyFilePatches.flatMap { it.repo }) } - patches.set(featurePatchDir.fileExists(project)) + patches.set(featurePatchDir.fileExists()) } val applyPatches = tasks.register("apply${namePart}Patches") { @@ -138,12 +138,12 @@ class PatchingTasks( fun setupUpstream() { val collectAccessTransform = tasks.register("collect${namePart}ATsFromPatches") { - patchDir.set(basePatchDir.fileExists(project)) - extraPatchDir.set(featurePatchDir.fileExists(project)) + patchDir.set(basePatchDir.fileExists()) + extraPatchDir.set(featurePatchDir.fileExists()) } val mergeCollectedAts = tasks.register("merge${namePart}ATs") { - firstFile.set(additionalAts.fileExists(project)) + firstFile.set(additionalAts.fileExists()) secondFile.set(collectAccessTransform.flatMap { it.outputFile }) } diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/SpigotTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/SpigotTasks.kt index ccddf0905..f4347f25c 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/SpigotTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/SpigotTasks.kt @@ -92,7 +92,7 @@ open class SpigotTasks( val patchMappings by tasks.registering { inputMappings.set(cleanupMappings.flatMap { it.outputMappings }) - patch.set(extension.paper.mappingsPatch.fileExists(project)) + patch.set(extension.paper.mappingsPatch.fileExists()) fromNamespace.set(SPIGOT_NAMESPACE) toNamespace.set(DEOBF_NAMESPACE) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/UpstreamConfigTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/UpstreamConfigTasks.kt index 9fafe1c93..e94e3948d 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/UpstreamConfigTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/UpstreamConfigTasks.kt @@ -56,11 +56,7 @@ class UpstreamConfigTasks( it.map { cfg -> ApplySingleFilePatches.Patch.patch(target.objects, upstream) { path = cfg.path - patchFile = layout.file( - cfg.patchFile.flatMap { f -> - if (f.path.exists()) target.providers.provider { f.asFile } else target.providers.provider { null } - } - ) + patchFile = cfg.patchFile.fileExists() outputFile = cfg.outputFile rejectsFile = cfg.rejectsFile } diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/util/project-util.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/util/project-util.kt index fa7e2f606..98e552295 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/util/project-util.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/util/project-util.kt @@ -36,7 +36,7 @@ import org.gradle.kotlin.dsl.* fun Project.createBuildTasks( spigot: SpigotExtension, - packagesToFix: Provider?>, + packagesToFix: Provider>, relocatedReobfMappings: Provider ): ServerArtifacts { val jar = tasks.named("jar", AbstractArchiveTask::class).flatMap { it.archiveFile } @@ -90,7 +90,7 @@ fun Project.createBuildTasks( return ServerArtifacts( spigot.enabled.flatMap { if (it) { - includeMappings.flatMap { it.outputJar } + includeMappings.flatMap { t -> t.outputJar } } else { jar } diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/PaperweightPatcher.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/PaperweightPatcher.kt index 927783758..243a56ea7 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/PaperweightPatcher.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/PaperweightPatcher.kt @@ -148,9 +148,9 @@ abstract class PaperweightPatcher : Plugin { forkName.set(upstream.name) repoName.set(input.name) workDir.set(workDirFromProp) - atFile.set(input.additionalAts.fileExists(project)) + atFile.set(input.additionalAts.fileExists()) ats.jst.from(project.configurations.named(JST_CONFIG)) - additionalPatch.set(input.additionalPatch.fileExists(project)) + additionalPatch.set(input.additionalPatch.fileExists()) } } @@ -170,9 +170,9 @@ abstract class PaperweightPatcher : Plugin { inputFrom.set(input.name) commitHash.set(upstream.ref) workDir.set(workDirFromProp) - atFile.set(input.additionalAts.fileExists(project)) + atFile.set(input.additionalAts.fileExists()) ats.jst.from(project.configurations.named(JST_CONFIG)) - additionalPatch.set(input.additionalPatch.fileExists(project)) + additionalPatch.set(input.additionalPatch.fileExists()) generateSources.set(input.generateSources.orElse(true)) generateResources.set(input.generateResources.orElse(true)) generateTestSources.set(input.generateTestSources.orElse(true)) diff --git a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/file.kt b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/file.kt index ba17088b3..1f905e123 100644 --- a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/file.kt +++ b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/file.kt @@ -25,7 +25,6 @@ package io.papermc.paperweight.util import java.io.InputStream import java.io.OutputStream import java.net.URI -import java.nio.file.FileAlreadyExistsException import java.nio.file.FileSystem import java.nio.file.FileSystemNotFoundException import java.nio.file.FileSystems @@ -33,7 +32,6 @@ import java.nio.file.Files import java.nio.file.Path import java.nio.file.PathMatcher import java.nio.file.attribute.DosFileAttributeView -import java.nio.file.attribute.FileAttribute import java.util.Arrays import java.util.stream.Collectors import java.util.stream.Stream @@ -193,17 +191,6 @@ private fun relativeCopyOrMove(baseDir: Path, file: Path, outputDir: Path, move: } } -fun Path.createParentDirectories(vararg attributes: FileAttribute<*>): Path = also { - val parent = it.parent - if (parent != null && !parent.isDirectory()) { - try { - parent.createDirectories(*attributes) - } catch (e: FileAlreadyExistsException) { - if (!parent.isDirectory()) throw e - } - } -} - val FileSystemLocation.path: Path get() = asFile.toPath() val Provider.path: Path diff --git a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/utils.kt b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/utils.kt index 5a9e71088..4d653072d 100644 --- a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/utils.kt +++ b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/utils.kt @@ -106,8 +106,8 @@ fun ProjectLayout.cacheDir(path: String) = projectDirectory.dir(".gradle/$CACHE_ fun Project.offlineMode(): Boolean = gradle.startParameter.isOffline -fun Provider.fileExists(project: Project): Provider { - return flatMap { project.provider { it.takeIf { f -> f.path.exists() } } } +fun Provider.fileExists(): Provider { + return map { it.takeIf { f -> f.path.exists() } } } @Suppress("UNCHECKED_CAST") @@ -448,7 +448,7 @@ fun FileCollection.resolveMacheMeta() = singleFile.toPath().openZipSafe().use { fun isIDEASync(): Boolean = java.lang.Boolean.getBoolean("idea.sync.active") -inline fun ObjectFactory.providerSet( +inline fun ObjectFactory.providerSet( vararg providers: Provider ): Provider> { if (providers.isEmpty()) { diff --git a/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/PaperweightUserDependenciesExtension.kt b/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/PaperweightUserDependenciesExtension.kt index 1c26289f6..f2430acf4 100644 --- a/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/PaperweightUserDependenciesExtension.kt +++ b/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/PaperweightUserDependenciesExtension.kt @@ -253,7 +253,7 @@ abstract class PaperweightUserDependenciesExtension( } @Suppress("unchecked_cast") - private fun nullAction(): Action { + private fun nullAction(): Action { return NullAction as Action } From 94807c3007beeaa2c120279301fb69ae1bb096be Mon Sep 17 00:00:00 2001 From: Toffikk Date: Thu, 25 Sep 2025 21:01:22 +0200 Subject: [PATCH 091/257] fix: revert shadow bump, causes issues with generate mappings --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 5b6e8e1c6..028663ed0 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -47,7 +47,7 @@ mockk = "io.mockk:mockk:1.14.5" # Gradle gradle-licenser = "net.kyori:indra-licenser-spotless:3.2.0" gradle-spotless = "com.diffplug.spotless:spotless-plugin-gradle:7.2.1" -gradle-shadow = "com.gradleup.shadow:com.gradleup.shadow.gradle.plugin:9.1.0" +gradle-shadow = "com.gradleup.shadow:com.gradleup.shadow.gradle.plugin:8.3.8" gradle-kotlin-dsl = "org.gradle.kotlin.kotlin-dsl:org.gradle.kotlin.kotlin-dsl.gradle.plugin:6.2.0" gradle-plugin-kotlin = { module = "org.jetbrains.kotlin.jvm:org.jetbrains.kotlin.jvm.gradle.plugin" } gradle-plugin-publish = "com.gradle.publish:plugin-publish-plugin:2.0.0" From bff3b0f5a54383eaf8cacd994668845f387c6c39 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Thu, 25 Sep 2025 21:01:42 +0200 Subject: [PATCH 092/257] chore: continue development cycle --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 4e0c596c3..42785107d 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ group = io.canvasmc.weaver -version = 2.3.5-SNAPSHOT +version = 2.3.6-SNAPSHOT org.gradle.caching = true org.gradle.configuration-cache = true From 801ee28cbc4fe52f46c253c0fa85ce238c9d9b06 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Sat, 27 Sep 2025 22:41:13 +0200 Subject: [PATCH 093/257] fix(rebuilding): fail fast on missing tag and allow to rebuild base patches without having per-file patches applied --- .../taskcontainers/MinecraftPatchingTasks.kt | 2 ++ .../core/taskcontainers/PatchingTasks.kt | 1 + .../core/tasks/patching/ApplyFilePatches.kt | 6 +--- .../core/tasks/patching/RebuildFilePatches.kt | 26 +++++++++++++++++ .../tasks/RebuildBaseGitPatches.kt | 29 +++++++------------ 5 files changed, 41 insertions(+), 23 deletions(-) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt index 2eaf136a2..a8abf4ad2 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt @@ -264,6 +264,7 @@ class MinecraftPatchingTasks( ats.jst.from(project.configurations.named(JST_CONFIG)) atFile.set(additionalAts.fileExists()) atFileOut.set(additionalAts.fileExists()) + identifier = configName } val rebuildResourcePatches = tasks.register(rebuildResourcePatchesName) { @@ -274,6 +275,7 @@ class MinecraftPatchingTasks( base.set(baseResources) patches.set(resourcePatchDir) gitFilePatches.set(this@MinecraftPatchingTasks.gitFilePatches) + identifier = configName } val rebuildFilePatches = tasks.register(rebuildFilePatchesName) { diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt index c4685d562..dd37e4148 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt @@ -198,6 +198,7 @@ class PatchingTasks( input.set(outputDir) patches.set(filePatchDir) gitFilePatches.set(this@PatchingTasks.gitFilePatches) + identifier = "$forkName $patchSetName" } val fixupFilePatches = tasks.register(fixupFilePatchesName) { diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyFilePatches.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyFilePatches.kt index 219f72768..47fdd2aec 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyFilePatches.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyFilePatches.kt @@ -69,10 +69,6 @@ abstract class ApplyFilePatches : BaseTask() { @get:Optional abstract val base: DirectoryProperty - @get:Optional - @get:Input - abstract val baseRef: Property - @get:Internal abstract val ref: Property @@ -92,7 +88,7 @@ abstract class ApplyFilePatches : BaseTask() { gitFilePatches.convention(false) moveFailedGitPatchesToRejects.convention(false) emitRejects.convention(true) - ref.convention(baseRef.orElse("patchedBase")) + ref.convention("patchedBase") } } diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/RebuildFilePatches.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/RebuildFilePatches.kt index 5fb4b685c..8bdf8046c 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/RebuildFilePatches.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/RebuildFilePatches.kt @@ -81,6 +81,9 @@ abstract class RebuildFilePatches : JavaLauncherTask() { @get:Nested val ats: ApplySourceATs = objects.newInstance() + @get:Input + abstract val identifier: Property + override fun init() { super.init() contextLines.convention(3) @@ -94,6 +97,29 @@ abstract class RebuildFilePatches : JavaLauncherTask() { val inputDir = input.convertToPath() val git = Git(inputDir) + + // fail fast if someone decides to name a patch with this name to avoid patch corruption + val fileCommit = git( + "log", + "--format=%H %s", + "--grep=^${identifier.get()} File Patches$", + "base..HEAD" + ).getText().lines().filter { it.isNotBlank() }.map { it.substringBefore(" ") } + if (fileCommit.size > 1) { + val count = fileCommit.size + throw PaperweightException( + "Exceeded the max amount of commits with the identifier: `${identifier.get()} File Patches`!\nGot $count commits, expected: 1" + ) + } else if (fileCommit.isEmpty()) { + throw PaperweightException( + "Could not find a commit with the identifier: `${identifier.get()} Base Patches`!\nHave you applied patches before rebuilding?" + ) + } + + // we update the appropriate tag to reflect the new repo state + git("tag", "-f", "file", fileCommit.joinToString()).executeSilently(silenceErr = true) + + // now we can safely work with the repo, having updated the tag git("stash", "push").executeSilently(silenceErr = true) git("checkout", "file").executeSilently(silenceErr = true) diff --git a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RebuildBaseGitPatches.kt b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RebuildBaseGitPatches.kt index f6af43069..a6d9e281c 100644 --- a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RebuildBaseGitPatches.kt +++ b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RebuildBaseGitPatches.kt @@ -70,33 +70,26 @@ abstract class RebuildBaseGitPatches : ControllableOutputTask() { fun run() { val git = Git(inputDir.path) - // fail fast if someone decides to name a patch with these names to avoid patch corruption - val patchedBaseCommitCount = git( - "rev-list", + // fail fast if someone decides to name a patch with this name to avoid patch corruption + val patchedBaseCommit = git( + "log", + "--format=%H %s", "--grep=^${identifier.get()} Base Patches$", "base..HEAD" - ).getText().lines().filter { it.isNotBlank() } - if (patchedBaseCommitCount.size > 1) { - val count = patchedBaseCommitCount.size + ).getText().lines().filter { it.isNotBlank() }.map { it.substringBefore(" ") } + if (patchedBaseCommit.size > 1) { + val count = patchedBaseCommit.size throw PaperweightException( "Exceeded the max amount of commits with the identifier: `${identifier.get()} Base Patches`!\nGot $count commits, expected: 1" ) - } - val fileCommitCount = git("rev-list", "--grep=^${identifier.get()} File Patches$", "base..HEAD").getText().lines().filter { it.isNotBlank() } - if (fileCommitCount.size > 1) { - val count = fileCommitCount.size + } else if (patchedBaseCommit.isEmpty()) { throw PaperweightException( - "Exceeded the max amount of commits with the identifier: `${identifier.get()} File Patches`!\nGot $count commits, expected: 1" + "Could not find a commit with the identifier: `${identifier.get()} Base Patches`!\nHave you applied patches before rebuilding?" ) } - // these have to be retrieved dynamically - val patchedBaseCommit = git("rev-list", "--grep=^${identifier.get()} Base Patches$", "--max-count=1", "base..HEAD").getText().trim() - val fileCommit = git("rev-list", "--grep=^${identifier.get()} File Patches$", "--max-count=1", "base..HEAD").getText().trim() - - // we update the appropriate tags to reflect the new repo state - git("tag", "-f", "patchedBase", patchedBaseCommit).executeSilently(silenceErr = true) - git("tag", "-f", "file", fileCommit).executeSilently(silenceErr = true) + // we update the tag to reflect the new repo state + git("tag", "-f", "patchedBase", patchedBaseCommit.joinToString()).executeSilently(silenceErr = true) val what = inputDir.path.name val patchFolder = patchDir.path From eb843c306ba8cf62ce1ecc441550c1ed12a92fe5 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Sun, 28 Sep 2025 00:23:37 +0200 Subject: [PATCH 094/257] chore: setup release publishing --- .github/workflows/deploy-snapshot.yml | 35 +++++++++++++++++++ .github/workflows/deploy.yml | 30 +++++++++------- .github/workflows/test.yml | 6 ++-- .../src/main/kotlin/config-publish.gradle.kts | 9 ++++- 4 files changed, 63 insertions(+), 17 deletions(-) create mode 100644 .github/workflows/deploy-snapshot.yml diff --git a/.github/workflows/deploy-snapshot.yml b/.github/workflows/deploy-snapshot.yml new file mode 100644 index 000000000..27fdd529a --- /dev/null +++ b/.github/workflows/deploy-snapshot.yml @@ -0,0 +1,35 @@ +name: Deploy Snapshot +on: + push: + branches: [ 'basepatches' ] + paths-ignore: + - 'license/*' + - 'readme.md' + - '.gitignore' + - '.gitattributes' + - '.editorconfig' + +jobs: + deploy: + name: Deploy Snapshot + runs-on: 'ubuntu-latest' + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-java@v4 + with: + java-version: 21 + distribution: 'temurin' + - name: Setup Gradle + uses: gradle/actions/setup-gradle@v4 + - name: Get project version + id: get_version + shell: bash + run: | + project_version=$(./gradlew -q --console=plain printVersion --no-daemon) + echo version=$project_version >> $GITHUB_OUTPUT + - name: Deploy snapshot version + if: endsWith(steps.get_version.outputs.version, '-SNAPSHOT') + run: ./gradlew -Dorg.gradle.parallel=true publishAllPublicationsToCanvasMC_SnapshotsRepository --no-daemon --stacktrace -Dorg.gradle.internal.http.socketTimeout=90000 -Dorg.gradle.internal.http.connectionTimeout=90000 + env: + PUBLISH_USER: ${{ secrets.PUBLISH_USER }} + PUBLISH_TOKEN: ${{ secrets.PUBLISH_TOKEN }} diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 6a5f1a220..81f8ddb26 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -1,26 +1,30 @@ -name: Deploy Snapshot +name: Deploy on: push: - branches: [ 'basepatches' ] - paths-ignore: - - 'license/*' - - 'readme.md' - - '.gitignore' - - '.gitattributes' - - '.editorconfig' + tags: [ 'v*' ] jobs: deploy: - name: Deploy Snapshot + name: Deploy runs-on: 'ubuntu-latest' steps: - - uses: actions/checkout@v5 - - uses: actions/setup-java@v5 + - uses: actions/checkout@v4 + - uses: actions/setup-java@v4 with: java-version: 21 distribution: 'temurin' - - name: Deploy Version - run: ./gradlew publishAllPublicationsToCanvasmcRepository + - name: Setup Gradle + uses: gradle/actions/setup-gradle@v4 + - name: Deploy release + run: ./gradlew publishAllPublicationsToCanvasMC_ReleasesRepository --no-daemon --stacktrace env: PUBLISH_USER: ${{ secrets.PUBLISH_USER }} PUBLISH_TOKEN: ${{ secrets.PUBLISH_TOKEN }} + - name: Parse tag + id: vars + run: echo ::set-output name=tag::${GITHUB_REF#refs/*/} + - name: Create release and changelog + uses: MC-Machinations/auto-release-changelog@v1.1.3 + with: + token: ${{ secrets.SUMMARY_TOKEN }} + title: paperweight ${{ steps.vars.outputs.tag }} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 89ad5fc53..ea795bb26 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -11,8 +11,8 @@ jobs: name: Test runs-on: 'ubuntu-latest' steps: - - uses: actions/checkout@v5 - - uses: actions/setup-java@v5 + - uses: actions/checkout@v4 + - uses: actions/setup-java@v4 with: java-version: 21 distribution: 'temurin' @@ -29,4 +29,4 @@ jobs: with: report_paths: '**/build/test-results/test/TEST-*.xml' detailed_summary: true - annotate_notice: true \ No newline at end of file + annotate_notice: true diff --git a/buildSrc/src/main/kotlin/config-publish.gradle.kts b/buildSrc/src/main/kotlin/config-publish.gradle.kts index 8a755e8bd..432c6ac8c 100644 --- a/buildSrc/src/main/kotlin/config-publish.gradle.kts +++ b/buildSrc/src/main/kotlin/config-publish.gradle.kts @@ -88,7 +88,14 @@ val shadowJar by tasks.existing(ShadowJar::class) { publishing { repositories { maven("https://maven.canvasmc.io/snapshots") { - name = "canvasmc" + name = "CanvasMC_Snapshots" + credentials { + username=System.getenv("PUBLISH_USER") + password=System.getenv("PUBLISH_TOKEN") + } + } + maven("https://maven.canvasmc.io/releases") { + name = "CanvasMC_Releases" credentials { username=System.getenv("PUBLISH_USER") password=System.getenv("PUBLISH_TOKEN") From 69d250c6c950df735ea0d7d50bd2715b4290506c Mon Sep 17 00:00:00 2001 From: Toffikk Date: Sun, 28 Sep 2025 00:24:19 +0200 Subject: [PATCH 095/257] chore: Merge upstream changes commit 33a330c0521067aa86d2c131c132ce0739148197 Author: Jason Penilla <11360596+jpenilla@users.noreply.github.com> Date: Sat Sep 27 14:59:11 2025 -0700 Fix userdev test package commit 9e75415fbb4f99185bcf2aeb6ec20d66726afd99 Author: Jason Penilla <11360596+jpenilla@users.noreply.github.com> Date: Sat Sep 27 14:54:52 2025 -0700 Update snapshot deployment config commit 08356cfc6e4b03b4ca0d4c39b9e3aa6c220b189e Author: Jason Penilla <11360596+jpenilla@users.noreply.github.com> Date: Sat Sep 27 14:52:17 2025 -0700 run spotless commit 40568e2c5e65bffeac2710ea99c36df031fdb719 Author: Jason Penilla <11360596+jpenilla@users.noreply.github.com> Date: Sat Sep 27 14:52:00 2025 -0700 Add userdev plugin application test commit a3f9f10e8ae8519fc30a8d25664720a16667bc20 Author: Jason Penilla <11360596+jpenilla@users.noreply.github.com> Date: Sat Sep 27 14:43:13 2025 -0700 Update PaperweightUserDependenciesExtension for Gradle 9.1 deprecations fixes #354 --- .../paperweight/userdev/PaperweightUser.kt | 3 +- .../PaperweightUserDependenciesExtension.kt | 70 +++++++------------ .../userdev/PaperweightUserTest.kt | 48 +++++++++++++ 3 files changed, 76 insertions(+), 45 deletions(-) create mode 100644 paperweight-userdev/src/test/kotlin/io/papermc/paperweight/userdev/PaperweightUserTest.kt diff --git a/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/PaperweightUser.kt b/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/PaperweightUser.kt index 520cd18be..7de7a52ce 100644 --- a/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/PaperweightUser.kt +++ b/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/PaperweightUser.kt @@ -117,7 +117,8 @@ abstract class PaperweightUser : Plugin { target.dependencies.extensions.create( PAPERWEIGHT_EXTENSION, PaperweightUserDependenciesExtension::class, - target.dependencies + target.dependencies, + target.dependencyFactory, ) createConfigurations(target, target.provider { userdevSetup }, setupTask) diff --git a/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/PaperweightUserDependenciesExtension.kt b/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/PaperweightUserDependenciesExtension.kt index f2430acf4..702a339ea 100644 --- a/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/PaperweightUserDependenciesExtension.kt +++ b/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/PaperweightUserDependenciesExtension.kt @@ -23,15 +23,19 @@ package io.papermc.paperweight.userdev import io.papermc.paperweight.util.constants.* +import javax.inject.Inject import org.gradle.api.Action import org.gradle.api.artifacts.ExternalModuleDependency import org.gradle.api.artifacts.MinimalExternalModuleDependency +import org.gradle.api.artifacts.dsl.DependencyFactory import org.gradle.api.artifacts.dsl.DependencyHandler import org.gradle.api.provider.Provider import org.gradle.kotlin.dsl.* -abstract class PaperweightUserDependenciesExtension( - private val dependencies: DependencyHandler +@Suppress("unused") +abstract class PaperweightUserDependenciesExtension @Inject constructor( + private val dependencies: DependencyHandler, + private val dependencyFactory: DependencyFactory, ) { /** * Adds a dependency on Paper's dev bundle to the dev bundle [org.gradle.api.artifacts.Configuration]. @@ -39,9 +43,6 @@ abstract class PaperweightUserDependenciesExtension( * @param version dependency version * @param group dependency group * @param artifactId dependency artifactId - * @param configuration dependency configuration - * @param classifier dependency classifier - * @param ext dependency extension * @param devBundleConfigurationName name of the dev bundle [org.gradle.api.artifacts.Configuration] * @param configurationAction action configuring the dependency * @return dependency @@ -51,13 +52,10 @@ abstract class PaperweightUserDependenciesExtension( version: String? = null, group: String = "io.papermc.paper", artifactId: String = "dev-bundle", - configuration: String? = null, - classifier: String? = null, - ext: String? = null, devBundleConfigurationName: String = DEV_BUNDLE_CONFIG, configurationAction: Action = nullAction() ): ExternalModuleDependency { - val dep = dependencies.create(group, artifactId, version, configuration, classifier, ext) + val dep = dependencyFactory.create(buildDependencyString(group, artifactId, version)) configurationAction(dep) dependencies.add(devBundleConfigurationName, dep) return dep @@ -69,9 +67,6 @@ abstract class PaperweightUserDependenciesExtension( * @param version dependency version * @param group dependency group * @param artifactId dependency artifactId - * @param configuration dependency configuration - * @param classifier dependency classifier - * @param ext dependency extension * @param devBundleConfigurationName name of the dev bundle [org.gradle.api.artifacts.Configuration] * @param configurationAction action configuring the dependency * @return dependency @@ -81,13 +76,10 @@ abstract class PaperweightUserDependenciesExtension( version: String? = null, group: String = "dev.folia", artifactId: String = "dev-bundle", - configuration: String? = null, - classifier: String? = null, - ext: String? = null, devBundleConfigurationName: String = DEV_BUNDLE_CONFIG, configurationAction: Action = nullAction() ): ExternalModuleDependency { - val dep = dependencies.create(group, artifactId, version, configuration, classifier, ext) + val dep = dependencyFactory.create(buildDependencyString(group, artifactId, version)) configurationAction(dep) dependencies.add(devBundleConfigurationName, dep) return dep @@ -99,9 +91,6 @@ abstract class PaperweightUserDependenciesExtension( * @param group dependency group * @param version dependency version * @param artifactId dependency artifactId - * @param configuration dependency configuration - * @param classifier dependency classifier - * @param ext dependency extension * @param devBundleConfigurationName name of the dev bundle [org.gradle.api.artifacts.Configuration] * @param configurationAction action configuring the dependency * @return dependency @@ -111,13 +100,10 @@ abstract class PaperweightUserDependenciesExtension( group: String, version: String? = null, artifactId: String = "dev-bundle", - configuration: String? = null, - classifier: String? = null, - ext: String? = null, devBundleConfigurationName: String = DEV_BUNDLE_CONFIG, configurationAction: Action = nullAction() ): ExternalModuleDependency { - val dep = dependencies.create(group, artifactId, version, configuration, classifier, ext) + val dep = dependencyFactory.create(buildDependencyString(group, artifactId, version)) configurationAction(dep) dependencies.add(devBundleConfigurationName, dep) return dep @@ -177,9 +163,6 @@ abstract class PaperweightUserDependenciesExtension( * @param version dependency version * @param group dependency group * @param artifactId dependency artifactId - * @param configuration dependency configuration - * @param classifier dependency classifier - * @param ext dependency extension * @param configurationAction action configuring the dependency * @return dependency */ @@ -188,12 +171,9 @@ abstract class PaperweightUserDependenciesExtension( version: String? = null, group: String = "dev.folia", artifactId: String = "dev-bundle", - configuration: String? = null, - classifier: String? = null, - ext: String? = null, configurationAction: Action = nullAction() ): ExternalModuleDependency { - val dep = dependencies.create(group, artifactId, version, configuration, classifier, ext) + val dep = dependencyFactory.create(buildDependencyString(group, artifactId, version)) configurationAction(dep) return dep } @@ -204,9 +184,6 @@ abstract class PaperweightUserDependenciesExtension( * @param version dependency version * @param group dependency group * @param artifactId dependency artifactId - * @param configuration dependency configuration - * @param classifier dependency classifier - * @param ext dependency extension * @param configurationAction action configuring the dependency * @return dependency */ @@ -215,12 +192,9 @@ abstract class PaperweightUserDependenciesExtension( version: String? = null, group: String = "io.papermc.paper", artifactId: String = "dev-bundle", - configuration: String? = null, - classifier: String? = null, - ext: String? = null, configurationAction: Action = nullAction() ): ExternalModuleDependency { - val dep = dependencies.create(group, artifactId, version, configuration, classifier, ext) + val dep = dependencyFactory.create(buildDependencyString(group, artifactId, version)) configurationAction(dep) return dep } @@ -231,9 +205,6 @@ abstract class PaperweightUserDependenciesExtension( * @param group dependency group * @param version dependency version * @param artifactId dependency artifactId - * @param configuration dependency configuration - * @param classifier dependency classifier - * @param ext dependency extension * @param configurationAction action configuring the dependency * @return dependency */ @@ -242,12 +213,9 @@ abstract class PaperweightUserDependenciesExtension( group: String, version: String? = null, artifactId: String = "dev-bundle", - configuration: String? = null, - classifier: String? = null, - ext: String? = null, configurationAction: Action = nullAction() ): ExternalModuleDependency { - val dep = dependencies.create(group, artifactId, version, configuration, classifier, ext) + val dep = dependencyFactory.create(buildDependencyString(group, artifactId, version)) configurationAction(dep) return dep } @@ -260,4 +228,18 @@ abstract class PaperweightUserDependenciesExtension( private object NullAction : Action { override fun execute(t: Any) {} } + + private fun buildDependencyString( + group: String, + artifactId: String, + version: String? + ): String { + val s = StringBuilder(group) + .append(':') + .append(artifactId) + if (version != null) { + s.append(':').append(version) + } + return s.toString() + } } diff --git a/paperweight-userdev/src/test/kotlin/io/papermc/paperweight/userdev/PaperweightUserTest.kt b/paperweight-userdev/src/test/kotlin/io/papermc/paperweight/userdev/PaperweightUserTest.kt new file mode 100644 index 000000000..d621ba131 --- /dev/null +++ b/paperweight-userdev/src/test/kotlin/io/papermc/paperweight/userdev/PaperweightUserTest.kt @@ -0,0 +1,48 @@ +/* + * paperweight is a Gradle plugin for the PaperMC project. + * + * Copyright (c) 2023 Kyle Wood (DenWav) + * Contributors + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 only, no later versions. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + * USA + */ + +package io.papermc.paperweight.userdev + +import java.nio.file.Path +import kotlin.test.Test +import kotlin.test.assertNotNull +import org.gradle.api.Project +import org.gradle.kotlin.dsl.getByType +import org.gradle.testfixtures.ProjectBuilder +import org.junit.jupiter.api.io.TempDir + +class PaperweightUserTest { + fun setupProject(dir: Path): Project { + return ProjectBuilder.builder() + .withProjectDir(dir.toFile()) + .build() + } + + @Test + fun testPluginApplication(@TempDir tmpDir: Path) { + val project = setupProject(tmpDir) + project.pluginManager.apply("io.papermc.paperweight.userdev") + + assertNotNull(project.extensions.getByType(PaperweightUserExtension::class)) + assertNotNull(project.dependencies.extensions.getByType(PaperweightUserDependenciesExtension::class)) + } +} From 77c94c323f633994e4713c990071d378cb50220b Mon Sep 17 00:00:00 2001 From: Toffikk Date: Sun, 28 Sep 2025 00:27:18 +0200 Subject: [PATCH 096/257] release: Version 2.3.6 --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 42785107d..2d054c1d7 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ group = io.canvasmc.weaver -version = 2.3.6-SNAPSHOT +version = 2.3.6 org.gradle.caching = true org.gradle.configuration-cache = true From 4df77d08a0618b2afda82b69a100935a9f621823 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Sun, 28 Sep 2025 00:31:57 +0200 Subject: [PATCH 097/257] fix(tests): apply weaver in userdev test --- .../io/papermc/paperweight/userdev/PaperweightUserTest.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/paperweight-userdev/src/test/kotlin/io/papermc/paperweight/userdev/PaperweightUserTest.kt b/paperweight-userdev/src/test/kotlin/io/papermc/paperweight/userdev/PaperweightUserTest.kt index d621ba131..5d5738f65 100644 --- a/paperweight-userdev/src/test/kotlin/io/papermc/paperweight/userdev/PaperweightUserTest.kt +++ b/paperweight-userdev/src/test/kotlin/io/papermc/paperweight/userdev/PaperweightUserTest.kt @@ -40,7 +40,7 @@ class PaperweightUserTest { @Test fun testPluginApplication(@TempDir tmpDir: Path) { val project = setupProject(tmpDir) - project.pluginManager.apply("io.papermc.paperweight.userdev") + project.pluginManager.apply("io.canvasmc.weaver.userdev") assertNotNull(project.extensions.getByType(PaperweightUserExtension::class)) assertNotNull(project.dependencies.extensions.getByType(PaperweightUserDependenciesExtension::class)) From fd9304138f1d5897153359b2abeaf97c87a43dc2 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Sun, 28 Sep 2025 00:34:55 +0200 Subject: [PATCH 098/257] chore: continue development cycle --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 2d054c1d7..81d1a925c 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ group = io.canvasmc.weaver -version = 2.3.6 +version = 2.3.7-SNAPSHOT org.gradle.caching = true org.gradle.configuration-cache = true From e51d6df8b8715bb60e0641db0a32342cf9343123 Mon Sep 17 00:00:00 2001 From: "tofik.07" <62406295+Toffikk@users.noreply.github.com> Date: Sun, 28 Sep 2025 00:37:53 +0200 Subject: [PATCH 099/257] chore: correct release title --- .github/workflows/deploy.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 81f8ddb26..d2b6cf12c 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -27,4 +27,4 @@ jobs: uses: MC-Machinations/auto-release-changelog@v1.1.3 with: token: ${{ secrets.SUMMARY_TOKEN }} - title: paperweight ${{ steps.vars.outputs.tag }} + title: weaver ${{ steps.vars.outputs.tag }} From bdaa471fbb4d312a734f4d413600193a97b8d79a Mon Sep 17 00:00:00 2001 From: Toffikk Date: Sun, 28 Sep 2025 00:52:22 +0200 Subject: [PATCH 100/257] chore: bump actions in workflows (this somehow got reverted?) --- .github/workflows/deploy-snapshot.yml | 4 ++-- .github/workflows/deploy.yml | 4 ++-- .github/workflows/test.yml | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/deploy-snapshot.yml b/.github/workflows/deploy-snapshot.yml index 27fdd529a..cba23a6e8 100644 --- a/.github/workflows/deploy-snapshot.yml +++ b/.github/workflows/deploy-snapshot.yml @@ -14,8 +14,8 @@ jobs: name: Deploy Snapshot runs-on: 'ubuntu-latest' steps: - - uses: actions/checkout@v4 - - uses: actions/setup-java@v4 + - uses: actions/checkout@v5 + - uses: actions/setup-java@v5 with: java-version: 21 distribution: 'temurin' diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index d2b6cf12c..83904d7f2 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -8,8 +8,8 @@ jobs: name: Deploy runs-on: 'ubuntu-latest' steps: - - uses: actions/checkout@v4 - - uses: actions/setup-java@v4 + - uses: actions/checkout@v5 + - uses: actions/setup-java@v5 with: java-version: 21 distribution: 'temurin' diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index ea795bb26..be182f83a 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -11,8 +11,8 @@ jobs: name: Test runs-on: 'ubuntu-latest' steps: - - uses: actions/checkout@v4 - - uses: actions/setup-java@v4 + - uses: actions/checkout@v5 + - uses: actions/setup-java@v5 with: java-version: 21 distribution: 'temurin' From e317c9a8d8a5f85afff6b7f737900af849049f1d Mon Sep 17 00:00:00 2001 From: Toffikk Date: Sun, 28 Sep 2025 18:20:22 +0200 Subject: [PATCH 101/257] feat: add a canvasDevBundle configuration to userdev --- .../paperweight/userdev/PaperweightUser.kt | 10 ++- .../PaperweightUserDependenciesExtension.kt | 61 +++++++++++++++++++ .../userdev/PaperweightUserExtension.kt | 7 +++ 3 files changed, 76 insertions(+), 2 deletions(-) diff --git a/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/PaperweightUser.kt b/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/PaperweightUser.kt index 7de7a52ce..b0246a314 100644 --- a/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/PaperweightUser.kt +++ b/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/PaperweightUser.kt @@ -170,6 +170,12 @@ abstract class PaperweightUser : Plugin { } } + if (userdev.injectCanvasRepository.get()) { + repositories.maven(CANVAS_MAVEN_REPO_URL) { + content { onlyForConfigurations(DEV_BUNDLE_CONFIG) } + } + } + if (userdev.applyJunitExclusionRule.get()) { applyJunitExclusionRule() } @@ -280,7 +286,7 @@ abstract class PaperweightUser : Plugin { if (hasDevBundle.isFailure || !hasDevBundle.getOrThrow()) { val message = "Unable to resolve a dev bundle, which is required for paperweight to function.\n" + "Add a dev bundle to the 'paperweightDevelopmentBundle' configuration (the dependencies.paperweight extension can" + - " help with this), and ensure there is a repository to resolve it from (the Paper repository is used by default)." + " help with this), and ensure there is a repository to resolve it from (the Paper and Canvas repository is used by default)." val ex = PaperweightException(message, hasDevBundle.exceptionOrNull()) throw ex /* TODO migrate this to the Problems API when it comes out of incubating @@ -289,7 +295,7 @@ abstract class PaperweightUser : Plugin { id("paperweight-userdev-cannot-resolve-dev-bundle", message) solution( "Add a dev bundle to the 'paperweightDevelopmentBundle' configuration (the dependencies.paperweight extension can" + - " help with this), and ensure there is a repository to resolve it from (the Paper repository is used by default)." + " help with this), and ensure there is a repository to resolve it from (the Paper and Canvas repository is used by default)." ) withException(ex) } diff --git a/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/PaperweightUserDependenciesExtension.kt b/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/PaperweightUserDependenciesExtension.kt index 702a339ea..cb14e43bd 100644 --- a/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/PaperweightUserDependenciesExtension.kt +++ b/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/PaperweightUserDependenciesExtension.kt @@ -85,6 +85,30 @@ abstract class PaperweightUserDependenciesExtension @Inject constructor( return dep } + /** + * Adds a dependency on Canvas's dev bundle to the dev bundle [org.gradle.api.artifacts.Configuration]. + * + * @param version dependency version + * @param group dependency group + * @param artifactId dependency artifactId + * @param devBundleConfigurationName name of the dev bundle [org.gradle.api.artifacts.Configuration] + * @param configurationAction action configuring the dependency + * @return dependency + */ + @JvmOverloads + fun canvasDevBundle( + version: String? = null, + group: String = "io.canvasmc.canvas", + artifactId: String = "dev-bundle", + devBundleConfigurationName: String = DEV_BUNDLE_CONFIG, + configurationAction: Action = nullAction() + ): ExternalModuleDependency { + val dep = dependencyFactory.create(buildDependencyString(group, artifactId, version)) + configurationAction(dep) + dependencies.add(devBundleConfigurationName, dep) + return dep + } + /** * Adds a dependency to the dev bundle [org.gradle.api.artifacts.Configuration]. * @@ -157,6 +181,43 @@ abstract class PaperweightUserDependenciesExtension @Inject constructor( dependencies.addProvider(DEV_BUNDLE_CONFIG, version.map { "dev.folia:dev-bundle:$it" }, configurationAction) } + /** + * Adds a dependency on the Canvas dev bundle to the [DEV_BUNDLE_CONFIG] configuration. + * + * Intended for use with Gradle version catalogs. + * + * @param version version provider + * @param configurationAction action configuring the dependency + */ + @JvmOverloads + fun canvasDevBundle( + version: Provider, + configurationAction: Action = nullAction() + ) { + dependencies.addProvider(DEV_BUNDLE_CONFIG, version.map { "io.canvasmc.canvas:dev-bundle:$it" }, configurationAction) + } + + /** + * Creates a Canvas dev bundle dependency without adding it to any configurations. + * + * @param version dependency version + * @param group dependency group + * @param artifactId dependency artifactId + * @param configurationAction action configuring the dependency + * @return dependency + */ + @JvmOverloads + fun canvasDevBundleDependency( + version: String? = null, + group: String = "io.canvasmc.canvas", + artifactId: String = "dev-bundle", + configurationAction: Action = nullAction() + ): ExternalModuleDependency { + val dep = dependencyFactory.create(buildDependencyString(group, artifactId, version)) + configurationAction(dep) + return dep + } + /** * Creates a Folia dev bundle dependency without adding it to any configurations. * diff --git a/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/PaperweightUserExtension.kt b/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/PaperweightUserExtension.kt index 44684cb90..f7186e5d4 100644 --- a/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/PaperweightUserExtension.kt +++ b/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/PaperweightUserExtension.kt @@ -49,6 +49,13 @@ abstract class PaperweightUserExtension( */ val injectPaperRepository: Property = objects.property().convention(true) + /** + * Whether to inject the Canvas maven repository for use by the dev bundle configuration. + * + * True by default to allow easily resolving Canvas development bundles. + */ + val injectCanvasRepository: Property = objects.property().convention(true) + /** * Configurations to add the Minecraft server dependency to. */ From 05813c1131712f2d6f1dff5e2dd25828bf029650 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Mon, 29 Sep 2025 17:32:22 +0200 Subject: [PATCH 102/257] chore: minor corrections --- .../io/papermc/paperweight/core/tasks/GeneratePatches.kt | 2 +- .../paperweight/core/tasks/patching/RebuildFilePatches.kt | 4 ++-- .../io/papermc/paperweight/tasks/RebuildBaseGitPatches.kt | 4 ++-- .../kotlin/io/papermc/paperweight/userdev/PaperweightUser.kt | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/GeneratePatches.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/GeneratePatches.kt index 6f3006862..7ec194682 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/GeneratePatches.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/GeneratePatches.kt @@ -114,7 +114,7 @@ abstract class GeneratePatches : BaseTask() { "${name.capitalized()} $repoName Patches", "-m", "Patch generated from ${commitLink(url, commit)}", - "--author=Generated Source " + "--author=Generated " ).runSilently(silenceErr = true) } private fun capitalizedName(name: String, api: Boolean): String { diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/RebuildFilePatches.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/RebuildFilePatches.kt index 8bdf8046c..948e9bccd 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/RebuildFilePatches.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/RebuildFilePatches.kt @@ -108,11 +108,11 @@ abstract class RebuildFilePatches : JavaLauncherTask() { if (fileCommit.size > 1) { val count = fileCommit.size throw PaperweightException( - "Exceeded the max amount of commits with the identifier: `${identifier.get()} File Patches`!\nGot $count commits, expected: 1" + "Exceeded the max amount of commits with the identifier: '${identifier.get()} File Patches' !\nGot $count commits, expected: 1" ) } else if (fileCommit.isEmpty()) { throw PaperweightException( - "Could not find a commit with the identifier: `${identifier.get()} Base Patches`!\nHave you applied patches before rebuilding?" + "Could not find a commit with the identifier: '${identifier.get()} File Patches' !\nHave you applied patches before rebuilding?" ) } diff --git a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RebuildBaseGitPatches.kt b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RebuildBaseGitPatches.kt index a6d9e281c..e040acc13 100644 --- a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RebuildBaseGitPatches.kt +++ b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RebuildBaseGitPatches.kt @@ -80,11 +80,11 @@ abstract class RebuildBaseGitPatches : ControllableOutputTask() { if (patchedBaseCommit.size > 1) { val count = patchedBaseCommit.size throw PaperweightException( - "Exceeded the max amount of commits with the identifier: `${identifier.get()} Base Patches`!\nGot $count commits, expected: 1" + "Exceeded the max amount of commits with the identifier: '${identifier.get()} Base Patches' !\nGot $count commits, expected: 1" ) } else if (patchedBaseCommit.isEmpty()) { throw PaperweightException( - "Could not find a commit with the identifier: `${identifier.get()} Base Patches`!\nHave you applied patches before rebuilding?" + "Could not find a commit with the identifier: '${identifier.get()} Base Patches' !\nHave you applied patches before rebuilding?" ) } diff --git a/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/PaperweightUser.kt b/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/PaperweightUser.kt index b0246a314..116715a4d 100644 --- a/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/PaperweightUser.kt +++ b/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/PaperweightUser.kt @@ -286,7 +286,7 @@ abstract class PaperweightUser : Plugin { if (hasDevBundle.isFailure || !hasDevBundle.getOrThrow()) { val message = "Unable to resolve a dev bundle, which is required for paperweight to function.\n" + "Add a dev bundle to the 'paperweightDevelopmentBundle' configuration (the dependencies.paperweight extension can" + - " help with this), and ensure there is a repository to resolve it from (the Paper and Canvas repository is used by default)." + " help with this), and ensure there is a repository to resolve it from (the Paper and Canvas repositories are used by default)." val ex = PaperweightException(message, hasDevBundle.exceptionOrNull()) throw ex /* TODO migrate this to the Problems API when it comes out of incubating @@ -295,7 +295,7 @@ abstract class PaperweightUser : Plugin { id("paperweight-userdev-cannot-resolve-dev-bundle", message) solution( "Add a dev bundle to the 'paperweightDevelopmentBundle' configuration (the dependencies.paperweight extension can" + - " help with this), and ensure there is a repository to resolve it from (the Paper and Canvas repository is used by default)." + " help with this), and ensure there is a repository to resolve it from (the Paper and Canvas repositories are used by default)." ) withException(ex) } From b57f40d62d77dac01791dddd80fefd5ae180894b Mon Sep 17 00:00:00 2001 From: Toffikk Date: Mon, 29 Sep 2025 17:39:14 +0200 Subject: [PATCH 103/257] chore: switch out the patchedBase tag for basepatches this is just a cosmetic change --- .../paperweight/core/taskcontainers/PatchingTasks.kt | 2 +- .../core/tasks/patching/ApplyBasePatches.kt | 4 ++-- .../core/tasks/patching/ApplyFilePatches.kt | 2 +- .../paperweight/tasks/RebuildBaseGitPatches.kt | 12 ++++++------ 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt index dd37e4148..bd3879d8b 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt @@ -206,7 +206,7 @@ class PatchingTasks( description = "Puts the currently tracked source changes into the $patchSetName file patches commit" repo.set(outputDir) - upstream.set("patchedBase") + upstream.set("basepatches") } val rebuildFeaturePatches = tasks.register(rebuildFeaturePatchesName) { diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyBasePatches.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyBasePatches.kt index 6f9bb2065..e51b0e1bc 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyBasePatches.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyBasePatches.kt @@ -161,8 +161,8 @@ abstract class ApplyBasePatches : ControllableOutputTask() { .setAllowEmpty(true) .setSign(false) .call() - git.tagDelete().setTags("patchedBase").call() - git.tag().setName("patchedBase").setTagger(ident).setSigned(false).call() + git.tagDelete().setTags("basepatches").call() + git.tag().setName("basepatches").setTagger(ident).setSigned(false).call() git.close() } diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyFilePatches.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyFilePatches.kt index 47fdd2aec..8f5be8c1f 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyFilePatches.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyFilePatches.kt @@ -88,7 +88,7 @@ abstract class ApplyFilePatches : BaseTask() { gitFilePatches.convention(false) moveFailedGitPatchesToRejects.convention(false) emitRejects.convention(true) - ref.convention("patchedBase") + ref.convention("basepatches") } } diff --git a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RebuildBaseGitPatches.kt b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RebuildBaseGitPatches.kt index e040acc13..31ce123e3 100644 --- a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RebuildBaseGitPatches.kt +++ b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RebuildBaseGitPatches.kt @@ -71,25 +71,25 @@ abstract class RebuildBaseGitPatches : ControllableOutputTask() { val git = Git(inputDir.path) // fail fast if someone decides to name a patch with this name to avoid patch corruption - val patchedBaseCommit = git( + val basepatchesCommit = git( "log", "--format=%H %s", "--grep=^${identifier.get()} Base Patches$", "base..HEAD" ).getText().lines().filter { it.isNotBlank() }.map { it.substringBefore(" ") } - if (patchedBaseCommit.size > 1) { - val count = patchedBaseCommit.size + if (basepatchesCommit.size > 1) { + val count = basepatchesCommit.size throw PaperweightException( "Exceeded the max amount of commits with the identifier: '${identifier.get()} Base Patches' !\nGot $count commits, expected: 1" ) - } else if (patchedBaseCommit.isEmpty()) { + } else if (basepatchesCommit.isEmpty()) { throw PaperweightException( "Could not find a commit with the identifier: '${identifier.get()} Base Patches' !\nHave you applied patches before rebuilding?" ) } // we update the tag to reflect the new repo state - git("tag", "-f", "patchedBase", patchedBaseCommit.joinToString()).executeSilently(silenceErr = true) + git("tag", "-f", "basepatches", basepatchesCommit.joinToString()).executeSilently(silenceErr = true) val what = inputDir.path.name val patchFolder = patchDir.path @@ -122,7 +122,7 @@ abstract class RebuildBaseGitPatches : ControllableOutputTask() { } val base = "base" - val stop = "patchedBase~1" // ~1 cuz we dont want to rebuild the marker commit + val stop = "basepatches~1" // ~1 cuz we dont want to rebuild the marker commit val commitCount = git("rev-list", "--count", "$base..$stop").getText().trim().toInt() if (commitCount <= 0) return // nothing to rebuild From 4d95b1fb1c07fc7a62197637869bba93f6cdfebe Mon Sep 17 00:00:00 2001 From: Toffikk Date: Mon, 29 Sep 2025 17:40:30 +0200 Subject: [PATCH 104/257] chore: remove no longer relevant comments --- .../core/taskcontainers/MinecraftPatchingTasks.kt | 8 -------- .../paperweight/core/taskcontainers/PatchingTasks.kt | 7 ------- 2 files changed, 15 deletions(-) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt index a8abf4ad2..473a2e244 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt @@ -217,14 +217,6 @@ class MinecraftPatchingTasks( applySourcePatchesFuzzy.configure { base.set(applyJavadocMappings.flatMap { it.outputDir }) } - - /* uncomment this when we enable validation of ATs since then seperate AT files will be forced, right now this breaks on projects with one AT file for many modules - val name = "rebuild${namePart}BasePatches" - if (name in tasks.names) { - tasks.named(name) { - base.set(setup.flatMap { it.outputDir }) - } - */ } private fun setupWritable() { diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt index bd3879d8b..a678b3cd8 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt @@ -159,13 +159,6 @@ class PatchingTasks( applyBasePatches.configure { input.set(setup.flatMap { it.outputDir }) } - /* uncomment this when we enable validation of ATs since then seperate AT files will be forced, right now this breaks on projects with one AT file for many modules - val name = "rebuild${namePart}BasePatches" - if (name in tasks.names) { - tasks.named(name) { - base.set(setup.flatMap { it.outputDir }) - } - */ } private fun setupWritable() { From 0df75ff86c648c518754975218ceac1d3495cc1e Mon Sep 17 00:00:00 2001 From: Toffikk Date: Mon, 29 Sep 2025 17:42:05 +0200 Subject: [PATCH 105/257] chore: rename mapping to mappings --- .../core/taskcontainers/MinecraftPatchingTasks.kt | 2 +- .../paperweight/core/tasks/SetupForkMinecraftSources.kt | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt index 473a2e244..03e4ca18d 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt @@ -207,7 +207,7 @@ class MinecraftPatchingTasks( outputDir.set(layout.cache.resolve(paperTaskOutput())) identifier.set(configName) mappingFile.from(config.javadocMappings) - mapping.jst.from(project.configurations.named(JST_CONFIG)) + mappings.jst.from(project.configurations.named(JST_CONFIG)) } applySourcePatches.configure { diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupForkMinecraftSources.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupForkMinecraftSources.kt index 978462bf5..2b43be2f9 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupForkMinecraftSources.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupForkMinecraftSources.kt @@ -56,7 +56,7 @@ abstract class SetupForkMinecraftSources : JavaLauncherTask() { val ats: ApplySourceATs = objects.newInstance() @get:Nested - val mapping: ApplyJavadocMappings = objects.newInstance() + val mappings: ApplyJavadocMappings = objects.newInstance() @get:InputFile @get:Optional @@ -105,9 +105,9 @@ abstract class SetupForkMinecraftSources : JavaLauncherTask() { } if (!mappingFile.isEmpty) { println("Applying javadoc mappings...") - mapping.run( + mappings.run( launcher.get(), - inputDir.path, + outputDir.path, outputDir.path, mappingFile.singleFile.toPath(), temporaryDir.toPath(), From 1df6b33b71eb96c897831229f6b0af64f44fd09c Mon Sep 17 00:00:00 2001 From: Toffikk Date: Mon, 29 Sep 2025 17:45:01 +0200 Subject: [PATCH 106/257] feat: add canvas and folia upstream extensions --- .../extension/PaperweightPatcherExtension.kt | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/extension/PaperweightPatcherExtension.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/extension/PaperweightPatcherExtension.kt index fca74cffc..483a07f17 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/extension/PaperweightPatcherExtension.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/extension/PaperweightPatcherExtension.kt @@ -55,4 +55,20 @@ abstract class PaperweightPatcherExtension @Inject constructor(private val objec applyUpstreamNested.convention(false) op.execute(this) } + + fun NamedDomainObjectContainer.folia( + op: Action + ): NamedDomainObjectProvider = register("folia") { + repo.convention(github("PaperMC", "Folia")) + applyUpstreamNested.convention(true) + op.execute(this) + } + + fun NamedDomainObjectContainer.canvas( + op: Action + ): NamedDomainObjectProvider = register("canvas") { + repo.convention(github("CraftCanvasMC", "Canvas")) + applyUpstreamNested.convention(true) + op.execute(this) + } } From 40806ef67976950780511886ac720901845e4734 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Mon, 29 Sep 2025 17:55:59 +0200 Subject: [PATCH 107/257] release: Version 2.3.7 --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 81d1a925c..24f69a4ab 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ group = io.canvasmc.weaver -version = 2.3.7-SNAPSHOT +version = 2.3.7 org.gradle.caching = true org.gradle.configuration-cache = true From 82d6f99dec6d15ec6e8d06147a3e3b82f2d43af0 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Mon, 29 Sep 2025 17:58:57 +0200 Subject: [PATCH 108/257] chore: continue development cycle --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 24f69a4ab..47ea9c281 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ group = io.canvasmc.weaver -version = 2.3.7 +version = 2.3.8-SNAPSHOT org.gradle.caching = true org.gradle.configuration-cache = true From 0972e0075bcc9c5d6157d5bcef112aaf7baf39db Mon Sep 17 00:00:00 2001 From: Toffikk Date: Mon, 29 Sep 2025 18:48:36 +0200 Subject: [PATCH 109/257] fix(forks): fix source apply when upstream has javadocs specified --- .../paperweight/core/tasks/SetupForkMinecraftSources.kt | 2 +- .../paperweight/core/tasks/patching/ApplyFilePatches.kt | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupForkMinecraftSources.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupForkMinecraftSources.kt index 2b43be2f9..fbafc03a5 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupForkMinecraftSources.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupForkMinecraftSources.kt @@ -112,7 +112,7 @@ abstract class SetupForkMinecraftSources : JavaLauncherTask() { mappingFile.singleFile.toPath(), temporaryDir.toPath(), ) - commitAndTag(git, "JDs", "${identifier.get()} Javadoc Mappings") + commitAndTag(git, "${identifier.get()}JDs", "${identifier.get()} Javadoc Mappings") } git.close() diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyFilePatches.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyFilePatches.kt index 8f5be8c1f..bed251b97 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyFilePatches.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyFilePatches.kt @@ -100,8 +100,8 @@ abstract class ApplyFilePatches : BaseTask() { if (base != null && base.toAbsolutePath() != repo.path.toAbsolutePath()) { recreateCloneDirectory(repo.path) - val checkoutFromJDs = hasJavadocs(base, "JDs") - val newRef = if (checkoutFromJDs) "JDs" else ref.get() + val checkoutFromJDs = hasJavadocs(base, "${identifier.get()}JDs") + val newRef = if (checkoutFromJDs) "${identifier.get()}JDs" else ref.get() ref.set(newRef) val git = Git(repo.path.createDirectories()) From 29bd82959615cabbe330acfc2681235099eaaa21 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Mon, 29 Sep 2025 22:35:21 +0200 Subject: [PATCH 110/257] fix(apply): only set the base for source apply on readOnly --- .../core/taskcontainers/MinecraftPatchingTasks.kt | 8 ++++++-- .../paperweight/core/tasks/patching/ApplyFilePatches.kt | 4 ++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt index 03e4ca18d..7507dbb4b 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt @@ -211,11 +211,15 @@ class MinecraftPatchingTasks( } applySourcePatches.configure { - base.set(applyJavadocMappings.flatMap { it.outputDir }) + if (readOnly) { + base.set(applyJavadocMappings.flatMap { it.outputDir }) + } } applySourcePatchesFuzzy.configure { - base.set(applyJavadocMappings.flatMap { it.outputDir }) + if (readOnly) { + base.set(applyJavadocMappings.flatMap { it.outputDir }) + } } } diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyFilePatches.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyFilePatches.kt index bed251b97..959369d17 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyFilePatches.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyFilePatches.kt @@ -112,6 +112,10 @@ abstract class ApplyFilePatches : BaseTask() { branchName = "main", ref = true, ) + } else { + val checkoutFromJDs = hasJavadocs(repo.path, "${identifier.get()}JDs") + val newRef = if (checkoutFromJDs) "${identifier.get()}JDs" else ref.get() + ref.set(newRef) } val repoPath = repo.path From b00d6a09fe71b857a30d8a3ef33ce78e363aaab0 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Mon, 29 Sep 2025 23:14:42 +0200 Subject: [PATCH 111/257] chore: fixup commit name --- .../papermc/paperweight/core/tasks/patching/ApplyBasePatches.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyBasePatches.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyBasePatches.kt index e51b0e1bc..00ad3bfac 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyBasePatches.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyBasePatches.kt @@ -152,7 +152,7 @@ abstract class ApplyBasePatches : ControllableOutputTask() { } private fun commit() { - val ident = PersonIdent(PersonIdent("Patched Base", "noreply+automated@papermc.io"), Instant.parse("1997-04-20T13:37:42.69Z")) + val ident = PersonIdent(PersonIdent("Base Patches", "noreply+automated@papermc.io"), Instant.parse("1997-04-20T13:37:42.69Z")) val git = Git.open(output.path.toFile()) git.add().addFilepattern(".").call() git.commit() From 670e3238ca99bb2fec7a57cde9b2e2e422202b1a Mon Sep 17 00:00:00 2001 From: Toffikk Date: Mon, 29 Sep 2025 23:23:16 +0200 Subject: [PATCH 112/257] release: Release 2.3.8 --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 47ea9c281..307c2b814 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ group = io.canvasmc.weaver -version = 2.3.8-SNAPSHOT +version = 2.3.8 org.gradle.caching = true org.gradle.configuration-cache = true From 18a9cdd5daf18cc2a4f521039ae9a9a73d66ba74 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Mon, 29 Sep 2025 23:24:21 +0200 Subject: [PATCH 113/257] chore: continue development cycle --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 307c2b814..07b03ca0f 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ group = io.canvasmc.weaver -version = 2.3.8 +version = 2.3.9-SNAPSHOT org.gradle.caching = true org.gradle.configuration-cache = true From 6df6302e19ecee29a99790159a5dbe36c214de89 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Mon, 29 Sep 2025 23:32:53 +0200 Subject: [PATCH 114/257] fix: actually depend on the jd mappings --- .../paperweight/core/taskcontainers/MinecraftPatchingTasks.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt index 7507dbb4b..7df49b287 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt @@ -211,12 +211,14 @@ class MinecraftPatchingTasks( } applySourcePatches.configure { + dependsOn(applyJavadocMappings) if (readOnly) { base.set(applyJavadocMappings.flatMap { it.outputDir }) } } applySourcePatchesFuzzy.configure { + dependsOn(applyJavadocMappings) if (readOnly) { base.set(applyJavadocMappings.flatMap { it.outputDir }) } From 97257cf508d628d4cd54d8b51e86a322f45e6886 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Tue, 30 Sep 2025 00:04:23 +0200 Subject: [PATCH 115/257] fix: revert some changes --- .../core/taskcontainers/MinecraftPatchingTasks.kt | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt index 7df49b287..03e4ca18d 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt @@ -211,17 +211,11 @@ class MinecraftPatchingTasks( } applySourcePatches.configure { - dependsOn(applyJavadocMappings) - if (readOnly) { - base.set(applyJavadocMappings.flatMap { it.outputDir }) - } + base.set(applyJavadocMappings.flatMap { it.outputDir }) } applySourcePatchesFuzzy.configure { - dependsOn(applyJavadocMappings) - if (readOnly) { - base.set(applyJavadocMappings.flatMap { it.outputDir }) - } + base.set(applyJavadocMappings.flatMap { it.outputDir }) } } From 6640759f5981d03a1784a5f349f23b1578bae5ba Mon Sep 17 00:00:00 2001 From: Toffikk Date: Tue, 30 Sep 2025 15:21:24 +0200 Subject: [PATCH 116/257] fix(deps): adapt shadow configuration for shadow 9.x and bump shadow to 9.2.2 since shadow 9.x, the INCLUDE duplication strategy has to be specified in order to properly merge service files --- buildSrc/src/main/kotlin/config-publish.gradle.kts | 3 ++- gradle/libs.versions.toml | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/buildSrc/src/main/kotlin/config-publish.gradle.kts b/buildSrc/src/main/kotlin/config-publish.gradle.kts index 432c6ac8c..31b7bab91 100644 --- a/buildSrc/src/main/kotlin/config-publish.gradle.kts +++ b/buildSrc/src/main/kotlin/config-publish.gradle.kts @@ -19,6 +19,7 @@ configurations.shadowRuntimeElements { fun ShadowJar.configureStandard() { configurations = listOf(shade) + duplicatesStrategy = DuplicatesStrategy.INCLUDE dependencies { exclude(dependency("org.jetbrains.kotlin:.*:.*")) @@ -26,7 +27,7 @@ fun ShadowJar.configureStandard() { } exclude("META-INF/*.SF", "META-INF/*.DSA", "META-INF/*.RSA", "OSGI-INF/**", "*.profile", "module-info.class", "ant_tasks/**", "OSGI-OPT/**", "META-INF/*.pro") - + mergeServiceFiles() } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 028663ed0..c8d9e6ae2 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -47,7 +47,7 @@ mockk = "io.mockk:mockk:1.14.5" # Gradle gradle-licenser = "net.kyori:indra-licenser-spotless:3.2.0" gradle-spotless = "com.diffplug.spotless:spotless-plugin-gradle:7.2.1" -gradle-shadow = "com.gradleup.shadow:com.gradleup.shadow.gradle.plugin:8.3.8" +gradle-shadow = "com.gradleup.shadow:com.gradleup.shadow.gradle.plugin:9.2.2" gradle-kotlin-dsl = "org.gradle.kotlin.kotlin-dsl:org.gradle.kotlin.kotlin-dsl.gradle.plugin:6.2.0" gradle-plugin-kotlin = { module = "org.jetbrains.kotlin.jvm:org.jetbrains.kotlin.jvm.gradle.plugin" } gradle-plugin-publish = "com.gradle.publish:plugin-publish-plugin:2.0.0" From 5bfc79304cc499622013f95fe54c3bcfa95627fd Mon Sep 17 00:00:00 2001 From: "tofik.07" <62406295+Toffikk@users.noreply.github.com> Date: Tue, 30 Sep 2025 15:23:10 +0200 Subject: [PATCH 117/257] chore: remove unintentional indent --- buildSrc/src/main/kotlin/config-publish.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/buildSrc/src/main/kotlin/config-publish.gradle.kts b/buildSrc/src/main/kotlin/config-publish.gradle.kts index 31b7bab91..ca1dfc762 100644 --- a/buildSrc/src/main/kotlin/config-publish.gradle.kts +++ b/buildSrc/src/main/kotlin/config-publish.gradle.kts @@ -27,7 +27,7 @@ fun ShadowJar.configureStandard() { } exclude("META-INF/*.SF", "META-INF/*.DSA", "META-INF/*.RSA", "OSGI-INF/**", "*.profile", "module-info.class", "ant_tasks/**", "OSGI-OPT/**", "META-INF/*.pro") - + mergeServiceFiles() } From 7c4f62f2cf6bc4349a25449609840e8d1e3108b6 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Tue, 30 Sep 2025 15:31:26 +0200 Subject: [PATCH 118/257] release: Version 2.3.9 --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 07b03ca0f..ccf7b3b0f 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ group = io.canvasmc.weaver -version = 2.3.9-SNAPSHOT +version = 2.3.9 org.gradle.caching = true org.gradle.configuration-cache = true From 84bb7c4e125e9d89dc3c68d3c382872ea9e13c6d Mon Sep 17 00:00:00 2001 From: Toffikk Date: Tue, 30 Sep 2025 18:16:44 +0200 Subject: [PATCH 119/257] chore: continue development cycle --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index ccf7b3b0f..5ba678855 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ group = io.canvasmc.weaver -version = 2.3.9 +version = 2.3.10-SNAPSHOT org.gradle.caching = true org.gradle.configuration-cache = true From 3c8c22bdd1e35e9ed14fc7052543c1800dc9d124 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Tue, 30 Sep 2025 18:17:00 +0200 Subject: [PATCH 120/257] fix: use proper duplicates strategy for shadow --- buildSrc/src/main/kotlin/config-publish.gradle.kts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/buildSrc/src/main/kotlin/config-publish.gradle.kts b/buildSrc/src/main/kotlin/config-publish.gradle.kts index ecbe5e978..85a23371c 100644 --- a/buildSrc/src/main/kotlin/config-publish.gradle.kts +++ b/buildSrc/src/main/kotlin/config-publish.gradle.kts @@ -27,7 +27,9 @@ configurations.shadowRuntimeElements { fun ShadowJar.configureStandard() { configurations = listOf(shade) - duplicatesStrategy = DuplicatesStrategy.INCLUDE + filesMatching("META-INF/services/**") { + duplicatesStrategy = DuplicatesStrategy.INCLUDE + } dependencies { exclude(dependency("org.jetbrains.kotlin:.*:.*")) From 16c1865442483301ba951750e963950a790c35ef Mon Sep 17 00:00:00 2001 From: "tofik.07" <62406295+Toffikk@users.noreply.github.com> Date: Tue, 30 Sep 2025 18:33:50 +0200 Subject: [PATCH 121/257] chore: use providers in the publish config --- buildSrc/src/main/kotlin/config-publish.gradle.kts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/buildSrc/src/main/kotlin/config-publish.gradle.kts b/buildSrc/src/main/kotlin/config-publish.gradle.kts index 85a23371c..8313b3b5c 100644 --- a/buildSrc/src/main/kotlin/config-publish.gradle.kts +++ b/buildSrc/src/main/kotlin/config-publish.gradle.kts @@ -101,15 +101,15 @@ publishing { maven("https://maven.canvasmc.io/snapshots") { name = "CanvasMC_Snapshots" credentials { - username=System.getenv("PUBLISH_USER") - password=System.getenv("PUBLISH_TOKEN") + username = providers.environmentVariable("PUBLISH_USER").orNull + password = providers.environmentVariable("PUBLISH_TOKEN").orNull } } maven("https://maven.canvasmc.io/releases") { name = "CanvasMC_Releases" credentials { - username=System.getenv("PUBLISH_USER") - password=System.getenv("PUBLISH_TOKEN") + username = providers.environmentVariable("PUBLISH_USER").orNull + password = providers.environmentVariable("PUBLISH_TOKEN").orNull } } } From bc700a57d48441911ad51e13bcc90490f464a9f1 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Sat, 4 Oct 2025 22:08:29 +0200 Subject: [PATCH 122/257] cleanup: remove unnecessary overload --- .../io/papermc/paperweight/core/extension/ForkConfig.kt | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/ForkConfig.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/ForkConfig.kt index 573923d88..baff15294 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/ForkConfig.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/ForkConfig.kt @@ -77,13 +77,7 @@ abstract class ForkConfig @Inject constructor( } } - fun javadocMappings(configuration: Configuration) { - javadocMappings.configure { - extendsFrom(configuration) - } - } - - fun javadocMappings(configuration: Provider) { + fun javadocMappings(configuration: Provider) { javadocMappings.configure { extendsFrom(configuration.get()) } From bf36e34197c0e14f512cef62a3b3f31017a57865 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Sun, 5 Oct 2025 17:54:10 +0200 Subject: [PATCH 123/257] opt: reduce I/O ops during per-file patches rebuilding --- .../paperweight/core/tasks/GeneratePatches.kt | 4 ++-- .../core/tasks/PrepareForPatchGeneration.kt | 5 +++-- .../core/tasks/patching/RebuildFilePatches.kt | 13 ++++--------- 3 files changed, 9 insertions(+), 13 deletions(-) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/GeneratePatches.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/GeneratePatches.kt index 7ec194682..eafe900fc 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/GeneratePatches.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/GeneratePatches.kt @@ -68,7 +68,7 @@ abstract class GeneratePatches : BaseTask() { abstract val tempOutput: DirectoryProperty override fun init() { - tempOutput.set(layout.buildDirectory.dir(paperTaskOutput(name))) + tempOutput.set(layout.cache.resolve(paperTaskOutput())) } @TaskAction @@ -80,7 +80,7 @@ abstract class GeneratePatches : BaseTask() { val inputDirs = preparedSource.files.map { it.toPath() } val outputToPatchesDirectory = patchesDirOutput.getOrElse(true) - val outputDir = outputDir.getOrElse(layout.buildDirectory.dir(paperTaskOutput(name)).get()) + val outputDir = outputDir.getOrElse(layout.buildDirectory.dir(paperTaskOutput()).get()) val cacheOutput = tempOutput.get().asFile.toPath() val tempOutput = cacheOutput.cleanFile() val outputDirPath = outputDir.asFile.toPath() diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/PrepareForPatchGeneration.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/PrepareForPatchGeneration.kt index 1761f71d1..d0c07aef0 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/PrepareForPatchGeneration.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/PrepareForPatchGeneration.kt @@ -25,6 +25,7 @@ package io.papermc.paperweight.core.tasks import io.papermc.paperweight.core.util.ApplySourceATs import io.papermc.paperweight.tasks.JavaLauncherTask import io.papermc.paperweight.util.* +import io.papermc.paperweight.util.cache import io.papermc.paperweight.util.constants.paperTaskOutput import kotlin.io.path.* import org.gradle.api.file.DirectoryProperty @@ -63,7 +64,7 @@ abstract class PrepareForPatchGeneration : JavaLauncherTask() { override fun init() { super.init() - outputDir.set(layout.buildDirectory.dir(paperTaskOutput("patchgen-work"))) + outputDir.set(layout.cache.resolve(paperTaskOutput())) } @TaskAction @@ -77,7 +78,7 @@ abstract class PrepareForPatchGeneration : JavaLauncherTask() { workDirectory.resolve("$outputName/${repoName.get()}") } val outputDir = outputDir.get().asFile.toPath().resolve(repoName.get()) - val outputDirPath = outputDir.cleanFile() + val outputDirPath = outputDir.cleanDir() sourceDir.copyRecursivelyTo(outputDirPath) if (additionalPatch.isPresent) { diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/RebuildFilePatches.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/RebuildFilePatches.kt index 948e9bccd..b06ffeb48 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/RebuildFilePatches.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/RebuildFilePatches.kt @@ -30,6 +30,7 @@ import io.papermc.paperweight.PaperweightException import io.papermc.paperweight.core.util.ApplySourceATs import io.papermc.paperweight.tasks.* import io.papermc.paperweight.util.* +import io.papermc.paperweight.util.constants.paperTaskOutput import java.nio.file.Path import kotlin.io.path.* import org.cadixdev.at.AccessTransformSet @@ -123,17 +124,10 @@ abstract class RebuildFilePatches : JavaLauncherTask() { git("stash", "push").executeSilently(silenceErr = true) git("checkout", "file").executeSilently(silenceErr = true) - // here we zip the parent commit's repo state and store it as our baseDir for later comparison overriding the baseDir prop val baseCommit = git("rev-parse", "HEAD~1").getText().trim() - val baseDir = temporaryDir.toPath().resolve("base") - val zippedTree = temporaryDir.toPath().resolve("base.zip") + val baseDir = layout.cache.resolve(paperTaskOutput()) - if (baseDir.exists()) baseDir.deleteRecursive() - if (zippedTree.exists()) zippedTree.deleteForcefully() - - git("archive", "--format=zip", baseCommit, "-o", zippedTree.absolutePathString()).executeSilently(silenceErr = true) - - unzip(zippedTree, baseDir) + git("worktree", "add", baseDir.absolutePathString(), baseCommit).executeSilently(silenceErr = true) val filesWithNewAts = if (!ats.jst.isEmpty) { handleAts( @@ -161,6 +155,7 @@ abstract class RebuildFilePatches : JavaLauncherTask() { rebuildWithDiffPatch(baseDir, inputDir, patchDir) } + git("worktree", "remove", baseDir.absolutePathString(), "--force").executeSilently(silenceErr = true) git("switch", "-").executeSilently(silenceErr = true) if (filesWithNewAts.isNotEmpty()) { try { From bbd967aabbc79fd0d02730b0af038791377e8a2b Mon Sep 17 00:00:00 2001 From: Toffikk Date: Sat, 11 Oct 2025 17:55:52 +0200 Subject: [PATCH 124/257] feat: Improve source generation from additional upstreams --- .../paperweight/core/tasks/GenerateSources.kt | 67 +++++++++++++------ .../paperweight/patcher/PaperweightPatcher.kt | 2 +- 2 files changed, 47 insertions(+), 22 deletions(-) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/GenerateSources.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/GenerateSources.kt index fecd5a046..6a0d86c5f 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/GenerateSources.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/GenerateSources.kt @@ -93,7 +93,7 @@ abstract class GenerateSources : JavaLauncherTask() { override fun init() { super.init() - tempOutput.set(layout.buildDirectory.dir(paperTaskOutput(name))) + tempOutput.set(layout.cache.resolve(paperTaskOutput())) } @TaskAction @@ -111,11 +111,13 @@ abstract class GenerateSources : JavaLauncherTask() { val outputName = inputFrom.get().substringBefore("-") val workDirectory = workDir.get().asFile.toPath() - val sourceDir = workDirectory.resolve("$outputName/${inputFrom.get()}") + val sourceDir = workDirectory.resolve("$outputName/${inputFrom.get()}/src") if (!sourceDir.exists()) return + val srcDirs = sourceDir.listDirectoryEntries().filter { it.isDirectory() } + if (srcDirs.size == 1 && srcDirs.first().name == "minecraft") return sourceDir.copyRecursivelyTo(tempOutput) - val atDirPath = tempOutput.resolve("src/main/java") + val atDirPath = tempOutput.resolve("main/java") val patchDir = tempOutput if (additionalPatch.isPresent) { @@ -141,34 +143,57 @@ abstract class GenerateSources : JavaLauncherTask() { ) for (output in outputs) { - output.deleteRecursive() - when (output) { - generatedOutput -> { - tempOutput.resolve("src/main/java").copyRecursivelyTo(output) - } - - generatedResourcesOutput -> { - tempOutput.resolve("src/main/resources").copyRecursivelyTo(output) - } - - generatedTestOutput -> { - tempOutput.resolve("src/test/java").copyRecursivelyTo(output) - } + val sourceDir = when (output) { + generatedOutput -> tempOutput.resolve("main/java") + generatedResourcesOutput -> tempOutput.resolve("main/resources") + generatedTestOutput -> tempOutput.resolve("test/java") + generatedTestResourcesOutput -> tempOutput.resolve("test/resources") + else -> continue + } + val packageRegex = Regex("""^\s*package\s+([\w.]+)\s*;""", RegexOption.MULTILINE) + val packagesToRemove = mutableSetOf() + val packages = mutableSetOf() - generatedTestResourcesOutput -> { - tempOutput.resolve("src/test/resources").copyRecursivelyTo(output) - } + sourceDir.filesMatchingRecursive("*.java").forEach { + val content = it.readText() + val pkg = packageRegex.find(content)?.groups?.get(1)?.value + if (pkg != null) packages += pkg } - output.filesMatchingRecursive("*.java").forEach { + output.filesMatchingRecursive("package-info.java").forEach { val content = it.readText() - it.writeText("// Generated from ${commitLink(url, commit)}\n$content") + val pkg = packageRegex.find(content)?.groups?.get(1)?.value + if (pkg != null) { + if (content.contains(" * @apiNote Generated from ${forkName.get()}")) { + packagesToRemove += pkg + } + } + } + packagesToRemove.forEach { pkg -> + val dir = output.resolve(pkg.replace('.', '/')) + dir.cleanDir() + } + packages.forEach { pkg -> + val srcDir = sourceDir.resolve(pkg.replace('.', '/')) + val dir = output.resolve(pkg.replace('.', '/')) + if (srcDir.exists() && srcDir.listDirectoryEntries().isNotEmpty()) { + srcDir.copyRecursivelyTo(dir) + val packageInfo = dir.resolve("package-info.java") + val javadoc = buildString { + appendLine("/**") + appendLine(" * @apiNote Generated from ${forkName.get()}") + appendLine(" */") + appendLine("package $pkg;") + } + packageInfo.writeText(javadoc) + } } } } private fun commitLink(url: String, hash: String): String { val cleanUrl = url.removeSuffix(".git") + if (cleanUrl.contains("github.com")) return "$cleanUrl/tree/$hash" return "$cleanUrl/commit/$hash" } } diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/PaperweightPatcher.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/PaperweightPatcher.kt index 243a56ea7..4b11ea389 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/PaperweightPatcher.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/PaperweightPatcher.kt @@ -164,7 +164,7 @@ abstract class PaperweightPatcher : Plugin { tasks.register("generate${name}Sources") { group = "source generation" description = "Generates sources from ${input.name}" - dependsOn(applyAdditionalUpstream) + dependsOn(checkoutTask) forkName.set(upstream.name) forkUrl.set(upstream.repo) inputFrom.set(input.name) From ad274909350c1d8b84d496822cbbfb600c58c540 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Tue, 14 Oct 2025 18:37:36 +0200 Subject: [PATCH 125/257] misc changes related to generation --- .../io/papermc/paperweight/core/tasks/GeneratePatches.kt | 4 ++-- .../io/papermc/paperweight/core/tasks/GenerateSources.kt | 5 +++-- .../paperweight/core/tasks/PrepareForPatchGeneration.kt | 7 ++++--- .../io/papermc/paperweight/patcher/PaperweightPatcher.kt | 2 +- 4 files changed, 10 insertions(+), 8 deletions(-) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/GeneratePatches.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/GeneratePatches.kt index eafe900fc..aa7fdd3b6 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/GeneratePatches.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/GeneratePatches.kt @@ -33,6 +33,7 @@ import org.gradle.api.provider.Property import org.gradle.api.tasks.* import org.gradle.kotlin.dsl.support.uppercaseFirstChar +@UntrackedTask(because = "GeneratePatches should always run when requested.") abstract class GeneratePatches : BaseTask() { @get:Input abstract val forkName: Property @@ -61,7 +62,6 @@ abstract class GeneratePatches : BaseTask() { abstract val apiProjectDir: DirectoryProperty @get:OutputDirectory - @get:Optional abstract val outputDir: DirectoryProperty @get:Internal @@ -80,7 +80,7 @@ abstract class GeneratePatches : BaseTask() { val inputDirs = preparedSource.files.map { it.toPath() } val outputToPatchesDirectory = patchesDirOutput.getOrElse(true) - val outputDir = outputDir.getOrElse(layout.buildDirectory.dir(paperTaskOutput()).get()) + val outputDir = outputDir.get() val cacheOutput = tempOutput.get().asFile.toPath() val tempOutput = cacheOutput.cleanFile() val outputDirPath = outputDir.asFile.toPath() diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/GenerateSources.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/GenerateSources.kt index 6a0d86c5f..0c77e83e1 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/GenerateSources.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/GenerateSources.kt @@ -34,6 +34,7 @@ import org.gradle.api.provider.Property import org.gradle.api.tasks.* import org.gradle.kotlin.dsl.newInstance +@UntrackedTask(because = "GenerateSources should always run when requested.") abstract class GenerateSources : JavaLauncherTask() { @get:Input abstract val forkName: Property @@ -48,7 +49,7 @@ abstract class GenerateSources : JavaLauncherTask() { abstract val inputFrom: Property @get:InputDirectory - @get:PathSensitive(PathSensitivity.RELATIVE) + @get:PathSensitive(PathSensitivity.NONE) abstract val workDir: DirectoryProperty @get:Internal @@ -59,7 +60,7 @@ abstract class GenerateSources : JavaLauncherTask() { @get:InputFile @get:Optional - @get:PathSensitive(PathSensitivity.NONE) + @get:PathSensitive(PathSensitivity.RELATIVE) abstract val atFile: RegularFileProperty @get:InputFile diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/PrepareForPatchGeneration.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/PrepareForPatchGeneration.kt index d0c07aef0..debb0c67f 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/PrepareForPatchGeneration.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/PrepareForPatchGeneration.kt @@ -34,7 +34,7 @@ import org.gradle.api.provider.Property import org.gradle.api.tasks.* import org.gradle.kotlin.dsl.newInstance -@UntrackedTask(because = "PrepareForPatchGeneration should always run when requested") +@UntrackedTask(because = "PrepareForPatchGeneration doesn't have stable inputs.") abstract class PrepareForPatchGeneration : JavaLauncherTask() { @get:Input @@ -44,6 +44,7 @@ abstract class PrepareForPatchGeneration : JavaLauncherTask() { abstract val repoName: Property @get:InputDirectory + @get:PathSensitive(PathSensitivity.NONE) abstract val workDir: DirectoryProperty @get:Nested @@ -51,12 +52,12 @@ abstract class PrepareForPatchGeneration : JavaLauncherTask() { @get:InputFile @get:Optional - @get:PathSensitive(PathSensitivity.NONE) + @get:PathSensitive(PathSensitivity.RELATIVE) abstract val atFile: RegularFileProperty @get:InputFile @get:Optional - @get:PathSensitive(PathSensitivity.NONE) + @get:PathSensitive(PathSensitivity.RELATIVE) abstract val additionalPatch: RegularFileProperty @get:OutputDirectory diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/PaperweightPatcher.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/PaperweightPatcher.kt index 4b11ea389..b60c34a2e 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/PaperweightPatcher.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/PaperweightPatcher.kt @@ -206,7 +206,7 @@ abstract class PaperweightPatcher : Plugin { patchesDirOutput.set(upstream.patchGenerationConfig.patchesDirOutput) serverProjectDir.set(serverProject) apiProjectDir.set(apiProject) - outputDir.set(upstream.patchGenerationConfig.outputDir) + outputDir.set(upstream.patchGenerationConfig.outputDir.orElse(project.layout.cacheDir(paperTaskOutput()))) } val genSources = tasks.register("generate${upstream.name.capitalized()}Sources") { From 46332b2973e029218ea98727889e18dc965dbd27 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Sun, 19 Oct 2025 20:58:28 +0200 Subject: [PATCH 126/257] fix: bring the publisher jar task name up to par with other bundling tasks Might break CI builds depending on the previous task name --- .../paperweight/core/PaperweightCore.kt | 2 +- .../core/taskcontainers/PaperclipTasks.kt | 40 +++++++++++-------- .../paperweight/core/util/project-util.kt | 2 +- .../paperweight/tasks/CreatePublisherJar.kt | 8 ---- 4 files changed, 26 insertions(+), 26 deletions(-) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/PaperweightCore.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/PaperweightCore.kt index ba5e89680..ec9972c97 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/PaperweightCore.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/PaperweightCore.kt @@ -161,7 +161,7 @@ abstract class PaperweightCore : Plugin { mappedJar, reobfJar, ext.minecraftVersion, - target.rootProject.name.lowercase() + target.provider { target.rootProject.name.lowercase() } ) target.afterEvaluate { diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PaperclipTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PaperclipTasks.kt index 4cdd2a643..d8fe08586 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PaperclipTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PaperclipTasks.kt @@ -49,14 +49,12 @@ class PaperclipTasks( mojangJar: Provider, reobfJar: Provider, private val mcVersion: Provider, - private val rootName: String, + private val rootName: Provider ) { init { - val buildNum: Provider = project.providers.environmentVariable("BUILD_NUMBER").orElse("local") - val publisherJarName: Provider = buildNum.map { build -> "libs/$rootName-build.$build.jar" } - val (createBundlerJar, createPaperclipJar) = project.createTasks("mojmap", publisherJarName) - val (createReobfBundlerJar, createReobfPaperclipJar) = project.createTasks("reobf", publisherJarName) + val (createBundlerJar, createPaperclipJar, createPublisherJar) = project.createTasks("mojmap") + val (createReobfBundlerJar, createReobfPaperclipJar, createReobfPublisherJar) = project.createTasks("reobf") createBundlerJar.serverJar(mojangJar) createReobfBundlerJar.serverJar(reobfJar) { @@ -67,15 +65,18 @@ class PaperclipTasks( createReobfPaperclipJar.bundlerJar(createReobfBundlerJar) { reobfRequiresDebug() } + + createReobfPublisherJar { + reobfRequiresDebug() + } } private fun Project.createTasks( classifier: String = "", - publisherJarName: Provider, ): Triple, TaskProvider, TaskProvider> { val bundlerTaskName = "create${classifier.capitalized()}BundlerJar" val paperclipTaskName = "create${classifier.capitalized()}PaperclipJar" - val publisherTaskName = "build${classifier.capitalized()}PublisherJar" + val publisherTaskName = "create${classifier.capitalized()}PublisherJar" val bundlerJarTask = tasks.register(bundlerTaskName) { group = "bundling" @@ -96,22 +97,29 @@ class PaperclipTasks( } val publisherJarTask = tasks.register(publisherTaskName) { group = "bundling" - description = "Build a ready-to-publish jar" + description = "Build a ready-to-publish paperclip jar" inputZip.set(paperclipJarTask.flatMap { it.outputZip }) - outputZip.convention(publisherJarName.map { layout.buildDirectory.file(it) }.flatMap { it }) + outputZip.convention(layout.buildDirectory.file(jarName("publisher", classifier).map { "libs/$it" })) } return Triple(bundlerJarTask, paperclipJarTask, publisherJarTask) } private fun Project.jarName(kind: String, classifier: String): Provider { - return bundlerJarName.map { - listOfNotNull( - it, - kind, - project.version, - classifier.takeIf { c -> c.isNotBlank() }, - ).joinToString("-") + ".jar" + return if (kind != "publisher") { + bundlerJarName.map { + listOfNotNull( + it, + kind, + project.version, + classifier.takeIf { c -> c.isNotBlank() }, + ).joinToString("-") + ".jar" + } + } else { + val buildNum = project.providers.environmentVariable("BUILD_NUMBER").orElse("local") + rootName.zip(buildNum) { name, build -> + "$name-build.$build.jar" + } } } diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/util/project-util.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/util/project-util.kt index 98e552295..a3c668ac9 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/util/project-util.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/util/project-util.kt @@ -104,7 +104,7 @@ fun Task.reobfRequiresDebug() { if (!paperweightDebug()) { throw PaperweightException( "Reobfuscated server jars are no longer supported and only exist for debugging purposes.\n" + - "If you are attempting to build a paperclip or bundler jar, use the 'mojmap' variant instead of 'reobf'.\n" + + "If you are attempting to build a publisher, paperclip or bundler jar, use the 'mojmap' variant instead of 'reobf'.\n" + "Enable paperweight debug mode to bypass this error.\n" ) } diff --git a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/CreatePublisherJar.kt b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/CreatePublisherJar.kt index a256eef46..30bff1c22 100644 --- a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/CreatePublisherJar.kt +++ b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/CreatePublisherJar.kt @@ -22,17 +22,9 @@ package io.papermc.paperweight.tasks -import io.papermc.paperweight.util.* import java.nio.file.Path -import kotlin.io.path.* -import org.gradle.api.tasks.* -import org.gradle.kotlin.dsl.* // dummy task abstract class CreatePublisherJar : ZippedTask() { - override fun init() { - super.init() - } - override fun run(rootDir: Path) {} } From d5532377f67b9de042cda97748a57f643bc7478b Mon Sep 17 00:00:00 2001 From: Toffikk Date: Sun, 19 Oct 2025 22:38:39 +0200 Subject: [PATCH 127/257] cleanup: rearrange diff --- .../core/taskcontainers/CoreTasks.kt | 4 +- .../taskcontainers/MinecraftPatchingTasks.kt | 37 ++++++++-------- .../core/taskcontainers/PaperclipTasks.kt | 1 - .../core/taskcontainers/PatchingTasks.kt | 42 ++++++++++--------- .../core/tasks/patching/RebuildFilePatches.kt | 1 - .../io/papermc/paperweight/FunctionalTest.kt | 1 + .../tasks/patching/ApplyFilePatchesTest.kt | 28 ++++++------- .../paperweight/tasks/ApplyAccessTransform.kt | 5 +-- .../tasks/CollectATsFromPatches.kt | 11 ++++- .../paperweight/tasks/GenerateDevBundle.kt | 2 +- .../tasks/RebuildBaseGitPatches.kt | 10 ++++- .../paperweight/tasks/RebuildGitPatches.kt | 12 ++---- .../tasks/RelocateClassNameConstants.kt | 6 +-- .../paperweight/util/constants/constants.kt | 2 - 14 files changed, 87 insertions(+), 75 deletions(-) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/CoreTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/CoreTasks.kt index 3346b8118..6c8a15a7f 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/CoreTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/CoreTasks.kt @@ -74,8 +74,8 @@ class CoreTasks( } val collectPaperATsFromPatches by tasks.registering(CollectATsFromPatches::class) { - patchDir.set(project.coreExt.paper.basePatchDir.fileExists()) - extraPatchDir.set(project.coreExt.paper.featurePatchDir.fileExists()) + basePatchDir.set(project.coreExt.paper.basePatchDir.fileExists()) + featurePatchDir.set(project.coreExt.paper.featurePatchDir.fileExists()) } val mergePaperATs by tasks.registering { diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt index 03e4ca18d..36165e369 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt @@ -74,20 +74,6 @@ class MinecraftPatchingTasks( private val namePart = if (readOnly) "${configName.capitalized()}Minecraft" else if (paper) "" else "Minecraft" - private fun ApplyBasePatches.configureApplyBasePatches() { - group() - description = "Applies $configName base patches to the Minecraft sources" - - input.set(baseSources) - if (readOnly) { - output.set(outputSrcFile) - } else { - output.set(outputSrc) - } - patches.set(basePatchDir.fileExists()) - identifier = configName - } - private fun ApplyFilePatches.configureApplyFilePatches() { group() description = "Applies $configName file patches to the Minecraft sources" @@ -105,6 +91,20 @@ class MinecraftPatchingTasks( identifier = configName } + private fun ApplyBasePatches.configureApplyBasePatches() { + group() + description = "Applies $configName base patches to the Minecraft sources" + + input.set(baseSources) + if (readOnly) { + output.set(outputSrcFile) + } else { + output.set(outputSrc) + } + patches.set(basePatchDir.fileExists()) + identifier = configName + } + val applyBasePatches = tasks.register("apply${namePart}BasePatches") { configureApplyBasePatches() } @@ -168,8 +168,8 @@ class MinecraftPatchingTasks( fun setupFork(config: ForkConfig) { val collectAccessTransform = tasks.register("collect${configName.capitalized()}MinecraftATsFromPatches") { - patchDir.set(basePatchDir.fileExists()) - extraPatchDir.set(featurePatchDir.fileExists()) + basePatchDir.set(basePatchDir.fileExists()) + featurePatchDir.set(featurePatchDir.fileExists()) } val mergeCollectedAts = tasks.register("merge${configName.capitalized()}MinecraftATs") { @@ -184,7 +184,7 @@ class MinecraftPatchingTasks( libraries.from(coreTasks.indexLibraryFiles.map { it.libraries }) } - val setup = tasks.register("run${configName.capitalized()}MinecraftSetup") { + val setup = tasks.register("run${configName.capitalized()}Setup") { description = "Applies $configName ATs and library imports to Minecraft sources" inputDir.set(baseSources) @@ -239,6 +239,8 @@ class MinecraftPatchingTasks( base.set(baseSources) // workaround to achieve the correct task execution order inputDir.set(outputSrc) patchDir.set(basePatchDir) + baseRef.set("base") + stopRef.set("basepatches~1") // ~1 cuz we dont want to rebuild the marker commit filterPatches.set(this@MinecraftPatchingTasks.filterPatches) identifier = configName } @@ -283,6 +285,7 @@ class MinecraftPatchingTasks( inputDir.set(outputSrc) patchDir.set(featurePatchDir) + baseRef.set("file") filterPatches.set(this@MinecraftPatchingTasks.filterPatches) } diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PaperclipTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PaperclipTasks.kt index d8fe08586..4ebc46aba 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PaperclipTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PaperclipTasks.kt @@ -51,7 +51,6 @@ class PaperclipTasks( private val mcVersion: Provider, private val rootName: Provider ) { - init { val (createBundlerJar, createPaperclipJar, createPublisherJar) = project.createTasks("mojmap") val (createReobfBundlerJar, createReobfPaperclipJar, createReobfPublisherJar) = project.createTasks("reobf") diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt index a678b3cd8..b6d35a802 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt @@ -31,7 +31,8 @@ import io.papermc.paperweight.core.tasks.patching.FixupFilePatches import io.papermc.paperweight.core.tasks.patching.RebuildFilePatches import io.papermc.paperweight.tasks.* import io.papermc.paperweight.util.* -import io.papermc.paperweight.util.constants.* +import io.papermc.paperweight.util.constants.JST_CONFIG +import io.papermc.paperweight.util.constants.paperTaskOutput import java.nio.file.Path import org.gradle.api.Project import org.gradle.api.Task @@ -61,22 +62,6 @@ class PatchingTasks( ) { private val namePart: String = if (readOnly) "${forkName.capitalized()}${patchSetName.capitalized()}" else patchSetName.capitalized() - private fun ApplyBasePatches.configureApplyBasePatches() { - group = taskGroup - description = "Applies $patchSetName base patches" - - input.set(baseDir) - if (readOnly) { - output.set(layout.cache.resolve(paperTaskOutput())) - } else { - output.set(outputDir) - } - - patches.set(basePatchDir.fileExists()) - baseRef.set("base") - identifier = "$forkName $patchSetName" - } - private fun ApplyFilePatches.configureApplyFilePatches() { group = taskGroup description = "Applies $patchSetName file patches" @@ -94,6 +79,22 @@ class PatchingTasks( identifier = "$forkName $patchSetName" } + private fun ApplyBasePatches.configureApplyBasePatches() { + group = taskGroup + description = "Applies $patchSetName base patches" + + input.set(baseDir) + if (readOnly) { + output.set(layout.cache.resolve(paperTaskOutput())) + } else { + output.set(outputDir) + } + + patches.set(basePatchDir.fileExists()) + baseRef.set("base") + identifier = "$forkName $patchSetName" + } + val applyBasePatches = tasks.register("apply${namePart}BasePatches") { configureApplyBasePatches() } @@ -138,8 +139,8 @@ class PatchingTasks( fun setupUpstream() { val collectAccessTransform = tasks.register("collect${namePart}ATsFromPatches") { - patchDir.set(basePatchDir.fileExists()) - extraPatchDir.set(featurePatchDir.fileExists()) + basePatchDir.set(basePatchDir.fileExists()) + featurePatchDir.set(featurePatchDir.fileExists()) } val mergeCollectedAts = tasks.register("merge${namePart}ATs") { @@ -180,6 +181,8 @@ class PatchingTasks( base.set(baseDir) // correct task execution order -> unused inputDir.set(outputDir) patchDir.set(basePatchDir) + baseRef.set("base") + stopRef.set("basepatches~1") // ~1 cuz we dont want to rebuild the marker commit filterPatches.set(this@PatchingTasks.filterPatches) identifier = "$forkName $patchSetName" } @@ -208,6 +211,7 @@ class PatchingTasks( dependsOn(rebuildFilePatches) inputDir.set(outputDir) + baseRef.set("file") patchDir.set(featurePatchDir) filterPatches.set(this@PatchingTasks.filterPatches) } diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/RebuildFilePatches.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/RebuildFilePatches.kt index b06ffeb48..849f1b704 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/RebuildFilePatches.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/RebuildFilePatches.kt @@ -232,7 +232,6 @@ abstract class RebuildFilePatches : JavaLauncherTask() { .summary(verbose.get()) .build() .operate() - return result.summary?.changedFiles } diff --git a/paperweight-core/src/test/kotlin/io/papermc/paperweight/FunctionalTest.kt b/paperweight-core/src/test/kotlin/io/papermc/paperweight/FunctionalTest.kt index 9ec503662..306b8d116 100644 --- a/paperweight-core/src/test/kotlin/io/papermc/paperweight/FunctionalTest.kt +++ b/paperweight-core/src/test/kotlin/io/papermc/paperweight/FunctionalTest.kt @@ -113,6 +113,7 @@ class FunctionalTest { .withArguments("rebuildPatches", "--stacktrace", "-Dfake=true") .withDebug(debug) .build() + assertEquals(rebP2.task(":test-server:rebuildPatches")?.outcome, TaskOutcome.SUCCESS) assertEquals( testResource.resolve("fake-patches/expected/0001-Test-Base.patch").readText(), diff --git a/paperweight-core/src/test/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyFilePatchesTest.kt b/paperweight-core/src/test/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyFilePatchesTest.kt index 1bf49994a..9607c3672 100644 --- a/paperweight-core/src/test/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyFilePatchesTest.kt +++ b/paperweight-core/src/test/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyFilePatchesTest.kt @@ -30,15 +30,15 @@ import org.gradle.kotlin.dsl.* import org.junit.jupiter.api.io.TempDir class ApplyFilePatchesTest : TaskTest() { - private lateinit var task2: ApplyBasePatches - private lateinit var task: ApplyFilePatches + private lateinit var task: ApplyBasePatches + private lateinit var task2: ApplyFilePatches @BeforeTest fun setup() { val project = setupProject() - task2 = project.tasks.register("applyBasePatches", ApplyBasePatches::class).get() - task = project.tasks.register("applyPatches", ApplyFilePatches::class) { - dependsOn(task2) + task = project.tasks.register("applyBasePatches", ApplyBasePatches::class).get() + task2 = project.tasks.register("applyPatches", ApplyFilePatches::class) { + dependsOn(task) } .get() } @@ -54,18 +54,18 @@ class ApplyFilePatchesTest : TaskTest() { setupGitRepo(input, "main") - task2.input.set(input) - task2.output.set(output) - task2.identifier.set("test") - - task.base.set(task2.output) - task.repo.set(output) - task.patches.set(patches) - task.verbose.set(true) + task.input.set(input) + task.output.set(output) task.identifier.set("test") - task2.run() + task2.base.set(task2.output) + task2.repo.set(output) + task2.patches.set(patches) + task2.verbose.set(true) + task2.identifier.set("test") + task.run() + task2.run() val testOutput = testResource.resolve("output") compareDir(tempDir, testOutput, "source") diff --git a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/ApplyAccessTransform.kt b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/ApplyAccessTransform.kt index 9506223f1..af5b7dd78 100644 --- a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/ApplyAccessTransform.kt +++ b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/ApplyAccessTransform.kt @@ -23,7 +23,6 @@ package io.papermc.paperweight.tasks import io.papermc.paperweight.util.* -import io.papermc.paperweight.util.constants.* import java.nio.file.Path import javax.inject.Inject import kotlin.io.path.* @@ -133,7 +132,7 @@ abstract class ApplyAccessTransform : JavaLauncherTask() { inheritanceProvider.set( it, ClassProviderInheritanceProvider( - ASM_OPCODES_VERSION, + Opcodes.ASM9, classProvider.get(inheritanceProvider.get(it)) as ClassProvider ) ) @@ -169,7 +168,7 @@ class AccessTransformerVisitor( private val at: AccessTransformSet, private val inheritanceProvider: InheritanceProvider, writer: ClassWriter -) : ClassVisitor(ASM_OPCODES_VERSION, writer) { +) : ClassVisitor(Opcodes.ASM9, writer) { private lateinit var classTransform: AccessTransformSet.Class diff --git a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/CollectATsFromPatches.kt b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/CollectATsFromPatches.kt index 2bccdafd8..714d9e435 100644 --- a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/CollectATsFromPatches.kt +++ b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/CollectATsFromPatches.kt @@ -45,7 +45,11 @@ abstract class CollectATsFromPatches : BaseTask() { @get:InputDirectory @get:Optional - abstract val patchDir: DirectoryProperty + abstract val basePatchDir: DirectoryProperty + + @get:InputDirectory + @get:Optional + abstract val featurePatchDir: DirectoryProperty @get:InputDirectory @get:Optional @@ -63,7 +67,10 @@ abstract class CollectATsFromPatches : BaseTask() { fun run() { outputFile.path.deleteForcefully() val patches = mutableListOf() - patchDir.orNull?.let { + basePatchDir.orNull?.let { + patches += it.path.listDirectoryEntries("*.patch") + } + featurePatchDir.orNull?.let { patches += it.path.listDirectoryEntries("*.patch") } extraPatchDir.orNull?.let { diff --git a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/GenerateDevBundle.kt b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/GenerateDevBundle.kt index 6717d4227..897a993ca 100644 --- a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/GenerateDevBundle.kt +++ b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/GenerateDevBundle.kt @@ -125,7 +125,7 @@ abstract class GenerateDevBundle : BaseTask() { if (file.isDirectory()) { workingDir.resolve(path).createDirectories() } else { - file.toPath().copyTo(workingDir.resolve(path), overwrite = true) + file.toPath().copyTo(workingDir.resolve(path)) } } workingDir.resolve(".git").deleteRecursive() diff --git a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RebuildBaseGitPatches.kt b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RebuildBaseGitPatches.kt index 31ce123e3..cdca19a27 100644 --- a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RebuildBaseGitPatches.kt +++ b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RebuildBaseGitPatches.kt @@ -45,6 +45,12 @@ abstract class RebuildBaseGitPatches : ControllableOutputTask() { @get:InputDirectory abstract val base: DirectoryProperty // we dont need this at all but we use this to trick gradle into the correct task execution order + @get:Input + abstract val baseRef: Property + + @get:Input + abstract val stopRef: Property + @get:OutputDirectory abstract val patchDir: DirectoryProperty @@ -121,8 +127,8 @@ abstract class RebuildBaseGitPatches : ControllableOutputTask() { patchFolder.createDirectories() } - val base = "base" - val stop = "basepatches~1" // ~1 cuz we dont want to rebuild the marker commit + val base = baseRef.get() + val stop = stopRef.get() val commitCount = git("rev-list", "--count", "$base..$stop").getText().trim().toInt() if (commitCount <= 0) return // nothing to rebuild diff --git a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RebuildGitPatches.kt b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RebuildGitPatches.kt index c260d4220..cdf40ae3f 100644 --- a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RebuildGitPatches.kt +++ b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RebuildGitPatches.kt @@ -40,6 +40,9 @@ abstract class RebuildGitPatches : ControllableOutputTask() { @get:InputDirectory abstract val inputDir: DirectoryProperty + @get:Input + abstract val baseRef: Property + @get:OutputDirectory abstract val patchDir: DirectoryProperty @@ -91,19 +94,12 @@ abstract class RebuildGitPatches : ControllableOutputTask() { } val git = Git(inputDir.path) - val base = "file" - val stop = "HEAD" - val commitCount = git("rev-list", "--count", "$base..$stop").getText().trim().toInt() - - if (commitCount <= 0) return // nothing to rebuild - - val range = "$base..$stop" git("fetch", "--all", "--prune", "--no-prune-tags").runSilently(silenceErr = true) git( "format-patch", "--diff-algorithm=myers", "--zero-commit", "--full-index", "--no-signature", "--no-stat", "-N", "-o", patchFolder.absolutePathString(), - range + baseRef.get() ).executeSilently() val patchDirGit = Git(patchFolder) patchDirGit("add", "-A", ".").executeSilently() diff --git a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RelocateClassNameConstants.kt b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RelocateClassNameConstants.kt index a614dc423..48ff178a7 100644 --- a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RelocateClassNameConstants.kt +++ b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RelocateClassNameConstants.kt @@ -23,7 +23,6 @@ package io.papermc.paperweight.tasks import io.papermc.paperweight.util.* -import io.papermc.paperweight.util.constants.* import java.nio.file.Path import kotlin.io.path.* import org.gradle.api.Action @@ -39,6 +38,7 @@ import org.gradle.api.tasks.TaskAction import org.gradle.kotlin.dsl.* import org.objectweb.asm.ClassVisitor import org.objectweb.asm.MethodVisitor +import org.objectweb.asm.Opcodes import org.objectweb.asm.tree.ClassNode @Suppress("LeakingThis") @@ -107,7 +107,7 @@ abstract class RelocateClassNameConstants : BaseTask() { private class ConstantRelocatingClassVisitor( parent: ClassVisitor, private val relocations: List - ) : ClassVisitor(ASM_OPCODES_VERSION, parent) { + ) : ClassVisitor(Opcodes.ASM9, parent) { override fun visitMethod( access: Int, name: String?, @@ -115,7 +115,7 @@ abstract class RelocateClassNameConstants : BaseTask() { signature: String?, exceptions: Array? ): MethodVisitor { - return object : MethodVisitor(ASM_OPCODES_VERSION, super.visitMethod(access, name, descriptor, signature, exceptions)) { + return object : MethodVisitor(Opcodes.ASM9, super.visitMethod(access, name, descriptor, signature, exceptions)) { override fun visitLdcInsn(value: Any?) { if (value is String) { var v: String = value 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 3b4201070..dbdf22ac0 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 @@ -23,7 +23,6 @@ package io.papermc.paperweight.util.constants import org.gradle.api.Task -import org.objectweb.asm.Opcodes const val PAPERWEIGHT_EXTENSION = "paperweight" const val PAPERWEIGHT_DEBUG = "paperweight.debug" @@ -54,7 +53,6 @@ const val MACHE_CONSTANTS_CONFIG = "macheConstants" const val MACHE_MINECRAFT_LIBRARIES_CONFIG = "macheMinecraftLibraries" const val MACHE_MINECRAFT_CONFIG = "macheMinecraft" const val MAPPED_JAR_OUTGOING_CONFIG = "mappedJarOutgoing" -const val ASM_OPCODES_VERSION = Opcodes.ASM9 const val JAVADOC_MAPPINGS_CONFIG = "JavadocMappings" const val JST_CONFIG = "javaSourceTransformer" const val DEV_BUNDLE_CONFIG = "paperweightDevelopmentBundle" From 54d937cd3dce620ccc9bafb217b76f84143e940c Mon Sep 17 00:00:00 2001 From: Toffikk Date: Sun, 19 Oct 2025 22:38:39 +0200 Subject: [PATCH 128/257] cleanup: rearrange diff --- .../core/taskcontainers/CoreTasks.kt | 4 +- .../taskcontainers/MinecraftPatchingTasks.kt | 37 +++++++++-------- .../core/taskcontainers/PaperclipTasks.kt | 1 - .../core/taskcontainers/PatchingTasks.kt | 40 ++++++++++--------- .../core/tasks/patching/RebuildFilePatches.kt | 1 - .../io/papermc/paperweight/FunctionalTest.kt | 1 + .../tasks/patching/ApplyFilePatchesTest.kt | 28 ++++++------- .../paperweight/tasks/ApplyAccessTransform.kt | 5 +-- .../tasks/CollectATsFromPatches.kt | 11 ++++- .../paperweight/tasks/GenerateDevBundle.kt | 2 +- .../tasks/RebuildBaseGitPatches.kt | 10 ++++- .../paperweight/tasks/RebuildGitPatches.kt | 12 ++---- .../tasks/RelocateClassNameConstants.kt | 6 +-- .../paperweight/util/constants/constants.kt | 2 - 14 files changed, 85 insertions(+), 75 deletions(-) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/CoreTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/CoreTasks.kt index 3346b8118..6c8a15a7f 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/CoreTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/CoreTasks.kt @@ -74,8 +74,8 @@ class CoreTasks( } val collectPaperATsFromPatches by tasks.registering(CollectATsFromPatches::class) { - patchDir.set(project.coreExt.paper.basePatchDir.fileExists()) - extraPatchDir.set(project.coreExt.paper.featurePatchDir.fileExists()) + basePatchDir.set(project.coreExt.paper.basePatchDir.fileExists()) + featurePatchDir.set(project.coreExt.paper.featurePatchDir.fileExists()) } val mergePaperATs by tasks.registering { diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt index 03e4ca18d..36165e369 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt @@ -74,20 +74,6 @@ class MinecraftPatchingTasks( private val namePart = if (readOnly) "${configName.capitalized()}Minecraft" else if (paper) "" else "Minecraft" - private fun ApplyBasePatches.configureApplyBasePatches() { - group() - description = "Applies $configName base patches to the Minecraft sources" - - input.set(baseSources) - if (readOnly) { - output.set(outputSrcFile) - } else { - output.set(outputSrc) - } - patches.set(basePatchDir.fileExists()) - identifier = configName - } - private fun ApplyFilePatches.configureApplyFilePatches() { group() description = "Applies $configName file patches to the Minecraft sources" @@ -105,6 +91,20 @@ class MinecraftPatchingTasks( identifier = configName } + private fun ApplyBasePatches.configureApplyBasePatches() { + group() + description = "Applies $configName base patches to the Minecraft sources" + + input.set(baseSources) + if (readOnly) { + output.set(outputSrcFile) + } else { + output.set(outputSrc) + } + patches.set(basePatchDir.fileExists()) + identifier = configName + } + val applyBasePatches = tasks.register("apply${namePart}BasePatches") { configureApplyBasePatches() } @@ -168,8 +168,8 @@ class MinecraftPatchingTasks( fun setupFork(config: ForkConfig) { val collectAccessTransform = tasks.register("collect${configName.capitalized()}MinecraftATsFromPatches") { - patchDir.set(basePatchDir.fileExists()) - extraPatchDir.set(featurePatchDir.fileExists()) + basePatchDir.set(basePatchDir.fileExists()) + featurePatchDir.set(featurePatchDir.fileExists()) } val mergeCollectedAts = tasks.register("merge${configName.capitalized()}MinecraftATs") { @@ -184,7 +184,7 @@ class MinecraftPatchingTasks( libraries.from(coreTasks.indexLibraryFiles.map { it.libraries }) } - val setup = tasks.register("run${configName.capitalized()}MinecraftSetup") { + val setup = tasks.register("run${configName.capitalized()}Setup") { description = "Applies $configName ATs and library imports to Minecraft sources" inputDir.set(baseSources) @@ -239,6 +239,8 @@ class MinecraftPatchingTasks( base.set(baseSources) // workaround to achieve the correct task execution order inputDir.set(outputSrc) patchDir.set(basePatchDir) + baseRef.set("base") + stopRef.set("basepatches~1") // ~1 cuz we dont want to rebuild the marker commit filterPatches.set(this@MinecraftPatchingTasks.filterPatches) identifier = configName } @@ -283,6 +285,7 @@ class MinecraftPatchingTasks( inputDir.set(outputSrc) patchDir.set(featurePatchDir) + baseRef.set("file") filterPatches.set(this@MinecraftPatchingTasks.filterPatches) } diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PaperclipTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PaperclipTasks.kt index d8fe08586..4ebc46aba 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PaperclipTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PaperclipTasks.kt @@ -51,7 +51,6 @@ class PaperclipTasks( private val mcVersion: Provider, private val rootName: Provider ) { - init { val (createBundlerJar, createPaperclipJar, createPublisherJar) = project.createTasks("mojmap") val (createReobfBundlerJar, createReobfPaperclipJar, createReobfPublisherJar) = project.createTasks("reobf") diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt index a678b3cd8..278e2e3b9 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt @@ -31,7 +31,8 @@ import io.papermc.paperweight.core.tasks.patching.FixupFilePatches import io.papermc.paperweight.core.tasks.patching.RebuildFilePatches import io.papermc.paperweight.tasks.* import io.papermc.paperweight.util.* -import io.papermc.paperweight.util.constants.* +import io.papermc.paperweight.util.constants.JST_CONFIG +import io.papermc.paperweight.util.constants.paperTaskOutput import java.nio.file.Path import org.gradle.api.Project import org.gradle.api.Task @@ -61,22 +62,6 @@ class PatchingTasks( ) { private val namePart: String = if (readOnly) "${forkName.capitalized()}${patchSetName.capitalized()}" else patchSetName.capitalized() - private fun ApplyBasePatches.configureApplyBasePatches() { - group = taskGroup - description = "Applies $patchSetName base patches" - - input.set(baseDir) - if (readOnly) { - output.set(layout.cache.resolve(paperTaskOutput())) - } else { - output.set(outputDir) - } - - patches.set(basePatchDir.fileExists()) - baseRef.set("base") - identifier = "$forkName $patchSetName" - } - private fun ApplyFilePatches.configureApplyFilePatches() { group = taskGroup description = "Applies $patchSetName file patches" @@ -94,6 +79,20 @@ class PatchingTasks( identifier = "$forkName $patchSetName" } + private fun ApplyBasePatches.configureApplyBasePatches() { + group = taskGroup + description = "Applies $patchSetName base patches" + + input.set(baseDir) + if (readOnly) { + output.set(layout.cache.resolve(paperTaskOutput())) + } else { + output.set(outputDir) + } + + patches.set(basePatchDir.fileExists()) + } + val applyBasePatches = tasks.register("apply${namePart}BasePatches") { configureApplyBasePatches() } @@ -138,8 +137,8 @@ class PatchingTasks( fun setupUpstream() { val collectAccessTransform = tasks.register("collect${namePart}ATsFromPatches") { - patchDir.set(basePatchDir.fileExists()) - extraPatchDir.set(featurePatchDir.fileExists()) + basePatchDir.set(basePatchDir.fileExists()) + featurePatchDir.set(featurePatchDir.fileExists()) } val mergeCollectedAts = tasks.register("merge${namePart}ATs") { @@ -180,6 +179,8 @@ class PatchingTasks( base.set(baseDir) // correct task execution order -> unused inputDir.set(outputDir) patchDir.set(basePatchDir) + baseRef.set("base") + stopRef.set("basepatches~1") // ~1 cuz we dont want to rebuild the marker commit filterPatches.set(this@PatchingTasks.filterPatches) identifier = "$forkName $patchSetName" } @@ -209,6 +210,7 @@ class PatchingTasks( inputDir.set(outputDir) patchDir.set(featurePatchDir) + baseRef.set("file") filterPatches.set(this@PatchingTasks.filterPatches) } diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/RebuildFilePatches.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/RebuildFilePatches.kt index b06ffeb48..849f1b704 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/RebuildFilePatches.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/RebuildFilePatches.kt @@ -232,7 +232,6 @@ abstract class RebuildFilePatches : JavaLauncherTask() { .summary(verbose.get()) .build() .operate() - return result.summary?.changedFiles } diff --git a/paperweight-core/src/test/kotlin/io/papermc/paperweight/FunctionalTest.kt b/paperweight-core/src/test/kotlin/io/papermc/paperweight/FunctionalTest.kt index 9ec503662..306b8d116 100644 --- a/paperweight-core/src/test/kotlin/io/papermc/paperweight/FunctionalTest.kt +++ b/paperweight-core/src/test/kotlin/io/papermc/paperweight/FunctionalTest.kt @@ -113,6 +113,7 @@ class FunctionalTest { .withArguments("rebuildPatches", "--stacktrace", "-Dfake=true") .withDebug(debug) .build() + assertEquals(rebP2.task(":test-server:rebuildPatches")?.outcome, TaskOutcome.SUCCESS) assertEquals( testResource.resolve("fake-patches/expected/0001-Test-Base.patch").readText(), diff --git a/paperweight-core/src/test/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyFilePatchesTest.kt b/paperweight-core/src/test/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyFilePatchesTest.kt index 1bf49994a..9607c3672 100644 --- a/paperweight-core/src/test/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyFilePatchesTest.kt +++ b/paperweight-core/src/test/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyFilePatchesTest.kt @@ -30,15 +30,15 @@ import org.gradle.kotlin.dsl.* import org.junit.jupiter.api.io.TempDir class ApplyFilePatchesTest : TaskTest() { - private lateinit var task2: ApplyBasePatches - private lateinit var task: ApplyFilePatches + private lateinit var task: ApplyBasePatches + private lateinit var task2: ApplyFilePatches @BeforeTest fun setup() { val project = setupProject() - task2 = project.tasks.register("applyBasePatches", ApplyBasePatches::class).get() - task = project.tasks.register("applyPatches", ApplyFilePatches::class) { - dependsOn(task2) + task = project.tasks.register("applyBasePatches", ApplyBasePatches::class).get() + task2 = project.tasks.register("applyPatches", ApplyFilePatches::class) { + dependsOn(task) } .get() } @@ -54,18 +54,18 @@ class ApplyFilePatchesTest : TaskTest() { setupGitRepo(input, "main") - task2.input.set(input) - task2.output.set(output) - task2.identifier.set("test") - - task.base.set(task2.output) - task.repo.set(output) - task.patches.set(patches) - task.verbose.set(true) + task.input.set(input) + task.output.set(output) task.identifier.set("test") - task2.run() + task2.base.set(task2.output) + task2.repo.set(output) + task2.patches.set(patches) + task2.verbose.set(true) + task2.identifier.set("test") + task.run() + task2.run() val testOutput = testResource.resolve("output") compareDir(tempDir, testOutput, "source") diff --git a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/ApplyAccessTransform.kt b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/ApplyAccessTransform.kt index 9506223f1..af5b7dd78 100644 --- a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/ApplyAccessTransform.kt +++ b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/ApplyAccessTransform.kt @@ -23,7 +23,6 @@ package io.papermc.paperweight.tasks import io.papermc.paperweight.util.* -import io.papermc.paperweight.util.constants.* import java.nio.file.Path import javax.inject.Inject import kotlin.io.path.* @@ -133,7 +132,7 @@ abstract class ApplyAccessTransform : JavaLauncherTask() { inheritanceProvider.set( it, ClassProviderInheritanceProvider( - ASM_OPCODES_VERSION, + Opcodes.ASM9, classProvider.get(inheritanceProvider.get(it)) as ClassProvider ) ) @@ -169,7 +168,7 @@ class AccessTransformerVisitor( private val at: AccessTransformSet, private val inheritanceProvider: InheritanceProvider, writer: ClassWriter -) : ClassVisitor(ASM_OPCODES_VERSION, writer) { +) : ClassVisitor(Opcodes.ASM9, writer) { private lateinit var classTransform: AccessTransformSet.Class diff --git a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/CollectATsFromPatches.kt b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/CollectATsFromPatches.kt index 2bccdafd8..714d9e435 100644 --- a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/CollectATsFromPatches.kt +++ b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/CollectATsFromPatches.kt @@ -45,7 +45,11 @@ abstract class CollectATsFromPatches : BaseTask() { @get:InputDirectory @get:Optional - abstract val patchDir: DirectoryProperty + abstract val basePatchDir: DirectoryProperty + + @get:InputDirectory + @get:Optional + abstract val featurePatchDir: DirectoryProperty @get:InputDirectory @get:Optional @@ -63,7 +67,10 @@ abstract class CollectATsFromPatches : BaseTask() { fun run() { outputFile.path.deleteForcefully() val patches = mutableListOf() - patchDir.orNull?.let { + basePatchDir.orNull?.let { + patches += it.path.listDirectoryEntries("*.patch") + } + featurePatchDir.orNull?.let { patches += it.path.listDirectoryEntries("*.patch") } extraPatchDir.orNull?.let { diff --git a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/GenerateDevBundle.kt b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/GenerateDevBundle.kt index 6717d4227..897a993ca 100644 --- a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/GenerateDevBundle.kt +++ b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/GenerateDevBundle.kt @@ -125,7 +125,7 @@ abstract class GenerateDevBundle : BaseTask() { if (file.isDirectory()) { workingDir.resolve(path).createDirectories() } else { - file.toPath().copyTo(workingDir.resolve(path), overwrite = true) + file.toPath().copyTo(workingDir.resolve(path)) } } workingDir.resolve(".git").deleteRecursive() diff --git a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RebuildBaseGitPatches.kt b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RebuildBaseGitPatches.kt index 31ce123e3..cdca19a27 100644 --- a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RebuildBaseGitPatches.kt +++ b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RebuildBaseGitPatches.kt @@ -45,6 +45,12 @@ abstract class RebuildBaseGitPatches : ControllableOutputTask() { @get:InputDirectory abstract val base: DirectoryProperty // we dont need this at all but we use this to trick gradle into the correct task execution order + @get:Input + abstract val baseRef: Property + + @get:Input + abstract val stopRef: Property + @get:OutputDirectory abstract val patchDir: DirectoryProperty @@ -121,8 +127,8 @@ abstract class RebuildBaseGitPatches : ControllableOutputTask() { patchFolder.createDirectories() } - val base = "base" - val stop = "basepatches~1" // ~1 cuz we dont want to rebuild the marker commit + val base = baseRef.get() + val stop = stopRef.get() val commitCount = git("rev-list", "--count", "$base..$stop").getText().trim().toInt() if (commitCount <= 0) return // nothing to rebuild diff --git a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RebuildGitPatches.kt b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RebuildGitPatches.kt index c260d4220..cdf40ae3f 100644 --- a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RebuildGitPatches.kt +++ b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RebuildGitPatches.kt @@ -40,6 +40,9 @@ abstract class RebuildGitPatches : ControllableOutputTask() { @get:InputDirectory abstract val inputDir: DirectoryProperty + @get:Input + abstract val baseRef: Property + @get:OutputDirectory abstract val patchDir: DirectoryProperty @@ -91,19 +94,12 @@ abstract class RebuildGitPatches : ControllableOutputTask() { } val git = Git(inputDir.path) - val base = "file" - val stop = "HEAD" - val commitCount = git("rev-list", "--count", "$base..$stop").getText().trim().toInt() - - if (commitCount <= 0) return // nothing to rebuild - - val range = "$base..$stop" git("fetch", "--all", "--prune", "--no-prune-tags").runSilently(silenceErr = true) git( "format-patch", "--diff-algorithm=myers", "--zero-commit", "--full-index", "--no-signature", "--no-stat", "-N", "-o", patchFolder.absolutePathString(), - range + baseRef.get() ).executeSilently() val patchDirGit = Git(patchFolder) patchDirGit("add", "-A", ".").executeSilently() diff --git a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RelocateClassNameConstants.kt b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RelocateClassNameConstants.kt index a614dc423..48ff178a7 100644 --- a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RelocateClassNameConstants.kt +++ b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RelocateClassNameConstants.kt @@ -23,7 +23,6 @@ package io.papermc.paperweight.tasks import io.papermc.paperweight.util.* -import io.papermc.paperweight.util.constants.* import java.nio.file.Path import kotlin.io.path.* import org.gradle.api.Action @@ -39,6 +38,7 @@ import org.gradle.api.tasks.TaskAction import org.gradle.kotlin.dsl.* import org.objectweb.asm.ClassVisitor import org.objectweb.asm.MethodVisitor +import org.objectweb.asm.Opcodes import org.objectweb.asm.tree.ClassNode @Suppress("LeakingThis") @@ -107,7 +107,7 @@ abstract class RelocateClassNameConstants : BaseTask() { private class ConstantRelocatingClassVisitor( parent: ClassVisitor, private val relocations: List - ) : ClassVisitor(ASM_OPCODES_VERSION, parent) { + ) : ClassVisitor(Opcodes.ASM9, parent) { override fun visitMethod( access: Int, name: String?, @@ -115,7 +115,7 @@ abstract class RelocateClassNameConstants : BaseTask() { signature: String?, exceptions: Array? ): MethodVisitor { - return object : MethodVisitor(ASM_OPCODES_VERSION, super.visitMethod(access, name, descriptor, signature, exceptions)) { + return object : MethodVisitor(Opcodes.ASM9, super.visitMethod(access, name, descriptor, signature, exceptions)) { override fun visitLdcInsn(value: Any?) { if (value is String) { var v: String = value 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 3b4201070..dbdf22ac0 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 @@ -23,7 +23,6 @@ package io.papermc.paperweight.util.constants import org.gradle.api.Task -import org.objectweb.asm.Opcodes const val PAPERWEIGHT_EXTENSION = "paperweight" const val PAPERWEIGHT_DEBUG = "paperweight.debug" @@ -54,7 +53,6 @@ const val MACHE_CONSTANTS_CONFIG = "macheConstants" const val MACHE_MINECRAFT_LIBRARIES_CONFIG = "macheMinecraftLibraries" const val MACHE_MINECRAFT_CONFIG = "macheMinecraft" const val MAPPED_JAR_OUTGOING_CONFIG = "mappedJarOutgoing" -const val ASM_OPCODES_VERSION = Opcodes.ASM9 const val JAVADOC_MAPPINGS_CONFIG = "JavadocMappings" const val JST_CONFIG = "javaSourceTransformer" const val DEV_BUNDLE_CONFIG = "paperweightDevelopmentBundle" From 925b0106ee42cecb7dbf7598ccf2ecd5dc8af139 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Sun, 19 Oct 2025 22:47:06 +0200 Subject: [PATCH 129/257] fix: a git error snuck in --- .../io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt | 1 - .../paperweight/core/tasks/patching/ApplyFilePatchesTest.kt | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt index 4e84b55d4..fcf0bc45d 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt @@ -211,7 +211,6 @@ class PatchingTasks( dependsOn(rebuildFilePatches) inputDir.set(outputDir) - baseRef.set("file") patchDir.set(featurePatchDir) baseRef.set("file") filterPatches.set(this@PatchingTasks.filterPatches) diff --git a/paperweight-core/src/test/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyFilePatchesTest.kt b/paperweight-core/src/test/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyFilePatchesTest.kt index 9607c3672..81a683099 100644 --- a/paperweight-core/src/test/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyFilePatchesTest.kt +++ b/paperweight-core/src/test/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyFilePatchesTest.kt @@ -58,7 +58,7 @@ class ApplyFilePatchesTest : TaskTest() { task.output.set(output) task.identifier.set("test") - task2.base.set(task2.output) + task2.base.set(task.output) task2.repo.set(output) task2.patches.set(patches) task2.verbose.set(true) From f83895dd9ce3ffdcee0dabe628557d9999658e25 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Mon, 20 Oct 2025 00:28:33 +0200 Subject: [PATCH 130/257] fix: resolve circular dependency errors --- .../paperweight/core/taskcontainers/MinecraftPatchingTasks.kt | 4 ++-- .../papermc/paperweight/core/taskcontainers/PatchingTasks.kt | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt index 36165e369..ba574e298 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt @@ -168,8 +168,8 @@ class MinecraftPatchingTasks( fun setupFork(config: ForkConfig) { val collectAccessTransform = tasks.register("collect${configName.capitalized()}MinecraftATsFromPatches") { - basePatchDir.set(basePatchDir.fileExists()) - featurePatchDir.set(featurePatchDir.fileExists()) + basePatchDir.set(this@MinecraftPatchingTasks.basePatchDir.fileExists()) + featurePatchDir.set(this@MinecraftPatchingTasks.featurePatchDir.fileExists()) } val mergeCollectedAts = tasks.register("merge${configName.capitalized()}MinecraftATs") { diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt index fcf0bc45d..18f3b5622 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt @@ -139,8 +139,8 @@ class PatchingTasks( fun setupUpstream() { val collectAccessTransform = tasks.register("collect${namePart}ATsFromPatches") { - basePatchDir.set(basePatchDir.fileExists()) - featurePatchDir.set(featurePatchDir.fileExists()) + basePatchDir.set(this@PatchingTasks.basePatchDir.fileExists()) + featurePatchDir.set(this@PatchingTasks.featurePatchDir.fileExists()) } val mergeCollectedAts = tasks.register("merge${namePart}ATs") { From ca0394fce044bd34af2fb7cbfb84f82cb1d06a46 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Mon, 20 Oct 2025 00:36:04 +0200 Subject: [PATCH 131/257] chore: unify the rest of tasks with the recent setup change --- .../paperweight/core/taskcontainers/MinecraftPatchingTasks.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt index ba574e298..07567478f 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt @@ -167,12 +167,12 @@ class MinecraftPatchingTasks( } fun setupFork(config: ForkConfig) { - val collectAccessTransform = tasks.register("collect${configName.capitalized()}MinecraftATsFromPatches") { + val collectAccessTransform = tasks.register("collect${configName.capitalized()}ATsFromPatches") { basePatchDir.set(this@MinecraftPatchingTasks.basePatchDir.fileExists()) featurePatchDir.set(this@MinecraftPatchingTasks.featurePatchDir.fileExists()) } - val mergeCollectedAts = tasks.register("merge${configName.capitalized()}MinecraftATs") { + val mergeCollectedAts = tasks.register("merge${configName.capitalized()}ATs") { firstFile.set(additionalAts.fileExists()) secondFile.set(collectAccessTransform.flatMap { it.outputFile }) } From 9801321420d0b5059aabf88d58a9f507882c35f4 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Mon, 20 Oct 2025 01:09:10 +0200 Subject: [PATCH 132/257] fix(feat): Unify the file patching classes, brings back fuzzy apply for resource patches --- .../core/taskcontainers/CoreTasks.kt | 2 +- .../taskcontainers/MinecraftPatchingTasks.kt | 8 +- .../core/tasks/patching/ApplyFilePatches.kt | 78 +++-- .../patching/ApplyResourceFilePatches.kt | 281 ------------------ 4 files changed, 57 insertions(+), 312 deletions(-) delete mode 100644 paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyResourceFilePatches.kt diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/CoreTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/CoreTasks.kt index 6c8a15a7f..14e1da477 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/CoreTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/CoreTasks.kt @@ -230,7 +230,7 @@ class CoreTasks( cfg.featurePatchDir, cfg.additionalAts, upstreamTasks.first.applyFeaturePatches.flatMap { it.repo }, - upstreamTasks.first.applyResourcePatches.flatMap { it.output }, + upstreamTasks.first.applyResourcePatches.flatMap { it.repo }, project.coreExt.gitFilePatches, project.coreExt.filterPatches, outputRoot, diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt index 07567478f..63635a8c4 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt @@ -29,7 +29,6 @@ import io.papermc.paperweight.core.tasks.patching.ApplyBasePatches import io.papermc.paperweight.core.tasks.patching.ApplyFeaturePatches import io.papermc.paperweight.core.tasks.patching.ApplyFilePatches import io.papermc.paperweight.core.tasks.patching.ApplyFilePatchesFuzzy -import io.papermc.paperweight.core.tasks.patching.ApplyResourceFilePatches import io.papermc.paperweight.core.tasks.patching.FixupFilePatches import io.papermc.paperweight.core.tasks.patching.RebuildFilePatches import io.papermc.paperweight.tasks.* @@ -117,15 +116,16 @@ class MinecraftPatchingTasks( configureApplyFilePatches() } - val applyResourcePatches = tasks.register("apply${namePart}ResourcePatches") { + val applyResourcePatches = tasks.register("apply${namePart}ResourcePatches") { group() description = "Applies $configName file patches to the Minecraft resources" - input.set(baseResources) - output.set(outputResources) + base.set(baseResources) + repo.set(outputResources) patches.set(resourcePatchDir.fileExists()) // TODO rejects? gitFilePatches.set(this@MinecraftPatchingTasks.gitFilePatches) + baseRef.set("main") identifier = configName } diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyFilePatches.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyFilePatches.kt index 959369d17..b0927c43b 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyFilePatches.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyFilePatches.kt @@ -36,7 +36,6 @@ import kotlin.io.path.* import org.eclipse.jgit.api.Git import org.eclipse.jgit.lib.PersonIdent import org.gradle.api.file.DirectoryProperty -import org.gradle.api.logging.LogLevel import org.gradle.api.provider.Property import org.gradle.api.tasks.* import org.gradle.api.tasks.options.Option @@ -70,7 +69,7 @@ abstract class ApplyFilePatches : BaseTask() { abstract val base: DirectoryProperty @get:Internal - abstract val ref: Property + abstract val baseRef: Property @get:Input @get:Optional @@ -88,7 +87,7 @@ abstract class ApplyFilePatches : BaseTask() { gitFilePatches.convention(false) moveFailedGitPatchesToRejects.convention(false) emitRejects.convention(true) - ref.convention("basepatches") + baseRef.convention("basepatches") } } @@ -97,35 +96,47 @@ abstract class ApplyFilePatches : BaseTask() { io.papermc.paperweight.util.Git.checkForGit() val base = base.pathOrNull + val repoPath = repo.path - if (base != null && base.toAbsolutePath() != repo.path.toAbsolutePath()) { - recreateCloneDirectory(repo.path) - val checkoutFromJDs = hasJavadocs(base, "${identifier.get()}JDs") - val newRef = if (checkoutFromJDs) "${identifier.get()}JDs" else ref.get() - ref.set(newRef) - - val git = Git(repo.path.createDirectories()) + if (base != null && baseRef.get() == "main") { + recreateCloneDirectory(repoPath) checkoutRepoFromUpstream( - git, + Git(repoPath), base, - ref.get(), - branchName = "main", - ref = true, + "main", + "upstream", + "main", + true, ) + setupGitHook(repoPath) + tagBase() } else { - val checkoutFromJDs = hasJavadocs(repo.path, "${identifier.get()}JDs") - val newRef = if (checkoutFromJDs) "${identifier.get()}JDs" else ref.get() - ref.set(newRef) - } - - val repoPath = repo.path - val git = Git(repoPath) - - if (git("checkout", "main").runSilently(silenceErr = true) != 0) { - git("checkout", "-b", "main").runSilently(silenceErr = true) + if (base != null && base.toAbsolutePath() != repoPath.toAbsolutePath()) { + recreateCloneDirectory(repoPath) + val checkoutFromJDs = hasJavadocs(base, "${identifier.get()}JDs") + val newRef = if (checkoutFromJDs) "${identifier.get()}JDs" else baseRef.get() + baseRef.set(newRef) + + val git = Git(repoPath.createDirectories()) + checkoutRepoFromUpstream( + git, + base, + baseRef.get(), + branchName = "main", + ref = true, + ) + } else { + val checkoutFromJDs = hasJavadocs(repoPath, "${identifier.get()}JDs") + val newRef = if (checkoutFromJDs) "${identifier.get()}JDs" else baseRef.get() + baseRef.set(newRef) + } + val git = Git(repoPath) + if (git("checkout", "main").runSilently(silenceErr = true) != 0) { + git("checkout", "-b", "main").runSilently(silenceErr = true) + } + git("reset", "--hard", baseRef.get()).runSilently(silenceErr = true) + git("gc").runSilently(silenceErr = true) } - git("reset", "--hard", ref.get()).runSilently(silenceErr = true) - git("gc").runSilently(silenceErr = true) val result = if (!patches.isPresent) { commit() @@ -158,6 +169,14 @@ abstract class ApplyFilePatches : BaseTask() { } } + private fun tagBase() { + val git = Git.open(repo.path.toFile()) + val ident = PersonIdent("base", "noreply+automated@papermc.io") + git.tagDelete().setTags("base").call() + git.tag().setName("base").setTagger(ident).setSigned(false).call() + git.close() + } + private fun shouldApplyWithGit(repoPath: Path): Boolean { val patchFiles = patches.path.filesMatchingRecursive("*.patch") val git = Git(repoPath) @@ -239,6 +258,13 @@ abstract class ApplyFilePatches : BaseTask() { return result.summary?.changedFiles } + private fun setupGitHook(outputPath: Path) { + val hook = outputPath.resolve(".git/hooks/post-rewrite") + hook.parent.createDirectories() + hook.writeText(javaClass.getResource("/post-rewrite.sh")!!.readText()) + hook.toFile().setExecutable(true) + } + private fun commit() { val ident = PersonIdent(PersonIdent("File", "noreply+automated@papermc.io"), Instant.parse("1997-04-20T13:37:42.69Z")) val git = Git.open(repo.path.toFile()) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyResourceFilePatches.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyResourceFilePatches.kt deleted file mode 100644 index 4290ee354..000000000 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyResourceFilePatches.kt +++ /dev/null @@ -1,281 +0,0 @@ -/* - * paperweight is a Gradle plugin for the PaperMC project. - * - * Copyright (c) 2023 Kyle Wood (DenWav) - * Contributors - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 only, no later versions. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 - * USA - */ - -package io.papermc.paperweight.core.tasks.patching - -import io.codechicken.diffpatch.cli.PatchOperation -import io.codechicken.diffpatch.match.FuzzyLineMatcher -import io.codechicken.diffpatch.util.Input as DiffInput -import io.codechicken.diffpatch.util.Output as DiffOutput -import io.codechicken.diffpatch.util.PatchMode -import io.papermc.paperweight.PaperweightException -import io.papermc.paperweight.tasks.* -import io.papermc.paperweight.util.* -import java.nio.file.Path -import java.time.Instant -import kotlin.io.path.* -import org.eclipse.jgit.api.Git -import org.eclipse.jgit.lib.PersonIdent -import org.eclipse.jgit.transport.URIish -import org.gradle.api.file.DirectoryProperty -import org.gradle.api.logging.LogLevel -import org.gradle.api.provider.Property -import org.gradle.api.tasks.* -import org.gradle.api.tasks.options.Option - -abstract class ApplyResourceFilePatches : BaseTask() { - - @get:Input - @get:Option( - option = "verbose", - description = "Prints out more info about the patching process", - ) - abstract val verbose: Property - - @get:PathSensitive(PathSensitivity.NONE) - @get:InputDirectory - abstract val input: DirectoryProperty - - @get:OutputDirectory - abstract val output: DirectoryProperty - - @get:PathSensitive(PathSensitivity.NONE) - @get:InputDirectory - @get:Optional - abstract val patches: DirectoryProperty - - @get:Internal - abstract val rejectsDir: DirectoryProperty - - @get:Optional - @get:Input - abstract val gitFilePatches: Property - - @get:Optional - @get:Input - abstract val baseRef: Property - - @get:Input - @get:Optional - abstract val identifier: Property - - // An additional remote to add and fetch from before applying patches (to bring in objects for 3-way merge). - @get:Input - @get:Optional - abstract val additionalRemote: Property - - @get:Input - abstract val additionalRemoteName: Property - - @get:Input - abstract val moveFailedGitPatchesToRejects: Property - - @get:Internal - abstract val emitRejects: Property - - init { - run { - verbose.convention(false) - gitFilePatches.convention(false) - additionalRemoteName.convention("old") - moveFailedGitPatchesToRejects.convention(false) - emitRejects.convention(true) - } - } - - @TaskAction - open fun run() { - io.papermc.paperweight.util.Git.checkForGit() - - val outputPath = output.path - recreateCloneDirectory(outputPath) - - checkoutRepoFromUpstream( - Git(outputPath), - input.path, - baseRef.getOrElse("main"), - "upstream", - "main", - baseRef.isPresent, - ) - - if (additionalRemote.isPresent) { - val jgit = Git.open(outputPath.toFile()) - jgit.remoteRemove().setRemoteName(additionalRemoteName.get()).call() - jgit.remoteAdd().setName(additionalRemoteName.get()).setUri(URIish(additionalRemote.get())).call() - jgit.fetch().setRemote(additionalRemoteName.get()).call() - } - - setupGitHook(outputPath) - - tagBase() - - val result = if (!patches.isPresent) { - commit() - 0 - } else if (gitFilePatches.get() && shouldApplyWithGit(outputPath)) { - applyWithGit(outputPath) - } else { - applyWithDiffPatch() - } - - if (!verbose.get()) { - logger.lifecycle("Applied $result patches") - } - } - - private fun recreateCloneDirectory(target: Path) { - if (target.exists()) { - if (target.resolve(".git").isDirectory()) { - val git = Git(target) - git("clean", "-fxd").runSilently(silenceErr = true) - git("reset", "--hard", "HEAD").runSilently(silenceErr = true) - } else { - for (entry in target.listDirectoryEntries()) { - entry.deleteRecursive() - } - target.createDirectories() - } - } else { - target.createDirectories() - } - } - - private fun tagBase() { - val git = Git.open(output.path.toFile()) - val ident = PersonIdent("base", "noreply+automated@papermc.io") - git.tagDelete().setTags("base").call() - git.tag().setName("base").setTagger(ident).setSigned(false).call() - git.close() - } - - private fun shouldApplyWithGit(repoPath: Path): Boolean { - val patchFiles = patches.path.filesMatchingRecursive("*.patch") - val git = Git(repoPath) - val canApply = patchFiles.any { patch -> - val result = git("apply", "--check", patch.absolutePathString()).getText(ignoreErr = true) - result.contains("error: corrupt patch at line") - } - return !canApply - } - - private fun applyWithGit(outputPath: Path): Int { - val git = Git(outputPath) - val patchFiles = patches.path.filesMatchingRecursive("*.patch") - if (moveFailedGitPatchesToRejects.get() && rejectsDir.isPresent) { - patchFiles.forEach { patch -> - val patchPathFromGit = outputPath.relativize(patch) - val responseCode = - git( - "-c", - "rerere.enabled=false", - "apply", - "--3way", - patchPathFromGit.pathString - ).runSilently(silenceOut = !verbose.get(), silenceErr = !verbose.get()) - when { - responseCode == 0 -> {} - responseCode > 1 -> throw PaperweightException("Failed to apply patch $patch: $responseCode") - responseCode == 1 -> { - val relativePatch = patches.path.relativize(patch) - val failedFile = relativePatch.parent.resolve(relativePatch.fileName.toString().substringBeforeLast(".patch")) - if (outputPath.resolve(failedFile).exists()) { - git("reset", "--", failedFile.pathString).executeSilently(silenceOut = !verbose.get(), silenceErr = !verbose.get()) - git("restore", failedFile.pathString).executeSilently(silenceOut = !verbose.get(), silenceErr = !verbose.get()) - } - - val rejectFile = rejectsDir.path.resolve(relativePatch) - patch.moveTo(rejectFile.createParentDirectories(), overwrite = true) - } - } - } - } else { - val patchStrings = patchFiles.map { outputPath.relativize(it).pathString } - patchStrings.chunked(12).forEach { - git("apply", "--3way", *it.toTypedArray()).executeSilently(silenceOut = !verbose.get(), silenceErr = !verbose.get()) - } - } - - commit() - - return patchFiles.size - } - - private fun applyWithDiffPatch(): Int? { - val builder = PatchOperation.builder() - .logTo(logger::lifecycle) - .baseInput(DiffInput.MultiInput.folder(output.path)) - .patchesInput(DiffInput.MultiInput.folder(patches.path)) - .patchedOutput(DiffOutput.MultiOutput.folder(output.path)) - .level(if (verbose.get()) io.codechicken.diffpatch.util.LogLevel.ALL else io.codechicken.diffpatch.util.LogLevel.INFO) - .mode(mode()) - .minFuzz(minFuzz()) - .summary(verbose.get()) - .lineEnding("\n") - .ignorePrefix(".git") - if (rejectsDir.isPresent && emitRejects.get()) { - builder.rejectsOutput(DiffOutput.MultiOutput.folder(rejectsDir.path)) - } - - val result = builder.build().operate() - - commit() - - if (result.exit != 0) { - val total = (result.summary?.failedMatches ?: 0) + (result.summary?.exactMatches ?: 0) + - (result.summary?.accessMatches ?: 0) + (result.summary?.offsetMatches ?: 0) + (result.summary?.fuzzyMatches ?: 0) - throw Exception("Failed to apply ${result.summary?.failedMatches}/$total hunks") - } - - return result.summary?.changedFiles - } - - private fun setupGitHook(outputPath: Path) { - val hook = outputPath.resolve(".git/hooks/post-rewrite") - hook.parent.createDirectories() - hook.writeText(javaClass.getResource("/post-rewrite.sh")!!.readText()) - hook.toFile().setExecutable(true) - } - - private fun commit() { - val ident = PersonIdent(PersonIdent("File", "noreply+automated@papermc.io"), Instant.parse("1997-04-20T13:37:42.69Z")) - val git = Git.open(output.path.toFile()) - git.add().addFilepattern(".").call() - git.commit() - .setMessage("${identifier.get()} File Patches") - .setAuthor(ident) - .setAllowEmpty(true) - .setSign(false) - .call() - git.tagDelete().setTags("file").call() - git.tag().setName("file").setTagger(ident).setSigned(false).call() - git.close() - } - - internal open fun mode(): PatchMode { - return PatchMode.OFFSET - } - - internal open fun minFuzz(): Float { - return FuzzyLineMatcher.DEFAULT_MIN_MATCH_SCORE - } -} From 96aecd8f31e71940c27e3e1107e6e14a2410dc5f Mon Sep 17 00:00:00 2001 From: Toffikk Date: Mon, 20 Oct 2025 01:43:22 +0200 Subject: [PATCH 133/257] fix: undo the accidental behaviour change introduced by the previous commit --- .../paperweight/core/taskcontainers/MinecraftPatchingTasks.kt | 2 +- .../papermc/paperweight/core/tasks/patching/ApplyFilePatches.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt index 63635a8c4..7361690da 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt @@ -265,8 +265,8 @@ class MinecraftPatchingTasks( group() description = "Rebuilds $configName file patches to the Minecraft resources" - input.set(outputResources) base.set(baseResources) + input.set(outputResources) patches.set(resourcePatchDir) gitFilePatches.set(this@MinecraftPatchingTasks.gitFilePatches) identifier = configName diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyFilePatches.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyFilePatches.kt index b0927c43b..d906078d1 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyFilePatches.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyFilePatches.kt @@ -106,7 +106,7 @@ abstract class ApplyFilePatches : BaseTask() { "main", "upstream", "main", - true, + false, ) setupGitHook(repoPath) tagBase() From 458f8eaf0ca6e05f61ee7c0870702623fba9fade Mon Sep 17 00:00:00 2001 From: Toffikk Date: Wed, 22 Oct 2025 00:21:37 +0200 Subject: [PATCH 134/257] fix(tests/logic): cleanup classes and fix some logic All tests are now fixed! --- .../extension/AdditionalUpstreamConfig.kt | 21 ------ .../core/taskcontainers/CoreTasks.kt | 2 +- .../taskcontainers/MinecraftPatchingTasks.kt | 16 ++--- .../core/taskcontainers/PatchingTasks.kt | 8 +-- .../core/tasks/patching/ApplyFilePatches.kt | 68 ++++++++++--------- .../core/tasks/patching/RebuildFilePatches.kt | 14 ++-- .../io/papermc/paperweight/FunctionalTest.kt | 6 +- .../tasks/patching/ApplyFilePatchesTest.kt | 4 +- .../functional_test/build-data/expected.at | 1 + .../fake-patches/expected/0001-Feature.patch | 6 +- .../fake-patches/expected/Test.java.patch | 8 ++- 11 files changed, 70 insertions(+), 84 deletions(-) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/AdditionalUpstreamConfig.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/AdditionalUpstreamConfig.kt index deca7d626..3f7fbabd0 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/AdditionalUpstreamConfig.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/AdditionalUpstreamConfig.kt @@ -22,7 +22,6 @@ package io.papermc.paperweight.core.extension -import javax.annotation.Nullable import javax.inject.Inject import org.gradle.api.Action import org.gradle.api.Named @@ -72,34 +71,18 @@ abstract class AdditionalUpstreamConfig @Inject constructor( return setName } - @get:Nullable abstract val generateSources: Property - - @get:Nullable abstract val generateResources: Property - - @get:Nullable abstract val generateTestSources: Property - - @get:Nullable abstract val generateTestResources: Property - @get:Nullable abstract val sourcesOutputDir: DirectoryProperty - - @get:Nullable abstract val resourcesOutputDir: DirectoryProperty - - @get:Nullable abstract val testSourcesOutputDir: DirectoryProperty - - @get:Nullable abstract val testResourcesOutputDir: DirectoryProperty - @get:Nullable abstract val additionalAts: RegularFileProperty - @get:Nullable abstract val additionalPatch: RegularFileProperty } } @@ -107,10 +90,8 @@ abstract class AdditionalUpstreamConfig @Inject constructor( abstract class PatchGenerationConfig @Inject constructor( objects: ObjectFactory ) { - @get:Nullable abstract val outputDir: DirectoryProperty - @get:Nullable abstract val patchesDirOutput: Property val inputConfig: NamedDomainObjectContainer = objects.domainObjectContainer( @@ -126,10 +107,8 @@ abstract class AdditionalUpstreamConfig @Inject constructor( return setName } - @get:Nullable abstract val additionalAts: RegularFileProperty - @get:Nullable abstract val additionalPatch: RegularFileProperty } } diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/CoreTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/CoreTasks.kt index 14e1da477..6c8a15a7f 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/CoreTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/CoreTasks.kt @@ -230,7 +230,7 @@ class CoreTasks( cfg.featurePatchDir, cfg.additionalAts, upstreamTasks.first.applyFeaturePatches.flatMap { it.repo }, - upstreamTasks.first.applyResourcePatches.flatMap { it.repo }, + upstreamTasks.first.applyResourcePatches.flatMap { it.output }, project.coreExt.gitFilePatches, project.coreExt.filterPatches, outputRoot, diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt index 7361690da..9c8572683 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt @@ -79,10 +79,10 @@ class MinecraftPatchingTasks( dependsOn(applyBasePatches) if (readOnly) { - base.set(applyBasePatches.flatMap { it.output }) - repo.set(outputSrcFile) + input.set(applyBasePatches.flatMap { it.output }) + output.set(outputSrcFile) } else { - repo.set(outputSrc) + output.set(outputSrc) } patches.set(sourcePatchDir.fileExists()) rejectsDir.set(this@MinecraftPatchingTasks.rejectsDir) @@ -120,8 +120,8 @@ class MinecraftPatchingTasks( group() description = "Applies $configName file patches to the Minecraft resources" - base.set(baseResources) - repo.set(outputResources) + input.set(baseResources) + output.set(outputResources) patches.set(resourcePatchDir.fileExists()) // TODO rejects? gitFilePatches.set(this@MinecraftPatchingTasks.gitFilePatches) @@ -141,7 +141,7 @@ class MinecraftPatchingTasks( dependsOn(applyFilePatches) if (readOnly) { - base.set(applySourcePatches.flatMap { it.repo }) + base.set(applySourcePatches.flatMap { it.output }) } repo.set(outputSrc) patches.set(featurePatchDir.fileExists()) @@ -211,11 +211,11 @@ class MinecraftPatchingTasks( } applySourcePatches.configure { - base.set(applyJavadocMappings.flatMap { it.outputDir }) + input.set(applyJavadocMappings.flatMap { it.outputDir }) } applySourcePatchesFuzzy.configure { - base.set(applyJavadocMappings.flatMap { it.outputDir }) + input.set(applyJavadocMappings.flatMap { it.outputDir }) } } diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt index 18f3b5622..1aff26314 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt @@ -68,10 +68,10 @@ class PatchingTasks( dependsOn(applyBasePatches) if (readOnly) { - base.set(applyBasePatches.flatMap { it.output }) - repo.set(layout.cache.resolve(paperTaskOutput())) + input.set(applyBasePatches.flatMap { it.output }) + output.set(layout.cache.resolve(paperTaskOutput())) } else { - repo.set(outputDir) + output.set(outputDir) } patches.set(filePatchDir.fileExists()) rejectsDir.set(this@PatchingTasks.rejectsDir) @@ -114,7 +114,7 @@ class PatchingTasks( repo.set(outputDir) if (readOnly) { - base.set(applyFilePatches.flatMap { it.repo }) + base.set(applyFilePatches.flatMap { it.output }) } patches.set(featurePatchDir.fileExists()) } diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyFilePatches.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyFilePatches.kt index d906078d1..5b0c067ed 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyFilePatches.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyFilePatches.kt @@ -62,11 +62,11 @@ abstract class ApplyFilePatches : BaseTask() { abstract val gitFilePatches: Property @get:OutputDirectory - abstract val repo: DirectoryProperty + abstract val output: DirectoryProperty @get:InputDirectory @get:Optional - abstract val base: DirectoryProperty + abstract val input: DirectoryProperty @get:Internal abstract val baseRef: Property @@ -95,42 +95,44 @@ abstract class ApplyFilePatches : BaseTask() { open fun run() { io.papermc.paperweight.util.Git.checkForGit() - val base = base.pathOrNull - val repoPath = repo.path + val input = input.pathOrNull + val outputPath = output.path - if (base != null && baseRef.get() == "main") { - recreateCloneDirectory(repoPath) + // special handling for resource patches + if (input != null && baseRef.get() == "main") { + recreateCloneDirectory(outputPath) checkoutRepoFromUpstream( - Git(repoPath), - base, + Git(outputPath), + input, "main", "upstream", - "main", - false, + baseRef.get(), + false, // we need to set ref to false for patching to properly work ) - setupGitHook(repoPath) + setupGitHook(outputPath) tagBase() } else { - if (base != null && base.toAbsolutePath() != repoPath.toAbsolutePath()) { - recreateCloneDirectory(repoPath) - val checkoutFromJDs = hasJavadocs(base, "${identifier.get()}JDs") + // rest of the original logic + if (input != null && input.toAbsolutePath() != outputPath.toAbsolutePath()) { + recreateCloneDirectory(outputPath) + val checkoutFromJDs = hasJavadocs(input, "${identifier.get()}JDs") val newRef = if (checkoutFromJDs) "${identifier.get()}JDs" else baseRef.get() baseRef.set(newRef) - val git = Git(repoPath.createDirectories()) + val git = Git(outputPath.createDirectories()) checkoutRepoFromUpstream( git, - base, + input, baseRef.get(), branchName = "main", ref = true, ) } else { - val checkoutFromJDs = hasJavadocs(repoPath, "${identifier.get()}JDs") + val checkoutFromJDs = hasJavadocs(outputPath, "${identifier.get()}JDs") val newRef = if (checkoutFromJDs) "${identifier.get()}JDs" else baseRef.get() baseRef.set(newRef) } - val git = Git(repoPath) + val git = Git(outputPath) if (git("checkout", "main").runSilently(silenceErr = true) != 0) { git("checkout", "-b", "main").runSilently(silenceErr = true) } @@ -141,8 +143,8 @@ abstract class ApplyFilePatches : BaseTask() { val result = if (!patches.isPresent) { commit() 0 - } else if (gitFilePatches.get() && shouldApplyWithGit(repoPath)) { - applyWithGit(repoPath) + } else if (gitFilePatches.get() && shouldApplyWithGit(outputPath)) { + applyWithGit(outputPath) } else { applyWithDiffPatch() } @@ -170,16 +172,16 @@ abstract class ApplyFilePatches : BaseTask() { } private fun tagBase() { - val git = Git.open(repo.path.toFile()) + val git = Git.open(output.path.toFile()) val ident = PersonIdent("base", "noreply+automated@papermc.io") git.tagDelete().setTags("base").call() git.tag().setName("base").setTagger(ident).setSigned(false).call() git.close() } - private fun shouldApplyWithGit(repoPath: Path): Boolean { + private fun shouldApplyWithGit(outputPath: Path): Boolean { val patchFiles = patches.path.filesMatchingRecursive("*.patch") - val git = Git(repoPath) + val git = Git(outputPath) val canApply = patchFiles.any { patch -> val result = git("apply", "--check", patch.absolutePathString()).getText(ignoreErr = true) result.contains("error: corrupt patch at line") @@ -187,12 +189,12 @@ abstract class ApplyFilePatches : BaseTask() { return !canApply } - private fun applyWithGit(repoPath: Path): Int { - val git = Git(repoPath) + private fun applyWithGit(outputPath: Path): Int { + val git = Git(outputPath) val patchFiles = patches.path.filesMatchingRecursive("*.patch") if (moveFailedGitPatchesToRejects.get() && rejectsDir.isPresent) { patchFiles.forEach { patch -> - val patchPathFromGit = repoPath.relativize(patch) + val patchPathFromGit = outputPath.relativize(patch) val responseCode = git( "-c", @@ -207,7 +209,7 @@ abstract class ApplyFilePatches : BaseTask() { responseCode == 1 -> { val relativePatch = patches.path.relativize(patch) val failedFile = relativePatch.parent.resolve(relativePatch.fileName.toString().substringBeforeLast(".patch")) - if (repoPath.resolve(failedFile).exists()) { + if (outputPath.resolve(failedFile).exists()) { git("reset", "--", failedFile.pathString).executeSilently(silenceOut = !verbose.get(), silenceErr = !verbose.get()) git("restore", failedFile.pathString).executeSilently(silenceOut = !verbose.get(), silenceErr = !verbose.get()) } @@ -218,7 +220,7 @@ abstract class ApplyFilePatches : BaseTask() { } } } else { - val patchStrings = patchFiles.map { repoPath.relativize(it).pathString } + val patchStrings = patchFiles.map { outputPath.relativize(it).pathString } patchStrings.chunked(12).forEach { git("apply", "--3way", *it.toTypedArray()).executeSilently(silenceOut = !verbose.get(), silenceErr = !verbose.get()) } @@ -232,9 +234,9 @@ abstract class ApplyFilePatches : BaseTask() { private fun applyWithDiffPatch(): Int? { val builder = PatchOperation.builder() .logTo(logger::lifecycle) - .baseInput(DiffInput.MultiInput.folder(repo.path)) + .baseInput(DiffInput.MultiInput.folder(output.path)) .patchesInput(DiffInput.MultiInput.folder(patches.path)) - .patchedOutput(DiffOutput.MultiOutput.folder(repo.path)) + .patchedOutput(DiffOutput.MultiOutput.folder(output.path)) .level(if (verbose.get()) io.codechicken.diffpatch.util.LogLevel.ALL else io.codechicken.diffpatch.util.LogLevel.INFO) .mode(mode()) .minFuzz(minFuzz()) @@ -267,7 +269,7 @@ abstract class ApplyFilePatches : BaseTask() { private fun commit() { val ident = PersonIdent(PersonIdent("File", "noreply+automated@papermc.io"), Instant.parse("1997-04-20T13:37:42.69Z")) - val git = Git.open(repo.path.toFile()) + val git = Git.open(output.path.toFile()) git.add().addFilepattern(".").call() git.commit() .setMessage("${identifier.get()} File Patches") @@ -280,8 +282,8 @@ abstract class ApplyFilePatches : BaseTask() { git.close() } - fun hasJavadocs(repoPath: Path, tag: String): Boolean { - val git = Git(repoPath) + fun hasJavadocs(outputPath: Path, tag: String): Boolean { + val git = Git(outputPath) val result = git("tag", "-l", tag).getText().trim() return result == tag } diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/RebuildFilePatches.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/RebuildFilePatches.kt index 849f1b704..21a352d21 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/RebuildFilePatches.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/RebuildFilePatches.kt @@ -61,6 +61,9 @@ abstract class RebuildFilePatches : JavaLauncherTask() { @get:InputDirectory abstract val base: DirectoryProperty + @get:Internal + abstract val baseDir: DirectoryProperty + @get:OutputDirectory abstract val patches: DirectoryProperty @@ -90,12 +93,14 @@ abstract class RebuildFilePatches : JavaLauncherTask() { contextLines.convention(3) verbose.convention(false) gitFilePatches.convention(false) + baseDir.set(layout.cache.resolve(paperTaskOutput())) } @TaskAction fun run() { val patchDir = patches.path.cleanDir() val inputDir = input.convertToPath() + val baseDir = baseDir.convertToPath() val git = Git(inputDir) @@ -124,10 +129,8 @@ abstract class RebuildFilePatches : JavaLauncherTask() { git("stash", "push").executeSilently(silenceErr = true) git("checkout", "file").executeSilently(silenceErr = true) - val baseCommit = git("rev-parse", "HEAD~1").getText().trim() - val baseDir = layout.cache.resolve(paperTaskOutput()) - - git("worktree", "add", baseDir.absolutePathString(), baseCommit).executeSilently(silenceErr = true) + val baseCommit = "file~1" + git("worktree", "add", "--force", baseDir.absolutePathString(), baseCommit).executeSilently(silenceErr = true) val filesWithNewAts = if (!ats.jst.isEmpty) { handleAts( @@ -155,7 +158,7 @@ abstract class RebuildFilePatches : JavaLauncherTask() { rebuildWithDiffPatch(baseDir, inputDir, patchDir) } - git("worktree", "remove", baseDir.absolutePathString(), "--force").executeSilently(silenceErr = true) + git("worktree", "remove", "--force", baseDir.absolutePathString()).executeSilently(silenceErr = true) git("switch", "-").executeSilently(silenceErr = true) if (filesWithNewAts.isNotEmpty()) { try { @@ -235,7 +238,6 @@ abstract class RebuildFilePatches : JavaLauncherTask() { return result.summary?.changedFiles } - // TODO: look into moving this earlier in the source tree, still most of it is broken anyway private fun handleAts( baseDir: Path, inputDir: Path, diff --git a/paperweight-core/src/test/kotlin/io/papermc/paperweight/FunctionalTest.kt b/paperweight-core/src/test/kotlin/io/papermc/paperweight/FunctionalTest.kt index 306b8d116..abe857cf0 100644 --- a/paperweight-core/src/test/kotlin/io/papermc/paperweight/FunctionalTest.kt +++ b/paperweight-core/src/test/kotlin/io/papermc/paperweight/FunctionalTest.kt @@ -91,7 +91,6 @@ class FunctionalTest { ) // add AT to source -> patch and AT file is updated - /* commented out since this also fails on normal paperweight println("adding at to source") modifyFile(tempDir.resolve("test-server/src/minecraft/java/Test.java")) { it.replace( @@ -105,16 +104,15 @@ class FunctionalTest { git("commit", "--fixup", "file").executeSilently() git("rebase", "--autosquash", "upstream/main").executeSilently() } - */ // base patch println("\nrebuilding base patch\n") val rebP2 = gradleRunner - .withArguments("rebuildPatches", "--stacktrace", "-Dfake=true") + .withArguments("rebuildBasePatches", "--stacktrace", "-Dfake=true") .withDebug(debug) .build() - assertEquals(rebP2.task(":test-server:rebuildPatches")?.outcome, TaskOutcome.SUCCESS) + assertEquals(rebP2.task(":test-server:rebuildBasePatches")?.outcome, TaskOutcome.SUCCESS) assertEquals( testResource.resolve("fake-patches/expected/0001-Test-Base.patch").readText(), tempDir.resolve("fake-patches/base/0001-Test-Base.patch").readText() diff --git a/paperweight-core/src/test/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyFilePatchesTest.kt b/paperweight-core/src/test/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyFilePatchesTest.kt index 81a683099..fe490b605 100644 --- a/paperweight-core/src/test/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyFilePatchesTest.kt +++ b/paperweight-core/src/test/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyFilePatchesTest.kt @@ -58,8 +58,8 @@ class ApplyFilePatchesTest : TaskTest() { task.output.set(output) task.identifier.set("test") - task2.base.set(task.output) - task2.repo.set(output) + task2.input.set(task.output) + task2.output.set(output) task2.patches.set(patches) task2.verbose.set(true) task2.identifier.set("test") diff --git a/paperweight-core/src/test/resources/functional_test/build-data/expected.at b/paperweight-core/src/test/resources/functional_test/build-data/expected.at index ec24867b7..a4d941bfc 100644 --- a/paperweight-core/src/test/resources/functional_test/build-data/expected.at +++ b/paperweight-core/src/test/resources/functional_test/build-data/expected.at @@ -2,3 +2,4 @@ # See CONTRIBUTING.md on how to add access transformers. public+f Test dum2 public-f Test getTest()Ljava/lang/String; +public-f Test getTest2()Ljava/lang/String; diff --git a/paperweight-core/src/test/resources/functional_test/fake-patches/expected/0001-Feature.patch b/paperweight-core/src/test/resources/functional_test/fake-patches/expected/0001-Feature.patch index 6f02cfb6d..a2cdcaa92 100644 --- a/paperweight-core/src/test/resources/functional_test/fake-patches/expected/0001-Feature.patch +++ b/paperweight-core/src/test/resources/functional_test/fake-patches/expected/0001-Feature.patch @@ -5,12 +5,12 @@ Subject: [PATCH] Feature diff --git a/Test.java b/Test.java -index b5106a3b36987dfdd5141ae1cbd229e6db020cd9..412773ad543cc9a5116044dc138cc08300b2dd1b 100644 +index ec4473011075a4028645a320ed777c8adce0f77d..55fb66e3462ad6acfb7054f416f07d142672b92c 100644 --- a/Test.java +++ b/Test.java @@ -13,4 +13,8 @@ public class Test { - public final String getTest2() { - return this.test + "2"; + public String getTest2() { + return this.test + "2"; // Woo } + + public String feature() { diff --git a/paperweight-core/src/test/resources/functional_test/fake-patches/expected/Test.java.patch b/paperweight-core/src/test/resources/functional_test/fake-patches/expected/Test.java.patch index 492f2f93a..08ab79fc2 100644 --- a/paperweight-core/src/test/resources/functional_test/fake-patches/expected/Test.java.patch +++ b/paperweight-core/src/test/resources/functional_test/fake-patches/expected/Test.java.patch @@ -1,6 +1,6 @@ --- a/Test.java +++ b/Test.java -@@ -7,7 +_,7 @@ +@@ -7,10 +_,10 @@ } public String getTest() { @@ -8,4 +8,8 @@ + return this.test; // WOOOO SOURCE } - public final String getTest2() { + public String getTest2() { +- return this.test + "2"; ++ return this.test + "2"; // Woo + } + } From c9d9481f3be4b3e16aa15c97a91fb22a436ed562 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Wed, 22 Oct 2025 00:32:47 +0200 Subject: [PATCH 135/257] [ci-skip] move some hunks around and adjust path sensitivity --- .../paperweight/core/tasks/GeneratePatches.kt | 2 +- .../paperweight/core/tasks/GenerateSources.kt | 2 +- .../core/tasks/PrepareForPatchGeneration.kt | 4 ++-- .../core/tasks/patching/ApplyFilePatches.kt | 15 ++++++++------- 4 files changed, 12 insertions(+), 11 deletions(-) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/GeneratePatches.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/GeneratePatches.kt index aa7fdd3b6..cd7d84825 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/GeneratePatches.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/GeneratePatches.kt @@ -48,7 +48,7 @@ abstract class GeneratePatches : BaseTask() { abstract val inputFrom: Property @get:InputFiles - @get:PathSensitive(PathSensitivity.RELATIVE) + @get:PathSensitive(PathSensitivity.NONE) abstract val preparedSource: ConfigurableFileCollection @get:Input diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/GenerateSources.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/GenerateSources.kt index 0c77e83e1..ca2bcaa06 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/GenerateSources.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/GenerateSources.kt @@ -60,7 +60,7 @@ abstract class GenerateSources : JavaLauncherTask() { @get:InputFile @get:Optional - @get:PathSensitive(PathSensitivity.RELATIVE) + @get:PathSensitive(PathSensitivity.NONE) abstract val atFile: RegularFileProperty @get:InputFile diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/PrepareForPatchGeneration.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/PrepareForPatchGeneration.kt index debb0c67f..0bde777f6 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/PrepareForPatchGeneration.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/PrepareForPatchGeneration.kt @@ -52,12 +52,12 @@ abstract class PrepareForPatchGeneration : JavaLauncherTask() { @get:InputFile @get:Optional - @get:PathSensitive(PathSensitivity.RELATIVE) + @get:PathSensitive(PathSensitivity.NONE) abstract val atFile: RegularFileProperty @get:InputFile @get:Optional - @get:PathSensitive(PathSensitivity.RELATIVE) + @get:PathSensitive(PathSensitivity.NONE) abstract val additionalPatch: RegularFileProperty @get:OutputDirectory diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyFilePatches.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyFilePatches.kt index 5b0c067ed..0ffd57eba 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyFilePatches.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyFilePatches.kt @@ -49,6 +49,14 @@ abstract class ApplyFilePatches : BaseTask() { ) abstract val verbose: Property + @get:Optional + @get:PathSensitive(PathSensitivity.NONE) + @get:InputDirectory + abstract val input: DirectoryProperty + + @get:OutputDirectory + abstract val output: DirectoryProperty + @get:PathSensitive(PathSensitivity.NONE) @get:InputDirectory @get:Optional @@ -61,13 +69,6 @@ abstract class ApplyFilePatches : BaseTask() { @get:Input abstract val gitFilePatches: Property - @get:OutputDirectory - abstract val output: DirectoryProperty - - @get:InputDirectory - @get:Optional - abstract val input: DirectoryProperty - @get:Internal abstract val baseRef: Property From 23deafb7dbe8465aa011983385a668edcbcf18c9 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Wed, 22 Oct 2025 19:22:08 +0200 Subject: [PATCH 136/257] chore: remove unnecessary rebuild safeguards This in practice should never happen due to the task wiring --- .../core/tasks/patching/RebuildFilePatches.kt | 12 +++--------- .../paperweight/tasks/RebuildBaseGitPatches.kt | 12 +++--------- 2 files changed, 6 insertions(+), 18 deletions(-) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/RebuildFilePatches.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/RebuildFilePatches.kt index 21a352d21..cf000c622 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/RebuildFilePatches.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/RebuildFilePatches.kt @@ -103,25 +103,19 @@ abstract class RebuildFilePatches : JavaLauncherTask() { val baseDir = baseDir.convertToPath() val git = Git(inputDir) - - // fail fast if someone decides to name a patch with this name to avoid patch corruption val fileCommit = git( "log", "--format=%H %s", "--grep=^${identifier.get()} File Patches$", "base..HEAD" ).getText().lines().filter { it.isNotBlank() }.map { it.substringBefore(" ") } + // fail fast if someone decides to name a patch with this name to avoid patch corruption if (fileCommit.size > 1) { - val count = fileCommit.size throw PaperweightException( - "Exceeded the max amount of commits with the identifier: '${identifier.get()} File Patches' !\nGot $count commits, expected: 1" - ) - } else if (fileCommit.isEmpty()) { - throw PaperweightException( - "Could not find a commit with the identifier: '${identifier.get()} File Patches' !\nHave you applied patches before rebuilding?" + "Exceeded the max amount of commits with the identifier: '${identifier.get()} File Patches' !\n" + + "Got ${fileCommit.size} commits, expected: 1" ) } - // we update the appropriate tag to reflect the new repo state git("tag", "-f", "file", fileCommit.joinToString()).executeSilently(silenceErr = true) diff --git a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RebuildBaseGitPatches.kt b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RebuildBaseGitPatches.kt index cdca19a27..2f5d81d4b 100644 --- a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RebuildBaseGitPatches.kt +++ b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RebuildBaseGitPatches.kt @@ -75,25 +75,19 @@ abstract class RebuildBaseGitPatches : ControllableOutputTask() { @TaskAction fun run() { val git = Git(inputDir.path) - - // fail fast if someone decides to name a patch with this name to avoid patch corruption val basepatchesCommit = git( "log", "--format=%H %s", "--grep=^${identifier.get()} Base Patches$", "base..HEAD" ).getText().lines().filter { it.isNotBlank() }.map { it.substringBefore(" ") } + // fail fast if someone decides to name a patch with this name to avoid patch corruption if (basepatchesCommit.size > 1) { - val count = basepatchesCommit.size throw PaperweightException( - "Exceeded the max amount of commits with the identifier: '${identifier.get()} Base Patches' !\nGot $count commits, expected: 1" - ) - } else if (basepatchesCommit.isEmpty()) { - throw PaperweightException( - "Could not find a commit with the identifier: '${identifier.get()} Base Patches' !\nHave you applied patches before rebuilding?" + "Exceeded the max amount of commits with the identifier: '${identifier.get()} Base Patches' !\n" + + "Got ${basepatchesCommit.size} commits, expected: 1" ) } - // we update the tag to reflect the new repo state git("tag", "-f", "basepatches", basepatchesCommit.joinToString()).executeSilently(silenceErr = true) From 5051d8dec03abd56e71d2e6c1d94baa8d0aa2dd6 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Wed, 22 Oct 2025 22:14:22 +0200 Subject: [PATCH 137/257] release: Version 2.3.10 --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 5ba678855..2a6e879f3 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ group = io.canvasmc.weaver -version = 2.3.10-SNAPSHOT +version = 2.3.10 org.gradle.caching = true org.gradle.configuration-cache = true From 1121ea3398e62f596d1111dceebc3f747df14d98 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Wed, 22 Oct 2025 22:25:51 +0200 Subject: [PATCH 138/257] chore: continue development cycle --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 2a6e879f3..13e141111 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ group = io.canvasmc.weaver -version = 2.3.10 +version = 2.3.11-SNAPSHOT org.gradle.caching = true org.gradle.configuration-cache = true From f94f26e25ed53abc3a173bfaf466da6178706186 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Sat, 8 Nov 2025 20:27:54 +0100 Subject: [PATCH 139/257] fix(deps): update diffpatch to v2.1.0.42 --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 90615a7de..2088dd5ee 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -32,7 +32,7 @@ hypo-mappings = { module = "dev.denwav.hypo:hypo-mappings", version.ref = "hypo" lorenzTiny = "net.fabricmc:lorenz-tiny:3.0.0" jbsdiff = "io.sigpipe:jbsdiff:1.0" -diffpatch = "io.codechicken:DiffPatch:2.0.1.40" +diffpatch = "io.codechicken:DiffPatch:2.1.0.42" serialize-core = { module = "org.jetbrains.kotlinx:kotlinx-serialization-core", version.ref = "serialize" } serialize-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "serialize" } From 2479ee2a536d9aad36c7e90ebd0174a520c3f764 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Sun, 9 Nov 2025 21:07:03 +0100 Subject: [PATCH 140/257] chore: rename the internal javadoc apply task for naming parity --- .../paperweight/core/taskcontainers/MinecraftPatchingTasks.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt index 9c8572683..f391d1978 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt @@ -201,8 +201,8 @@ class MinecraftPatchingTasks( input.set(setup.flatMap { it.outputDir }) } - val applyJavadocMappings = tasks.register("applyJavadocMappingsFrom${configName.capitalized()}ToMinecraft") { - description = "When applicable, applies javadocs from the specified parchment mappings in $configName to Minecraft for source patch apply" + val applyJavadocMappings = tasks.register("apply${configName.capitalized()}JavadocMappings") { + description = "Applies javadocs from the specified parchment mappings in $configName to Minecraft sources" inputDir.set(applyBasePatches.flatMap { it.output }) outputDir.set(layout.cache.resolve(paperTaskOutput())) identifier.set(configName) From b611eed57925c98ce9f048d633a4f86a87710697 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Sun, 9 Nov 2025 21:16:21 +0100 Subject: [PATCH 141/257] chore: use the built in github repo secret for release deployments --- .github/workflows/deploy.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 2f91dadf1..8b7f3db28 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -26,5 +26,5 @@ jobs: - name: Create release and changelog uses: MC-Machinations/auto-release-changelog@v1.1.3 with: - token: ${{ secrets.SUMMARY_TOKEN }} + token: ${{ secrets.GITHUB_TOKEN }} title: weaver ${{ steps.vars.outputs.tag }} From 8bd560eb4b845d62dec48c0b65d9fffb61ab5e56 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Sun, 9 Nov 2025 22:47:50 +0100 Subject: [PATCH 142/257] chore: move commit validation to an util --- .../core/tasks/patching/RebuildFilePatches.kt | 18 ++++++++++-------- .../tasks/RebuildBaseGitPatches.kt | 19 ++++++++++--------- .../kotlin/io/papermc/paperweight/util/git.kt | 9 +++++++++ 3 files changed, 29 insertions(+), 17 deletions(-) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/RebuildFilePatches.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/RebuildFilePatches.kt index cf000c622..6e537c4e5 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/RebuildFilePatches.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/RebuildFilePatches.kt @@ -108,14 +108,16 @@ abstract class RebuildFilePatches : JavaLauncherTask() { "--format=%H %s", "--grep=^${identifier.get()} File Patches$", "base..HEAD" - ).getText().lines().filter { it.isNotBlank() }.map { it.substringBefore(" ") } - // fail fast if someone decides to name a patch with this name to avoid patch corruption - if (fileCommit.size > 1) { - throw PaperweightException( - "Exceeded the max amount of commits with the identifier: '${identifier.get()} File Patches' !\n" + - "Got ${fileCommit.size} commits, expected: 1" - ) - } + ).getText() + .lineSequence() + .map(String::trim) + .filter(String::isNotEmpty) + .map { it.substringBefore(" ") } + .toList() + + // throw if false, since that means the repo state is corrupted and we can't rebuild safely + validateSingleCommit(identifier, "File", fileCommit) + // we update the appropriate tag to reflect the new repo state git("tag", "-f", "file", fileCommit.joinToString()).executeSilently(silenceErr = true) diff --git a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RebuildBaseGitPatches.kt b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RebuildBaseGitPatches.kt index 2f5d81d4b..5f05dbe87 100644 --- a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RebuildBaseGitPatches.kt +++ b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RebuildBaseGitPatches.kt @@ -22,7 +22,6 @@ package io.papermc.paperweight.tasks -import io.papermc.paperweight.PaperweightException import io.papermc.paperweight.util.* import java.nio.file.Path import java.util.concurrent.ConcurrentLinkedQueue @@ -80,14 +79,16 @@ abstract class RebuildBaseGitPatches : ControllableOutputTask() { "--format=%H %s", "--grep=^${identifier.get()} Base Patches$", "base..HEAD" - ).getText().lines().filter { it.isNotBlank() }.map { it.substringBefore(" ") } - // fail fast if someone decides to name a patch with this name to avoid patch corruption - if (basepatchesCommit.size > 1) { - throw PaperweightException( - "Exceeded the max amount of commits with the identifier: '${identifier.get()} Base Patches' !\n" + - "Got ${basepatchesCommit.size} commits, expected: 1" - ) - } + ).getText() + .lineSequence() + .map(String::trim) + .filter(String::isNotEmpty) + .map { it.substringBefore(" ") } + .toList() + + // throw if false, since that means the repo state is corrupted and we can't rebuild safely + validateSingleCommit(identifier, "Base", basepatchesCommit) + // we update the tag to reflect the new repo state git("tag", "-f", "basepatches", basepatchesCommit.joinToString()).executeSilently(silenceErr = true) diff --git a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/git.kt b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/git.kt index 00b52d4d2..3f6f14d39 100644 --- a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/git.kt +++ b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/git.kt @@ -292,3 +292,12 @@ fun checkoutRepoFromUpstream( .executeSilently(silenceErr = true) git("gc").runSilently(silenceErr = true) } + +fun validateSingleCommit(identifier: Provider, kind: String, commits: List) { + if (commits.size != 1) { + throw PaperweightException( + "Invalid amount of commits with the identifier: '${identifier.get()} $kind Patches'!\n" + + "Got ${commits.size} commits, expected: 1" + ) + } +} From f2435eafca844012cec85499cd55d07e254e48d8 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Sun, 30 Nov 2025 08:32:13 +0100 Subject: [PATCH 143/257] feat: hooks --- .../paperweight/userdev/PaperweightUser.kt | 1 + .../userdev/PaperweightUserExtension.kt | 7 +++++++ .../userdev/internal/setup/UserdevSetupTask.kt | 16 +++++++++++++++- 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/PaperweightUser.kt b/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/PaperweightUser.kt index 116715a4d..e31e5679c 100644 --- a/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/PaperweightUser.kt +++ b/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/PaperweightUser.kt @@ -112,6 +112,7 @@ abstract class PaperweightUser : Plugin { val setupTask = target.tasks.register("paperweightUserdevSetup", UserdevSetupTask::class) { group = GENERAL_TASK_GROUP launcher.set(userdev.javaLauncher) + injectServerJar.set(userdev.injectServerJar) } target.dependencies.extensions.create( diff --git a/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/PaperweightUserExtension.kt b/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/PaperweightUserExtension.kt index f7186e5d4..922a4c18c 100644 --- a/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/PaperweightUserExtension.kt +++ b/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/PaperweightUserExtension.kt @@ -66,6 +66,13 @@ abstract class PaperweightUserExtension( ) ) + /** + * Whether to inject the server jar into the generated compile/runtime configurations + * + * True by defaults. + */ + val injectServerJar: Property = objects.property().convention(true) + /** * Whether to patch dependencies to exclude `junit:junit` from the transitive dependencies of `com.googlecode.json-simple:json-simple`. * diff --git a/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/internal/setup/UserdevSetupTask.kt b/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/internal/setup/UserdevSetupTask.kt index 0a6b0c565..b6e24e143 100644 --- a/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/internal/setup/UserdevSetupTask.kt +++ b/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/internal/setup/UserdevSetupTask.kt @@ -34,6 +34,7 @@ import org.gradle.api.file.RegularFileProperty import org.gradle.api.provider.Property import org.gradle.api.services.ServiceReference import org.gradle.api.tasks.CompileClasspath +import org.gradle.api.tasks.Input import org.gradle.api.tasks.InputFiles import org.gradle.api.tasks.Optional import org.gradle.api.tasks.OutputFile @@ -91,9 +92,16 @@ abstract class UserdevSetupTask : JavaLauncherTask() { @get:Inject abstract val progressLoggerFactory: ProgressLoggerFactory + @get:Input + abstract val injectServerJar: Property + + @get:OutputFile + abstract val processedServerJar: RegularFileProperty + override fun init() { super.init() mappedServerJar.set(layout.cache.resolve(paperTaskOutput("mappedServerJar", "jar"))) + processedServerJar.set(layout.cache.resolve(paperTaskOutput("processedServerJar", "jar"))) reobfMappings.set(layout.cache.resolve(paperTaskOutput("reobfMappings", "tiny"))) } @@ -123,7 +131,13 @@ abstract class UserdevSetupTask : JavaLauncherTask() { logger.lifecycle("Completed setup in ${formatNs(generatedIn)}") val copiedTime = measureNanoTime { - result.mainOutput.copyTo(mappedServerJar.path.createParentDirectories(), overwrite = true) + // for horizon, a bit dirty + if (injectServerJar.get()) { + result.mainOutput.copyTo(mappedServerJar.path.createParentDirectories(), overwrite = true) + } else { + result.mainOutput.copyTo(processedServerJar.path.createParentDirectories(), overwrite = true) + mappedServerJar.path.deleteForcefully() + } result.legacyOutput?.copyTo(legacyPaperclipResult.path.createParentDirectories(), overwrite = true) reobfMappings.path.createParentDirectories().deleteForcefully() setupService.get().extractReobfMappings(reobfMappings.path.createParentDirectories()) From 049578752acbddb1636d1cd0de8c808e791d8f9a Mon Sep 17 00:00:00 2001 From: Toffikk Date: Mon, 1 Dec 2025 09:39:08 +0100 Subject: [PATCH 144/257] fix: improve horizon hooks --- .../paperweight/userdev/PaperweightUser.kt | 8 +++---- .../userdev/internal/setup/SetupHandler.kt | 5 +++-- .../internal/setup/SetupHandlerImpl.kt | 19 +++++++++++++---- .../userdev/internal/setup/UserdevSetup.kt | 16 ++++++++++---- .../internal/setup/UserdevSetupTask.kt | 16 +------------- .../internal/setup/v2/SetupHandlerImplV2.kt | 17 ++++++++++++--- .../internal/setup/v5/SetupHandlerImplV5.kt | 21 +++++++++++++++---- 7 files changed, 66 insertions(+), 36 deletions(-) diff --git a/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/PaperweightUser.kt b/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/PaperweightUser.kt index e31e5679c..1b96be22b 100644 --- a/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/PaperweightUser.kt +++ b/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/PaperweightUser.kt @@ -112,7 +112,6 @@ abstract class PaperweightUser : Plugin { val setupTask = target.tasks.register("paperweightUserdevSetup", UserdevSetupTask::class) { group = GENERAL_TASK_GROUP launcher.set(userdev.javaLauncher) - injectServerJar.set(userdev.injectServerJar) } target.dependencies.extensions.create( @@ -122,7 +121,7 @@ abstract class PaperweightUser : Plugin { target.dependencyFactory, ) - createConfigurations(target, target.provider { userdevSetup }, setupTask) + createConfigurations(target, target.provider { userdevSetup }, setupTask, userdev) val reobfJar by target.tasks.registering { group = GENERAL_TASK_GROUP @@ -322,6 +321,7 @@ abstract class PaperweightUser : Plugin { target: Project, userdevSetup: Provider, setupTask: TaskProvider, + userdev: PaperweightUserExtension, ) { target.configurations.register(MACHE_CONFIG) { dependenciesFrom { userdevSetup.get().mache } @@ -353,14 +353,14 @@ abstract class PaperweightUser : Plugin { target.configurations.register(MOJANG_MAPPED_SERVER_CONFIG) { defaultDependencies { userdevSetup.get() - .populateCompileConfiguration(createContext(target, setupTask), this) + .populateCompileConfiguration(createContext(target, setupTask), this, userdev.injectServerJar) } } target.configurations.register(MOJANG_MAPPED_SERVER_RUNTIME_CONFIG) { defaultDependencies { userdevSetup.get() - .populateRuntimeConfiguration(createContext(target, setupTask), this) + .populateRuntimeConfiguration(createContext(target, setupTask), this, userdev.injectServerJar) } } } diff --git a/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/internal/setup/SetupHandler.kt b/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/internal/setup/SetupHandler.kt index 14ac83dba..309430cb0 100644 --- a/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/internal/setup/SetupHandler.kt +++ b/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/internal/setup/SetupHandler.kt @@ -37,6 +37,7 @@ import org.gradle.api.artifacts.dsl.DependencyFactory import org.gradle.api.file.FileCollection import org.gradle.api.file.ProjectLayout import org.gradle.api.logging.Logger +import org.gradle.api.provider.Property import org.gradle.api.tasks.TaskProvider import org.gradle.internal.logging.progress.ProgressLogger import org.gradle.internal.logging.progress.ProgressLoggerFactory @@ -46,9 +47,9 @@ import org.gradle.kotlin.dsl.* import org.gradle.workers.WorkerExecutor interface SetupHandler { - fun populateCompileConfiguration(context: ConfigurationContext, dependencySet: DependencySet) + fun populateCompileConfiguration(context: ConfigurationContext, dependencySet: DependencySet, injectServerJar: Property) - fun populateRuntimeConfiguration(context: ConfigurationContext, dependencySet: DependencySet) + fun populateRuntimeConfiguration(context: ConfigurationContext, dependencySet: DependencySet, injectServerJar: Property) data class ArtifactsResult( val mainOutput: Path, diff --git a/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/internal/setup/SetupHandlerImpl.kt b/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/internal/setup/SetupHandlerImpl.kt index 695d55c2e..d91eef9c5 100644 --- a/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/internal/setup/SetupHandlerImpl.kt +++ b/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/internal/setup/SetupHandlerImpl.kt @@ -42,6 +42,7 @@ import java.nio.file.Path import kotlin.io.path.* import org.gradle.api.artifacts.DependencySet import org.gradle.api.file.FileCollection +import org.gradle.api.provider.Property import org.gradle.kotlin.dsl.* class SetupHandlerImpl( @@ -151,13 +152,23 @@ class SetupHandlerImpl( return dispatcher } - override fun populateCompileConfiguration(context: SetupHandler.ConfigurationContext, dependencySet: DependencySet) { - dependencySet.add(context.dependencyFactory.create(context.layout.files(context.setupTask.flatMap { it.mappedServerJar }))) + override fun populateCompileConfiguration( + context: SetupHandler.ConfigurationContext, + dependencySet: DependencySet, + injectServerJar: Property + ) { + if (injectServerJar.get()) { + dependencySet.add(context.dependencyFactory.create(context.layout.files(context.setupTask.flatMap { it.mappedServerJar }))) + } dependencySet.add(context.dependencyFactory.create(context.devBundleCoordinates)) } - override fun populateRuntimeConfiguration(context: SetupHandler.ConfigurationContext, dependencySet: DependencySet) { - populateCompileConfiguration(context, dependencySet) + override fun populateRuntimeConfiguration( + context: SetupHandler.ConfigurationContext, + dependencySet: DependencySet, + injectServerJar: Property + ) { + populateCompileConfiguration(context, dependencySet, injectServerJar) } @Volatile diff --git a/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/internal/setup/UserdevSetup.kt b/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/internal/setup/UserdevSetup.kt index 41154f8d1..dfc464334 100644 --- a/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/internal/setup/UserdevSetup.kt +++ b/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/internal/setup/UserdevSetup.kt @@ -66,12 +66,20 @@ abstract class UserdevSetup : BuildService, SetupHandle } // begin delegate to setup - override fun populateCompileConfiguration(context: SetupHandler.ConfigurationContext, dependencySet: DependencySet) { - setup.populateCompileConfiguration(context, dependencySet) + override fun populateCompileConfiguration( + context: SetupHandler.ConfigurationContext, + dependencySet: DependencySet, + injectServerJar: Property + ) { + setup.populateCompileConfiguration(context, dependencySet, injectServerJar) } - override fun populateRuntimeConfiguration(context: SetupHandler.ConfigurationContext, dependencySet: DependencySet) { - setup.populateRuntimeConfiguration(context, dependencySet) + override fun populateRuntimeConfiguration( + context: SetupHandler.ConfigurationContext, + dependencySet: DependencySet, + injectServerJar: Property + ) { + setup.populateRuntimeConfiguration(context, dependencySet, injectServerJar) } override fun generateArtifacts(context: SetupHandler.ExecutionContext): SetupHandler.ArtifactsResult { diff --git a/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/internal/setup/UserdevSetupTask.kt b/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/internal/setup/UserdevSetupTask.kt index b6e24e143..0a6b0c565 100644 --- a/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/internal/setup/UserdevSetupTask.kt +++ b/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/internal/setup/UserdevSetupTask.kt @@ -34,7 +34,6 @@ import org.gradle.api.file.RegularFileProperty import org.gradle.api.provider.Property import org.gradle.api.services.ServiceReference import org.gradle.api.tasks.CompileClasspath -import org.gradle.api.tasks.Input import org.gradle.api.tasks.InputFiles import org.gradle.api.tasks.Optional import org.gradle.api.tasks.OutputFile @@ -92,16 +91,9 @@ abstract class UserdevSetupTask : JavaLauncherTask() { @get:Inject abstract val progressLoggerFactory: ProgressLoggerFactory - @get:Input - abstract val injectServerJar: Property - - @get:OutputFile - abstract val processedServerJar: RegularFileProperty - override fun init() { super.init() mappedServerJar.set(layout.cache.resolve(paperTaskOutput("mappedServerJar", "jar"))) - processedServerJar.set(layout.cache.resolve(paperTaskOutput("processedServerJar", "jar"))) reobfMappings.set(layout.cache.resolve(paperTaskOutput("reobfMappings", "tiny"))) } @@ -131,13 +123,7 @@ abstract class UserdevSetupTask : JavaLauncherTask() { logger.lifecycle("Completed setup in ${formatNs(generatedIn)}") val copiedTime = measureNanoTime { - // for horizon, a bit dirty - if (injectServerJar.get()) { - result.mainOutput.copyTo(mappedServerJar.path.createParentDirectories(), overwrite = true) - } else { - result.mainOutput.copyTo(processedServerJar.path.createParentDirectories(), overwrite = true) - mappedServerJar.path.deleteForcefully() - } + result.mainOutput.copyTo(mappedServerJar.path.createParentDirectories(), overwrite = true) result.legacyOutput?.copyTo(legacyPaperclipResult.path.createParentDirectories(), overwrite = true) reobfMappings.path.createParentDirectories().deleteForcefully() setupService.get().extractReobfMappings(reobfMappings.path.createParentDirectories()) diff --git a/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/internal/setup/v2/SetupHandlerImplV2.kt b/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/internal/setup/v2/SetupHandlerImplV2.kt index 55b9b9f65..a542a48c7 100644 --- a/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/internal/setup/v2/SetupHandlerImplV2.kt +++ b/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/internal/setup/v2/SetupHandlerImplV2.kt @@ -37,6 +37,7 @@ import io.papermc.paperweight.util.constants.* import java.nio.file.Path import kotlin.io.path.* import org.gradle.api.artifacts.DependencySet +import org.gradle.api.provider.Property import org.gradle.jvm.toolchain.JavaLanguageVersion import org.gradle.kotlin.dsl.* @@ -214,8 +215,14 @@ class SetupHandlerImplV2( return dispatcher } - override fun populateCompileConfiguration(context: SetupHandler.ConfigurationContext, dependencySet: DependencySet) { - dependencySet.add(context.dependencyFactory.create(context.layout.files(context.setupTask.flatMap { it.mappedServerJar }))) + override fun populateCompileConfiguration( + context: SetupHandler.ConfigurationContext, + dependencySet: DependencySet, + injectServerJar: Property + ) { + if (injectServerJar.get()) { + dependencySet.add(context.dependencyFactory.create(context.layout.files(context.setupTask.flatMap { it.mappedServerJar }))) + } listOfNotNull( bundle.config.apiCoordinates, bundle.config.mojangApiCoordinates @@ -227,7 +234,11 @@ class SetupHandlerImplV2( } } - override fun populateRuntimeConfiguration(context: SetupHandler.ConfigurationContext, dependencySet: DependencySet) { + override fun populateRuntimeConfiguration( + context: SetupHandler.ConfigurationContext, + dependencySet: DependencySet, + injectServerJar: Property + ) { dependencySet.add(context.dependencyFactory.create(context.layout.files(context.setupTask.flatMap { it.legacyPaperclipResult }))) } diff --git a/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/internal/setup/v5/SetupHandlerImplV5.kt b/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/internal/setup/v5/SetupHandlerImplV5.kt index 69cf54b60..c360a80e4 100644 --- a/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/internal/setup/v5/SetupHandlerImplV5.kt +++ b/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/internal/setup/v5/SetupHandlerImplV5.kt @@ -39,6 +39,7 @@ import io.papermc.paperweight.util.constants.* import java.nio.file.Path import kotlin.io.path.* import org.gradle.api.artifacts.DependencySet +import org.gradle.api.provider.Property import org.gradle.jvm.toolchain.JavaLanguageVersion import org.gradle.kotlin.dsl.* @@ -184,8 +185,14 @@ class SetupHandlerImplV5( return dispatcher } - override fun populateCompileConfiguration(context: SetupHandler.ConfigurationContext, dependencySet: DependencySet) { - dependencySet.add(context.dependencyFactory.create(context.layout.files(context.setupTask.flatMap { it.mappedServerJar }))) + override fun populateCompileConfiguration( + context: SetupHandler.ConfigurationContext, + dependencySet: DependencySet, + injectServerJar: Property + ) { + if (injectServerJar.get()) { + dependencySet.add(context.dependencyFactory.create(context.layout.files(context.setupTask.flatMap { it.mappedServerJar }))) + } listOfNotNull( bundle.config.apiCoordinates, bundle.config.mojangApiCoordinates @@ -197,8 +204,14 @@ class SetupHandlerImplV5( } } - override fun populateRuntimeConfiguration(context: SetupHandler.ConfigurationContext, dependencySet: DependencySet) { - dependencySet.add(context.dependencyFactory.create(context.layout.files(context.setupTask.flatMap { it.mappedServerJar }))) + override fun populateRuntimeConfiguration( + context: SetupHandler.ConfigurationContext, + dependencySet: DependencySet, + injectServerJar: Property + ) { + if (injectServerJar.get()) { + dependencySet.add(context.dependencyFactory.create(context.layout.files(context.setupTask.flatMap { it.mappedServerJar }))) + } listOfNotNull( bundle.config.apiCoordinates, bundle.config.mojangApiCoordinates From 811ee1a07cdf0f1ef719b3e6678e8b896f7af6fe Mon Sep 17 00:00:00 2001 From: "tofik.07" Date: Wed, 10 Dec 2025 12:27:10 +0100 Subject: [PATCH 145/257] chore: fix checkstyle plugin application test --- .../io/papermc/paperweight/checkstyle/PaperCheckstyleTest.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/paperweight-core/src/test/kotlin/io/papermc/paperweight/checkstyle/PaperCheckstyleTest.kt b/paperweight-core/src/test/kotlin/io/papermc/paperweight/checkstyle/PaperCheckstyleTest.kt index c10c16cfd..b6a22d7c8 100644 --- a/paperweight-core/src/test/kotlin/io/papermc/paperweight/checkstyle/PaperCheckstyleTest.kt +++ b/paperweight-core/src/test/kotlin/io/papermc/paperweight/checkstyle/PaperCheckstyleTest.kt @@ -42,7 +42,7 @@ class PaperCheckstyleTest { @Test fun testPluginApplication(@TempDir tmpDir: Path) { val project = setupProject(tmpDir) - project.pluginManager.apply("io.papermc.paperweight.paper-checkstyle") + project.pluginManager.apply("io.canvasmc.weaver.paper-checkstyle") assertNotNull(project.plugins.getPlugin(PaperCheckstylePlugin::class)) assertNotNull(project.extensions.getByType(CheckstyleExtension::class)) From e927d297d6b6a03d2ae7c67d54c9b481d350d745 Mon Sep 17 00:00:00 2001 From: "tofik.07" Date: Wed, 10 Dec 2025 12:34:10 +0100 Subject: [PATCH 146/257] chore: change checkstyle plugin coordinates --- ....properties => io.canvasmc.weaver.paper-checkstyle.properties} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename paperweight-core/src/main/resources/META-INF/gradle-plugins/{io.papermc.paperweight.paper-checkstyle.properties => io.canvasmc.weaver.paper-checkstyle.properties} (100%) diff --git a/paperweight-core/src/main/resources/META-INF/gradle-plugins/io.papermc.paperweight.paper-checkstyle.properties b/paperweight-core/src/main/resources/META-INF/gradle-plugins/io.canvasmc.weaver.paper-checkstyle.properties similarity index 100% rename from paperweight-core/src/main/resources/META-INF/gradle-plugins/io.papermc.paperweight.paper-checkstyle.properties rename to paperweight-core/src/main/resources/META-INF/gradle-plugins/io.canvasmc.weaver.paper-checkstyle.properties From e7e32c7aa3f77ab82b5dc60d78bed0a03efa8324 Mon Sep 17 00:00:00 2001 From: "tofik.07" Date: Wed, 10 Dec 2025 12:36:16 +0100 Subject: [PATCH 147/257] chore: update plugin coords for source generator too --- ....properties => io.canvasmc.weaver.source-generator.properties} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename paperweight-core/src/main/resources/META-INF/gradle-plugins/{io.papermc.paperweight.source-generator.properties => io.canvasmc.weaver.source-generator.properties} (100%) diff --git a/paperweight-core/src/main/resources/META-INF/gradle-plugins/io.papermc.paperweight.source-generator.properties b/paperweight-core/src/main/resources/META-INF/gradle-plugins/io.canvasmc.weaver.source-generator.properties similarity index 100% rename from paperweight-core/src/main/resources/META-INF/gradle-plugins/io.papermc.paperweight.source-generator.properties rename to paperweight-core/src/main/resources/META-INF/gradle-plugins/io.canvasmc.weaver.source-generator.properties From 84e7ebf66260886eb777c41bb2cfbf9d9a0679a1 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Thu, 18 Dec 2025 02:27:09 +0100 Subject: [PATCH 148/257] feat: scan AT file for library imports --- .../taskcontainers/MinecraftPatchingTasks.kt | 1 + .../core/tasks/ImportLibraryFiles.kt | 28 +++++++++++++++++-- 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt index f391d1978..3b9b73d92 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt @@ -179,6 +179,7 @@ class MinecraftPatchingTasks( val importLibFiles = tasks.register("import${configName.capitalized()}LibraryFiles") { patches.from(config.featurePatchDir, config.sourcePatchDir, config.basePatchDir) + atFile.set(mergeCollectedAts.flatMap { it.outputFile }) devImports.set(config.devImports.fileExists()) libraryFileIndex.set(coreTasks.indexLibraryFiles.flatMap { it.outputFile }) libraries.from(coreTasks.indexLibraryFiles.map { it.libraries }) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/ImportLibraryFiles.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/ImportLibraryFiles.kt index 922955c89..c8ebbd636 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/ImportLibraryFiles.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/ImportLibraryFiles.kt @@ -107,6 +107,10 @@ abstract class ImportLibraryFiles : BaseTask() { @get:InputFiles abstract val patches: ConfigurableFileCollection + @get:Optional + @get:InputFile + abstract val atFile: RegularFileProperty + @get:Optional @get:InputFile abstract val devImports: RegularFileProperty @@ -131,6 +135,7 @@ abstract class ImportLibraryFiles : BaseTask() { ioDispatcher("ImportLibraryFiles").use { dispatcher -> importLibraryFiles( patchFiles, + atFile.pathOrNull, devImports.pathOrNull, outputDir.path, libraries.sourcesJars(), @@ -144,6 +149,7 @@ abstract class ImportLibraryFiles : BaseTask() { private fun importLibraryFiles( patches: Iterable, + atFile: Path?, importsFile: Path?, targetDir: Path, libFiles: List, @@ -152,7 +158,7 @@ abstract class ImportLibraryFiles : BaseTask() { dispatcher: CoroutineDispatcher, ) = runBlocking { // Import library classes - val allImports = findLibraryImports(importsFile, libFiles, index, patches, dispatcher) + val allImports = findLibraryImports(importsFile, libFiles, index, patches, atFile, dispatcher) val importsByLib = allImports.groupBy { it.libraryFileName } logger.log(if (printOutput) LogLevel.LIFECYCLE else LogLevel.DEBUG, "Importing {} classes from library sources...", allImports.size) @@ -191,6 +197,7 @@ abstract class ImportLibraryFiles : BaseTask() { libFiles: List, index: Set, patchFiles: Iterable, + atFile: Path?, dispatcher: CoroutineDispatcher, ): Set { val result = hashSetOf() @@ -207,14 +214,15 @@ abstract class ImportLibraryFiles : BaseTask() { } } - // Scan patches for necessary imports - result += findNeededLibraryImports(patchFiles, index, dispatcher) + // Scan patches and the AT file for necessary imports + result += findNeededLibraryImports(patchFiles, atFile, index, dispatcher) return result } private suspend fun findNeededLibraryImports( patchFiles: Iterable, + atFile: Path?, index: Set, dispatcher: CoroutineDispatcher, ): Set { @@ -231,6 +239,20 @@ abstract class ImportLibraryFiles : BaseTask() { needed += value } } + + atFile?.useLines { lines -> + lines.filterNot { it.startsWith("#") } + .forEach { line -> + val parts = line.split(' ') + if (parts.size < 2) return@forEach + val className = parts[1] + val key = className.replace('.', '/').substringBefore('$') + ".java" + val value = knownImportMap[key] + if (value != null) { + needed += value + } + } + } return needed } } From 1c7ec86602d4e1ebe920d2c3561fd6e01574fcb0 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Thu, 18 Dec 2025 02:38:43 +0100 Subject: [PATCH 149/257] Version 2.3.11 --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 13e141111..4b4bcef17 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ group = io.canvasmc.weaver -version = 2.3.11-SNAPSHOT +version = 2.3.11 org.gradle.caching = true org.gradle.configuration-cache = true From daae4563f941668f47438683e520ec24cc1bdd81 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Thu, 18 Dec 2025 02:40:59 +0100 Subject: [PATCH 150/257] chore: continue development cycle --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 4b4bcef17..5182777a5 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ group = io.canvasmc.weaver -version = 2.3.11 +version = 2.3.12-SNAPSHOT org.gradle.caching = true org.gradle.configuration-cache = true From ef688aab6cfa66bf92dac61296596387c4cc56d4 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Thu, 18 Dec 2025 21:38:17 +0100 Subject: [PATCH 151/257] feat: Support patch roulette and old repo fetching for forks through a gradle property The property is formatted as follows -> `old(fork name - the one specified in `activeFork =` in the server project's `build.gradle.kts` file)Commit` So for example, for Canvas it would look like this: `oldCanvasCommit = (commit hash)` in either the `gradle.properties` file or `-PoldCanvasCommit=(commit hash)` --- .../paperweight/core/PaperweightCore.kt | 22 +++++ .../taskcontainers/MinecraftPatchingTasks.kt | 3 + .../core/tasks/SetupForkMinecraftSources.kt | 96 +++++++++++++++++++ .../paperweight/util/constants/constants.kt | 2 +- 4 files changed, 122 insertions(+), 1 deletion(-) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/PaperweightCore.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/PaperweightCore.kt index ec9972c97..2fd0f38e2 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/PaperweightCore.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/PaperweightCore.kt @@ -28,6 +28,8 @@ import io.papermc.paperweight.core.extension.PaperweightCoreExtension import io.papermc.paperweight.core.taskcontainers.CoreTasks import io.papermc.paperweight.core.taskcontainers.DevBundleTasks import io.papermc.paperweight.core.taskcontainers.PaperclipTasks +import io.papermc.paperweight.core.tasks.patching.ApplyBasePatches +import io.papermc.paperweight.core.tasks.patching.ApplyFilePatches import io.papermc.paperweight.core.tasks.patchroulette.PatchRouletteTasks import io.papermc.paperweight.core.util.coreExt import io.papermc.paperweight.core.util.createBuildTasks @@ -200,6 +202,7 @@ abstract class PaperweightCore : Plugin { mappedJar, ) + val name = coreExt.activeFork.get().name if (coreExt.updatingMinecraft.oldPaperCommit.isPresent) { tasks.paperPatchingTasks.applyBasePatches.configure { additionalRemote = layout.cache.resolve( @@ -217,6 +220,25 @@ abstract class PaperweightCore : Plugin { coreExt.paper.rejectsDir, layout.projectDirectory.dir("src/minecraft/java"), ) + } else if (target.hasProperty("old${name}Commit")) { + // old commit fetching for forks through a gradle property + target.tasks.named("applyMinecraftBasePatches").configure { + additionalRemote = layout.cache.resolve( + "$PAPER_PATH/old${name.capitalized()}/${target.providers.gradleProperty("old${name.capitalized()}Commit").get()}" + + "/$name-server/src/minecraft/java" + ).absolutePathString() + } + target.tasks.named("applyMinecraftSourcePatches").configure { + emitRejects = false + } // if we ever make features/base emit rejects by default, we should update this. + + PatchRouletteTasks( + target, + name.lowercase(), + coreExt.minecraftVersion, + coreExt.activeFork.get().rejectsDir, + layout.projectDirectory.dir("src/minecraft/java"), + ) } } } diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt index 3b9b73d92..a31176596 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt @@ -192,6 +192,9 @@ class MinecraftPatchingTasks( outputDir.set(layout.cache.resolve(paperTaskOutput())) identifier.set(configName) + if (project.hasProperty("old${configName.capitalized()}Commit")) { + oldCommit.convention(project.providers.gradleProperty("old${configName.capitalized()}Commit")) + } libraryImports.set(importLibFiles.flatMap { it.outputDir }) atFile.set(mergeCollectedAts.flatMap { it.outputFile }) ats.jst.from(project.configurations.named(JST_CONFIG)) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupForkMinecraftSources.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupForkMinecraftSources.kt index 880498ff6..3a7166863 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupForkMinecraftSources.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupForkMinecraftSources.kt @@ -22,13 +22,19 @@ package io.papermc.paperweight.core.tasks +import io.papermc.paperweight.PaperweightException import io.papermc.paperweight.core.util.ApplyJavadocMappings import io.papermc.paperweight.core.util.ApplySourceATs +import io.papermc.paperweight.core.util.coreExt import io.papermc.paperweight.tasks.* import io.papermc.paperweight.util.* +import io.papermc.paperweight.util.constants.PAPER_PATH import io.papermc.paperweight.util.constants.paperTaskOutput +import java.util.concurrent.TimeUnit import kotlin.io.path.* import org.eclipse.jgit.api.Git +import org.eclipse.jgit.api.ResetCommand +import org.eclipse.jgit.transport.URIish import org.gradle.api.file.ConfigurableFileCollection import org.gradle.api.file.DirectoryProperty import org.gradle.api.file.RegularFileProperty @@ -82,10 +88,18 @@ abstract class SetupForkMinecraftSources : JavaLauncherTask() { @get:Input abstract val identifier: Property + @get:Input + @get:Optional + abstract val oldCommit: Property + + @get:Input + abstract val additionalRemoteName: Property + override fun init() { super.init() atWorkingDir.set(layout.cache.resolve(paperTaskOutput(name = "${name}_atWorkingDir"))) mappingsWorkingDir.set(layout.cache.resolve(paperTaskOutput(name = "${name}_mappingsWorkingDir"))) + additionalRemoteName.convention("old") } @TaskAction @@ -95,6 +109,10 @@ abstract class SetupForkMinecraftSources : JavaLauncherTask() { val git = Git.open(outputDir.path.toFile()) + if (oldCommit.isPresent) { + setupOld() + } + if (libraryImports.isPresent) { libraryImports.path.walk().forEach { val outFile = out.resolve(it.relativeTo(libraryImports.path).invariantSeparatorsPathString) @@ -131,4 +149,82 @@ abstract class SetupForkMinecraftSources : JavaLauncherTask() { git.close() } + + private fun setupOld() { + val name = project.coreExt.activeFork.get().name.capitalized() + logger.lifecycle("Setting up $name commit ${oldCommit.get()} to use as base for 3-way apply...") + + val rootProjectDir = layout.projectDirectory.dir("../").path + val oldDir = layout.cache.resolve("$PAPER_PATH/old$name/${oldCommit.get()}") + val oldLog = layout.cache.resolve("$PAPER_PATH/old$name/${oldCommit.get()}.log") + + val oldGit: Git + if (oldDir.exists()) { + oldGit = Git.open(oldDir.toFile()) + } else { + oldDir.createParentDirectories() + oldGit = Git.init() + .setDirectory(oldDir.toFile()) + .setInitialBranch("main") + .call() + oldGit.remoteRemove().setRemoteName("origin").call() + oldGit.remoteAdd().setName("origin").setUri(URIish(rootProjectDir.absolutePathString())).call() + } + + val upstream = Git.open(rootProjectDir.toFile()) + val upstreamConfig = upstream.repository.config + val upstreamReachableSHA1 = upstreamConfig.getString("uploadpack", null, "allowreachablesha1inwant") + val upstreamConfigContainsUploadPack = upstreamConfig.sections.contains("uploadpack") + try { + // Temporarily allow fetching reachable sha1 refs from the "upstream" repository. + upstreamConfig.setBoolean("uploadpack", null, "allowreachablesha1inwant", true) + upstreamConfig.save() + oldGit.fetch().setDepth(1).setRemote("origin").setRefSpecs(oldCommit.get()).call() + oldGit.reset().setMode(ResetCommand.ResetType.HARD).setRef(oldCommit.get()).call() + } finally { + if (upstreamReachableSHA1 == null) { + if (upstreamConfigContainsUploadPack) { + upstreamConfig.unset("uploadpack", null, "allowreachablesha1inwant") + } else { + upstreamConfig.unsetSection("uploadpack", null) + } + } else { + upstreamConfig.setString("uploadpack", null, "allowreachablesha1inwant", upstreamReachableSHA1) + } + upstreamConfig.save() + upstream.close() + } + + oldGit.close() + + val isWindows = System.getProperty("os.name").lowercase().contains("win") + oldLog.outputStream().use { logOut -> + val args = arrayOf( + "applyAllPatches", + "--console", + "plain", + "--stacktrace", + "-Dpaperweight.debug=true" + ) + val command = if (isWindows) { + listOf("cmd.exe", "/C", "gradlew.bat " + args.joinToString(" ")) + } else { + listOf("./gradlew", *args) + } + val processBuilder = ProcessBuilder(command) + processBuilder.directory(oldDir) + val process = processBuilder.start() + + val outFuture = redirect(process.inputStream, logOut) + val errFuture = redirect(process.errorStream, logOut) + + val exit = process.waitFor() + outFuture.get(500L, TimeUnit.MILLISECONDS) + errFuture.get(500L, TimeUnit.MILLISECONDS) + + if (exit != 0) { + throw PaperweightException("Failed to apply old $name, see log at $oldLog") + } + } + } } 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 525775929..83a1c03b0 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 @@ -69,7 +69,7 @@ const val MACHE_REPO_NAME = "paperweightMacheRepository" const val JST_REPO_NAME = "paperweightJstRepository" const val CACHE_PATH = "caches" -private const val PAPER_PATH = "paperweight" +const val PAPER_PATH = "paperweight" const val LOCK_DIR = "$PAPER_PATH/lock" const val USERDEV_SETUP_LOCK = "$LOCK_DIR/userdev/setup.lock" From 5728dfc1a7fb833a09ebffa7dcc4f9055b9c530f Mon Sep 17 00:00:00 2001 From: Toffikk Date: Thu, 18 Dec 2025 21:47:52 +0100 Subject: [PATCH 152/257] fix: only call get on fork name if we're a fork --- .../kotlin/io/papermc/paperweight/core/PaperweightCore.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/PaperweightCore.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/PaperweightCore.kt index 2fd0f38e2..8766d624a 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/PaperweightCore.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/PaperweightCore.kt @@ -202,7 +202,6 @@ abstract class PaperweightCore : Plugin { mappedJar, ) - val name = coreExt.activeFork.get().name if (coreExt.updatingMinecraft.oldPaperCommit.isPresent) { tasks.paperPatchingTasks.applyBasePatches.configure { additionalRemote = layout.cache.resolve( @@ -220,7 +219,8 @@ abstract class PaperweightCore : Plugin { coreExt.paper.rejectsDir, layout.projectDirectory.dir("src/minecraft/java"), ) - } else if (target.hasProperty("old${name}Commit")) { + } else if (coreExt.activeFork.isPresent && target.hasProperty("old${coreExt.activeFork.get().name}Commit")) { + val name = coreExt.activeFork.get().name // old commit fetching for forks through a gradle property target.tasks.named("applyMinecraftBasePatches").configure { additionalRemote = layout.cache.resolve( From 793086aa637dc01c3ce97bd60316ed66c49ce7c3 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Thu, 18 Dec 2025 23:48:58 +0100 Subject: [PATCH 153/257] fix: resolve deprecation warnings in SetupForkMinecraftSources --- .../core/taskcontainers/MinecraftPatchingTasks.kt | 1 + .../core/tasks/SetupForkMinecraftSources.kt | 15 +++++++++++---- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt index a31176596..62a9a6283 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt @@ -194,6 +194,7 @@ class MinecraftPatchingTasks( if (project.hasProperty("old${configName.capitalized()}Commit")) { oldCommit.convention(project.providers.gradleProperty("old${configName.capitalized()}Commit")) + oldOutputDir.set(layout.cache.resolve("$PAPER_PATH/old${configName.capitalized()}")) } libraryImports.set(importLibFiles.flatMap { it.outputDir }) atFile.set(mergeCollectedAts.flatMap { it.outputFile }) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupForkMinecraftSources.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupForkMinecraftSources.kt index 3a7166863..b668dad5c 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupForkMinecraftSources.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupForkMinecraftSources.kt @@ -28,7 +28,6 @@ import io.papermc.paperweight.core.util.ApplySourceATs import io.papermc.paperweight.core.util.coreExt import io.papermc.paperweight.tasks.* import io.papermc.paperweight.util.* -import io.papermc.paperweight.util.constants.PAPER_PATH import io.papermc.paperweight.util.constants.paperTaskOutput import java.util.concurrent.TimeUnit import kotlin.io.path.* @@ -60,6 +59,10 @@ abstract class SetupForkMinecraftSources : JavaLauncherTask() { @get:OutputDirectory abstract val outputDir: DirectoryProperty + @get:OutputDirectory + @get:Optional + abstract val oldOutputDir: DirectoryProperty + @get:Internal abstract val atWorkingDir: DirectoryProperty @@ -88,6 +91,9 @@ abstract class SetupForkMinecraftSources : JavaLauncherTask() { @get:Input abstract val identifier: Property + @get:Internal + abstract val forkName: Property + @get:Input @get:Optional abstract val oldCommit: Property @@ -100,6 +106,7 @@ abstract class SetupForkMinecraftSources : JavaLauncherTask() { atWorkingDir.set(layout.cache.resolve(paperTaskOutput(name = "${name}_atWorkingDir"))) mappingsWorkingDir.set(layout.cache.resolve(paperTaskOutput(name = "${name}_mappingsWorkingDir"))) additionalRemoteName.convention("old") + forkName.convention(project.coreExt.activeFork.map { it.name.capitalized() }) } @TaskAction @@ -151,12 +158,12 @@ abstract class SetupForkMinecraftSources : JavaLauncherTask() { } private fun setupOld() { - val name = project.coreExt.activeFork.get().name.capitalized() + val name = forkName.get() logger.lifecycle("Setting up $name commit ${oldCommit.get()} to use as base for 3-way apply...") val rootProjectDir = layout.projectDirectory.dir("../").path - val oldDir = layout.cache.resolve("$PAPER_PATH/old$name/${oldCommit.get()}") - val oldLog = layout.cache.resolve("$PAPER_PATH/old$name/${oldCommit.get()}.log") + val oldDir = oldOutputDir.get().path.resolve(oldCommit.get()) + val oldLog = oldOutputDir.get().path.resolve("${oldCommit.get()}.log") val oldGit: Git if (oldDir.exists()) { From 955be4ba6e4e75fd2f338248805c595fa27f5f93 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Sat, 20 Dec 2025 17:26:18 +0100 Subject: [PATCH 154/257] fix: allow rebuilding base patches after conflict resolution --- .../paperweight/tasks/RebuildBaseGitPatches.kt | 10 ++++++---- .../main/kotlin/io/papermc/paperweight/util/git.kt | 11 +++++++++++ 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RebuildBaseGitPatches.kt b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RebuildBaseGitPatches.kt index 5f05dbe87..2a17f3dc0 100644 --- a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RebuildBaseGitPatches.kt +++ b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RebuildBaseGitPatches.kt @@ -86,11 +86,13 @@ abstract class RebuildBaseGitPatches : ControllableOutputTask() { .map { it.substringBefore(" ") } .toList() - // throw if false, since that means the repo state is corrupted and we can't rebuild safely - validateSingleCommit(identifier, "Base", basepatchesCommit) + // throw if above 1, since that means the repo state is corrupted and we can't rebuild safely + val commitNumber = validateSingleOrNullCommit(identifier, "Base", basepatchesCommit) // we update the tag to reflect the new repo state - git("tag", "-f", "basepatches", basepatchesCommit.joinToString()).executeSilently(silenceErr = true) + if (commitNumber == 1) { + git("tag", "-f", "basepatches", basepatchesCommit.joinToString()).executeSilently(silenceErr = true) + } val what = inputDir.path.name val patchFolder = patchDir.path @@ -123,7 +125,7 @@ abstract class RebuildBaseGitPatches : ControllableOutputTask() { } val base = baseRef.get() - val stop = stopRef.get() + val stop = if (commitNumber == 1) stopRef.get() else "HEAD" // HEAD if null commit val commitCount = git("rev-list", "--count", "$base..$stop").getText().trim().toInt() if (commitCount <= 0) return // nothing to rebuild diff --git a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/git.kt b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/git.kt index 3f6f14d39..8ff8e7b84 100644 --- a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/git.kt +++ b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/git.kt @@ -293,6 +293,17 @@ fun checkoutRepoFromUpstream( git("gc").runSilently(silenceErr = true) } +fun validateSingleOrNullCommit(identifier: Provider, kind: String, commits: List): Int { + // > 1 hack bc its possible we were in the middle of rebasing + if (commits.size > 1) { + throw PaperweightException( + "Invalid amount of commits with the identifier: '${identifier.get()} $kind Patches'!\n" + + "Got ${commits.size} commits, expected: ≤1" + ) + } + return commits.size +} + fun validateSingleCommit(identifier: Provider, kind: String, commits: List) { if (commits.size != 1) { throw PaperweightException( From b976b0f01acbf31034b5b99b4c2dc67b649484fd Mon Sep 17 00:00:00 2001 From: "tofik.07" Date: Sat, 20 Dec 2025 22:43:43 +0100 Subject: [PATCH 155/257] fix: resolve mismatch between old commit property reads --- .../main/kotlin/io/papermc/paperweight/core/PaperweightCore.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/PaperweightCore.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/PaperweightCore.kt index 8766d624a..4bc3409aa 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/PaperweightCore.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/PaperweightCore.kt @@ -219,7 +219,7 @@ abstract class PaperweightCore : Plugin { coreExt.paper.rejectsDir, layout.projectDirectory.dir("src/minecraft/java"), ) - } else if (coreExt.activeFork.isPresent && target.hasProperty("old${coreExt.activeFork.get().name}Commit")) { + } else if (coreExt.activeFork.isPresent && target.hasProperty("old${coreExt.activeFork.get().name.capitalized()}Commit")) { val name = coreExt.activeFork.get().name // old commit fetching for forks through a gradle property target.tasks.named("applyMinecraftBasePatches").configure { From 40474145c12d0751ce60706f39478e82dbf8b6ad Mon Sep 17 00:00:00 2001 From: Toffikk Date: Sat, 20 Dec 2025 23:54:23 +0100 Subject: [PATCH 156/257] fix: generate objects from the full history for 3-way apply --- .../core/tasks/SetupForkMinecraftSources.kt | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupForkMinecraftSources.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupForkMinecraftSources.kt index b668dad5c..4a65606d3 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupForkMinecraftSources.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupForkMinecraftSources.kt @@ -106,7 +106,7 @@ abstract class SetupForkMinecraftSources : JavaLauncherTask() { atWorkingDir.set(layout.cache.resolve(paperTaskOutput(name = "${name}_atWorkingDir"))) mappingsWorkingDir.set(layout.cache.resolve(paperTaskOutput(name = "${name}_mappingsWorkingDir"))) additionalRemoteName.convention("old") - forkName.convention(project.coreExt.activeFork.map { it.name.capitalized() }) + forkName.convention(project.coreExt.activeFork.map { it.name }) } @TaskAction @@ -159,7 +159,7 @@ abstract class SetupForkMinecraftSources : JavaLauncherTask() { private fun setupOld() { val name = forkName.get() - logger.lifecycle("Setting up $name commit ${oldCommit.get()} to use as base for 3-way apply...") + logger.lifecycle("Setting up ${name.capitalized()} commit ${oldCommit.get()} to use as base for 3-way apply...") val rootProjectDir = layout.projectDirectory.dir("../").path val oldDir = oldOutputDir.get().path.resolve(oldCommit.get()) @@ -230,7 +230,20 @@ abstract class SetupForkMinecraftSources : JavaLauncherTask() { errFuture.get(500L, TimeUnit.MILLISECONDS) if (exit != 0) { - throw PaperweightException("Failed to apply old $name, see log at $oldLog") + throw PaperweightException("Failed to apply old ${name.capitalized()}, see log at $oldLog") + } else { + // for object generation + val process1 = ProcessBuilder("git", "rebase", "ROOT", "--force").directory(oldDir.resolve("$name-server/src/minecraft/java")).start() + val outFuture1 = redirect(process1.inputStream, logOut) + val errFuture1 = redirect(process1.errorStream, logOut) + val exit1 = process1.waitFor() + + outFuture1.get(500L, TimeUnit.MILLISECONDS) + errFuture1.get(500L, TimeUnit.MILLISECONDS) + + if (exit1 != 0) { + throw PaperweightException("Failed to rebase commits in old ${name.capitalized()}, see log at $oldLog") + } } } } From 492b869b4262ab130890bcf96879a4dee27d820e Mon Sep 17 00:00:00 2001 From: Toffikk Date: Sun, 21 Dec 2025 01:36:18 +0100 Subject: [PATCH 157/257] Revert "fix: generate objects from the full history for 3-way apply" This reverts commit 40474145c12d0751ce60706f39478e82dbf8b6ad. --- .../core/tasks/SetupForkMinecraftSources.kt | 19 +++---------------- 1 file changed, 3 insertions(+), 16 deletions(-) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupForkMinecraftSources.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupForkMinecraftSources.kt index 4a65606d3..b668dad5c 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupForkMinecraftSources.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupForkMinecraftSources.kt @@ -106,7 +106,7 @@ abstract class SetupForkMinecraftSources : JavaLauncherTask() { atWorkingDir.set(layout.cache.resolve(paperTaskOutput(name = "${name}_atWorkingDir"))) mappingsWorkingDir.set(layout.cache.resolve(paperTaskOutput(name = "${name}_mappingsWorkingDir"))) additionalRemoteName.convention("old") - forkName.convention(project.coreExt.activeFork.map { it.name }) + forkName.convention(project.coreExt.activeFork.map { it.name.capitalized() }) } @TaskAction @@ -159,7 +159,7 @@ abstract class SetupForkMinecraftSources : JavaLauncherTask() { private fun setupOld() { val name = forkName.get() - logger.lifecycle("Setting up ${name.capitalized()} commit ${oldCommit.get()} to use as base for 3-way apply...") + logger.lifecycle("Setting up $name commit ${oldCommit.get()} to use as base for 3-way apply...") val rootProjectDir = layout.projectDirectory.dir("../").path val oldDir = oldOutputDir.get().path.resolve(oldCommit.get()) @@ -230,20 +230,7 @@ abstract class SetupForkMinecraftSources : JavaLauncherTask() { errFuture.get(500L, TimeUnit.MILLISECONDS) if (exit != 0) { - throw PaperweightException("Failed to apply old ${name.capitalized()}, see log at $oldLog") - } else { - // for object generation - val process1 = ProcessBuilder("git", "rebase", "ROOT", "--force").directory(oldDir.resolve("$name-server/src/minecraft/java")).start() - val outFuture1 = redirect(process1.inputStream, logOut) - val errFuture1 = redirect(process1.errorStream, logOut) - val exit1 = process1.waitFor() - - outFuture1.get(500L, TimeUnit.MILLISECONDS) - errFuture1.get(500L, TimeUnit.MILLISECONDS) - - if (exit1 != 0) { - throw PaperweightException("Failed to rebase commits in old ${name.capitalized()}, see log at $oldLog") - } + throw PaperweightException("Failed to apply old $name, see log at $oldLog") } } } From 0fccb5ab704488f34481e1f9e447c2719186de52 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Sat, 3 Jan 2026 16:33:05 +0100 Subject: [PATCH 158/257] feat: Auto AT file discovery for non-minecraft sources --- .../io/papermc/paperweight/core/extension/UpstreamConfig.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/UpstreamConfig.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/UpstreamConfig.kt index 977f24064..45f62c376 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/UpstreamConfig.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/UpstreamConfig.kt @@ -101,7 +101,8 @@ abstract class UpstreamConfig @Inject constructor( abstract val excludes: SetProperty abstract val outputDir: DirectoryProperty - abstract val additionalAts: RegularFileProperty + val buildDataDir: DirectoryProperty = objects.directoryProperty().convention(outputDir.dir("../build-data")) + val additionalAts: RegularFileProperty = objects.fileProperty().convention(buildDataDir.file("$name.at")) abstract val patchesDir: DirectoryProperty val rejectsDir: DirectoryProperty = objects.dirFrom(patchesDir, "rejected") From 5585763297e17d02f7d094a804f1d96c9b2cd605 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Sat, 3 Jan 2026 18:41:26 +0100 Subject: [PATCH 159/257] Version 2.3.12 --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 5182777a5..13ec34045 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ group = io.canvasmc.weaver -version = 2.3.12-SNAPSHOT +version = 2.3.12 org.gradle.caching = true org.gradle.configuration-cache = true From 2b72ef064c8d8767011feb437bba8a9865f8580a Mon Sep 17 00:00:00 2001 From: Toffikk Date: Sat, 3 Jan 2026 18:43:36 +0100 Subject: [PATCH 160/257] chore: continue development cycle --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 13ec34045..cc99def54 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ group = io.canvasmc.weaver -version = 2.3.12 +version = 2.4.0-SNAPSHOT org.gradle.caching = true org.gradle.configuration-cache = true From 6589bfe8f46e5ad9cb8d8698b28328a6d4a588c0 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Sun, 4 Jan 2026 21:24:57 +0100 Subject: [PATCH 161/257] chore!: rip out javadoc mappings logic and break on unseperated ATs --- .../paperweight/core/extension/ForkConfig.kt | 17 +--- .../core/extension/UpstreamConfig.kt | 7 +- .../taskcontainers/MinecraftPatchingTasks.kt | 21 ++--- .../core/taskcontainers/PatchingTasks.kt | 8 +- .../core/tasks/SetupForkMinecraftSources.kt | 28 ------ .../core/tasks/patching/ApplyFilePatches.kt | 27 ++---- .../core/util/ApplyJavadocMappings.kt | 85 ------------------- .../paperweight/util/constants/constants.kt | 1 - 8 files changed, 24 insertions(+), 170 deletions(-) delete mode 100644 paperweight-core/src/main/kotlin/io/papermc/paperweight/core/util/ApplyJavadocMappings.kt diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/ForkConfig.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/ForkConfig.kt index baff15294..737859282 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/ForkConfig.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/ForkConfig.kt @@ -23,12 +23,10 @@ package io.papermc.paperweight.core.extension import io.papermc.paperweight.util.* -import io.papermc.paperweight.util.constants.JAVADOC_MAPPINGS_CONFIG import javax.inject.Inject import org.gradle.api.Action import org.gradle.api.Named import org.gradle.api.Project -import org.gradle.api.artifacts.Configuration import org.gradle.api.file.DirectoryProperty import org.gradle.api.file.RegularFileProperty import org.gradle.api.model.ObjectFactory @@ -41,7 +39,7 @@ abstract class ForkConfig @Inject constructor( private val configName: String, providers: ProviderFactory, objects: ObjectFactory, - private val project: Project, + project: Project, ) : Named { override fun getName(): String { return configName @@ -69,19 +67,6 @@ abstract class ForkConfig @Inject constructor( }.orElse( providers.provider { objects.newInstance("paper", false) } ) - val javadocMappings = project.configurations.register(name + JAVADOC_MAPPINGS_CONFIG) - - fun javadocMappings(dep: String) { - javadocMappings.configure { - dependencies.add(project.dependencies.create(dep)) - } - } - - fun javadocMappings(configuration: Provider) { - javadocMappings.configure { - extendsFrom(configuration.get()) - } - } val upstream: UpstreamConfig by lazy { upstreamProvider.get() diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/UpstreamConfig.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/UpstreamConfig.kt index 45f62c376..6307717b1 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/UpstreamConfig.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/UpstreamConfig.kt @@ -35,6 +35,7 @@ import org.gradle.api.model.ObjectFactory import org.gradle.api.provider.ListProperty import org.gradle.api.provider.Property import org.gradle.api.provider.Provider +import org.gradle.api.provider.ProviderFactory import org.gradle.api.provider.SetProperty import org.gradle.kotlin.dsl.* @@ -93,6 +94,7 @@ abstract class UpstreamConfig @Inject constructor( abstract class DirectoryPatchSet @Inject constructor( objects: ObjectFactory, + providers: ProviderFactory, private val setName: String, ) : Named { override fun getName(): String = setName @@ -102,7 +104,7 @@ abstract class UpstreamConfig @Inject constructor( abstract val outputDir: DirectoryProperty val buildDataDir: DirectoryProperty = objects.directoryProperty().convention(outputDir.dir("../build-data")) - val additionalAts: RegularFileProperty = objects.fileProperty().convention(buildDataDir.file("$name.at")) + val additionalAts: RegularFileProperty = objects.fileFrom(buildDataDir, providers.provider { "$name.at" }) abstract val patchesDir: DirectoryProperty val rejectsDir: DirectoryProperty = objects.dirFrom(patchesDir, "rejected") @@ -113,8 +115,9 @@ abstract class UpstreamConfig @Inject constructor( abstract class RepoPatchSet @Inject constructor( objects: ObjectFactory, + providers: ProviderFactory, name: String, - ) : DirectoryPatchSet(objects, name) { + ) : DirectoryPatchSet(objects, providers, name) { abstract val upstreamRepo: Property fun Provider.patchedRepo(name: String): Provider = diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt index 62a9a6283..79e1c3409 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt @@ -205,22 +205,11 @@ class MinecraftPatchingTasks( applyBasePatches.configure { input.set(setup.flatMap { it.outputDir }) } - - val applyJavadocMappings = tasks.register("apply${configName.capitalized()}JavadocMappings") { - description = "Applies javadocs from the specified parchment mappings in $configName to Minecraft sources" - inputDir.set(applyBasePatches.flatMap { it.output }) - outputDir.set(layout.cache.resolve(paperTaskOutput())) - identifier.set(configName) - mappingFile.from(config.javadocMappings) - mappings.jst.from(project.configurations.named(JST_CONFIG)) - } - - applySourcePatches.configure { - input.set(applyJavadocMappings.flatMap { it.outputDir }) - } - - applySourcePatchesFuzzy.configure { - input.set(applyJavadocMappings.flatMap { it.outputDir }) + val name = "rebuild${namePart}BasePatches" + if (name in tasks.names) { + tasks.named(name) { + base.set(setup.flatMap { it.outputDir }) + } } } diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt index 1aff26314..6211c8ca8 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt @@ -149,7 +149,7 @@ class PatchingTasks( } val setup = tasks.register("run${namePart}Setup") { - description = "Applies $forkName ATs to non-minecraft sources" + description = "Applies $forkName ATs to $namePart sources" inputDir.set(baseDir) outputDir.set(layout.cache.resolve(paperTaskOutput())) identifier.set(namePart) @@ -160,6 +160,12 @@ class PatchingTasks( applyBasePatches.configure { input.set(setup.flatMap { it.outputDir }) } + val name = "rebuild${namePart}BasePatches" + if (name in tasks.names) { + tasks.named(name) { + base.set(setup.flatMap { it.outputDir }) + } + } } private fun setupWritable() { diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupForkMinecraftSources.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupForkMinecraftSources.kt index b668dad5c..f3b8a77c5 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupForkMinecraftSources.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupForkMinecraftSources.kt @@ -23,7 +23,6 @@ package io.papermc.paperweight.core.tasks import io.papermc.paperweight.PaperweightException -import io.papermc.paperweight.core.util.ApplyJavadocMappings import io.papermc.paperweight.core.util.ApplySourceATs import io.papermc.paperweight.core.util.coreExt import io.papermc.paperweight.tasks.* @@ -34,20 +33,16 @@ import kotlin.io.path.* import org.eclipse.jgit.api.Git import org.eclipse.jgit.api.ResetCommand import org.eclipse.jgit.transport.URIish -import org.gradle.api.file.ConfigurableFileCollection import org.gradle.api.file.DirectoryProperty import org.gradle.api.file.RegularFileProperty import org.gradle.api.provider.Property import org.gradle.api.tasks.Input import org.gradle.api.tasks.InputDirectory import org.gradle.api.tasks.InputFile -import org.gradle.api.tasks.InputFiles import org.gradle.api.tasks.Internal import org.gradle.api.tasks.Nested import org.gradle.api.tasks.Optional import org.gradle.api.tasks.OutputDirectory -import org.gradle.api.tasks.PathSensitive -import org.gradle.api.tasks.PathSensitivity import org.gradle.api.tasks.TaskAction import org.gradle.kotlin.dsl.* @@ -66,24 +61,13 @@ abstract class SetupForkMinecraftSources : JavaLauncherTask() { @get:Internal abstract val atWorkingDir: DirectoryProperty - @get:Internal - abstract val mappingsWorkingDir: DirectoryProperty - @get:Nested val ats: ApplySourceATs = objects.newInstance() - @get:Nested - val mappings: ApplyJavadocMappings = objects.newInstance() - @get:InputFile @get:Optional abstract val atFile: RegularFileProperty - @get:PathSensitive(PathSensitivity.NONE) - @get:InputFiles - @get:Optional - abstract val mappingFile: ConfigurableFileCollection - @get:Optional @get:InputDirectory abstract val libraryImports: DirectoryProperty @@ -104,7 +88,6 @@ abstract class SetupForkMinecraftSources : JavaLauncherTask() { override fun init() { super.init() atWorkingDir.set(layout.cache.resolve(paperTaskOutput(name = "${name}_atWorkingDir"))) - mappingsWorkingDir.set(layout.cache.resolve(paperTaskOutput(name = "${name}_mappingsWorkingDir"))) additionalRemoteName.convention("old") forkName.convention(project.coreExt.activeFork.map { it.name.capitalized() }) } @@ -142,17 +125,6 @@ abstract class SetupForkMinecraftSources : JavaLauncherTask() { ) commitAndTag(git, "ATs", "${identifier.get()} ATs") } - if (!mappingFile.isEmpty) { - println("Applying javadoc mappings...") - mappings.run( - launcher.get(), - outputDir.path, - outputDir.path, - mappingFile.singleFile.toPath(), - mappingsWorkingDir.path, - ) - commitAndTag(git, "${identifier.get()}JDs", "${identifier.get()} Javadoc Mappings") - } git.close() } diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyFilePatches.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyFilePatches.kt index 0ffd57eba..5e5c80f96 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyFilePatches.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyFilePatches.kt @@ -49,9 +49,9 @@ abstract class ApplyFilePatches : BaseTask() { ) abstract val verbose: Property - @get:Optional @get:PathSensitive(PathSensitivity.NONE) @get:InputDirectory + @get:Optional abstract val input: DirectoryProperty @get:OutputDirectory @@ -69,7 +69,7 @@ abstract class ApplyFilePatches : BaseTask() { @get:Input abstract val gitFilePatches: Property - @get:Internal + @get:Input abstract val baseRef: Property @get:Input @@ -96,15 +96,14 @@ abstract class ApplyFilePatches : BaseTask() { open fun run() { io.papermc.paperweight.util.Git.checkForGit() - val input = input.pathOrNull val outputPath = output.path // special handling for resource patches - if (input != null && baseRef.get() == "main") { + if (input.pathOrNull != null && baseRef.get() == "main") { recreateCloneDirectory(outputPath) checkoutRepoFromUpstream( Git(outputPath), - input, + input.path, "main", "upstream", baseRef.get(), @@ -114,24 +113,16 @@ abstract class ApplyFilePatches : BaseTask() { tagBase() } else { // rest of the original logic - if (input != null && input.toAbsolutePath() != outputPath.toAbsolutePath()) { + if (input.pathOrNull != null && input.path.toAbsolutePath() != outputPath.toAbsolutePath()) { recreateCloneDirectory(outputPath) - val checkoutFromJDs = hasJavadocs(input, "${identifier.get()}JDs") - val newRef = if (checkoutFromJDs) "${identifier.get()}JDs" else baseRef.get() - baseRef.set(newRef) - val git = Git(outputPath.createDirectories()) checkoutRepoFromUpstream( git, - input, + input.path, baseRef.get(), branchName = "main", ref = true, ) - } else { - val checkoutFromJDs = hasJavadocs(outputPath, "${identifier.get()}JDs") - val newRef = if (checkoutFromJDs) "${identifier.get()}JDs" else baseRef.get() - baseRef.set(newRef) } val git = Git(outputPath) if (git("checkout", "main").runSilently(silenceErr = true) != 0) { @@ -283,12 +274,6 @@ abstract class ApplyFilePatches : BaseTask() { git.close() } - fun hasJavadocs(outputPath: Path, tag: String): Boolean { - val git = Git(outputPath) - val result = git("tag", "-l", tag).getText().trim() - return result == tag - } - internal open fun mode(): PatchMode { return PatchMode.OFFSET } diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/util/ApplyJavadocMappings.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/util/ApplyJavadocMappings.kt deleted file mode 100644 index 258321a82..000000000 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/util/ApplyJavadocMappings.kt +++ /dev/null @@ -1,85 +0,0 @@ -/* - * paperweight is a Gradle plugin for the PaperMC project. - * - * Copyright (c) 2023 Kyle Wood (DenWav) - * Contributors - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 only, no later versions. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 - * USA - */ - -package io.papermc.paperweight.core.util - -import io.papermc.paperweight.util.* -import java.nio.file.Path -import kotlin.io.path.* -import org.gradle.api.file.ConfigurableFileCollection -import org.gradle.api.provider.Property -import org.gradle.api.tasks.Classpath -import org.gradle.api.tasks.CompileClasspath -import org.gradle.api.tasks.Input -import org.gradle.jvm.toolchain.JavaLauncher - -abstract class ApplyJavadocMappings { - @get:Classpath - abstract val jst: ConfigurableFileCollection - - @get:Input - abstract val memory: Property - - @get:CompileClasspath - abstract val jstClasspath: ConfigurableFileCollection - - init { - memory.convention("1G") - } - - fun run( - javaLauncher: JavaLauncher, - input: Path, - output: Path, - mappingFile: Path, - workDir: Path, - - ) { - workDir.deleteRecursive() - workDir.createDirectories() - javaLauncher.runJar( - jst, - workDir, - workDir.resolve("log.txt"), - jvmArgs = listOf("-Xmx${memory.get()}"), - args = jstArgs(input, output, mappingFile).toTypedArray() - ) - } - - private fun jstArgs( - inputDir: Path, - outputDir: Path, - mappingFile: Path, - ): List { - return listOf( - "--in-format=FOLDER", - "--out-format=FOLDER", - "--enable-parchment", - "--parchment-mappings=$mappingFile", - "--hidden-prefix=.git", - "--no-apply-parchment-renames", - *jstClasspath.files.map { "--classpath=${it.absolutePath}" }.toTypedArray(), - inputDir.absolutePathString(), - outputDir.absolutePathString(), - ) - } -} 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 83a1c03b0..60f164f31 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 @@ -54,7 +54,6 @@ const val MACHE_CONSTANTS_CONFIG = "macheConstants" const val MACHE_MINECRAFT_LIBRARIES_CONFIG = "macheMinecraftLibraries" const val MACHE_MINECRAFT_CONFIG = "macheMinecraft" const val MAPPED_JAR_OUTGOING_CONFIG = "mappedJarOutgoing" -const val JAVADOC_MAPPINGS_CONFIG = "JavadocMappings" const val JST_CONFIG = "javaSourceTransformer" const val DEV_BUNDLE_CONFIG = "paperweightDevelopmentBundle" const val MOJANG_MAPPED_SERVER_CONFIG = "mojangMappedServer" From faaf36d260dacb977f33b7626df5e946f1950fee Mon Sep 17 00:00:00 2001 From: Toffikk Date: Sun, 4 Jan 2026 21:49:25 +0100 Subject: [PATCH 162/257] fix(tasks): missed this `dependsOn` when renaming base feature patches to base patches --- .../io/papermc/paperweight/core/taskcontainers/CoreTasks.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/CoreTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/CoreTasks.kt index 6c8a15a7f..0932127a4 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/CoreTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/CoreTasks.kt @@ -275,7 +275,7 @@ class CoreTasks( description = "Applies all Minecraft and upstream server base patches " + "(equivalent to '${serverTasks.applyBasePatches.name} applyServerBasePatches')" dependsOn(serverTasks.applyBasePatches) - dependsOn("applyServerBaseFeaturePatches") + dependsOn("applyServerBasePatches") } val applyAllServerFilePatches = project.tasks.register("applyAllServerFilePatches") { group = "patching" From fa927759cd54490ea21fc3c7ee104861080f8137 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Sun, 4 Jan 2026 22:03:08 +0100 Subject: [PATCH 163/257] chore: revert to using paper's jst --- gradle/libs.versions.toml | 2 +- .../kotlin/io/papermc/paperweight/core/PaperweightCore.kt | 2 +- .../paperweight/core/extension/PaperweightCoreExtension.kt | 2 +- .../io/papermc/paperweight/patcher/PaperweightPatcher.kt | 2 +- .../patcher/extension/PaperweightPatcherExtension.kt | 4 ++-- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index ae612906d..e3785edb9 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -3,7 +3,7 @@ asm = "9.9" lorenz = "0.5.8" hypo = "1.2.4" serialize = "1.9.0" -jst = "1.0.68+canvas.2-SNAPSHOT" +jst = "1.0.68+paper.2-SNAPSHOT" tinyRemapper = "0.12.0" # For paper-checkstyle plugin checkstyle = "12.1.2" diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/PaperweightCore.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/PaperweightCore.kt index 4bc3409aa..37bd423a7 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/PaperweightCore.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/PaperweightCore.kt @@ -105,7 +105,7 @@ abstract class PaperweightCore : Plugin { target.configurations.register(JST_CONFIG) { defaultDependencies { // add(project.dependencies.create("net.neoforged.jst:jst-cli-bundle:${LibraryVersions.JST}")) - add(target.dependencies.create("io.canvasmc.jst:jst-cli-bundle:${LibraryVersions.JST}")) + add(target.dependencies.create("io.papermc.jst:jst-cli-bundle:${LibraryVersions.JST}")) } } diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/PaperweightCoreExtension.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/PaperweightCoreExtension.kt index 6a5a55731..5a30d012e 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/PaperweightCoreExtension.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/PaperweightCoreExtension.kt @@ -40,7 +40,7 @@ abstract class PaperweightCoreExtension @Inject constructor(objects: ObjectFacto val bundlerJarName: Property = objects.property().convention("paper") val macheRepo: Property = objects.property().convention(PAPER_MAVEN_REPO_URL) - val jstRepo: Property = objects.property().convention(CANVAS_MAVEN_REPO_URL) + val jstRepo: Property = objects.property().convention(PAPER_MAVEN_REPO_URL) val gitFilePatches: Property = objects.property().convention(false) val filterPatches: Property = objects.property().convention(true) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/PaperweightPatcher.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/PaperweightPatcher.kt index b60c34a2e..cd3cc6dff 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/PaperweightPatcher.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/PaperweightPatcher.kt @@ -57,7 +57,7 @@ abstract class PaperweightPatcher : Plugin { target.configurations.register(JST_CONFIG) { defaultDependencies { // add(project.dependencies.create("net.neoforged.jst:jst-cli-bundle:${LibraryVersions.JST}")) - add(target.dependencies.create("io.canvasmc.jst:jst-cli-bundle:${LibraryVersions.JST}")) + add(target.dependencies.create("io.papermc.jst:jst-cli-bundle:${LibraryVersions.JST}")) } } diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/extension/PaperweightPatcherExtension.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/extension/PaperweightPatcherExtension.kt index 483a07f17..7ec37a277 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/extension/PaperweightPatcherExtension.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/extension/PaperweightPatcherExtension.kt @@ -24,7 +24,7 @@ package io.papermc.paperweight.patcher.extension import io.papermc.paperweight.core.extension.AdditionalUpstreamConfig import io.papermc.paperweight.core.extension.UpstreamConfig -import io.papermc.paperweight.util.constants.CANVAS_MAVEN_REPO_URL +import io.papermc.paperweight.util.constants.PAPER_MAVEN_REPO_URL import javax.inject.Inject import org.gradle.api.Action import org.gradle.api.NamedDomainObjectContainer @@ -38,7 +38,7 @@ abstract class PaperweightPatcherExtension @Inject constructor(private val objec val gitFilePatches: Property = objects.property().convention(false) val filterPatches: Property = objects.property().convention(true) - val jstRepo: Property = objects.property().convention(CANVAS_MAVEN_REPO_URL) + val jstRepo: Property = objects.property().convention(PAPER_MAVEN_REPO_URL) val upstreams: NamedDomainObjectContainer = objects.domainObjectContainer(UpstreamConfig::class) { objects.newInstance(it, true) From b25688004c0e2c43b12640dec87ed29266bc48ff Mon Sep 17 00:00:00 2001 From: Toffikk Date: Mon, 5 Jan 2026 14:46:03 +0100 Subject: [PATCH 164/257] fix(git-utils): respect the ignoreErr boolean --- .../src/main/kotlin/io/papermc/paperweight/util/git.kt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/git.kt b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/git.kt index 8ff8e7b84..5b8739983 100644 --- a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/git.kt +++ b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/git.kt @@ -212,7 +212,10 @@ class Command(private val processBuilder: ProcessBuilder, private val command: S } fun execute(ignoreErr: Boolean) { - run() + val res = run() + if (!ignoreErr && res != 0) { + throw PaperweightException("Command finished with $res exit code: $command") + } } fun executeSilently(silenceOut: Boolean = true, silenceErr: Boolean = false) { From 5c4220c4b37df6b3a3f29dc7aad983a61914e5a3 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Mon, 5 Jan 2026 14:54:08 +0100 Subject: [PATCH 165/257] chore(cleanup): correct a comment and remove an unnecessary @get:Optional --- .../io/papermc/paperweight/tasks/RebuildBaseGitPatches.kt | 1 - .../src/main/kotlin/io/papermc/paperweight/util/git.kt | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RebuildBaseGitPatches.kt b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RebuildBaseGitPatches.kt index 2a17f3dc0..03177ecb2 100644 --- a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RebuildBaseGitPatches.kt +++ b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RebuildBaseGitPatches.kt @@ -40,7 +40,6 @@ abstract class RebuildBaseGitPatches : ControllableOutputTask() { @get:InputDirectory abstract val inputDir: DirectoryProperty - @get:Optional @get:InputDirectory abstract val base: DirectoryProperty // we dont need this at all but we use this to trick gradle into the correct task execution order diff --git a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/git.kt b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/git.kt index 5b8739983..dece9b1b8 100644 --- a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/git.kt +++ b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/git.kt @@ -297,7 +297,7 @@ fun checkoutRepoFromUpstream( } fun validateSingleOrNullCommit(identifier: Provider, kind: String, commits: List): Int { - // > 1 hack bc its possible we were in the middle of rebasing + // > 1 hack bc its possible we encountered an apply error so the marker commit wasn't generated if (commits.size > 1) { throw PaperweightException( "Invalid amount of commits with the identifier: '${identifier.get()} $kind Patches'!\n" + From b79361c5c92e6c7d9dfbc28a73319ee5a29e87c8 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Mon, 5 Jan 2026 15:41:49 +0100 Subject: [PATCH 166/257] fix(tasks): derive fork name from ForkConfig instead of rootProject.name for publisher jar tasks --- .../kotlin/io/papermc/paperweight/core/PaperweightCore.kt | 2 +- .../papermc/paperweight/core/taskcontainers/PaperclipTasks.kt | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/PaperweightCore.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/PaperweightCore.kt index 37bd423a7..33c18dd60 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/PaperweightCore.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/PaperweightCore.kt @@ -163,7 +163,7 @@ abstract class PaperweightCore : Plugin { mappedJar, reobfJar, ext.minecraftVersion, - target.provider { target.rootProject.name.lowercase() } + ext.activeFork.map { it.name }.orElse("paper") ) target.afterEvaluate { diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PaperclipTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PaperclipTasks.kt index 4ebc46aba..54da83de3 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PaperclipTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PaperclipTasks.kt @@ -49,7 +49,7 @@ class PaperclipTasks( mojangJar: Provider, reobfJar: Provider, private val mcVersion: Provider, - private val rootName: Provider + private val forkName: Provider ) { init { val (createBundlerJar, createPaperclipJar, createPublisherJar) = project.createTasks("mojmap") @@ -116,7 +116,7 @@ class PaperclipTasks( } } else { val buildNum = project.providers.environmentVariable("BUILD_NUMBER").orElse("local") - rootName.zip(buildNum) { name, build -> + forkName.zip(buildNum) { name, build -> "$name-build.$build.jar" } } From bc3e792f2fee54ebb6e07b057ea6cc3a7c9b503f Mon Sep 17 00:00:00 2001 From: Toffikk Date: Sat, 17 Jan 2026 02:18:32 +0100 Subject: [PATCH 167/257] chore: update workflow for new branch --- .github/workflows/deploy-snapshot.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deploy-snapshot.yml b/.github/workflows/deploy-snapshot.yml index 2609f1e3d..1e9311636 100644 --- a/.github/workflows/deploy-snapshot.yml +++ b/.github/workflows/deploy-snapshot.yml @@ -1,7 +1,7 @@ name: Deploy Snapshot on: push: - branches: [ 'basepatches' ] + branches: [ 'main' ] paths-ignore: - 'license/*' - 'readme.md' From c156df88a076433287d7cbc182a3943ca645dff3 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Sun, 25 Jan 2026 17:29:58 +0100 Subject: [PATCH 168/257] feat: expose configuration to fail on invalid ATs, closes #344 --- .../paperweight/core/extension/PaperweightCoreExtension.kt | 2 ++ .../papermc/paperweight/core/taskcontainers/CoreTasks.kt | 1 + .../core/taskcontainers/MinecraftPatchingTasks.kt | 2 ++ .../paperweight/core/tasks/SetupForkMinecraftSources.kt | 4 ++++ .../paperweight/core/tasks/SetupMinecraftSources.kt | 4 ++++ .../paperweight/core/tasks/patching/RebuildFilePatches.kt | 1 + .../io/papermc/paperweight/core/util/ApplySourceATs.kt | 7 +++++-- 7 files changed, 19 insertions(+), 2 deletions(-) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/PaperweightCoreExtension.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/PaperweightCoreExtension.kt index d386ee1e6..df3b973a0 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/PaperweightCoreExtension.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/PaperweightCoreExtension.kt @@ -79,4 +79,6 @@ abstract class PaperweightCoreExtension @Inject constructor(objects: ObjectFacto fun updatingMinecraft(action: Action) { action.execute(updatingMinecraft) } + + val validateAts: Property = objects.property().convention(false) } diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/CoreTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/CoreTasks.kt index 776db9975..4b3599d33 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/CoreTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/CoreTasks.kt @@ -101,6 +101,7 @@ class CoreTasks( oldPaperCommit.convention(project.coreExt.updatingMinecraft.oldPaperCommit) inputFile.set(macheDecompileJar.flatMap { it.outputJar }) predicate.set { Files.isRegularFile(it) && it.toString().endsWith(".java") } + validateAts.set(project.coreExt.validateAts) } val setupMacheSources by tasks.registering(SetupMinecraftSources::class) { diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt index 8de9acb5a..7f30124dd 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt @@ -30,6 +30,7 @@ import io.papermc.paperweight.core.tasks.patching.ApplyFilePatches import io.papermc.paperweight.core.tasks.patching.ApplyFilePatchesFuzzy import io.papermc.paperweight.core.tasks.patching.FixupFilePatches import io.papermc.paperweight.core.tasks.patching.RebuildFilePatches +import io.papermc.paperweight.core.util.coreExt import io.papermc.paperweight.tasks.* import io.papermc.paperweight.util.* import io.papermc.paperweight.util.constants.* @@ -171,6 +172,7 @@ class MinecraftPatchingTasks( atFile.set(mergeCollectedAts.flatMap { it.outputFile }) ats.jst.from(project.configurations.named(JST_CONFIG)) ats.jstClasspath.from(project.configurations.named(MACHE_MINECRAFT_LIBRARIES_CONFIG)) + validateAts.set(project.coreExt.validateAts) } applySourcePatches.configure { diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupForkMinecraftSources.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupForkMinecraftSources.kt index 673a0b94d..976686c9a 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupForkMinecraftSources.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupForkMinecraftSources.kt @@ -59,6 +59,9 @@ abstract class SetupForkMinecraftSources : JavaLauncherTask() { @get:Optional abstract val atFile: RegularFileProperty + @get:Input + abstract val validateAts: Property + @get:Optional @get:InputDirectory abstract val libraryImports: DirectoryProperty @@ -86,6 +89,7 @@ abstract class SetupForkMinecraftSources : JavaLauncherTask() { outputDir.path, atFile.path, atWorkingDir.path, + validate = validateAts.get(), ) commitAndTag(git, "ATs", "${identifier.get()} ATs") } diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupMinecraftSources.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupMinecraftSources.kt index 70110a9c3..a5e899378 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupMinecraftSources.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupMinecraftSources.kt @@ -69,6 +69,9 @@ abstract class SetupMinecraftSources : JavaLauncherZippedTask() { @get:Input abstract val oldPaperCommit: Property + @get:Input + abstract val validateAts: Property + @get:Nested val ats: ApplySourceATs = objects.newInstance() @@ -182,6 +185,7 @@ abstract class SetupMinecraftSources : JavaLauncherZippedTask() { outputPath, atFile.path, atWorkingDir.path, + validate = validateAts.get(), ) if (!oldPaperCommit.isPresent) { commitAndTag(git, "ATs", "paper ATs") diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/RebuildFilePatches.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/RebuildFilePatches.kt index 68c016b1d..82cb14178 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/RebuildFilePatches.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/RebuildFilePatches.kt @@ -258,6 +258,7 @@ abstract class RebuildFilePatches : JavaLauncherTask() { at, temporaryDir.toPath().resolve("jst_work"), singleFile = true, + validate = false, ) println("NEW: " + decomp.readText()) } diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/util/ApplySourceATs.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/util/ApplySourceATs.kt index b19114435..dc6cd8d89 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/util/ApplySourceATs.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/util/ApplySourceATs.kt @@ -53,6 +53,7 @@ abstract class ApplySourceATs { atFile: Path, workDir: Path, singleFile: Boolean = false, + validate: Boolean, ) { workDir.deleteRecursive() workDir.createDirectories() @@ -61,7 +62,7 @@ abstract class ApplySourceATs { workDir, workDir.resolve("log.txt"), jvmArgs = listOf("-Xmx${memory.get()}"), - args = jstArgs(input, output, atFile, singleFile).toTypedArray() + args = jstArgs(input, output, atFile, singleFile, validate).toTypedArray() ) } @@ -70,8 +71,10 @@ abstract class ApplySourceATs { outputDir: Path, atFile: Path, singleFile: Boolean = false, + validate: Boolean, ): List { val format = if (singleFile) "FILE" else "FOLDER" + val validation = if (validate) "ERROR" else "LOG" return listOf( "--in-format=$format", "--out-format=$format", @@ -79,7 +82,7 @@ abstract class ApplySourceATs { "--access-transformer=$atFile", "--access-transformer-inherit-method=true", "--hidden-prefix=.git", - // "--access-transformer-validation=ERROR", + "--access-transformer-validation=$validation", *jstClasspath.files.map { "--classpath=${it.absolutePath}" }.toTypedArray(), inputDir.absolutePathString(), outputDir.absolutePathString(), From adf6f62513ed71bea9d3677f1b4e2ca58cd0ebb8 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Sun, 25 Jan 2026 17:38:47 +0100 Subject: [PATCH 169/257] fix: provide a default --- .../io/papermc/paperweight/core/taskcontainers/CoreTasks.kt | 2 +- .../papermc/paperweight/core/tasks/SetupMinecraftSources.kt | 1 + .../paperweight/core/tasks/patching/RebuildFilePatches.kt | 1 - .../kotlin/io/papermc/paperweight/core/util/ApplySourceATs.kt | 4 ++-- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/CoreTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/CoreTasks.kt index 4b3599d33..29b3490de 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/CoreTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/CoreTasks.kt @@ -101,7 +101,6 @@ class CoreTasks( oldPaperCommit.convention(project.coreExt.updatingMinecraft.oldPaperCommit) inputFile.set(macheDecompileJar.flatMap { it.outputJar }) predicate.set { Files.isRegularFile(it) && it.toString().endsWith(".java") } - validateAts.set(project.coreExt.validateAts) } val setupMacheSources by tasks.registering(SetupMinecraftSources::class) { @@ -113,6 +112,7 @@ class CoreTasks( atFile.set(mergePaperATs.flatMap { it.outputFile }) ats.jstClasspath.from(project.configurations.named(MACHE_MINECRAFT_LIBRARIES_CONFIG)) ats.jst.from(project.configurations.named(JST_CONFIG)) + validateAts.set(project.coreExt.validateAts) } val extractMacheSources by tasks.registering(ExtractMinecraftSources::class) { diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupMinecraftSources.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupMinecraftSources.kt index a5e899378..0ffac1187 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupMinecraftSources.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupMinecraftSources.kt @@ -86,6 +86,7 @@ abstract class SetupMinecraftSources : JavaLauncherZippedTask() { override fun init() { super.init() atWorkingDir.set(layout.cache.resolve(paperTaskOutput(name = "${name}_atWorkingDir"))) + validateAts.convention(false) } override fun run(outputPath: Path) { diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/RebuildFilePatches.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/RebuildFilePatches.kt index 82cb14178..68c016b1d 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/RebuildFilePatches.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/RebuildFilePatches.kt @@ -258,7 +258,6 @@ abstract class RebuildFilePatches : JavaLauncherTask() { at, temporaryDir.toPath().resolve("jst_work"), singleFile = true, - validate = false, ) println("NEW: " + decomp.readText()) } diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/util/ApplySourceATs.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/util/ApplySourceATs.kt index dc6cd8d89..137334d61 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/util/ApplySourceATs.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/util/ApplySourceATs.kt @@ -53,7 +53,7 @@ abstract class ApplySourceATs { atFile: Path, workDir: Path, singleFile: Boolean = false, - validate: Boolean, + validate: Boolean = false, ) { workDir.deleteRecursive() workDir.createDirectories() @@ -71,7 +71,7 @@ abstract class ApplySourceATs { outputDir: Path, atFile: Path, singleFile: Boolean = false, - validate: Boolean, + validate: Boolean = false, ): List { val format = if (singleFile) "FILE" else "FOLDER" val validation = if (validate) "ERROR" else "LOG" From c635f8eaae85478a981d4b7bd485003bbcc1b3d6 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Sun, 25 Jan 2026 17:49:20 +0100 Subject: [PATCH 170/257] fix: don't default to false, always require it to be set --- .../papermc/paperweight/core/tasks/SetupMinecraftSources.kt | 2 +- .../paperweight/core/tasks/patching/RebuildFilePatches.kt | 1 + .../kotlin/io/papermc/paperweight/core/util/ApplySourceATs.kt | 4 ++-- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupMinecraftSources.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupMinecraftSources.kt index 0ffac1187..ca8da3bf5 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupMinecraftSources.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupMinecraftSources.kt @@ -85,8 +85,8 @@ abstract class SetupMinecraftSources : JavaLauncherZippedTask() { override fun init() { super.init() - atWorkingDir.set(layout.cache.resolve(paperTaskOutput(name = "${name}_atWorkingDir"))) validateAts.convention(false) + atWorkingDir.set(layout.cache.resolve(paperTaskOutput(name = "${name}_atWorkingDir"))) } override fun run(outputPath: Path) { diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/RebuildFilePatches.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/RebuildFilePatches.kt index 68c016b1d..82cb14178 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/RebuildFilePatches.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/RebuildFilePatches.kt @@ -258,6 +258,7 @@ abstract class RebuildFilePatches : JavaLauncherTask() { at, temporaryDir.toPath().resolve("jst_work"), singleFile = true, + validate = false, ) println("NEW: " + decomp.readText()) } diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/util/ApplySourceATs.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/util/ApplySourceATs.kt index 137334d61..dc6cd8d89 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/util/ApplySourceATs.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/util/ApplySourceATs.kt @@ -53,7 +53,7 @@ abstract class ApplySourceATs { atFile: Path, workDir: Path, singleFile: Boolean = false, - validate: Boolean = false, + validate: Boolean, ) { workDir.deleteRecursive() workDir.createDirectories() @@ -71,7 +71,7 @@ abstract class ApplySourceATs { outputDir: Path, atFile: Path, singleFile: Boolean = false, - validate: Boolean = false, + validate: Boolean, ): List { val format = if (singleFile) "FILE" else "FOLDER" val validation = if (validate) "ERROR" else "LOG" From 456e77959237e94d3929fc360005b6acb218b498 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Sun, 25 Jan 2026 20:24:15 +0100 Subject: [PATCH 171/257] fix: mark validateAts as optional --- .../io/papermc/paperweight/core/tasks/SetupMinecraftSources.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupMinecraftSources.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupMinecraftSources.kt index ca8da3bf5..1ff859996 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupMinecraftSources.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupMinecraftSources.kt @@ -69,6 +69,7 @@ abstract class SetupMinecraftSources : JavaLauncherZippedTask() { @get:Input abstract val oldPaperCommit: Property + @get:Optional @get:Input abstract val validateAts: Property @@ -85,7 +86,6 @@ abstract class SetupMinecraftSources : JavaLauncherZippedTask() { override fun init() { super.init() - validateAts.convention(false) atWorkingDir.set(layout.cache.resolve(paperTaskOutput(name = "${name}_atWorkingDir"))) } From 4dcfc633be43968dea46252c07d00bae6f582fd4 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Sun, 25 Jan 2026 21:40:13 +0100 Subject: [PATCH 172/257] fix: specify the validateAts property for weaver specific tasks --- .../paperweight/core/taskcontainers/PatchingTasks.kt | 2 ++ .../io/papermc/paperweight/core/tasks/GenerateSources.kt | 6 +++++- .../paperweight/core/tasks/PrepareForPatchGeneration.kt | 6 +++++- .../io/papermc/paperweight/core/tasks/SetupForkSources.kt | 4 ++++ .../io/papermc/paperweight/patcher/PaperweightPatcher.kt | 3 +++ 5 files changed, 19 insertions(+), 2 deletions(-) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt index 6211c8ca8..f712738ee 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt @@ -29,6 +29,7 @@ import io.papermc.paperweight.core.tasks.patching.ApplyFilePatches import io.papermc.paperweight.core.tasks.patching.ApplyFilePatchesFuzzy import io.papermc.paperweight.core.tasks.patching.FixupFilePatches import io.papermc.paperweight.core.tasks.patching.RebuildFilePatches +import io.papermc.paperweight.core.util.coreExt import io.papermc.paperweight.tasks.* import io.papermc.paperweight.util.* import io.papermc.paperweight.util.constants.JST_CONFIG @@ -155,6 +156,7 @@ class PatchingTasks( identifier.set(namePart) atFile.set(mergeCollectedAts.flatMap { it.outputFile }) ats.jst.from(project.configurations.named(JST_CONFIG)) + validateAts.set(project.coreExt.validateAts) } applyBasePatches.configure { diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/GenerateSources.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/GenerateSources.kt index ca2bcaa06..60313d518 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/GenerateSources.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/GenerateSources.kt @@ -63,6 +63,9 @@ abstract class GenerateSources : JavaLauncherTask() { @get:PathSensitive(PathSensitivity.NONE) abstract val atFile: RegularFileProperty + @get:Input + abstract val validateAts: Property + @get:InputFile @get:Optional @get:PathSensitive(PathSensitivity.NONE) @@ -132,7 +135,8 @@ abstract class GenerateSources : JavaLauncherTask() { atDirPath, atDirPath, atFile.path, - temporaryDir.toPath() + temporaryDir.toPath(), + validate = validateAts.get(), ) } diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/PrepareForPatchGeneration.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/PrepareForPatchGeneration.kt index 0bde777f6..19a7a8f89 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/PrepareForPatchGeneration.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/PrepareForPatchGeneration.kt @@ -55,6 +55,9 @@ abstract class PrepareForPatchGeneration : JavaLauncherTask() { @get:PathSensitive(PathSensitivity.NONE) abstract val atFile: RegularFileProperty + @get:Input + abstract val validateAts: Property + @get:InputFile @get:Optional @get:PathSensitive(PathSensitivity.NONE) @@ -93,7 +96,8 @@ abstract class PrepareForPatchGeneration : JavaLauncherTask() { outputDirPath, outputDirPath, atFile.path, - temporaryDir.toPath() + temporaryDir.toPath(), + validate = validateAts.get(), ) } } diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupForkSources.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupForkSources.kt index 5550cba2a..39224c5a4 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupForkSources.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupForkSources.kt @@ -55,6 +55,9 @@ abstract class SetupForkSources : JavaLauncherTask() { @get:Optional abstract val atFile: RegularFileProperty + @get:Input + abstract val validateAts: Property + @get:Input abstract val identifier: Property @@ -73,6 +76,7 @@ abstract class SetupForkSources : JavaLauncherTask() { outputDir.path, atFile.path, temporaryDir.toPath(), + validate = validateAts.get(), ) commitAndTag(git, "ATs", "${identifier.get()} ATs") } diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/PaperweightPatcher.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/PaperweightPatcher.kt index cd3cc6dff..581028454 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/PaperweightPatcher.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/PaperweightPatcher.kt @@ -28,6 +28,7 @@ import io.papermc.paperweight.core.tasks.GeneratePatches import io.papermc.paperweight.core.tasks.GenerateSources import io.papermc.paperweight.core.tasks.PrepareForPatchGeneration import io.papermc.paperweight.core.tasks.RunNestedBuild +import io.papermc.paperweight.core.util.coreExt import io.papermc.paperweight.patcher.extension.PaperweightPatcherExtension import io.papermc.paperweight.util.* import io.papermc.paperweight.util.constants.* @@ -150,6 +151,7 @@ abstract class PaperweightPatcher : Plugin { workDir.set(workDirFromProp) atFile.set(input.additionalAts.fileExists()) ats.jst.from(project.configurations.named(JST_CONFIG)) + validateAts.set(project.coreExt.validateAts) additionalPatch.set(input.additionalPatch.fileExists()) } } @@ -172,6 +174,7 @@ abstract class PaperweightPatcher : Plugin { workDir.set(workDirFromProp) atFile.set(input.additionalAts.fileExists()) ats.jst.from(project.configurations.named(JST_CONFIG)) + validateAts.set(project.coreExt.validateAts) additionalPatch.set(input.additionalPatch.fileExists()) generateSources.set(input.generateSources.orElse(true)) generateResources.set(input.generateResources.orElse(true)) From 36651046a9deb1bbfc003e6f088e519cdc22bb0c Mon Sep 17 00:00:00 2001 From: Toffikk Date: Mon, 26 Jan 2026 00:39:26 +0100 Subject: [PATCH 173/257] feat: allow for specifying ATs for upstream sources These changes provide fork developers with a way to specify an AT file (or have it detected automatically) for upstream sources, such as `paper-server` and `paper-api` for a fork of Paper. This allows fork developers to benefit from using ATs while modifying these sources without needing to clutter patches, with lots of manual access transforms, and as such this greatly improves the overall comfort of making more involving changes, in the sources. It also allows for fork devs to be able to organize ATs in one place which makes it easier to review later. The implementation proposed in this commit works as follows: 1. Derive the location of the build-data dir from the output dir specified in the PatchRepo/Directory's config. 2. Search for an AT file, by the name of the patch repo/directory as specified. 3. Apply them before applying source patches. The locations in steps 1 and 2 can be overriden per patch repo/directory by just overriding the val. In order to achieve this change, we introduce a new task, with its implementation sitting in SetupForkUpstreamSources which applies those ATs. Everything else not explained here or even explained should follow the behaviour of AT application for minecraft sources. The change has been tested in production in an organisation fork of paperweight for over 6 months with both a nested fork project that uses them heavily and then fork projects depending on the nested fork project, and no issues have been reported or arose, so i'm confident that this implementation will work properly. --- .../core/extension/UpstreamConfig.kt | 7 +- .../core/taskcontainers/PatchingTasks.kt | 41 +++++++++ .../taskcontainers/UpstreamConfigTasks.kt | 5 +- .../core/tasks/SetupForkUpstreamSources.kt | 85 +++++++++++++++++++ 4 files changed, 136 insertions(+), 2 deletions(-) create mode 100644 paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupForkUpstreamSources.kt diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/UpstreamConfig.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/UpstreamConfig.kt index c448db831..2b1dbf803 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/UpstreamConfig.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/UpstreamConfig.kt @@ -35,6 +35,7 @@ import org.gradle.api.model.ObjectFactory import org.gradle.api.provider.ListProperty import org.gradle.api.provider.Property import org.gradle.api.provider.Provider +import org.gradle.api.provider.ProviderFactory import org.gradle.api.provider.SetProperty import org.gradle.kotlin.dsl.* @@ -93,6 +94,7 @@ abstract class UpstreamConfig @Inject constructor( abstract class DirectoryPatchSet @Inject constructor( objects: ObjectFactory, + providers: ProviderFactory, private val setName: String, ) : Named { override fun getName(): String = setName @@ -101,6 +103,8 @@ abstract class UpstreamConfig @Inject constructor( abstract val excludes: SetProperty abstract val outputDir: DirectoryProperty + val buildDataDir: DirectoryProperty = objects.directoryProperty().convention(outputDir.dir("../build-data")) + val additionalAts: RegularFileProperty = objects.fileFrom(buildDataDir, providers.provider { "$name.at" }) abstract val patchesDir: DirectoryProperty val rejectsDir: DirectoryProperty = objects.dirFrom(patchesDir, "rejected") @@ -110,8 +114,9 @@ abstract class UpstreamConfig @Inject constructor( abstract class RepoPatchSet @Inject constructor( objects: ObjectFactory, + providers: ProviderFactory, name: String, - ) : DirectoryPatchSet(objects, name) { + ) : DirectoryPatchSet(objects, providers, name) { abstract val upstreamRepo: Property fun Provider.patchedRepo(name: String): Provider = diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt index d9cf03739..d00238d6f 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt @@ -22,6 +22,7 @@ package io.papermc.paperweight.core.taskcontainers +import io.papermc.paperweight.core.tasks.SetupForkUpstreamSources import io.papermc.paperweight.core.tasks.patching.ApplyFeaturePatches import io.papermc.paperweight.core.tasks.patching.ApplyFilePatches import io.papermc.paperweight.core.tasks.patching.ApplyFilePatchesFuzzy @@ -29,12 +30,14 @@ import io.papermc.paperweight.core.tasks.patching.FixupFilePatches import io.papermc.paperweight.core.tasks.patching.RebuildFilePatches import io.papermc.paperweight.tasks.* import io.papermc.paperweight.util.* +import io.papermc.paperweight.util.constants.JST_CONFIG import io.papermc.paperweight.util.constants.paperTaskOutput import java.nio.file.Path import org.gradle.api.Project import org.gradle.api.Task import org.gradle.api.file.Directory import org.gradle.api.file.DirectoryProperty +import org.gradle.api.file.RegularFileProperty import org.gradle.api.provider.Provider import org.gradle.api.tasks.TaskContainer import org.gradle.kotlin.dsl.* @@ -48,6 +51,7 @@ class PatchingTasks( private val filePatchDir: DirectoryProperty, private val rejectsDir: DirectoryProperty, private val featurePatchDir: DirectoryProperty, + private val additionalAts: RegularFileProperty, private val baseDir: Provider, private val gitFilePatches: Provider, private val filterPatches: Provider, @@ -110,6 +114,40 @@ class PatchingTasks( } } + fun setupUpstream() { + val collectAccessTransform = tasks.register("collect${namePart}ATsFromPatches") { + patchDir.set(this@PatchingTasks.featurePatchDir.fileExists()) + } + + val mergeCollectedAts = tasks.register("merge${namePart}ATs") { + firstFile.set(additionalAts.fileExists()) + secondFile.set(collectAccessTransform.flatMap { it.outputFile }) + } + + val setup = tasks.register("run${namePart}Setup") { + description = "Applies $forkName ATs to $namePart sources" + inputDir.set(baseDir) + outputDir.set(layout.cache.resolve(paperTaskOutput())) + identifier.set(namePart) + atFile.set(mergeCollectedAts.flatMap { it.outputFile }) + ats.jst.from(project.configurations.named(JST_CONFIG)) + } + + applyFilePatches.configure { + input.set(setup.flatMap { it.outputDir }) + } + + applyFilePatchesFuzzy.configure { + input.set(setup.flatMap { it.outputDir }) + } + val name = "rebuild${namePart}FilePatches" + if (name in tasks.names) { + tasks.named(name) { + base.set(setup.flatMap { it.outputDir }) + } + } + } + private fun setupWritable() { listOf( applyFilePatches, @@ -129,6 +167,9 @@ class PatchingTasks( input.set(outputDir) patches.set(filePatchDir) gitFilePatches.set(this@PatchingTasks.gitFilePatches) + ats.jst.from(project.configurations.named(JST_CONFIG)) + atFile.set(additionalAts.fileExists()) + atFileOut.set(additionalAts.fileExists()) } val fixupFilePatches = tasks.register(fixupFilePatchesName) { diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/UpstreamConfigTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/UpstreamConfigTasks.kt index ab22724d8..322825661 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/UpstreamConfigTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/UpstreamConfigTasks.kt @@ -118,7 +118,7 @@ class UpstreamConfigTasks( createBaseFromDirectoryInRepo(cfg) } - return PatchingTasks( + val tasks = PatchingTasks( target, forkName, cfg.name, @@ -127,11 +127,14 @@ class UpstreamConfigTasks( cfg.filePatchDir, cfg.rejectsDir, cfg.featurePatchDir, + cfg.additionalAts, base, gitFilePatches, filterPatches, cfg.outputDir.path, ) + tasks.setupUpstream() + return tasks } private fun createBaseFromRepo(cfg: UpstreamConfig.RepoPatchSet): Provider { diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupForkUpstreamSources.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupForkUpstreamSources.kt new file mode 100644 index 000000000..c08c6e451 --- /dev/null +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupForkUpstreamSources.kt @@ -0,0 +1,85 @@ +/* + * paperweight is a Gradle plugin for the PaperMC project. + * + * Copyright (c) 2023 Kyle Wood (DenWav) + * Contributors + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 only, no later versions. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + * USA + */ + +package io.papermc.paperweight.core.tasks + +import io.papermc.paperweight.core.util.ApplySourceATs +import io.papermc.paperweight.tasks.* +import io.papermc.paperweight.util.* +import kotlin.io.path.* +import org.eclipse.jgit.api.Git +import org.eclipse.jgit.lib.PersonIdent +import org.gradle.api.file.DirectoryProperty +import org.gradle.api.file.RegularFileProperty +import org.gradle.api.provider.Property +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.InputDirectory +import org.gradle.api.tasks.InputFile +import org.gradle.api.tasks.Nested +import org.gradle.api.tasks.Optional +import org.gradle.api.tasks.OutputDirectory +import org.gradle.api.tasks.TaskAction +import org.gradle.kotlin.dsl.* + +abstract class SetupForkUpstreamSources : JavaLauncherTask() { + + @get:InputDirectory + abstract val inputDir: DirectoryProperty + + @get:OutputDirectory + abstract val outputDir: DirectoryProperty + + @get:Nested + val ats: ApplySourceATs = objects.newInstance() + + @get:InputFile + @get:Optional + abstract val atFile: RegularFileProperty + + @get:Input + abstract val identifier: Property + + @TaskAction + fun run() { + val out = outputDir.path.cleanDir() + inputDir.path.copyRecursivelyTo(out) + + val git = Git.open(outputDir.path.toFile()) + + if (atFile.isPresent && atFile.path.readText().isNotBlank()) { + println("Applying access transformers...") + ats.run( + launcher.get(), + inputDir.path, + outputDir.path, + atFile.path, + temporaryDir.toPath(), + ) + commitAndTag(git, "ATs", "${identifier.get()} ATs") + } + val ident = PersonIdent("base", "noreply+automated@papermc.io") + git.tagDelete().setTags("base").call() + git.tag().setName("base").setTagger(ident).setSigned(false).call() + + git.close() + } +} From 3b474df01ca5aa606f78c05eb1852f8740cdd5e9 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Mon, 26 Jan 2026 01:03:59 +0100 Subject: [PATCH 174/257] fix: bring up-to-par with paperweight pr impl this fixes an issue that i did not notice earlier lol --- .../paperweight/core/taskcontainers/PatchingTasks.kt | 7 +++++-- .../{SetupForkSources.kt => SetupForkUpstreamSources.kt} | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) rename paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/{SetupForkSources.kt => SetupForkUpstreamSources.kt} (97%) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt index f712738ee..06c45eca3 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt @@ -22,7 +22,7 @@ package io.papermc.paperweight.core.taskcontainers -import io.papermc.paperweight.core.tasks.SetupForkSources +import io.papermc.paperweight.core.tasks.SetupForkUpstreamSources import io.papermc.paperweight.core.tasks.patching.ApplyBasePatches import io.papermc.paperweight.core.tasks.patching.ApplyFeaturePatches import io.papermc.paperweight.core.tasks.patching.ApplyFilePatches @@ -149,7 +149,7 @@ class PatchingTasks( secondFile.set(collectAccessTransform.flatMap { it.outputFile }) } - val setup = tasks.register("run${namePart}Setup") { + val setup = tasks.register("run${namePart}Setup") { description = "Applies $forkName ATs to $namePart sources" inputDir.set(baseDir) outputDir.set(layout.cache.resolve(paperTaskOutput())) @@ -202,6 +202,9 @@ class PatchingTasks( input.set(outputDir) patches.set(filePatchDir) gitFilePatches.set(this@PatchingTasks.gitFilePatches) + ats.jst.from(project.configurations.named(JST_CONFIG)) + atFile.set(additionalAts.fileExists()) + atFileOut.set(additionalAts.fileExists()) identifier = "$forkName $patchSetName" } diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupForkSources.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupForkUpstreamSources.kt similarity index 97% rename from paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupForkSources.kt rename to paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupForkUpstreamSources.kt index 39224c5a4..fc2010ba7 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupForkSources.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupForkUpstreamSources.kt @@ -40,7 +40,7 @@ import org.gradle.api.tasks.OutputDirectory import org.gradle.api.tasks.TaskAction import org.gradle.kotlin.dsl.* -abstract class SetupForkSources : JavaLauncherTask() { +abstract class SetupForkUpstreamSources : JavaLauncherTask() { @get:InputDirectory abstract val inputDir: DirectoryProperty From 55d76c06bc913ae4f04ae9981c1e51af005b91eb Mon Sep 17 00:00:00 2001 From: "tofik.07" Date: Mon, 26 Jan 2026 03:49:33 +0100 Subject: [PATCH 175/257] chore: remove unnecessary this@PatchingTasks specifier --- .../io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt index d00238d6f..204ced3fd 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt @@ -116,7 +116,7 @@ class PatchingTasks( fun setupUpstream() { val collectAccessTransform = tasks.register("collect${namePart}ATsFromPatches") { - patchDir.set(this@PatchingTasks.featurePatchDir.fileExists()) + patchDir.set(featurePatchDir.fileExists()) } val mergeCollectedAts = tasks.register("merge${namePart}ATs") { From 3e49a665bca5a1287575a1990ee7e2880055794f Mon Sep 17 00:00:00 2001 From: Toffikk Date: Mon, 26 Jan 2026 01:18:38 +0100 Subject: [PATCH 176/257] fix: dont cast patcher ext to core ext --- .../io/papermc/paperweight/core/taskcontainers/CoreTasks.kt | 1 + .../paperweight/core/taskcontainers/PatchingTasks.kt | 4 ++-- .../paperweight/core/taskcontainers/UpstreamConfigTasks.kt | 2 ++ .../io/papermc/paperweight/patcher/PaperweightPatcher.kt | 6 +++--- .../patcher/extension/PaperweightPatcherExtension.kt | 1 + 5 files changed, 9 insertions(+), 5 deletions(-) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/CoreTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/CoreTasks.kt index 74c862ff2..397aa976a 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/CoreTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/CoreTasks.kt @@ -254,6 +254,7 @@ class CoreTasks( } else { "upstream server patching" }, + project.coreExt.validateAts, project.coreExt.gitFilePatches, project.coreExt.filterPatches, null, diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt index 06c45eca3..19d8635d9 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt @@ -29,7 +29,6 @@ import io.papermc.paperweight.core.tasks.patching.ApplyFilePatches import io.papermc.paperweight.core.tasks.patching.ApplyFilePatchesFuzzy import io.papermc.paperweight.core.tasks.patching.FixupFilePatches import io.papermc.paperweight.core.tasks.patching.RebuildFilePatches -import io.papermc.paperweight.core.util.coreExt import io.papermc.paperweight.tasks.* import io.papermc.paperweight.util.* import io.papermc.paperweight.util.constants.JST_CONFIG @@ -50,6 +49,7 @@ class PatchingTasks( private val patchSetName: String, private val taskGroup: String, private val readOnly: Boolean, + private val validateAts: Provider, private val basePatchDir: DirectoryProperty, private val filePatchDir: DirectoryProperty, private val rejectsDir: DirectoryProperty, @@ -156,7 +156,7 @@ class PatchingTasks( identifier.set(namePart) atFile.set(mergeCollectedAts.flatMap { it.outputFile }) ats.jst.from(project.configurations.named(JST_CONFIG)) - validateAts.set(project.coreExt.validateAts) + validateAts.set(this@PatchingTasks.validateAts) } applyBasePatches.configure { diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/UpstreamConfigTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/UpstreamConfigTasks.kt index e94e3948d..b1a2077bb 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/UpstreamConfigTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/UpstreamConfigTasks.kt @@ -43,6 +43,7 @@ class UpstreamConfigTasks( private val upstreamDir: Provider, private val readOnly: Boolean, private val taskGroup: String, + private val validateAts: Provider, private val gitFilePatches: Provider, private val filterPatches: Provider, private val setupUpstream: TaskProvider?, @@ -124,6 +125,7 @@ class UpstreamConfigTasks( cfg.name, taskGroup, readOnly, + validateAts, cfg.basePatchDir, cfg.filePatchDir, cfg.rejectsDir, diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/PaperweightPatcher.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/PaperweightPatcher.kt index 581028454..4ddd5bd0d 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/PaperweightPatcher.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/PaperweightPatcher.kt @@ -28,7 +28,6 @@ import io.papermc.paperweight.core.tasks.GeneratePatches import io.papermc.paperweight.core.tasks.GenerateSources import io.papermc.paperweight.core.tasks.PrepareForPatchGeneration import io.papermc.paperweight.core.tasks.RunNestedBuild -import io.papermc.paperweight.core.util.coreExt import io.papermc.paperweight.patcher.extension.PaperweightPatcherExtension import io.papermc.paperweight.util.* import io.papermc.paperweight.util.constants.* @@ -100,6 +99,7 @@ abstract class PaperweightPatcher : Plugin { checkoutTask.flatMap { it.outputDir }, !isBaseExecution, "patching", + patcher.validateAts, patcher.gitFilePatches, patcher.filterPatches, applyUpstream, @@ -151,7 +151,7 @@ abstract class PaperweightPatcher : Plugin { workDir.set(workDirFromProp) atFile.set(input.additionalAts.fileExists()) ats.jst.from(project.configurations.named(JST_CONFIG)) - validateAts.set(project.coreExt.validateAts) + validateAts.set(patcher.validateAts) additionalPatch.set(input.additionalPatch.fileExists()) } } @@ -174,7 +174,7 @@ abstract class PaperweightPatcher : Plugin { workDir.set(workDirFromProp) atFile.set(input.additionalAts.fileExists()) ats.jst.from(project.configurations.named(JST_CONFIG)) - validateAts.set(project.coreExt.validateAts) + validateAts.set(patcher.validateAts) additionalPatch.set(input.additionalPatch.fileExists()) generateSources.set(input.generateSources.orElse(true)) generateResources.set(input.generateResources.orElse(true)) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/extension/PaperweightPatcherExtension.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/extension/PaperweightPatcherExtension.kt index 7ec37a277..734f26ddd 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/extension/PaperweightPatcherExtension.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/extension/PaperweightPatcherExtension.kt @@ -36,6 +36,7 @@ import org.gradle.kotlin.dsl.* @Suppress("unused", "MemberVisibilityCanBePrivate") abstract class PaperweightPatcherExtension @Inject constructor(private val objects: ObjectFactory) { + val validateAts: Property = objects.property().convention(false) val gitFilePatches: Property = objects.property().convention(false) val filterPatches: Property = objects.property().convention(true) val jstRepo: Property = objects.property().convention(PAPER_MAVEN_REPO_URL) From 3ddbc21a433cb50cdd6ea2ec906c12da046e6ed0 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Tue, 27 Jan 2026 02:14:44 +0100 Subject: [PATCH 177/257] chore: cleanup tagging logic --- .../paperweight/core/tasks/SetupForkUpstreamSources.kt | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupForkUpstreamSources.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupForkUpstreamSources.kt index c08c6e451..fd42e5de2 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupForkUpstreamSources.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupForkUpstreamSources.kt @@ -27,7 +27,6 @@ import io.papermc.paperweight.tasks.* import io.papermc.paperweight.util.* import kotlin.io.path.* import org.eclipse.jgit.api.Git -import org.eclipse.jgit.lib.PersonIdent import org.gradle.api.file.DirectoryProperty import org.gradle.api.file.RegularFileProperty import org.gradle.api.provider.Property @@ -76,9 +75,7 @@ abstract class SetupForkUpstreamSources : JavaLauncherTask() { ) commitAndTag(git, "ATs", "${identifier.get()} ATs") } - val ident = PersonIdent("base", "noreply+automated@papermc.io") - git.tagDelete().setTags("base").call() - git.tag().setName("base").setTagger(ident).setSigned(false).call() + commitAndTag(git, "base") git.close() } From 2043efc1474582d2cdea49889fd2f914266d28d5 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Tue, 3 Feb 2026 18:21:28 +0100 Subject: [PATCH 178/257] fix: provide compile classpath for JST --- .../papermc/paperweight/core/taskcontainers/PatchingTasks.kt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt index 19d8635d9..a7e92592b 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt @@ -39,6 +39,7 @@ import org.gradle.api.Task import org.gradle.api.file.Directory import org.gradle.api.file.DirectoryProperty import org.gradle.api.file.RegularFileProperty +import org.gradle.api.plugins.JavaPlugin import org.gradle.api.provider.Provider import org.gradle.api.tasks.TaskContainer import org.gradle.kotlin.dsl.* @@ -156,6 +157,7 @@ class PatchingTasks( identifier.set(namePart) atFile.set(mergeCollectedAts.flatMap { it.outputFile }) ats.jst.from(project.configurations.named(JST_CONFIG)) + ats.jstClasspath.from(project.configurations.named(JavaPlugin.COMPILE_CLASSPATH_CONFIGURATION_NAME)) validateAts.set(this@PatchingTasks.validateAts) } @@ -202,6 +204,7 @@ class PatchingTasks( input.set(outputDir) patches.set(filePatchDir) gitFilePatches.set(this@PatchingTasks.gitFilePatches) + ats.jstClasspath.from(project.configurations.named(JavaPlugin.COMPILE_CLASSPATH_CONFIGURATION_NAME)) ats.jst.from(project.configurations.named(JST_CONFIG)) atFile.set(additionalAts.fileExists()) atFileOut.set(additionalAts.fileExists()) From 93077cb9125239ecb6c060c3491973cca3b77904 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Tue, 3 Feb 2026 18:29:16 +0100 Subject: [PATCH 179/257] chore: indent --- .../paperweight/core/taskcontainers/MinecraftPatchingTasks.kt | 1 + .../papermc/paperweight/core/taskcontainers/PatchingTasks.kt | 3 +++ 2 files changed, 4 insertions(+) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt index 0be3980e9..d2acefc34 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt @@ -197,6 +197,7 @@ class MinecraftPatchingTasks( oldCommit.convention(project.providers.gradleProperty("old${configName.capitalized()}Commit")) oldOutputDir.set(layout.cache.resolve("$PAPER_PATH/old${configName.capitalized()}")) } + libraryImports.set(importLibFiles.flatMap { it.outputDir }) atFile.set(mergeCollectedAts.flatMap { it.outputFile }) ats.jst.from(project.configurations.named(JST_CONFIG)) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt index a7e92592b..f190c7c0c 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt @@ -152,9 +152,11 @@ class PatchingTasks( val setup = tasks.register("run${namePart}Setup") { description = "Applies $forkName ATs to $namePart sources" + inputDir.set(baseDir) outputDir.set(layout.cache.resolve(paperTaskOutput())) identifier.set(namePart) + atFile.set(mergeCollectedAts.flatMap { it.outputFile }) ats.jst.from(project.configurations.named(JST_CONFIG)) ats.jstClasspath.from(project.configurations.named(JavaPlugin.COMPILE_CLASSPATH_CONFIGURATION_NAME)) @@ -204,6 +206,7 @@ class PatchingTasks( input.set(outputDir) patches.set(filePatchDir) gitFilePatches.set(this@PatchingTasks.gitFilePatches) + ats.jstClasspath.from(project.configurations.named(JavaPlugin.COMPILE_CLASSPATH_CONFIGURATION_NAME)) ats.jst.from(project.configurations.named(JST_CONFIG)) atFile.set(additionalAts.fileExists()) From c8e472c39dfc2ac98aecb8a3a34348ac3e03308f Mon Sep 17 00:00:00 2001 From: Toffikk Date: Tue, 3 Feb 2026 19:20:04 +0100 Subject: [PATCH 180/257] fix: provide the classpaths of subprojects This is needed for being fully able to represent types in API projects as they are resolved from the root project and not nested, meaning the compile classpath passed is null as its from the root project. --- .../paperweight/core/taskcontainers/PatchingTasks.kt | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt index f190c7c0c..93c74d4f2 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt @@ -159,7 +159,10 @@ class PatchingTasks( atFile.set(mergeCollectedAts.flatMap { it.outputFile }) ats.jst.from(project.configurations.named(JST_CONFIG)) - ats.jstClasspath.from(project.configurations.named(JavaPlugin.COMPILE_CLASSPATH_CONFIGURATION_NAME)) + ats.jstClasspath.from( + project.configurations.named(JavaPlugin.COMPILE_CLASSPATH_CONFIGURATION_NAME), + *project.subprojects.map { it.provider { it.configurations.named(JavaPlugin.COMPILE_CLASSPATH_CONFIGURATION_NAME).get().files } }.toTypedArray() + ) validateAts.set(this@PatchingTasks.validateAts) } @@ -207,7 +210,10 @@ class PatchingTasks( patches.set(filePatchDir) gitFilePatches.set(this@PatchingTasks.gitFilePatches) - ats.jstClasspath.from(project.configurations.named(JavaPlugin.COMPILE_CLASSPATH_CONFIGURATION_NAME)) + ats.jstClasspath.from( + project.configurations.named(JavaPlugin.COMPILE_CLASSPATH_CONFIGURATION_NAME), + *project.subprojects.map { it.provider { it.configurations.named(JavaPlugin.COMPILE_CLASSPATH_CONFIGURATION_NAME).get().files } }.toTypedArray() + ) ats.jst.from(project.configurations.named(JST_CONFIG)) atFile.set(additionalAts.fileExists()) atFileOut.set(additionalAts.fileExists()) From f93aa30bf7c2ce8f7b84b36fe3fa43831a7d2f2b Mon Sep 17 00:00:00 2001 From: Toffikk Date: Tue, 3 Feb 2026 20:11:43 +0100 Subject: [PATCH 181/257] fix: append the mache minecraft jar to JST's classpath fixes more binary representation issues --- .../io/papermc/paperweight/core/PaperweightCore.kt | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/PaperweightCore.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/PaperweightCore.kt index 33c18dd60..3977abebc 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/PaperweightCore.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/PaperweightCore.kt @@ -28,8 +28,10 @@ import io.papermc.paperweight.core.extension.PaperweightCoreExtension import io.papermc.paperweight.core.taskcontainers.CoreTasks import io.papermc.paperweight.core.taskcontainers.DevBundleTasks import io.papermc.paperweight.core.taskcontainers.PaperclipTasks +import io.papermc.paperweight.core.tasks.SetupForkUpstreamSources import io.papermc.paperweight.core.tasks.patching.ApplyBasePatches import io.papermc.paperweight.core.tasks.patching.ApplyFilePatches +import io.papermc.paperweight.core.tasks.patching.RebuildFilePatches import io.papermc.paperweight.core.tasks.patchroulette.PatchRouletteTasks import io.papermc.paperweight.core.util.coreExt import io.papermc.paperweight.core.util.createBuildTasks @@ -51,6 +53,7 @@ import org.gradle.api.tasks.Delete import org.gradle.api.tasks.SourceSet import org.gradle.api.tasks.bundling.AbstractArchiveTask import org.gradle.kotlin.dsl.* +import org.gradle.kotlin.dsl.withType abstract class PaperweightCore : Plugin { @get:Inject @@ -196,6 +199,17 @@ abstract class PaperweightCore : Plugin { mache.get().addRepositories(this) mache.get().addDependencies(this) + // attach the mache minecraft jar so JST can properly create binary representations + target.tasks.withType().configureEach { + dependsOn(target.tasks.named("macheRemapJar")) + ats.jstClasspath.from(target.configurations.named(MACHE_MINECRAFT_CONFIG)) + } + + target.tasks.withType().configureEach { + dependsOn(target.tasks.named("macheRemapJar")) + ats.jstClasspath.from(target.configurations.named(MACHE_MINECRAFT_CONFIG)) + } + tasks.afterEvaluate() devBundleTasks.configureAfterEvaluate( From 04de79a442ad436285f0005f6ce506371aa16a35 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Tue, 3 Feb 2026 20:21:31 +0100 Subject: [PATCH 182/257] format --- .../core/taskcontainers/PatchingTasks.kt | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt index 93c74d4f2..20d7922c9 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt @@ -161,7 +161,13 @@ class PatchingTasks( ats.jst.from(project.configurations.named(JST_CONFIG)) ats.jstClasspath.from( project.configurations.named(JavaPlugin.COMPILE_CLASSPATH_CONFIGURATION_NAME), - *project.subprojects.map { it.provider { it.configurations.named(JavaPlugin.COMPILE_CLASSPATH_CONFIGURATION_NAME).get().files } }.toTypedArray() + *project.subprojects.map { + it.provider { + it.configurations.named( + JavaPlugin.COMPILE_CLASSPATH_CONFIGURATION_NAME + ).get().files + } + }.toTypedArray() ) validateAts.set(this@PatchingTasks.validateAts) } @@ -212,7 +218,13 @@ class PatchingTasks( ats.jstClasspath.from( project.configurations.named(JavaPlugin.COMPILE_CLASSPATH_CONFIGURATION_NAME), - *project.subprojects.map { it.provider { it.configurations.named(JavaPlugin.COMPILE_CLASSPATH_CONFIGURATION_NAME).get().files } }.toTypedArray() + *project.subprojects.map { + it.provider { + it.configurations.named( + JavaPlugin.COMPILE_CLASSPATH_CONFIGURATION_NAME + ).get().files + } + }.toTypedArray() ) ats.jst.from(project.configurations.named(JST_CONFIG)) atFile.set(additionalAts.fileExists()) From e5e4e5bb4004966f0a934b461914370d655c8072 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Tue, 3 Feb 2026 20:32:13 +0100 Subject: [PATCH 183/257] fix(normal AT logic/paperweight issue): append compile classpath for minecraft ATs This fixes some issues with JST and forks as there are custom types from the API imported by paper, which arent contained in the mache libraries config and that causes JST to fail to create binary representations for some methods/classes thus not allowing to AT them, this fixes it --- .../core/taskcontainers/MinecraftPatchingTasks.kt | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt index d2acefc34..25271d409 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt @@ -41,6 +41,7 @@ import org.gradle.api.Task import org.gradle.api.file.Directory import org.gradle.api.file.DirectoryProperty import org.gradle.api.file.RegularFileProperty +import org.gradle.api.plugins.JavaPlugin import org.gradle.api.provider.Provider import org.gradle.api.tasks.TaskContainer import org.gradle.kotlin.dsl.* @@ -201,7 +202,10 @@ class MinecraftPatchingTasks( libraryImports.set(importLibFiles.flatMap { it.outputDir }) atFile.set(mergeCollectedAts.flatMap { it.outputFile }) ats.jst.from(project.configurations.named(JST_CONFIG)) - ats.jstClasspath.from(project.configurations.named(MACHE_MINECRAFT_LIBRARIES_CONFIG)) + ats.jstClasspath.from( + project.configurations.named(MACHE_MINECRAFT_CONFIG), + project.configurations.named(JavaPlugin.COMPILE_CLASSPATH_CONFIGURATION_NAME) + ) validateAts.set(project.coreExt.validateAts) } @@ -251,7 +255,10 @@ class MinecraftPatchingTasks( patches.set(sourcePatchDir) gitFilePatches.set(this@MinecraftPatchingTasks.gitFilePatches) - ats.jstClasspath.from(project.configurations.named(MACHE_MINECRAFT_CONFIG)) + ats.jstClasspath.from( + project.configurations.named(MACHE_MINECRAFT_CONFIG), + project.configurations.named(JavaPlugin.COMPILE_CLASSPATH_CONFIGURATION_NAME) + ) ats.jst.from(project.configurations.named(JST_CONFIG)) atFile.set(additionalAts.fileExists()) atFileOut.set(additionalAts.fileExists()) From 81c66efcaad6b4ad7431653e5715ee3b584eae48 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Tue, 3 Feb 2026 20:39:58 +0100 Subject: [PATCH 184/257] Revert "fix(normal AT logic/paperweight issue): append compile classpath for minecraft ATs" This reverts commit e5e4e5bb4004966f0a934b461914370d655c8072. seems to fail the tests, although the output in the log file is correct locally --- .../core/taskcontainers/MinecraftPatchingTasks.kt | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt index 25271d409..d2acefc34 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt @@ -41,7 +41,6 @@ import org.gradle.api.Task import org.gradle.api.file.Directory import org.gradle.api.file.DirectoryProperty import org.gradle.api.file.RegularFileProperty -import org.gradle.api.plugins.JavaPlugin import org.gradle.api.provider.Provider import org.gradle.api.tasks.TaskContainer import org.gradle.kotlin.dsl.* @@ -202,10 +201,7 @@ class MinecraftPatchingTasks( libraryImports.set(importLibFiles.flatMap { it.outputDir }) atFile.set(mergeCollectedAts.flatMap { it.outputFile }) ats.jst.from(project.configurations.named(JST_CONFIG)) - ats.jstClasspath.from( - project.configurations.named(MACHE_MINECRAFT_CONFIG), - project.configurations.named(JavaPlugin.COMPILE_CLASSPATH_CONFIGURATION_NAME) - ) + ats.jstClasspath.from(project.configurations.named(MACHE_MINECRAFT_LIBRARIES_CONFIG)) validateAts.set(project.coreExt.validateAts) } @@ -255,10 +251,7 @@ class MinecraftPatchingTasks( patches.set(sourcePatchDir) gitFilePatches.set(this@MinecraftPatchingTasks.gitFilePatches) - ats.jstClasspath.from( - project.configurations.named(MACHE_MINECRAFT_CONFIG), - project.configurations.named(JavaPlugin.COMPILE_CLASSPATH_CONFIGURATION_NAME) - ) + ats.jstClasspath.from(project.configurations.named(MACHE_MINECRAFT_CONFIG)) ats.jst.from(project.configurations.named(JST_CONFIG)) atFile.set(additionalAts.fileExists()) atFileOut.set(additionalAts.fileExists()) From 42182b22712718c8db1a25e929e2b6ff8ca15c5e Mon Sep 17 00:00:00 2001 From: Toffikk Date: Thu, 12 Feb 2026 00:39:45 +0100 Subject: [PATCH 185/257] chore: capitalise ATs --- .../paperweight/core/extension/PaperweightCoreExtension.kt | 2 +- .../io/papermc/paperweight/core/taskcontainers/CoreTasks.kt | 2 +- .../paperweight/core/taskcontainers/MinecraftPatchingTasks.kt | 2 +- .../paperweight/core/tasks/SetupForkMinecraftSources.kt | 4 ++-- .../papermc/paperweight/core/tasks/SetupMinecraftSources.kt | 4 ++-- .../patcher/extension/PaperweightPatcherExtension.kt | 1 + 6 files changed, 8 insertions(+), 7 deletions(-) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/PaperweightCoreExtension.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/PaperweightCoreExtension.kt index df3b973a0..a725e68ef 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/PaperweightCoreExtension.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/PaperweightCoreExtension.kt @@ -80,5 +80,5 @@ abstract class PaperweightCoreExtension @Inject constructor(objects: ObjectFacto action.execute(updatingMinecraft) } - val validateAts: Property = objects.property().convention(false) + val validateATs: Property = objects.property().convention(false) } diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/CoreTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/CoreTasks.kt index 29b3490de..6a30dd86d 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/CoreTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/CoreTasks.kt @@ -112,7 +112,7 @@ class CoreTasks( atFile.set(mergePaperATs.flatMap { it.outputFile }) ats.jstClasspath.from(project.configurations.named(MACHE_MINECRAFT_LIBRARIES_CONFIG)) ats.jst.from(project.configurations.named(JST_CONFIG)) - validateAts.set(project.coreExt.validateAts) + validateATs.set(project.coreExt.validateATs) } val extractMacheSources by tasks.registering(ExtractMinecraftSources::class) { diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt index 7f30124dd..0bb22cfc0 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt @@ -172,7 +172,7 @@ class MinecraftPatchingTasks( atFile.set(mergeCollectedAts.flatMap { it.outputFile }) ats.jst.from(project.configurations.named(JST_CONFIG)) ats.jstClasspath.from(project.configurations.named(MACHE_MINECRAFT_LIBRARIES_CONFIG)) - validateAts.set(project.coreExt.validateAts) + validateATs.set(project.coreExt.validateATs) } applySourcePatches.configure { diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupForkMinecraftSources.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupForkMinecraftSources.kt index 976686c9a..362c32322 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupForkMinecraftSources.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupForkMinecraftSources.kt @@ -60,7 +60,7 @@ abstract class SetupForkMinecraftSources : JavaLauncherTask() { abstract val atFile: RegularFileProperty @get:Input - abstract val validateAts: Property + abstract val validateATs: Property @get:Optional @get:InputDirectory @@ -89,7 +89,7 @@ abstract class SetupForkMinecraftSources : JavaLauncherTask() { outputDir.path, atFile.path, atWorkingDir.path, - validate = validateAts.get(), + validate = validateATs.get(), ) commitAndTag(git, "ATs", "${identifier.get()} ATs") } diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupMinecraftSources.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupMinecraftSources.kt index 1ff859996..3262c5da8 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupMinecraftSources.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupMinecraftSources.kt @@ -71,7 +71,7 @@ abstract class SetupMinecraftSources : JavaLauncherZippedTask() { @get:Optional @get:Input - abstract val validateAts: Property + abstract val validateATs: Property @get:Nested val ats: ApplySourceATs = objects.newInstance() @@ -186,7 +186,7 @@ abstract class SetupMinecraftSources : JavaLauncherZippedTask() { outputPath, atFile.path, atWorkingDir.path, - validate = validateAts.get(), + validate = validateATs.get(), ) if (!oldPaperCommit.isPresent) { commitAndTag(git, "ATs", "paper ATs") diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/extension/PaperweightPatcherExtension.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/extension/PaperweightPatcherExtension.kt index 18fbc9591..bf374a7bf 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/extension/PaperweightPatcherExtension.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/extension/PaperweightPatcherExtension.kt @@ -36,6 +36,7 @@ abstract class PaperweightPatcherExtension @Inject constructor(private val objec val gitFilePatches: Property = objects.property().convention(false) val filterPatches: Property = objects.property().convention(true) + val validateATs: Property = objects.property().convention(false) val upstreams: NamedDomainObjectContainer = objects.domainObjectContainer(UpstreamConfig::class) { objects.newInstance(it, true) From f65f51b35601be290127890fd6368d6668eea2da Mon Sep 17 00:00:00 2001 From: Toffikk Date: Tue, 24 Feb 2026 02:08:22 +0100 Subject: [PATCH 186/257] chore: cleanup impl --- .../core/taskcontainers/PatchingTasks.kt | 20 ++++++------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt index 20d7922c9..faef8808d 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt @@ -161,13 +161,9 @@ class PatchingTasks( ats.jst.from(project.configurations.named(JST_CONFIG)) ats.jstClasspath.from( project.configurations.named(JavaPlugin.COMPILE_CLASSPATH_CONFIGURATION_NAME), - *project.subprojects.map { - it.provider { - it.configurations.named( - JavaPlugin.COMPILE_CLASSPATH_CONFIGURATION_NAME - ).get().files - } - }.toTypedArray() + project.subprojects.map { + it.files(it.configurations.named(JavaPlugin.COMPILE_CLASSPATH_CONFIGURATION_NAME)) + } ) validateAts.set(this@PatchingTasks.validateAts) } @@ -218,13 +214,9 @@ class PatchingTasks( ats.jstClasspath.from( project.configurations.named(JavaPlugin.COMPILE_CLASSPATH_CONFIGURATION_NAME), - *project.subprojects.map { - it.provider { - it.configurations.named( - JavaPlugin.COMPILE_CLASSPATH_CONFIGURATION_NAME - ).get().files - } - }.toTypedArray() + project.subprojects.map { + it.files(it.configurations.named(JavaPlugin.COMPILE_CLASSPATH_CONFIGURATION_NAME)) + } ) ats.jst.from(project.configurations.named(JST_CONFIG)) atFile.set(additionalAts.fileExists()) From 6796334ca3dd622b9195b22efcd83767160e5e7b Mon Sep 17 00:00:00 2001 From: Toffikk Date: Tue, 24 Feb 2026 02:13:51 +0100 Subject: [PATCH 187/257] fix: resolve gradle errors while running upstream setup --- .../papermc/paperweight/core/taskcontainers/PatchingTasks.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt index faef8808d..c8a125e40 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt @@ -162,7 +162,7 @@ class PatchingTasks( ats.jstClasspath.from( project.configurations.named(JavaPlugin.COMPILE_CLASSPATH_CONFIGURATION_NAME), project.subprojects.map { - it.files(it.configurations.named(JavaPlugin.COMPILE_CLASSPATH_CONFIGURATION_NAME)) + it.configurations.named(JavaPlugin.COMPILE_CLASSPATH_CONFIGURATION_NAME).map { it.files } } ) validateAts.set(this@PatchingTasks.validateAts) @@ -215,7 +215,7 @@ class PatchingTasks( ats.jstClasspath.from( project.configurations.named(JavaPlugin.COMPILE_CLASSPATH_CONFIGURATION_NAME), project.subprojects.map { - it.files(it.configurations.named(JavaPlugin.COMPILE_CLASSPATH_CONFIGURATION_NAME)) + it.configurations.named(JavaPlugin.COMPILE_CLASSPATH_CONFIGURATION_NAME).map { it.files } } ) ats.jst.from(project.configurations.named(JST_CONFIG)) From 92ab00a16c97bfee8700f1e53e1dc92c45a5cd04 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Tue, 24 Feb 2026 02:46:15 +0100 Subject: [PATCH 188/257] fix: workaround the latest review --- .../core/taskcontainers/PatchingTasks.kt | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt index c8a125e40..99de97a38 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt @@ -22,6 +22,7 @@ package io.papermc.paperweight.core.taskcontainers +import io.papermc.paperweight.core.PaperweightCore import io.papermc.paperweight.core.tasks.SetupForkUpstreamSources import io.papermc.paperweight.core.tasks.patching.ApplyBasePatches import io.papermc.paperweight.core.tasks.patching.ApplyFeaturePatches @@ -161,8 +162,10 @@ class PatchingTasks( ats.jst.from(project.configurations.named(JST_CONFIG)) ats.jstClasspath.from( project.configurations.named(JavaPlugin.COMPILE_CLASSPATH_CONFIGURATION_NAME), - project.subprojects.map { - it.configurations.named(JavaPlugin.COMPILE_CLASSPATH_CONFIGURATION_NAME).map { it.files } + project.subprojects.mapNotNull { + if (!it.plugins.hasPlugin(PaperweightCore::class)) { + it.configurations.named(JavaPlugin.COMPILE_CLASSPATH_CONFIGURATION_NAME).map { it.files } + } else null } ) validateAts.set(this@PatchingTasks.validateAts) @@ -214,8 +217,10 @@ class PatchingTasks( ats.jstClasspath.from( project.configurations.named(JavaPlugin.COMPILE_CLASSPATH_CONFIGURATION_NAME), - project.subprojects.map { - it.configurations.named(JavaPlugin.COMPILE_CLASSPATH_CONFIGURATION_NAME).map { it.files } + project.subprojects.mapNotNull { + if (!it.plugins.hasPlugin(PaperweightCore::class)) { + it.configurations.named(JavaPlugin.COMPILE_CLASSPATH_CONFIGURATION_NAME).map { it.files } + } else null } ) ats.jst.from(project.configurations.named(JST_CONFIG)) From 8f6c38436552fd8e55992ce4a8ff513aeea3b0a8 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Tue, 24 Feb 2026 02:48:11 +0100 Subject: [PATCH 189/257] chore: format --- .../paperweight/core/taskcontainers/PatchingTasks.kt | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt index 99de97a38..75c758148 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt @@ -165,7 +165,9 @@ class PatchingTasks( project.subprojects.mapNotNull { if (!it.plugins.hasPlugin(PaperweightCore::class)) { it.configurations.named(JavaPlugin.COMPILE_CLASSPATH_CONFIGURATION_NAME).map { it.files } - } else null + } else { + null + } } ) validateAts.set(this@PatchingTasks.validateAts) @@ -220,7 +222,9 @@ class PatchingTasks( project.subprojects.mapNotNull { if (!it.plugins.hasPlugin(PaperweightCore::class)) { it.configurations.named(JavaPlugin.COMPILE_CLASSPATH_CONFIGURATION_NAME).map { it.files } - } else null + } else { + null + } } ) ats.jst.from(project.configurations.named(JST_CONFIG)) From d633868dd4c931c1c6672b43d37b2f5be2a47892 Mon Sep 17 00:00:00 2001 From: "tofik.07" Date: Wed, 18 Mar 2026 20:45:43 +0100 Subject: [PATCH 190/257] Support for 26.1+ (#2) * Update Gradle wrapper * fix(deps): update junit-framework monorepo to v6.0.3 * chore(deps): update mikepenz/action-junit-report action to v6 * fix(deps): update asm to v9.9.1 * Start Paper 26.1 development groundwork - remove legacy Spigot/reobf task pipeline from paperweight-core and paperweight-lib - simplify paperclip packaging to use the built server jar directly - update codebook task wiring and bump fallback toolchain target to Java 25 - add userdev setup handler v7 and support dev bundle format 8 for 26.1+ - add patchroulette cancel --force for recovery workflows - mark legacy reobf extraction path in old setup handler as follow-up work * Prepare for 26.1 (#382) * Start Paper 26.1 development groundwork - remove legacy Spigot/reobf task pipeline from paperweight-core and paperweight-lib - simplify paperclip packaging to use the built server jar directly - update codebook task wiring and bump fallback toolchain target to Java 25 - add userdev setup handler v7 and support dev bundle format 8 for 26.1+ - add patchroulette cancel --force for recovery workflows - mark legacy reobf extraction path in old setup handler as follow-up work * Allow selecting specific patches for roulette apply * Add autoAccept flag to PatchRouletteApply * Fix typo * Update tests * spotlessApply * fix(deps): update dependency com.gradleup.shadow:com.gradleup.shadow.gradle.plugin to v9.3.2 * fix(deps): update dependency io.mockk:mockk to v1.14.9 * add back updatingMinecraft prop check for patch roulette tasks --------- Co-authored-by: Jason Penilla <11360596+jpenilla@users.noreply.github.com> Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/test.yml | 2 +- .../src/main/kotlin/config-kotlin.gradle.kts | 6 +- gradle/libs.versions.toml | 8 +- gradle/wrapper/gradle-wrapper.jar | Bin 45457 -> 46175 bytes gradle/wrapper/gradle-wrapper.properties | 2 +- .../paperweight/core/PaperweightCore.kt | 21 +- .../extension/PaperweightCoreExtension.kt | 13 - .../core/taskcontainers/AllTasks.kt | 28 +- .../core/taskcontainers/CoreTasks.kt | 5 +- .../core/taskcontainers/DevBundleTasks.kt | 9 - .../core/taskcontainers/InitialTasks.kt | 22 -- .../core/taskcontainers/PaperclipTasks.kt | 33 +- .../core/taskcontainers/SpigotTasks.kt | 102 ------ .../tasks/patchroulette/PatchRouletteApply.kt | 57 +++- .../patchroulette/PatchRouletteCancel.kt | 10 +- .../paperweight/core/util/project-util.kt | 78 ----- .../patchroulette/PatchRouletteApplyTest.kt | 50 +++ .../src/test/resources/fake_mache/mache.json | 66 +--- .../functional_test/test-server/build.gradle | 4 +- .../io/papermc/paperweight/tasks/FilterJar.kt | 58 ---- .../paperweight/tasks/FixJarForReobf.kt | 227 ------------- .../paperweight/tasks/GenerateDevBundle.kt | 13 +- .../paperweight/tasks/GenerateMappings.kt | 144 +++----- .../tasks/GenerateRelocatedReobfMappings.kt | 155 --------- .../tasks/GenerateReobfMappings.kt | 315 ------------------ .../tasks/GenerateSpigotMappings.kt | 312 ----------------- .../paperweight/tasks/IncludeMappings.kt | 66 ---- .../paperweight/tasks/PatchMappings.kt | 108 ------ .../tasks/RelocateClassNameConstants.kt | 145 -------- .../paperweight/tasks/SpigotRemapJar.kt | 179 ---------- .../tasks/UnpackSpigotBuildData.kt | 86 ----- .../paperweight/tasks/mache/RunCodebook.kt | 43 ++- .../util/data/mache/MacheDependencies.kt | 4 +- .../paperweight/util/data/mache/MacheMeta.kt | 4 +- .../io/papermc/paperweight/util/utils.kt | 2 +- .../userdev/internal/setup/DevBundles.kt | 6 +- .../internal/setup/SetupHandlerImpl.kt | 6 +- .../internal/setup/v2/SetupHandlerImplV2.kt | 1 + .../userdev/internal/setup/v7/DevBundleV7.kt | 19 +- .../internal/setup/v7/SetupHandlerImplV7.kt | 276 +++++++++++++++ 40 files changed, 516 insertions(+), 2169 deletions(-) delete mode 100644 paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/SpigotTasks.kt delete mode 100644 paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/FilterJar.kt delete mode 100644 paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/FixJarForReobf.kt delete mode 100644 paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/GenerateRelocatedReobfMappings.kt delete mode 100644 paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/GenerateReobfMappings.kt delete mode 100644 paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/GenerateSpigotMappings.kt delete mode 100644 paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/IncludeMappings.kt delete mode 100644 paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/PatchMappings.kt delete mode 100644 paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RelocateClassNameConstants.kt delete mode 100644 paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/SpigotRemapJar.kt delete mode 100644 paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/UnpackSpigotBuildData.kt rename paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/SpigotExtension.kt => paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/internal/setup/v7/DevBundleV7.kt (66%) create mode 100644 paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/internal/setup/v7/SetupHandlerImplV7.kt diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index bf9673533..eb5618af8 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -24,7 +24,7 @@ jobs: git config --global user.name "GitHub Actions" ./gradlew build --no-daemon --stacktrace - name: Publish Test Report - uses: mikepenz/action-junit-report@v5 + uses: mikepenz/action-junit-report@v6 if: always() with: report_paths: '**/build/test-results/test/TEST-*.xml' diff --git a/buildSrc/src/main/kotlin/config-kotlin.gradle.kts b/buildSrc/src/main/kotlin/config-kotlin.gradle.kts index d79cda011..9f3b815b8 100644 --- a/buildSrc/src/main/kotlin/config-kotlin.gradle.kts +++ b/buildSrc/src/main/kotlin/config-kotlin.gradle.kts @@ -64,9 +64,9 @@ testing { val test by getting(JvmTestSuite::class) { useKotlinTest(embeddedKotlinVersion) dependencies { - implementation("org.junit.jupiter:junit-jupiter-engine:6.0.0") - implementation("org.junit.jupiter:junit-jupiter-params:6.0.0") - implementation("org.junit.platform:junit-platform-launcher:6.0.0") + implementation("org.junit.jupiter:junit-jupiter-engine:6.0.3") + implementation("org.junit.jupiter:junit-jupiter-params:6.0.3") + implementation("org.junit.platform:junit-platform-launcher:6.0.3") } targets.configureEach { diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index e3785edb9..4c481a7b5 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,5 +1,5 @@ [versions] -asm = "9.9" +asm = "9.9.1" lorenz = "0.5.8" hypo = "1.2.4" serialize = "1.9.0" @@ -44,13 +44,13 @@ jst = { module = "io.canvasmc.jst:jst-cli-bundle", version.ref = "jst" } tinyRemapper = { module = "net.fabricmc:tiny-remapper", version.ref = "tinyRemapper" } # test -mockk = "io.mockk:mockk:1.14.6" +mockk = "io.mockk:mockk:1.14.9" # Gradle gradle-licenser = "net.kyori:indra-licenser-spotless:4.0.0" gradle-spotless = "com.diffplug.spotless:spotless-plugin-gradle:8.0.0" -gradle-shadow = "com.gradleup.shadow:com.gradleup.shadow.gradle.plugin:9.2.2" -gradle-kotlin-dsl = "org.gradle.kotlin.kotlin-dsl:org.gradle.kotlin.kotlin-dsl.gradle.plugin:6.2.0" +gradle-shadow = "com.gradleup.shadow:com.gradleup.shadow.gradle.plugin:9.3.2" +gradle-kotlin-dsl = "org.gradle.kotlin.kotlin-dsl:org.gradle.kotlin.kotlin-dsl.gradle.plugin:6.4.2" gradle-plugin-kotlin = { module = "org.jetbrains.kotlin.jvm:org.jetbrains.kotlin.jvm.gradle.plugin" } gradle-plugin-publish = "com.gradle.publish:plugin-publish-plugin:2.0.0" diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 8bdaf60c75ab801e22807dde59e12a8735a34077..61285a659d17295f1de7c53e24fdf13ad755c379 100644 GIT binary patch delta 36855 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%0Aonjyf`q#Bg+;H(_UceX22V^ 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 { } } - val jar = target.tasks.named("jar", AbstractArchiveTask::class) - tasks.generateReobfMappings { - inputJar.set(jar.flatMap { it.archiveFile }) - } - tasks.generateRelocatedReobfMappings { - inputJar.set(jar.flatMap { it.archiveFile }) - } - val (mappedJar, reobfJar) = target.createBuildTasks( - ext.spigot, - ext.reobfPackagesToFix, - tasks.generateRelocatedReobfMappings.flatMap { it.outputMappings }, - ) + val serverJar = target.tasks.named("jar", AbstractArchiveTask::class).flatMap { it.archiveFile } PaperclipTasks( target, @@ -163,8 +151,7 @@ abstract class PaperweightCore : Plugin { tasks.extractFromBundler.flatMap { it.versionJson }, tasks.extractFromBundler.flatMap { it.serverLibrariesList }, tasks.downloadServerJar.flatMap { it.outputJar }, - mappedJar, - reobfJar, + serverJar, ext.minecraftVersion, ext.activeFork.map { it.name }.orElse("paper") ) @@ -213,10 +200,10 @@ abstract class PaperweightCore : Plugin { tasks.afterEvaluate() devBundleTasks.configureAfterEvaluate( - mappedJar, + serverJar, ) - if (coreExt.updatingMinecraft.oldPaperCommit.isPresent) { + if (coreExt.updatingMinecraft.oldPaperCommit.isPresent || target.providers.gradleProperty("updatingMinecraft").orNull == "true") { tasks.paperPatchingTasks.applyBasePatches.configure { additionalRemote = layout.cache.resolve( "$OLD_PAPER_PATH/${coreExt.updatingMinecraft.oldPaperCommit.get()}/paper-server/src/minecraft/java" diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/PaperweightCoreExtension.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/PaperweightCoreExtension.kt index d88de899b..f79414726 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/PaperweightCoreExtension.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/PaperweightCoreExtension.kt @@ -49,21 +49,8 @@ abstract class PaperweightCoreExtension @Inject constructor(objects: ObjectFacto listOf("/*.class", "/net/minecraft/**", "/com/mojang/math/**") ) - val reobfPackagesToFix: ListProperty = objects.listProperty() - - val spigot = objects.newInstance().also { spigot -> - spigot.enabled.convention( - spigot.buildDataRef.zip(spigot.packageVersion) { ref, pkg -> ref.isNotBlank() && pkg.isNotBlank() } - .orElse(false) - ) - } val paper = objects.newInstance(project) - @Suppress("unused") - fun spigot(action: Action) { - action.execute(spigot) - } - @Suppress("unused") fun paper(action: Action) { action.execute(paper) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/AllTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/AllTasks.kt index 2f790a5fc..a859c0a6b 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/AllTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/AllTasks.kt @@ -43,7 +43,7 @@ open class AllTasks( cache: Path = project.layout.cache, extension: PaperweightCoreExtension = project.coreExt, downloadService: Provider = project.download -) : SpigotTasks(project) { +) : InitialTasks(project) { val downloadMcLibrariesSources by tasks.registering { mcLibrariesFile.set(extractFromBundler.flatMap { it.serverLibrariesTxt }) @@ -74,30 +74,4 @@ open class AllTasks( downloader.set(downloadService) } - - val generateReobfMappings by tasks.registering { - inputMappings.set(patchMappings.flatMap { it.outputMappings }) - notchToSpigotMappings.set(generateSpigotMappings.flatMap { it.notchToSpigotMappings }) - sourceMappings.set(generateMappings.flatMap { it.outputMappings }) - // TODO: spigot uses javac now(?) so is this needed anymore? - // spigotRecompiledClasses.set(remapSpigotSources.flatMap { it.spigotRecompiledClasses }) - - reobfMappings.set(cache.resolve(REOBF_MOJANG_SPIGOT_MAPPINGS)) - } - - val patchReobfMappings by tasks.registering { - inputMappings.set(generateReobfMappings.flatMap { it.reobfMappings }) - patch.set(extension.paper.reobfMappingsPatch.fileExists()) - - fromNamespace.set(DEOBF_NAMESPACE) - toNamespace.set(SPIGOT_NAMESPACE) - - outputMappings.set(cache.resolve(PATCHED_REOBF_MOJANG_SPIGOT_MAPPINGS)) - } - - val generateRelocatedReobfMappings by tasks.registering { - inputMappings.set(patchReobfMappings.flatMap { it.outputMappings }) - outputMappings.set(cache.resolve(RELOCATED_PATCHED_REOBF_MOJANG_SPIGOT_MAPPINGS)) - craftBukkitPackageVersion.set(extension.spigot.packageVersion) - } } diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/CoreTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/CoreTasks.kt index 26d629c31..5190bc1d1 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/CoreTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/CoreTasks.kt @@ -51,13 +51,10 @@ class CoreTasks( val macheRemapJar by tasks.registering(RunCodebook::class) { serverJar.set(extractFromBundler.flatMap { it.serverJar }) - serverMappings.set(downloadMappings.flatMap { it.outputFile }) - remapperArgs.set(mache.map { it.remapperArgs }) + codebookArgs.set(mache.map { it.remapperArgs }) codebookClasspath.from(project.configurations.named(MACHE_CODEBOOK_CONFIG)) minecraftClasspath.from(project.configurations.named(MACHE_MINECRAFT_LIBRARIES_CONFIG)) - remapperClasspath.from(project.configurations.named(MACHE_REMAPPER_CONFIG)) - paramMappings.from(project.configurations.named(MACHE_PARAM_MAPPINGS_CONFIG)) constants.from(project.configurations.named(MACHE_CONSTANTS_CONFIG)) outputJar.set(layout.cache.resolve(FINAL_REMAPPED_CODEBOOK_JAR)) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/DevBundleTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/DevBundleTasks.kt index 5591378d6..fa54119e1 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/DevBundleTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/DevBundleTasks.kt @@ -68,15 +68,6 @@ class DevBundleTasks( minecraftVersion.set(project.coreExt.minecraftVersion) mojangMappedPaperclipFile.set(paperclipForDevBundle.flatMap { it.outputZip }) - reobfMappingsFile.set( - project.coreExt.spigot.enabled.flatMap { - if (it) { - coreTasks.generateRelocatedReobfMappings.flatMap { t -> t.outputMappings } - } else { - null - } - } - ) } fun configureAfterEvaluate(serverJar: Provider) { diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/InitialTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/InitialTasks.kt index 82af6d641..54641e0f0 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/InitialTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/InitialTasks.kt @@ -71,14 +71,6 @@ open class InitialTasks( } private val versionManifest = downloadMcVersionManifest.flatMap { it.outputFile }.map { gson.fromJson(it) } - val downloadMappings by tasks.registering { - url.set(versionManifest.map { version -> version.serverMappingsDownload().url }) - expectedHash.set(versionManifest.map { version -> version.serverMappingsDownload().hash() }) - outputFile.set(cache.resolve(SERVER_MAPPINGS)) - - downloader.set(downloadService) - } - val downloadServerJar by tasks.registering { downloadUrl.set(versionManifest.map { version -> version.serverDownload().url }) expectedHash.set(versionManifest.map { version -> version.serverDownload().hash() }) @@ -96,18 +88,4 @@ open class InitialTasks( serverLibraryJars.set(cache.resolve(MINECRAFT_JARS_PATH)) serverJar.set(cache.resolve(SERVER_JAR)) } - - val filterVanillaJar by tasks.registering { - inputJar.set(extractFromBundler.flatMap { it.serverJar }) - includes.set(extension.vanillaJarIncludes) - } - - val generateMappings by tasks.registering { - vanillaJar.set(filterVanillaJar.flatMap { it.outputJar }) - libraries.from(extractFromBundler.map { it.serverLibraryJars.asFileTree }) - - vanillaMappings.set(downloadMappings.flatMap { it.outputFile }) - - outputMappings.set(cache.resolve(MOJANG_MAPPINGS)) - } } diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PaperclipTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PaperclipTasks.kt index 54da83de3..37b1a3b39 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PaperclipTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PaperclipTasks.kt @@ -23,7 +23,6 @@ package io.papermc.paperweight.core.taskcontainers import com.google.gson.JsonObject -import io.papermc.paperweight.core.util.reobfRequiresDebug import io.papermc.paperweight.tasks.* import io.papermc.paperweight.util.* import io.papermc.paperweight.util.constants.* @@ -46,28 +45,16 @@ class PaperclipTasks( private val bundlerVersionJson: Provider, private val serverLibrariesList: Provider, private val vanillaJar: Provider, - mojangJar: Provider, - reobfJar: Provider, + serverJar: Provider, private val mcVersion: Provider, private val forkName: Provider ) { init { - val (createBundlerJar, createPaperclipJar, createPublisherJar) = project.createTasks("mojmap") - val (createReobfBundlerJar, createReobfPaperclipJar, createReobfPublisherJar) = project.createTasks("reobf") - - createBundlerJar.serverJar(mojangJar) - createReobfBundlerJar.serverJar(reobfJar) { - reobfRequiresDebug() - } + val (createBundlerJar, createPaperclipJar, createPublisherJar) = project.createTasks() + createBundlerJar.serverJar(serverJar) createPaperclipJar.bundlerJar(createBundlerJar) - createReobfPaperclipJar.bundlerJar(createReobfBundlerJar) { - reobfRequiresDebug() - } - - createReobfPublisherJar { - reobfRequiresDebug() - } + createPublisherJar.paperclipJar(createPaperclipJar) } private fun Project.createTasks( @@ -98,7 +85,6 @@ class PaperclipTasks( group = "bundling" description = "Build a ready-to-publish paperclip jar" - inputZip.set(paperclipJarTask.flatMap { it.outputZip }) outputZip.convention(layout.buildDirectory.file(jarName("publisher", classifier).map { "libs/$it" })) } return Triple(bundlerJarTask, paperclipJarTask, publisherJarTask) @@ -115,7 +101,7 @@ class PaperclipTasks( ).joinToString("-") + ".jar" } } else { - val buildNum = project.providers.environmentVariable("BUILD_NUMBER").orElse("local") + val buildNum = providers.environmentVariable("BUILD_NUMBER").orElse("local") forkName.zip(buildNum) { name, build -> "$name-build.$build.jar" } @@ -172,6 +158,15 @@ class PaperclipTasks( op.execute(this) } + private fun TaskProvider.paperclipJar( + createPaperclipJar: TaskProvider, + op: Action = Action {}, + ) = configure { + inputZip.set(createPaperclipJar.flatMap { it.outputZip }) + + op.execute(this) + } + companion object { fun NamedDomainObjectContainer.registerVersionArtifact( name: String, diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/SpigotTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/SpigotTasks.kt deleted file mode 100644 index f4347f25c..000000000 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/SpigotTasks.kt +++ /dev/null @@ -1,102 +0,0 @@ -/* - * paperweight is a Gradle plugin for the PaperMC project. - * - * Copyright (c) 2023 Kyle Wood (DenWav) - * Contributors - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 only, no later versions. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 - * USA - */ - -package io.papermc.paperweight.core.taskcontainers - -import io.papermc.paperweight.core.extension.PaperweightCoreExtension -import io.papermc.paperweight.core.util.coreExt -import io.papermc.paperweight.tasks.* -import io.papermc.paperweight.util.* -import io.papermc.paperweight.util.constants.* -import io.papermc.paperweight.util.data.* -import java.nio.file.Path -import org.gradle.api.Project -import org.gradle.api.provider.Provider -import org.gradle.api.tasks.TaskContainer -import org.gradle.kotlin.dsl.* - -@Suppress("MemberVisibilityCanBePrivate") -open class SpigotTasks( - project: Project, - tasks: TaskContainer = project.tasks, - cache: Path = project.layout.cache, - extension: PaperweightCoreExtension = project.coreExt, -) : InitialTasks(project) { - - val cloneSpigotBuildData by tasks.registering { - url.set("https://hub.spigotmc.org/stash/scm/spigot/builddata.git") - ref.set(project.coreExt.spigot.buildDataRef) - } - - val unpackSpigotBuildData by tasks.registering { - buildDataZip.set(cloneSpigotBuildData.flatMap { it.outputZip }) - } - - val buildDataInfo: Provider = unpackSpigotBuildData - .flatMap { it.buildDataInfoFile } - .map { gson.fromJson(it.path) } - - val generateSpigotMappings by tasks.registering { - classMappings.set(unpackSpigotBuildData.flatMap { it.classMappings }) - - sourceMappings.set(generateMappings.flatMap { it.outputMappings }) - - outputMappings.set(cache.resolve(SPIGOT_MOJANG_MAPPINGS)) - notchToSpigotMappings.set(cache.resolve(OBF_SPIGOT_MAPPINGS)) - spigotMemberMappings.set(cache.resolve(SPIGOT_MEMBER_MAPPINGS)) - } - - val spigotRemapJar by tasks.registering { - inputJar.set(filterVanillaJar.flatMap { it.outputJar }) - classMappings.set(unpackSpigotBuildData.flatMap { it.classMappings }) - accessTransformers.set(unpackSpigotBuildData.flatMap { it.atFile }) - - memberMappings.set(generateSpigotMappings.flatMap { it.spigotMemberMappings }) - - mcVersion.set(extension.minecraftVersion) - - specialSourceJar.set(unpackSpigotBuildData.flatMap { it.specialSourceJar }) - specialSource2Jar.set(unpackSpigotBuildData.flatMap { it.specialSource2Jar }) - - classMapCommand.set(buildDataInfo.map { it.classMapCommand }) - memberMapCommand.set(buildDataInfo.map { it.memberMapCommand }) - finalMapCommand.set(buildDataInfo.map { it.finalMapCommand }) - } - - val cleanupMappings by tasks.registering { - sourceJar.set(spigotRemapJar.flatMap { it.outputJar }) - libraries.from(extractFromBundler.map { it.serverLibraryJars.asFileTree }) - inputMappings.set(generateSpigotMappings.flatMap { it.outputMappings }) - - outputMappings.set(cache.resolve(CLEANED_SPIGOT_MOJANG_MAPPINGS)) - } - - val patchMappings by tasks.registering { - inputMappings.set(cleanupMappings.flatMap { it.outputMappings }) - patch.set(extension.paper.mappingsPatch.fileExists()) - - fromNamespace.set(SPIGOT_NAMESPACE) - toNamespace.set(DEOBF_NAMESPACE) - - outputMappings.set(cache.resolve(PATCHED_SPIGOT_MOJANG_MAPPINGS)) - } -} diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patchroulette/PatchRouletteApply.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patchroulette/PatchRouletteApply.kt index a86b069b7..ff42a81a3 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patchroulette/PatchRouletteApply.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patchroulette/PatchRouletteApply.kt @@ -50,6 +50,9 @@ import org.gradle.api.tasks.options.Option * Paperweight will select `n` patches, prioritizing patches in the current package. * The only time less than `n` patches will be selected is if the entire patch roulette * instance has less than `n` patches available, in which case all of them will be selected. + * - `[,...]`: A comma separated list of patch paths. + * Paperweight will attempt to select exactly the provided patch paths. + * If any of the provided patches are not available, the task will fail. */ abstract class PatchRouletteApply : AbstractPatchRouletteTask() { @@ -72,6 +75,11 @@ abstract class PatchRouletteApply : AbstractPatchRouletteTask() { @get:Option(option = "reapplyPatches", description = "Whether to reapply current selected patched") abstract val reapplyPatches: Property + @get:Input + @get:Optional + @get:Option(option = "accept", description = "Automatically accept selected patches") + abstract val autoAccept: Property + override fun run() { config.path.createParentDirectories() var config = if (config.path.isRegularFile()) { @@ -126,14 +134,16 @@ abstract class PatchRouletteApply : AbstractPatchRouletteTask() { logger.lifecycle("===============================================") patches.forEach { logger.lifecycle(it.pathString) } - var response: String? = null - while (response != "y" && response != "n") { - response = System.`in`.bufferedReader().readLine() - } - if (response == "n") { - config = config.copy(skip = config.skip + patches) - this.config.path.writeText(gson.toJson(config)) - continue + if (!autoAccept.getOrElse(false)) { + var response: String? = null + while (response != "y" && response != "n") { + response = System.`in`.bufferedReader().readLine() + } + if (response == "n") { + config = config.copy(skip = config.skip + patches) + this.config.path.writeText(gson.toJson(config)) + continue + } } try { @@ -209,17 +219,42 @@ abstract class PatchRouletteApply : AbstractPatchRouletteTask() { } } + data class SpecificPatches(val patches: List) : PatchSelectionStrategy { + override fun select(config: Config, available: List): Pair> { + val availableSet = available.toSet() + val unavailable = patches.filterNot { availableSet.contains(it) } + if (unavailable.isNotEmpty()) { + throw PaperweightException( + "Failed to select specific patches. The following patches are unavailable: " + + unavailable.joinToString(", ") { it.invariantSeparatorsPathString } + ) + } + + return config to patches + } + } + fun select(config: Config, available: List): Pair> companion object { fun parse(input: String): PatchSelectionStrategy { try { + val normalizedInput = input.trim() return when { - input.endsWith("!") -> NumericInPackage(input.substring(0, input.length - 1).toInt(), true) - else -> NumericInPackage(input.toInt()) + normalizedInput.matches( + Regex("\\d+!") + ) -> NumericInPackage(normalizedInput.substring(0, normalizedInput.length - 1).toInt(), true) + normalizedInput.matches(Regex("\\d+")) -> NumericInPackage(normalizedInput.toInt()) + else -> { + val patches = normalizedInput.split(',').map { it.trim() }.filter { it.isNotEmpty() } + if (patches.isEmpty()) { + throw PaperweightException("Failed to parse patch selection strategy $normalizedInput") + } + SpecificPatches(patches.map { Path(it) }) + } } } catch (e: Exception) { - throw PaperweightException("Failed to parse patch selection strategy $input", e) + throw PaperweightException("Failed to parse patch selection strategy ${input.trim()}", e) } } } diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patchroulette/PatchRouletteCancel.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patchroulette/PatchRouletteCancel.kt index b3a07b8e3..b87b6dab6 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patchroulette/PatchRouletteCancel.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patchroulette/PatchRouletteCancel.kt @@ -42,20 +42,26 @@ abstract class PatchRouletteCancel : AbstractPatchRouletteTask() { @get:Option(option = "patch", description = "Cancel a patch by path instead of using the current patch") abstract val patch: Property + @get:Input + @get:Optional + @get:Option(option = "force", description = "Force canceling a patch even if it's not currently being worked on") + abstract val force: Property + override fun run() { val config = if (config.path.isRegularFile()) { gson.fromJson(config.path) } else { throw PaperweightException("No config exists") } - if (config.currentPatches.isEmpty()) { + val force = force.getOrElse(false) + if (!force && config.currentPatches.isEmpty()) { throw PaperweightException("No current patch in config") } val patchesToCancel = if (!patch.isPresent) { config.currentPatches } else { - if (!config.currentPatches.contains(Path(patch.get()))) { + if (!force && !config.currentPatches.contains(Path(patch.get()))) { throw PaperweightException("Cannot cancel patch ${patch.get()} as it isn't currently being worked on!") } diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/util/project-util.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/util/project-util.kt index a3c668ac9..5954f4a71 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/util/project-util.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/util/project-util.kt @@ -23,81 +23,8 @@ package io.papermc.paperweight.core.util import io.papermc.paperweight.PaperweightException -import io.papermc.paperweight.core.extension.SpigotExtension -import io.papermc.paperweight.tasks.* -import io.papermc.paperweight.util.* import io.papermc.paperweight.util.constants.* -import org.gradle.api.Project import org.gradle.api.Task -import org.gradle.api.file.RegularFile -import org.gradle.api.provider.Provider -import org.gradle.api.tasks.bundling.AbstractArchiveTask -import org.gradle.kotlin.dsl.* - -fun Project.createBuildTasks( - spigot: SpigotExtension, - packagesToFix: Provider>, - relocatedReobfMappings: Provider -): ServerArtifacts { - val jar = tasks.named("jar", AbstractArchiveTask::class).flatMap { it.archiveFile } - - val fixJarForReobf by tasks.registering { - group = "build" - inputJar.set(jar) - packagesToProcess.set(packagesToFix) - } - - val includeMappings by tasks.registering { - group = "build" - inputJar.set(fixJarForReobf.flatMap { it.outputJar }) - mappings.set(relocatedReobfMappings) - mappingsDest.set("META-INF/mappings/reobf.tiny") - } - - // We only need to manually relocate references to CB class names in string constants, the actual relocation is handled by the mappings - val relocateConstants by tasks.registering { - group = "build" - inputJar.set(includeMappings.flatMap { it.outputJar }) - } - afterEvaluate { - relocateConstants { - relocate("org.bukkit.craftbukkit", "org.bukkit.craftbukkit.${spigot.packageVersion.get()}") { - // This is not actually needed as there are no string constant references to Main - // exclude("org.bukkit.craftbukkit.Main*") - } - } - } - - val reobfJar by tasks.registering { - group = "build" - description = "Re-obfuscate the built jar to obf mappings" - - reobfRequiresDebug() - - inputJar.set(relocateConstants.flatMap { it.outputJar }) - - mappingsFile.set(relocatedReobfMappings) - - fromNamespace.set(DEOBF_NAMESPACE) - toNamespace.set(SPIGOT_NAMESPACE) - - remapper.from(configurations.named(REMAPPER_CONFIG)) - remapperArgs.set(TinyRemapper.minecraftRemapArgs) - - outputJar.set(layout.buildDirectory.map { it.dir("libs").file("${project.name}-${project.version}-reobf.jar") }) - } - - return ServerArtifacts( - spigot.enabled.flatMap { - if (it) { - includeMappings.flatMap { t -> t.outputJar } - } else { - jar - } - }, - reobfJar.flatMap { it.outputJar } - ) -} fun Task.reobfRequiresDebug() { doFirst { @@ -110,8 +37,3 @@ fun Task.reobfRequiresDebug() { } } } - -data class ServerArtifacts( - val mappedJar: Provider, - val reobfJar: Provider, -) diff --git a/paperweight-core/src/test/kotlin/io/papermc/paperweight/core/tasks/patchroulette/PatchRouletteApplyTest.kt b/paperweight-core/src/test/kotlin/io/papermc/paperweight/core/tasks/patchroulette/PatchRouletteApplyTest.kt index dca09e16e..e9c318e5d 100644 --- a/paperweight-core/src/test/kotlin/io/papermc/paperweight/core/tasks/patchroulette/PatchRouletteApplyTest.kt +++ b/paperweight-core/src/test/kotlin/io/papermc/paperweight/core/tasks/patchroulette/PatchRouletteApplyTest.kt @@ -22,9 +22,11 @@ package io.papermc.paperweight.core.tasks.patchroulette +import io.papermc.paperweight.PaperweightException import kotlin.io.path.* import kotlin.test.Test import kotlin.test.assertEquals +import kotlin.test.assertFailsWith import org.junit.jupiter.api.Assertions.assertFalse import org.junit.jupiter.api.Assertions.assertTrue import org.junit.jupiter.params.ParameterizedTest @@ -41,6 +43,8 @@ class PatchRouletteApplyTest { assertEquals(10, strategy.count) assertFalse(strategy.enforceCount) } + + is PatchRouletteApply.PatchSelectionStrategy.SpecificPatches -> error("Unexpected strategy type") } } @@ -52,6 +56,34 @@ class PatchRouletteApplyTest { assertEquals(20, strategy.count) assertTrue(strategy.enforceCount) } + + is PatchRouletteApply.PatchSelectionStrategy.SpecificPatches -> error("Unexpected strategy type") + } + } + + @Test + fun `test patch strategy parsing specific patches`() { + val strategy = PatchRouletteApply.PatchSelectionStrategy.parse("io/papermc/paper/block/Block.java, io/papermc/paper/entity/Entity.java") + when (strategy) { + is PatchRouletteApply.PatchSelectionStrategy.SpecificPatches -> { + assertEquals( + listOf(Path("io/papermc/paper/block/Block.java"), Path("io/papermc/paper/entity/Entity.java")), + strategy.patches + ) + } + + is PatchRouletteApply.PatchSelectionStrategy.NumericInPackage -> error("Unexpected strategy type") + } + } + + @Test + fun `test specific patch selection fails when patch unavailable`() { + val strategy = PatchRouletteApply.PatchSelectionStrategy.SpecificPatches( + listOf(Path("io/papermc/paper/block/Block.java"), Path("io/papermc/paper/entity/Missing.java")) + ) + + assertFailsWith { + strategy.select(PatchRouletteApply.Config(listOf(), null, listOf()), mockAvailablePatches().map { Path(it) }) } } @@ -137,6 +169,24 @@ class PatchRouletteApplyTest { "io/papermc/paper/entity/Entity3.java" ) ) + ), + Arguments.of( + PatchRouletteApply.PatchSelectionStrategy.SpecificPatches( + listOf( + Path("io/papermc/paper/block/BlockState.java"), + Path("io/papermc/paper/entity/Entity.java") + ) + ), + listOf( + "io/papermc/paper/block/BlockState.java", + "io/papermc/paper/entity/Entity.java" + ), + listOf( + listOf( + "io/papermc/paper/block/BlockState.java", + "io/papermc/paper/entity/Entity.java" + ) + ) ) ) diff --git a/paperweight-core/src/test/resources/fake_mache/mache.json b/paperweight-core/src/test/resources/fake_mache/mache.json index 5bf2d2a6d..1572ea46c 100644 --- a/paperweight-core/src/test/resources/fake_mache/mache.json +++ b/paperweight-core/src/test/resources/fake_mache/mache.json @@ -6,70 +6,26 @@ { "group": "io.papermc.codebook", "name": "codebook-cli", - "version": "1.0.10", + "version": "2.0.0-SNAPSHOT", "classifier": "all" } ], - "paramMappings": [ - { - "group": "org.parchmentmc.data", - "name": "parchment-1.21", - "version": "2024.07.28", - "extension": "zip" - } - ], "constants": [ ], - "remapper": [ - { - "group": "net.neoforged", - "name": "AutoRenamingTool", - "version": "1.0.14", - "classifier": "all" - } - ], "decompiler": [ { "group": "org.vineflower", "name": "vineflower", - "version": "1.11.0-20241204.173358-53" + "version": "1.11.2" } ] }, "repositories": [ - { - "url": "https://maven.fabricmc.net/", - "name": "FabricMC", - "groups": [ - "net.fabricmc" - ] - }, - { - "url": "https://maven.neoforged.net/releases/", - "name": "NeoForged", - "groups": [ - "net.neoforged", - "net.minecraftforge" - ] - }, { "url": "https://repo.papermc.io/repository/maven-public/", "name": "PaperMC", "groups": [ - "io.papermc" - ] - }, - { - "url": "https://maven.parchmentmc.org/", - "name": "ParchmentMC", - "groups": [ - "org.parchmentmc" - ] - }, - { - "url": "https://s01.oss.sonatype.org/content/repositories/snapshots/", - "name": "sonatype snapshots", - "groups": [ + "io.papermc", "org.vineflower" ] } @@ -95,12 +51,10 @@ ], "remapperArgs": [ "--temp-dir={tempDir}", - "--remapper-file={remapperFile}", - "--mappings-file={mappingsFile}", - "--params-file={paramsFile}", "--output={output}", "--input={input}", - "--input-classpath={inputClasspath}" + "--input-classpath={inputClasspath}", + "--hypo-parallelism=1" ], "additionalCompileDependencies": { "compileOnly": [ @@ -108,6 +62,16 @@ "group": "org.jetbrains", "name": "annotations", "version": "24.0.1" + }, + { + "group": "com.google.code.findbugs", + "name": "jsr305", + "version": "3.0.2" + }, + { + "group": "org.checkerframework", + "name": "checker-qual", + "version": "3.49.0" } ] } diff --git a/paperweight-core/src/test/resources/functional_test/test-server/build.gradle b/paperweight-core/src/test/resources/functional_test/test-server/build.gradle index 1e7185c18..c0f204edc 100644 --- a/paperweight-core/src/test/resources/functional_test/test-server/build.gradle +++ b/paperweight-core/src/test/resources/functional_test/test-server/build.gradle @@ -23,7 +23,7 @@ dependencies { // this could be resolved by creating a fake maven repo that holds proper module metadata implementation 'com.github.oshi:oshi-core:6.6.5' } else { - mache 'io.papermc:mache:1.21.4+build.4' + mache 'io.papermc:mache:26.1-snapshot-11+build.2' } } @@ -48,6 +48,6 @@ paperweight { resourcePatchDir.set(file('patches/resources')) featurePatchDir.set(file('patches/features')) } - minecraftVersion = '1.21.4' + minecraftVersion = '26.1-snapshot-11' } } diff --git a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/FilterJar.kt b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/FilterJar.kt deleted file mode 100644 index 479b232d1..000000000 --- a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/FilterJar.kt +++ /dev/null @@ -1,58 +0,0 @@ -/* - * paperweight is a Gradle plugin for the PaperMC project. - * - * Copyright (c) 2023 Kyle Wood (DenWav) - * Contributors - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 only, no later versions. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 - * USA - */ - -package io.papermc.paperweight.tasks - -import io.papermc.paperweight.util.* -import org.gradle.api.file.RegularFileProperty -import org.gradle.api.provider.ListProperty -import org.gradle.api.tasks.CacheableTask -import org.gradle.api.tasks.Classpath -import org.gradle.api.tasks.Input -import org.gradle.api.tasks.OutputFile -import org.gradle.api.tasks.TaskAction - -@CacheableTask -abstract class FilterJar : BaseTask() { - - @get:Classpath - abstract val inputJar: RegularFileProperty - - @get:Input - abstract val includes: ListProperty - - @get:OutputFile - abstract val outputJar: RegularFileProperty - - override fun init() { - outputJar.convention(defaultOutput()) - } - - @TaskAction - open fun run() { - filterJar( - inputJar.path, - outputJar.path, - includes.get() - ) - } -} diff --git a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/FixJarForReobf.kt b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/FixJarForReobf.kt deleted file mode 100644 index 51c688103..000000000 --- a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/FixJarForReobf.kt +++ /dev/null @@ -1,227 +0,0 @@ -/* - * paperweight is a Gradle plugin for the PaperMC project. - * - * Copyright (c) 2023 Kyle Wood (DenWav) - * Contributors - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 only, no later versions. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 - * USA - */ - -package io.papermc.paperweight.tasks - -import io.papermc.paperweight.util.* -import java.nio.file.Path -import javax.inject.Inject -import kotlin.io.path.* -import org.gradle.api.file.RegularFileProperty -import org.gradle.api.provider.ListProperty -import org.gradle.api.tasks.Classpath -import org.gradle.api.tasks.Input -import org.gradle.api.tasks.Internal -import org.gradle.api.tasks.Optional -import org.gradle.api.tasks.OutputFile -import org.gradle.api.tasks.TaskAction -import org.gradle.kotlin.dsl.* -import org.gradle.workers.WorkAction -import org.gradle.workers.WorkParameters -import org.gradle.workers.WorkerExecutor -import org.objectweb.asm.Opcodes -import org.objectweb.asm.tree.ClassNode -import org.objectweb.asm.tree.FieldInsnNode -import org.objectweb.asm.tree.MethodNode - -abstract class FixJarForReobf : JavaLauncherTask() { - - @get:Classpath - abstract val inputJar: RegularFileProperty - - @get:Optional - @get:Input - abstract val packagesToProcess: ListProperty - - @get:OutputFile - abstract val outputJar: RegularFileProperty - - @get:Internal - abstract val jvmargs: ListProperty - - @get:Inject - abstract val workerExecutor: WorkerExecutor - - override fun init() { - super.init() - - outputJar.convention(defaultOutput()) - jvmargs.convention(listOf("-Xmx2G")) - } - - @TaskAction - fun run() { - val pack = packagesToProcess.orNull - if (pack == null) { - inputJar.path.copyTo(outputJar.path) - return - } - - val queue = workerExecutor.processIsolation { - forkOptions.jvmArgs(jvmargs.get()) - forkOptions.executable(launcher.get().executablePath.path.absolutePathString()) - } - - queue.submit(FixJarForReobfWorker::class) { - inputJar.set(this@FixJarForReobf.inputJar.path) - packagesToProcess.set(pack) - outputJar.set(this@FixJarForReobf.outputJar.path) - } - } - - interface FixJarForReobfParams : WorkParameters { - val inputJar: RegularFileProperty - val packagesToProcess: ListProperty - val outputJar: RegularFileProperty - } - - abstract class FixJarForReobfWorker : WorkAction { - - override fun execute() { - val packages = normalize(parameters.packagesToProcess.get()) - - val output = parameters.outputJar.path - output.parent.createDirectories() - output.deleteForcefully() - - output.writeZip().use { out -> - parameters.inputJar.path.openZip().use { jarFile -> - JarProcessing.processJar(jarFile, out, FixForReobfProcessor(packages)) - } - } - } - - class FixForReobfProcessor(private val packages: List) : JarProcessing.ClassProcessor.NodeBased { - override fun shouldProcess(file: Path): Boolean = - packages.any { file.toString().startsWith(it) } - - override fun processClass(node: ClassNode, classNodeCache: ClassNodeCache) { - FieldAccessNormalizer(node, classNodeCache).visitNode() - } - } - - private fun normalize(input: List): List { - return input.map { name -> - '/' + name.removePrefix("/").replace('.', '/') - } - } - } -} - -/* - * This resolves issues caused by reobf prior to the reobf process. After reobf this is impossible to do - the field access become ambiguous (which is - * what this fixes). - * - * What exactly this is fixing requires some knowledge around how the JVM handles field accesses in the first place - Mumfrey described this process - * in detail with some great diagrams several years ago, you can read that here: https://github.com/MinecraftForge/MinecraftForge/pull/3055 - * - * The goal of this class is to check all field access instructions (not field declarations) and follow the JVM's rules for field binding in order - * to determine the _intended_ owning class of a field access. Prior to reobf all of this works exactly as expected when looking at Java source code, - * but after reobf there are many cases that look like this: - * - * field `a` declared in class `Foo` - * field `a` declared in class `Bar` which extends `Foo` - * - * In the deobfuscated code these fields would have different names, so they won't overlap and the JVM will output field access instructions described - * in the link above. Reobf generally only changes the field's name and type (and the name of the owner class), but it doesn't actually fix the issue - * where field accesses which used to be unambiguous are now ambiguous. - * - * So with that in mind, this class will look at field access instructions and match the actual field the instruction is trying to access (even if - * it's not directly declared in the owner class) and change the owner accordingly. This will keep field accesses unambiguous even after reobf with - * conflicting field names. - */ -class FieldAccessNormalizer(private val node: ClassNode, private val classNodeCache: ClassNodeCache) : AsmUtil { - - fun visitNode() { - for (method in node.methods) { - visitMethod(method) - } - } - - private fun visitMethod(method: MethodNode) { - for (instruction in method.instructions) { - val fieldInst = instruction as? FieldInsnNode ?: continue - visitFieldInst(fieldInst) - } - } - - private fun visitFieldInst(instruction: FieldInsnNode) { - val ownerNode = findTargetFieldDeclaration(instruction) ?: return - instruction.owner = ownerNode.name - } - - private fun findTargetFieldDeclaration(instruction: FieldInsnNode): ClassNode? { - val fieldName = instruction.name - - var className: String? = instruction.owner - while (className != null) { - val currentNode = classNodeCache.findClass(className) ?: return null - - val fieldNode = currentNode.fields.firstOrNull { it.name == fieldName } - if (fieldNode != null) { - /* - * We need to determine if this field node can actually be accessed by the caller (the original `node`). - * For example, consider the following class hierarchy: - * - * class Foo - * public field text - * class Bar extends Foo - * private field text - * class Cat extends Bar - * - * If `Cat` contains a method which accesses `this.text` then by Java's field access rules the field access would bind to `Foo.text` - * rather than `Bar.text`, even though `Bar.text` shadows `Foo.text`. This is of course because `Cat` is not able to access `Bar.text` - * since it's a private field. Private fields are of course the easier case to handle - we also have to check protected fields if the - * original `node` does not extend the field's declaring class, and package private if the classes aren't in the same package. - */ - - if (Opcodes.ACC_PRIVATE in fieldNode.access) { - // This is only legal if the field node owner and the original node match - if (currentNode.name == node.name) { - return currentNode - } - } else if (Opcodes.ACC_PROTECTED in fieldNode.access) { - var walkingNode: ClassNode? = node - while (walkingNode != null) { - if (walkingNode.name == currentNode.name) { - return currentNode - } - walkingNode = classNodeCache.findClass(walkingNode.superName) - } - } else if (Opcodes.ACC_PUBLIC in fieldNode.access) { - return currentNode - } else { - // package private field - val currentPackage = currentNode.name.substringBeforeLast('/') - val originalPackage = node.name.substringBeforeLast('/') - if (currentPackage == originalPackage) { - return currentNode - } - } - } - - className = currentNode.superName - } - - return null - } -} diff --git a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/GenerateDevBundle.kt b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/GenerateDevBundle.kt index 897a993ca..d32e52837 100644 --- a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/GenerateDevBundle.kt +++ b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/GenerateDevBundle.kt @@ -43,7 +43,6 @@ import org.gradle.api.tasks.Input import org.gradle.api.tasks.InputDirectory import org.gradle.api.tasks.InputFile import org.gradle.api.tasks.InputFiles -import org.gradle.api.tasks.Optional import org.gradle.api.tasks.OutputFile import org.gradle.api.tasks.TaskAction @@ -70,10 +69,6 @@ abstract class GenerateDevBundle : BaseTask() { @get:Input abstract val macheDep: Property - @get:InputFile - @get:Optional - abstract val reobfMappingsFile: RegularFileProperty - @get:OutputFile abstract val devBundleFile: RegularFileProperty @@ -103,9 +98,6 @@ abstract class GenerateDevBundle : BaseTask() { val dataZip = zip.getPath(dataDir) dataZip.createDirectories() - if (reobfMappingsFile.isPresent) { - reobfMappingsFile.path.copyTo(dataZip.resolve(reobfMappingsFileName)) - } mojangMappedPaperclipFile.path.copyTo(dataZip.resolve(mojangMappedPaperclipFileName)) val patchesZip = zip.getPath(patchesDir) @@ -191,7 +183,6 @@ abstract class GenerateDevBundle : BaseTask() { minecraftVersion = minecraftVersion.get(), mache = createMacheDep(), patchDir = patchTargetDir, - reobfMappingsFile = if (reobfMappingsFile.isPresent) "$dataTargetDir/$reobfMappingsFileName" else null, mojangMappedPaperclipFile = "$dataTargetDir/$mojangMappedPaperclipFileName", libraryRepositories = libraryRepositories.get(), pluginRemapArgs = TinyRemapper.pluginRemapArgs, @@ -205,17 +196,15 @@ abstract class GenerateDevBundle : BaseTask() { val minecraftVersion: String, val mache: MavenDep, val patchDir: String, - val reobfMappingsFile: String?, val mojangMappedPaperclipFile: String, val libraryRepositories: List, val pluginRemapArgs: List, ) companion object { - const val reobfMappingsFileName = "$DEOBF_NAMESPACE-$SPIGOT_NAMESPACE-reobf.tiny" const val mojangMappedPaperclipFileName = "paperclip-$DEOBF_NAMESPACE.jar" // Should be bumped when the dev bundle config/contents changes in a way which will require users to update paperweight - const val currentDataVersion = 7 + const val currentDataVersion = 8 } } diff --git a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/GenerateMappings.kt b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/GenerateMappings.kt index 6e4b46cd4..6075b731e 100644 --- a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/GenerateMappings.kt +++ b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/GenerateMappings.kt @@ -36,7 +36,6 @@ import dev.denwav.hypo.model.ClassProviderRoot import io.papermc.paperweight.util.* import io.papermc.paperweight.util.constants.* import java.nio.file.Path -import javax.inject.Inject import kotlin.io.path.* import org.cadixdev.lorenz.MappingSet import org.cadixdev.lorenz.merge.FieldMergeStrategy @@ -53,9 +52,7 @@ import org.cadixdev.lorenz.model.MethodParameterMapping import org.cadixdev.lorenz.model.TopLevelClassMapping import org.gradle.api.file.ConfigurableFileCollection import org.gradle.api.file.RegularFileProperty -import org.gradle.api.provider.ListProperty import org.gradle.api.provider.Property -import org.gradle.api.tasks.* import org.gradle.jvm.toolchain.JavaLauncher import org.gradle.kotlin.dsl.* import org.gradle.workers.WorkAction @@ -79,7 +76,7 @@ fun generateMappings( forkOptions.executable(launcher.executablePath.path.absolutePathString()) } - queue.submit(GenerateMappings.GenerateMappingsAction::class) { + queue.submit(GenerateMappingsAction::class) { vanillaJar.set(vanillaJarPath) libraries.from(libraryPaths) vanillaMappings.set(vanillaMappingsPath) @@ -91,106 +88,57 @@ fun generateMappings( return queue } -@CacheableTask -abstract class GenerateMappings : JavaLauncherTask() { - - @get:Classpath - abstract val vanillaJar: RegularFileProperty - - @get:Classpath - abstract val libraries: ConfigurableFileCollection - - @get:InputFile - @get:PathSensitive(PathSensitivity.NONE) - abstract val vanillaMappings: RegularFileProperty - - @get:InputFile - @get:Optional - @get:PathSensitive(PathSensitivity.NONE) - abstract val paramMappings: RegularFileProperty - - @get:OutputFile - abstract val outputMappings: RegularFileProperty - - @get:Internal - abstract val jvmargs: ListProperty - - @get:Inject - abstract val workerExecutor: WorkerExecutor - - override fun init() { - super.init() - - jvmargs.convention(listOf("-Xmx1G")) - } - - @TaskAction - fun run() { - generateMappings( - vanillaJar.path, - libraries.files.map { it.toPath() }, - vanillaMappings.path, - paramMappings.pathOrNull, - outputMappings.path, - DEOBF_NAMESPACE, - workerExecutor, - launcher.get(), - jvmargs.get() - ) - } - - interface GenerateMappingsParams : WorkParameters { - val vanillaJar: RegularFileProperty - val libraries: ConfigurableFileCollection - val vanillaMappings: RegularFileProperty - val paramMappings: RegularFileProperty - val outputMappings: RegularFileProperty - val deobfNamespace: Property - } +interface GenerateMappingsParams : WorkParameters { + val vanillaJar: RegularFileProperty + val libraries: ConfigurableFileCollection + val vanillaMappings: RegularFileProperty + val paramMappings: RegularFileProperty + val outputMappings: RegularFileProperty + val deobfNamespace: Property +} - abstract class GenerateMappingsAction : WorkAction { +abstract class GenerateMappingsAction : WorkAction { - override fun execute() { - val vanillaMappings = MappingFormats.PROGUARD.createReader(parameters.vanillaMappings.path).use { it.read() }.reverse() + override fun execute() { + val vanillaMappings = MappingFormats.PROGUARD.createReader(parameters.vanillaMappings.path).use { it.read() }.reverse() - val paramMappings = parameters.paramMappings.orNull?.let { mappingsFile -> - mappingsFile.path.openZip().use { fs -> - val path = fs.getPath("mappings", "mappings.tiny") - MappingFormats.TINY.read(path, "official", "named") - } + val paramMappings = parameters.paramMappings.orNull?.let { mappingsFile -> + mappingsFile.path.openZip().use { fs -> + val path = fs.getPath("mappings", "mappings.tiny") + MappingFormats.TINY.read(path, "official", "named") } + } - val merged = paramMappings?.let { - MappingSetMerger.create( - vanillaMappings, - it, - MergeConfig.builder() - .withFieldMergeStrategy(FieldMergeStrategy.STRICT) - .withMergeHandler(ParamsMergeHandler()) - .build() - ).merge() - } ?: vanillaMappings - - val filledMerged = HypoContext.builder() - .withProvider(AsmClassDataProvider.of(ClassProviderRoot.fromJar(parameters.vanillaJar.path))) - .withContextProvider(AsmClassDataProvider.of(parameters.libraries.toJarClassProviderRoots())) - .withContextProvider(AsmClassDataProvider.of(ClassProviderRoot.ofJdk())) - .build().use { hypoContext -> - HydrationManager.createDefault() - .register(BridgeMethodHydrator.create()) - .register(SuperConstructorHydrator.create()) - .hydrate(hypoContext) - - ChangeChain.create() - .addLink(RemoveUnusedMappings.create()) - .addLink(PropagateMappingsUp.create()) - .addLink(CopyMappingsDown.create()) - .applyChain(merged, MappingsCompletionManager.create(hypoContext)) - } + val merged = paramMappings?.let { + MappingSetMerger.create( + vanillaMappings, + it, + MergeConfig.builder() + .withFieldMergeStrategy(FieldMergeStrategy.STRICT) + .withMergeHandler(ParamsMergeHandler()) + .build() + ).merge() + } ?: vanillaMappings + + val filledMerged = HypoContext.builder() + .withProvider(AsmClassDataProvider.of(ClassProviderRoot.fromJar(parameters.vanillaJar.path))) + .withContextProvider(AsmClassDataProvider.of(parameters.libraries.toJarClassProviderRoots())) + .withContextProvider(AsmClassDataProvider.of(ClassProviderRoot.ofJdk())) + .build().use { hypoContext -> + HydrationManager.createDefault() + .register(BridgeMethodHydrator.create()) + .register(SuperConstructorHydrator.create()) + .hydrate(hypoContext) + + ChangeChain.create() + .addLink(RemoveUnusedMappings.create()) + .addLink(PropagateMappingsUp.create()) + .addLink(CopyMappingsDown.create()) + .applyChain(merged, MappingsCompletionManager.create(hypoContext)) + } - ensureParentExists(parameters.outputMappings) - MappingFormats.TINY.write(filledMerged, parameters.outputMappings.path, OBF_NAMESPACE, parameters.deobfNamespace.get()) - } + ensureParentExists(parameters.outputMappings) + MappingFormats.TINY.write(filledMerged, parameters.outputMappings.path, OBF_NAMESPACE, parameters.deobfNamespace.get()) } } diff --git a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/GenerateRelocatedReobfMappings.kt b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/GenerateRelocatedReobfMappings.kt deleted file mode 100644 index 9d7a0afb6..000000000 --- a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/GenerateRelocatedReobfMappings.kt +++ /dev/null @@ -1,155 +0,0 @@ -/* - * paperweight is a Gradle plugin for the PaperMC project. - * - * Copyright (c) 2023 Kyle Wood (DenWav) - * Contributors - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 only, no later versions. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 - * USA - */ - -package io.papermc.paperweight.tasks - -import dev.denwav.hypo.asm.AsmClassDataProvider -import dev.denwav.hypo.core.HypoConfig -import dev.denwav.hypo.core.HypoContext -import dev.denwav.hypo.hydrate.HydrationManager -import dev.denwav.hypo.mappings.ChangeChain -import dev.denwav.hypo.mappings.ChangeRegistry -import dev.denwav.hypo.mappings.MappingsCompletionManager -import dev.denwav.hypo.mappings.contributors.ChangeContributor -import dev.denwav.hypo.model.ClassProviderRoot -import dev.denwav.hypo.model.data.ClassData -import io.papermc.paperweight.util.* -import io.papermc.paperweight.util.constants.* -import javax.inject.Inject -import kotlin.io.path.* -import org.cadixdev.lorenz.model.ClassMapping -import org.gradle.api.file.RegularFileProperty -import org.gradle.api.provider.ListProperty -import org.gradle.api.provider.Property -import org.gradle.api.tasks.* -import org.gradle.kotlin.dsl.* -import org.gradle.workers.WorkAction -import org.gradle.workers.WorkParameters -import org.gradle.workers.WorkerExecutor - -abstract class GenerateRelocatedReobfMappings : JavaLauncherTask() { - - @get:InputFile - @get:PathSensitive(PathSensitivity.NONE) - abstract val inputMappings: RegularFileProperty - - @get:Classpath - abstract val inputJar: RegularFileProperty - - @get:OutputFile - abstract val outputMappings: RegularFileProperty - - @get:Internal - abstract val jvmArgs: ListProperty - - @get:Inject - abstract val workerExecutor: WorkerExecutor - - @get:Input - abstract val craftBukkitPackageVersion: Property - - override fun init() { - super.init() - - jvmArgs.convention(listOf("-Xmx2G")) - } - - @TaskAction - fun run() { - val queue = workerExecutor.processIsolation { - forkOptions.jvmArgs(jvmArgs.get()) - forkOptions.executable(launcher.get().executablePath.path.absolutePathString()) - } - - queue.submit(Action::class) { - inputMappings.set(this@GenerateRelocatedReobfMappings.inputMappings) - inputJar.set(this@GenerateRelocatedReobfMappings.inputJar) - craftBukkitPackageVersion.set(this@GenerateRelocatedReobfMappings.craftBukkitPackageVersion) - - outputMappings.set(this@GenerateRelocatedReobfMappings.outputMappings) - } - } - - abstract class Action : WorkAction { - interface Parameters : WorkParameters { - val inputMappings: RegularFileProperty - val inputJar: RegularFileProperty - val craftBukkitPackageVersion: Property - - val outputMappings: RegularFileProperty - } - - override fun execute() { - val mappingsIn = MappingFormats.TINY.read( - parameters.inputMappings.path, - DEOBF_NAMESPACE, - SPIGOT_NAMESPACE - ) - val mappingsOut = HypoContext.builder() - .withConfig(HypoConfig.builder().setRequireFullClasspath(false).withParallelism(1).build()) - .withProvider(AsmClassDataProvider.of(ClassProviderRoot.fromJar(parameters.inputJar.path))) - .withContextProvider(AsmClassDataProvider.of(ClassProviderRoot.ofJdk())) - .build().use { hypoContext -> - HydrationManager.createDefault().hydrate(hypoContext) - ChangeChain.create() - .addLink(CraftBukkitRelocation(parameters.craftBukkitPackageVersion.get())) - .applyChain(mappingsIn, MappingsCompletionManager.create(hypoContext)) - } - MappingFormats.TINY.write( - mappingsOut, - parameters.outputMappings.path, - DEOBF_NAMESPACE, - SPIGOT_NAMESPACE - ) - } - } - - class CraftBukkitRelocation(packageVersion: String) : ChangeContributor { - companion object { - const val PREFIX = "org/bukkit/craftbukkit/" - const val MAIN = "${PREFIX}Main" - } - - private val relocateTo: String = "$PREFIX$packageVersion" - - override fun name(): String = "CraftBukkitRelocation" - - override fun contribute( - currentClass: ClassData?, - classMapping: ClassMapping<*, *>?, - context: HypoContext, - registry: ChangeRegistry - ) { - if (currentClass == null || classMapping != null) { - return - } - if (currentClass.name().startsWith(PREFIX) && !currentClass.name().startsWith(MAIN)) { - registry.submitChange( - GenerateReobfMappings.AddClassMappingChange( - currentClass.name(), - "$relocateTo/${currentClass.name().substring(PREFIX.length)}" - ) - ) - } - } - } -} diff --git a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/GenerateReobfMappings.kt b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/GenerateReobfMappings.kt deleted file mode 100644 index 5ceef3922..000000000 --- a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/GenerateReobfMappings.kt +++ /dev/null @@ -1,315 +0,0 @@ -/* - * paperweight is a Gradle plugin for the PaperMC project. - * - * Copyright (c) 2023 Kyle Wood (DenWav) - * Contributors - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 only, no later versions. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 - * USA - */ - -package io.papermc.paperweight.tasks - -import dev.denwav.hypo.asm.AsmClassDataProvider -import dev.denwav.hypo.asm.hydrate.BridgeMethodHydrator -import dev.denwav.hypo.asm.hydrate.SuperConstructorHydrator -import dev.denwav.hypo.core.HypoConfig -import dev.denwav.hypo.core.HypoContext -import dev.denwav.hypo.hydrate.HydrationManager -import dev.denwav.hypo.mappings.ChangeChain -import dev.denwav.hypo.mappings.ChangeRegistry -import dev.denwav.hypo.mappings.ClassMappingsChange -import dev.denwav.hypo.mappings.MappingsCompletionManager -import dev.denwav.hypo.mappings.changes.MemberReference -import dev.denwav.hypo.mappings.changes.RemoveMappingChange -import dev.denwav.hypo.mappings.contributors.ChangeContributor -import dev.denwav.hypo.mappings.contributors.RemoveUnusedMappings -import dev.denwav.hypo.model.ClassProviderRoot -import dev.denwav.hypo.model.data.ClassData -import io.papermc.paperweight.util.* -import io.papermc.paperweight.util.constants.* -import javax.inject.Inject -import kotlin.io.path.* -import org.cadixdev.lorenz.MappingSet -import org.cadixdev.lorenz.model.ClassMapping -import org.gradle.api.file.RegularFileProperty -import org.gradle.api.provider.ListProperty -import org.gradle.api.tasks.* -import org.gradle.kotlin.dsl.* -import org.gradle.workers.WorkAction -import org.gradle.workers.WorkParameters -import org.gradle.workers.WorkerExecutor - -abstract class GenerateReobfMappings : JavaLauncherTask() { - - @get:InputFile - @get:PathSensitive(PathSensitivity.NONE) - abstract val inputMappings: RegularFileProperty - - @get:InputFile - @get:PathSensitive(PathSensitivity.NONE) - abstract val notchToSpigotMappings: RegularFileProperty - - @get:InputFile - @get:PathSensitive(PathSensitivity.NONE) - abstract val sourceMappings: RegularFileProperty - - @get:Classpath - abstract val inputJar: RegularFileProperty - - @get:OutputFile - abstract val reobfMappings: RegularFileProperty - - @get:Internal - abstract val jvmArgs: ListProperty - - @get:Inject - abstract val workerExecutor: WorkerExecutor - - @get:InputFile - @get:Optional - @get:PathSensitive(PathSensitivity.NONE) - abstract val spigotRecompiledClasses: RegularFileProperty - - override fun init() { - super.init() - - jvmArgs.convention(listOf("-Xmx2G")) - } - - @TaskAction - fun run() { - val queue = workerExecutor.processIsolation { - forkOptions.jvmArgs(jvmArgs.get()) - forkOptions.executable(launcher.get().executablePath.path.absolutePathString()) - } - - queue.submit(GenerateReobfMappingsAction::class) { - inputMappings.set(this@GenerateReobfMappings.inputMappings) - notchToSpigotMappings.set(this@GenerateReobfMappings.notchToSpigotMappings) - sourceMappings.set(this@GenerateReobfMappings.sourceMappings) - inputJar.set(this@GenerateReobfMappings.inputJar) - spigotRecompiles.set(spigotRecompiledClasses.pathOrNull) - - reobfMappings.set(this@GenerateReobfMappings.reobfMappings) - } - } - - // https://github.com/PaperMC/paperweight/issues/18 - class PropagateOuterClassMappings(private val mappings: MappingSet) : ChangeContributor { - - override fun contribute(currentClass: ClassData?, classMapping: ClassMapping<*, *>?, context: HypoContext, registry: ChangeRegistry) { - if (currentClass == null || classMapping != null) { - return - } - - val name = currentClass.name().substringAfterLast("$") - - val outer = currentClass.outerClass() ?: return - val outerMappings = mappings.getClassMapping(outer.name()).orNull ?: return - if (outerMappings.innerClassMappings.any { it.deobfuscatedName == name }) { - return - } - - registry.submitChange(AddClassMappingChange(currentClass.name(), name)) - } - - override fun name(): String = "PropagateOuterClassMappings" - } - - class AddClassMappingChange(private val target: String, private val deobfName: String) : ClassMappingsChange { - override fun targetClass(): String = target - - override fun applyChange(input: MappingSet) { - input.getOrCreateClassMapping(target).deobfuscatedName = deobfName - } - } - - class RemoveRecompiledSyntheticMemberMappings(private val recompiledClasses: Set) : ChangeContributor { - override fun contribute( - currentClass: ClassData?, - classMapping: ClassMapping<*, *>?, - context: HypoContext, - registry: ChangeRegistry - ) { - if (currentClass == null || classMapping == null) { - return - } - if (currentClass.rootClass().name() !in recompiledClasses) { - return - } - - for (method in currentClass.methods()) { - if (method.isSynthetic) { - registry.submitChange(RemoveMappingChange.of(MemberReference.of(method))) - } - } - - for (field in currentClass.fields()) { - if (field.isSynthetic) { - registry.submitChange(RemoveMappingChange.of(MemberReference.of(field))) - } - } - } - - private fun ClassData.rootClass(): ClassData = - allOuterClasses().getOrNull(0) ?: this - - private fun ClassData.allOuterClasses(list: MutableList = ArrayList()): List { - val outer = outerClass() ?: return list.reversed() - list.add(outer) - return outer.allOuterClasses(list) - } - - override fun name(): String = "RemoveRecompiledSyntheticMemberMappings" - } - - interface GenerateReobfMappingsParams : WorkParameters { - val inputMappings: RegularFileProperty - val notchToSpigotMappings: RegularFileProperty - val sourceMappings: RegularFileProperty - val inputJar: RegularFileProperty - val spigotRecompiles: RegularFileProperty - - val reobfMappings: RegularFileProperty - } - - abstract class GenerateReobfMappingsAction : WorkAction { - - override fun execute() { - val spigotToMojang = MappingFormats.TINY.read( - parameters.inputMappings.path, - SPIGOT_NAMESPACE, - DEOBF_NAMESPACE - ) - val obfToSpigot = MappingFormats.TINY.read( - parameters.notchToSpigotMappings.path, - OBF_NAMESPACE, - SPIGOT_NAMESPACE - ) - val obfToMojang = MappingFormats.TINY.read( - parameters.sourceMappings.path, - OBF_NAMESPACE, - DEOBF_NAMESPACE - ) - - val outputMappings = mergeSpigotWithMojangMemberMappings(obfToSpigot, obfToMojang, spigotToMojang) - - val spigotRecompiles = parameters.spigotRecompiles.pathOrNull?.readLines()?.toSet() ?: emptySet() - - val cleanedOutputMappings = HypoContext.builder() - .withConfig(HypoConfig.builder().setRequireFullClasspath(false).withParallelism(1).build()) - .withProvider(AsmClassDataProvider.of(ClassProviderRoot.fromJar(parameters.inputJar.path))) - .withContextProvider(AsmClassDataProvider.of(ClassProviderRoot.ofJdk())) - .build().use { hypoContext -> - HydrationManager.createDefault() - .register(BridgeMethodHydrator.create()) - .register(SuperConstructorHydrator.create()) - .hydrate(hypoContext) - - ChangeChain.create() - .addLink(RemoveUnusedMappings.create()) - .addLink(RemoveRecompiledSyntheticMemberMappings(spigotRecompiles)) - .addLink(PropagateOuterClassMappings(outputMappings)) - .applyChain(outputMappings, MappingsCompletionManager.create(hypoContext)) - } - - MappingFormats.TINY.write( - cleanedOutputMappings, - parameters.reobfMappings.path, - DEOBF_NAMESPACE, - SPIGOT_NAMESPACE - ) - } - - private fun mergeSpigotWithMojangMemberMappings(obfToSpigot: MappingSet, obfToMojang: MappingSet, spigotToMojang: MappingSet): MappingSet { - val output = MappingSet.create() - - for (mojangClassMapping in obfToMojang.topLevelClassMappings) { - val spigotClassMapping = obfToSpigot.getTopLevelClassMapping(mojangClassMapping.obfuscatedName).orNull - val paperMojangClassMapping = spigotClassMapping?.deobfuscatedName?.let { spigotToMojang.getTopLevelClassMapping(it) }?.orNull - - val fromClassName = mojangClassMapping.deobfuscatedName - val toClassName = if (spigotClassMapping != null && spigotClassMapping.obfuscatedName != spigotClassMapping.deobfuscatedName) { - spigotClassMapping.deobfuscatedName - } else { - mojangClassMapping.deobfuscatedName - } - - // package-info and nullability annotations don't need to be reobfed - if (fromClassName.endsWith("package-info") || fromClassName.endsWith("NonnullByDefault")) { - continue - } - - val newClassMapping = output.createTopLevelClassMapping(fromClassName, toClassName) - mergeSpigotWithMojangMemberMappings(spigotClassMapping, mojangClassMapping, paperMojangClassMapping, newClassMapping) - } - - return output - } - - private fun mergeSpigotWithMojangMemberMappings( - spigotClassMapping: ClassMapping<*, *>?, - mojangClassMapping: ClassMapping<*, *>, - paperMojangClassMapping: ClassMapping<*, *>?, - targetMappings: ClassMapping<*, *> - ) { - for (mojangInnerClassMapping in mojangClassMapping.innerClassMappings) { - val spigotInnerClassMapping = spigotClassMapping?.getInnerClassMapping(mojangInnerClassMapping.obfuscatedName)?.orNull - val paperMojangInnerClassMapping = spigotInnerClassMapping?.deobfuscatedName - ?.let { paperMojangClassMapping?.getInnerClassMapping(it) }?.orNull - - val fromInnerClassName = mojangInnerClassMapping.deobfuscatedName - val toInnerClassName = spigotInnerClassMapping?.deobfuscatedName ?: mojangInnerClassMapping.deobfuscatedName - - val newInnerClassMapping = targetMappings.createInnerClassMapping(fromInnerClassName, toInnerClassName) - mergeSpigotWithMojangMemberMappings( - spigotInnerClassMapping, - mojangInnerClassMapping, - paperMojangInnerClassMapping, - newInnerClassMapping - ) - } - - for (fieldMapping in mojangClassMapping.fieldMappings) { - targetMappings.createFieldMapping(fieldMapping.deobfuscatedSignature, fieldMapping.obfuscatedName) - } - for (methodMapping in mojangClassMapping.methodMappings) { - targetMappings.createMethodMapping(methodMapping.deobfuscatedSignature, methodMapping.obfuscatedName) - } - - // Pick up any changes made through mappings patches - if (paperMojangClassMapping != null) { - for (fieldMapping in paperMojangClassMapping.fieldMappings) { - val obfName = mojangClassMapping.fieldMappings - .firstOrNull { it.deobfuscatedSignature == fieldMapping.deobfuscatedSignature }?.obfuscatedName ?: continue - val deobfFieldType = fieldMapping.deobfuscatedSignature.type.orNull ?: continue - - targetMappings.getOrCreateFieldMapping(fieldMapping.deobfuscatedName, deobfFieldType).also { - it.deobfuscatedName = obfName - } - } - for (methodMapping in paperMojangClassMapping.methodMappings) { - val obfName = mojangClassMapping.methodMappings - .firstOrNull { it.deobfuscatedSignature == methodMapping.deobfuscatedSignature }?.obfuscatedName ?: continue - - targetMappings.getOrCreateMethodMapping(methodMapping.deobfuscatedSignature).also { - it.deobfuscatedName = obfName - } - } - } - } - } -} diff --git a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/GenerateSpigotMappings.kt b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/GenerateSpigotMappings.kt deleted file mode 100644 index 220c761bc..000000000 --- a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/GenerateSpigotMappings.kt +++ /dev/null @@ -1,312 +0,0 @@ -/* - * paperweight is a Gradle plugin for the PaperMC project. - * - * Copyright (c) 2023 Kyle Wood (DenWav) - * Contributors - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 only, no later versions. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 - * USA - */ - -package io.papermc.paperweight.tasks - -import io.papermc.paperweight.util.* -import io.papermc.paperweight.util.constants.* -import org.cadixdev.bombe.type.signature.FieldSignature -import org.cadixdev.bombe.type.signature.MethodSignature -import org.cadixdev.lorenz.MappingSet -import org.cadixdev.lorenz.merge.MappingSetMerger -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.model.ClassMapping -import org.cadixdev.lorenz.model.FieldMapping -import org.cadixdev.lorenz.model.InnerClassMapping -import org.cadixdev.lorenz.model.MethodMapping -import org.cadixdev.lorenz.model.TopLevelClassMapping -import org.gradle.api.file.RegularFileProperty -import org.gradle.api.tasks.CacheableTask -import org.gradle.api.tasks.InputFile -import org.gradle.api.tasks.OutputFile -import org.gradle.api.tasks.PathSensitive -import org.gradle.api.tasks.PathSensitivity -import org.gradle.api.tasks.TaskAction - -@CacheableTask -abstract class GenerateSpigotMappings : BaseTask() { - - @get:InputFile - @get:PathSensitive(PathSensitivity.NONE) - abstract val classMappings: RegularFileProperty - - @get:InputFile - @get:PathSensitive(PathSensitivity.NONE) - abstract val sourceMappings: RegularFileProperty - - @get:OutputFile - abstract val notchToSpigotMappings: RegularFileProperty - - @get:OutputFile - abstract val outputMappings: RegularFileProperty - - @get:OutputFile - abstract val spigotMemberMappings: RegularFileProperty - - @TaskAction - fun run() { - val spigotClassMappings = MappingFormats.CSRG.createReader(classMappings.path).use { it.read() } - - val sourceMappings = MappingFormats.TINY.read( - sourceMappings.path, - OBF_NAMESPACE, - DEOBF_NAMESPACE - ) - - val notchToSpigotSet = MappingSetMerger.create( - spigotClassMappings, - sourceMappings, - MergeConfig.builder() - .withMergeHandler(SpigotMappingsMergerHandler) - .build() - ).merge() - - val spigotToNamedSet = notchToSpigotSet.reverse().merge(sourceMappings) - - MappingFormats.TINY.write( - notchToSpigotSet, - notchToSpigotMappings.path, - OBF_NAMESPACE, - SPIGOT_NAMESPACE - ) - - MappingFormats.TINY.write( - spigotToNamedSet, - outputMappings.path, - SPIGOT_NAMESPACE, - DEOBF_NAMESPACE - ) - - val spigotMembers = createSpigotMemberMappings(sourceMappings, spigotClassMappings) - MappingFormats.CSRG.write(spigotMembers, spigotMemberMappings.path) - } -} - -object SpigotMappingsMergerHandler : MappingSetMergerHandler { - - // - // TOP LEVEL CLASS - // - - override fun mergeTopLevelClassMappings( - left: TopLevelClassMapping, - right: TopLevelClassMapping, - target: MappingSet, - context: MergeContext - ): MergeResult { - throw IllegalStateException("Unexpectedly merged class: ${left.fullObfuscatedName}") - } - - override fun mergeDuplicateTopLevelClassMappings( - left: TopLevelClassMapping, - right: TopLevelClassMapping, - rightContinuation: TopLevelClassMapping?, - target: MappingSet, - context: MergeContext - ): MergeResult { - // If both are provided, keep spigot - return MergeResult( - target.createTopLevelClassMapping(left.obfuscatedName, left.deobfuscatedName), - right - ) - } - - override fun addLeftTopLevelClassMapping( - left: TopLevelClassMapping, - target: MappingSet, - context: MergeContext - ): MergeResult { - throw IllegalStateException( - "Unexpected added class from Spigot: ${left.fullObfuscatedName} - ${left.fullDeobfuscatedName}" - ) - } - - override fun addRightTopLevelClassMapping( - right: TopLevelClassMapping, - target: MappingSet, - context: MergeContext - ): MergeResult { - // This is a mapping Spigot is totally missing - return MergeResult( - target.createTopLevelClassMapping(right.obfuscatedName, right.obfuscatedName), - right - ) - } - - // - // INNER CLASS - // - - override fun mergeInnerClassMappings( - left: InnerClassMapping, - right: InnerClassMapping, - target: ClassMapping<*, *>, - context: MergeContext - ): MergeResult { - throw IllegalStateException("Unexpectedly merged class: ${left.fullObfuscatedName}") - } - - override fun mergeDuplicateInnerClassMappings( - left: InnerClassMapping, - right: InnerClassMapping, - rightContinuation: InnerClassMapping?, - target: ClassMapping<*, *>, - context: MergeContext - ): MergeResult { - return MergeResult( - target.createInnerClassMapping(left.obfuscatedName, left.deobfuscatedName), - right - ) - } - - override fun addLeftInnerClassMapping( - left: InnerClassMapping, - target: ClassMapping<*, *>, - context: MergeContext - ): MergeResult { - throw IllegalStateException( - "Unexpected added class from Spigot: ${left.fullObfuscatedName} - ${left.fullDeobfuscatedName}" - ) - } - - override fun addRightInnerClassMapping( - right: InnerClassMapping, - target: ClassMapping<*, *>, - context: MergeContext - ): MergeResult { - // We want to get all of the inner classes from mojmap, but not the mojmap names - return MergeResult(target.createInnerClassMapping(right.obfuscatedName, right.obfuscatedName), right) - } - - // - // FIELD - // - - override fun mergeFieldMappings( - left: FieldMapping, - strictRight: FieldMapping?, - looseRight: FieldMapping?, - target: ClassMapping<*, *>, - context: MergeContext - ): FieldMapping { - throw IllegalStateException("Unexpectedly merged field: ${left.fullObfuscatedName}") - } - - override fun mergeDuplicateFieldMappings( - left: FieldMapping, - strictRightDuplicate: FieldMapping?, - looseRightDuplicate: FieldMapping?, - strictRightContinuation: FieldMapping?, - looseRightContinuation: FieldMapping?, - target: ClassMapping<*, *>, - context: MergeContext - ): FieldMapping { - val right = strictRightDuplicate ?: looseRightDuplicate ?: strictRightContinuation ?: looseRightContinuation ?: left - return target.createFieldMapping(right.signature, left.deobfuscatedName) - } - - override fun addLeftFieldMapping(left: FieldMapping, target: ClassMapping<*, *>, context: MergeContext): FieldMapping? { - throw IllegalStateException( - "Unexpected added field from Spigot: ${left.fullObfuscatedName} - ${left.fullDeobfuscatedName}" - ) - } - - // - // METHOD - // - - override fun mergeMethodMappings( - left: MethodMapping, - standardRight: MethodMapping?, - wiggledRight: MethodMapping?, - target: ClassMapping<*, *>, - context: MergeContext - ): MergeResult { - throw IllegalStateException("Unexpectedly merged method: $left") - } - - override fun mergeDuplicateMethodMappings( - left: MethodMapping, - strictRightDuplicate: MethodMapping?, - looseRightDuplicate: MethodMapping?, - strictRightContinuation: MethodMapping?, - looseRightContinuation: MethodMapping?, - target: ClassMapping<*, *>, - context: MergeContext - ): MergeResult { - val right = strictRightDuplicate ?: looseRightDuplicate ?: strictRightContinuation ?: looseRightContinuation ?: left - return MergeResult(target.createMethodMapping(left.signature, left.deobfuscatedName), right) - } - - override fun addLeftMethodMapping( - left: MethodMapping, - target: ClassMapping<*, *>, - context: MergeContext - ): MergeResult { - throw IllegalStateException( - "Unexpected added method from Spigot: ${left.fullObfuscatedName} - ${left.fullDeobfuscatedName}" - ) - } -} - -private fun createSpigotMemberMappings(mappings: MappingSet, spigotClassMappings: MappingSet): MappingSet { - val newMappings = MappingSet.create() - - for (topLevelClassMapping in mappings.topLevelClassMappings) { - val name = spigotClassMappings.getTopLevelClassMapping(topLevelClassMapping.obfuscatedName).orElse(topLevelClassMapping).deobfuscatedName - val newClassMappings = newMappings.createTopLevelClassMapping(name, name) - createSpigotMemberMappings(topLevelClassMapping, newClassMappings, spigotClassMappings) - } - - return newMappings -} - -private fun createSpigotMemberMappings(old: ClassMapping<*, *>, new: ClassMapping<*, *>, spigotClassMappings: MappingSet) { - for (innerClassMapping in old.innerClassMappings) { - val name = spigotClassMappings.getClassMapping(innerClassMapping.fullObfuscatedName) - .map { it.deobfuscatedName } - .orElse(innerClassMapping.obfuscatedName) - val newClassMappings = new.createInnerClassMapping(name, name) - createSpigotMemberMappings(innerClassMapping, newClassMappings, spigotClassMappings) - } - - for (fieldMapping in old.fieldMappings) { - new.createFieldMapping(FieldSignature(fieldMapping.obfuscatedName, fieldMapping.type.get()), fieldMapping.deobfuscatedName) - } - - for (methodMapping in old.methodMappings) { - if (methodMapping.deobfuscatedName.contains("$") || - methodMapping.deobfuscatedName == "" || - methodMapping.deobfuscatedName == "" - ) { - continue - } - - val desc = spigotClassMappings.deobfuscate(methodMapping.descriptor) - new.createMethodMapping(MethodSignature(methodMapping.obfuscatedName, desc)).also { - it.deobfuscatedName = methodMapping.deobfuscatedName - } - } -} diff --git a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/IncludeMappings.kt b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/IncludeMappings.kt deleted file mode 100644 index 7e99f8616..000000000 --- a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/IncludeMappings.kt +++ /dev/null @@ -1,66 +0,0 @@ -/* - * paperweight is a Gradle plugin for the PaperMC project. - * - * Copyright (c) 2023 Kyle Wood (DenWav) - * Contributors - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 only, no later versions. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 - * USA - */ - -package io.papermc.paperweight.tasks - -import io.papermc.paperweight.util.* -import kotlin.io.path.* -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.api.tasks.OutputFile -import org.gradle.api.tasks.TaskAction - -abstract class IncludeMappings : BaseTask() { - @get:InputFile - abstract val inputJar: RegularFileProperty - - @get:InputFile - abstract val mappings: RegularFileProperty - - @get:Input - abstract val mappingsDest: Property - - @get:OutputFile - abstract val outputJar: RegularFileProperty - - override fun init() { - super.init() - outputJar.convention(defaultOutput()) - } - - @TaskAction - fun addMappings() { - outputJar.path.parent.createDirectories() - inputJar.path.copyTo(outputJar.path, overwrite = true) - outputJar.path.openZip().use { fs -> - val dest = fs.getPath(mappingsDest.get()) - dest.parent.createDirectories() - mappings.path.copyTo(dest) - - fs.modifyManifest { - mainAttributes.putValue("Included-Mappings-Hash", mappings.path.hashFile(HashingAlgorithm.SHA256).asHexString().uppercase()) - } - } - } -} diff --git a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/PatchMappings.kt b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/PatchMappings.kt deleted file mode 100644 index 48aa69336..000000000 --- a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/PatchMappings.kt +++ /dev/null @@ -1,108 +0,0 @@ -/* - * paperweight is a Gradle plugin for the PaperMC project. - * - * Copyright (c) 2023 Kyle Wood (DenWav) - * Contributors - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 only, no later versions. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 - * USA - */ - -package io.papermc.paperweight.tasks - -import io.papermc.paperweight.util.* -import java.nio.file.Path -import kotlin.io.path.* -import net.fabricmc.lorenztiny.TinyMappingFormat -import org.cadixdev.lorenz.MappingSet -import org.gradle.api.file.RegularFileProperty -import org.gradle.api.provider.Property -import org.gradle.api.tasks.* - -abstract class PatchMappings : BaseTask() { - - @get:InputFile - @get:PathSensitive(PathSensitivity.NONE) - abstract val inputMappings: RegularFileProperty - - @get:Optional - @get:InputFile - @get:PathSensitive(PathSensitivity.NONE) - abstract val patch: RegularFileProperty - - @get:Input - abstract val fromNamespace: Property - - @get:Input - abstract val toNamespace: Property - - @get:OutputFile - abstract val outputMappings: RegularFileProperty - - @TaskAction - fun run() { - appendPatch( - inputMappings.path, - patch.pathOrNull, - outputMappings.path - ) - } - - private fun appendPatch(input: Path, patch: Path?, output: Path) { - val mappings = MappingFormats.TINY.readCommented( - input, - fromNamespace.get(), - toNamespace.get() - ) - patch?.let { - MappingFormats.TINY.readCommented( - it, - fromNamespace.get(), - toNamespace.get(), - mappings - ) - } - - MappingFormats.TINY.write(mappings, output, fromNamespace.get(), toNamespace.get()) - } - - private fun TinyMappingFormat.readCommented( - mappings: Path, - fromNamespace: String, - toNamespace: String, - into: MappingSet? = null - ): MappingSet { - val temp = createTempFile("patch", "tiny") - try { - val comment = commentRegex() - // tiny format doesn't allow comments, so we manually remove them - // The tiny mappings reader also doesn't have a InputStream or Reader input... - mappings.useLines { lines -> - temp.bufferedWriter().use { writer -> - for (line in lines) { - val newLine = comment.replace(line, "") - if (newLine.isNotBlank()) { - writer.appendLine(newLine) - } - } - } - } - return into?.let { read(it, temp, fromNamespace, toNamespace) } - ?: read(temp, fromNamespace, toNamespace) - } finally { - temp.deleteForcefully() - } - } -} diff --git a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RelocateClassNameConstants.kt b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RelocateClassNameConstants.kt deleted file mode 100644 index 48ff178a7..000000000 --- a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RelocateClassNameConstants.kt +++ /dev/null @@ -1,145 +0,0 @@ -/* - * paperweight is a Gradle plugin for the PaperMC project. - * - * Copyright (c) 2023 Kyle Wood (DenWav) - * Contributors - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 only, no later versions. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 - * USA - */ - -package io.papermc.paperweight.tasks - -import io.papermc.paperweight.util.* -import java.nio.file.Path -import kotlin.io.path.* -import org.gradle.api.Action -import org.gradle.api.file.RegularFileProperty -import org.gradle.api.provider.ListProperty -import org.gradle.api.provider.Property -import org.gradle.api.tasks.Input -import org.gradle.api.tasks.InputFile -import org.gradle.api.tasks.Nested -import org.gradle.api.tasks.Optional -import org.gradle.api.tasks.OutputFile -import org.gradle.api.tasks.TaskAction -import org.gradle.kotlin.dsl.* -import org.objectweb.asm.ClassVisitor -import org.objectweb.asm.MethodVisitor -import org.objectweb.asm.Opcodes -import org.objectweb.asm.tree.ClassNode - -@Suppress("LeakingThis") -abstract class RelocateClassNameConstants : BaseTask() { - @get:InputFile - abstract val inputJar: RegularFileProperty - - @get:OutputFile - abstract val outputJar: RegularFileProperty - - @get:Nested - @get:Optional - abstract val relocations: ListProperty - - @get:Input - @get:Optional - abstract val processOnly: ListProperty - - fun relocate(fromPackage: String, toPackage: String, op: Action) { - relocations.add( - objects.newInstance().apply { - this.fromPackage.set(fromPackage) - this.toPackage.set(toPackage) - op.execute(this) - } - ) - } - - init { - outputJar.convention(defaultOutput()) - processOnly.convention( - listOf( - "org/bukkit/craftbukkit/**/*.class", - "org/bukkit/craftbukkit/*.class" - ) - ) - } - - @TaskAction - fun run() { - outputJar.path.deleteForcefully() - outputJar.path.parent.createDirectories() - val relocations = relocations.get().map { - RelocationWrapper(Relocation(null, it.fromPackage.get(), it.toPackage.get(), emptyList())) - } - outputJar.path.writeZip().use { outputFs -> - inputJar.path.openZip().use { inputFs -> - val includes = processOnly.getOrElse(emptyList()).map { - inputFs.getPathMatcher("glob:${if (it.startsWith('/')) it else "/$it"}") - } - JarProcessing.processJar( - inputFs, - outputFs, - object : JarProcessing.ClassProcessor.VisitorBased { - override fun shouldProcess(file: Path): Boolean = - includes.isEmpty() || includes.any { it.matches(file) } - - override fun processClass(node: ClassNode, parent: ClassVisitor, classNodeCache: ClassNodeCache): ClassVisitor = - ConstantRelocatingClassVisitor(parent, relocations) - } - ) - } - } - } - - private class ConstantRelocatingClassVisitor( - parent: ClassVisitor, - private val relocations: List - ) : ClassVisitor(Opcodes.ASM9, parent) { - override fun visitMethod( - access: Int, - name: String?, - descriptor: String?, - signature: String?, - exceptions: Array? - ): MethodVisitor { - return object : MethodVisitor(Opcodes.ASM9, super.visitMethod(access, name, descriptor, signature, exceptions)) { - override fun visitLdcInsn(value: Any?) { - if (value is String) { - var v: String = value - for (relocation in relocations) { - if (v.startsWith(relocation.fromDot)) { - v = v.replace(relocation.fromDot, relocation.toDot) - } else if (v.startsWith(relocation.fromSlash)) { - v = v.replace(relocation.fromSlash, relocation.toSlash) - } - } - super.visitLdcInsn(v) - } else { - super.visitLdcInsn(value) - } - } - } - } - } - - abstract class RelocationInput { - @get:Input - abstract val fromPackage: Property - - @get:Input - abstract val toPackage: Property - } -} diff --git a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/SpigotRemapJar.kt b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/SpigotRemapJar.kt deleted file mode 100644 index 7abe3c0fd..000000000 --- a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/SpigotRemapJar.kt +++ /dev/null @@ -1,179 +0,0 @@ -/* - * paperweight is a Gradle plugin for the PaperMC project. - * - * Copyright (c) 2023 Kyle Wood (DenWav) - * Contributors - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 only, no later versions. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 - * USA - */ - -package io.papermc.paperweight.tasks - -import io.papermc.paperweight.PaperweightException -import io.papermc.paperweight.util.* -import io.papermc.paperweight.util.constants.* -import kotlin.io.path.* -import org.gradle.api.file.RegularFileProperty -import org.gradle.api.provider.Property -import org.gradle.api.tasks.* - -@CacheableTask -abstract class SpigotRemapJar : JavaLauncherTask() { - - @get:Classpath - abstract val inputJar: RegularFileProperty - - @get:InputFile - @get:PathSensitive(PathSensitivity.NONE) - abstract val classMappings: RegularFileProperty - - @get:InputFile - @get:PathSensitive(PathSensitivity.NONE) - abstract val memberMappings: RegularFileProperty - - @get:Input - abstract val mcVersion: Property - - @get:InputFile - @get:PathSensitive(PathSensitivity.NONE) - abstract val accessTransformers: RegularFileProperty - - @get:Classpath - abstract val specialSourceJar: RegularFileProperty - - @get:Classpath - abstract val specialSource2Jar: RegularFileProperty - - @get:Input - abstract val classMapCommand: Property - - @get:Input - abstract val memberMapCommand: Property - - @get:Input - abstract val finalMapCommand: Property - - @get:OutputFile - abstract val outputJar: RegularFileProperty - - override fun init() { - super.init() - - outputJar.convention(defaultOutput()) - } - - @TaskAction - fun run() { - val inputJarPath = inputJar.path.absolutePathString() - - val outputJarFile = outputJar.path - val outputJarPath = outputJarFile.absolutePathString() - - val classJarFile = outputJarFile.resolveSibling(outputJarFile.name + ".classes") - val membersJarFile = outputJarFile.resolveSibling(outputJarFile.name + ".members") - val classJarPath = classJarFile.absolutePathString() - val membersJarPath = membersJarFile.absolutePathString() - - val classMappingPath = classMappings.path.absolutePathString() - val accessTransformersPath = accessTransformers.path.absolutePathString() - - val spigotMembersPath = memberMappings.path.absolutePathString() - - val work = temporaryDir.toPath() - - val spigotEmptyMappings = layout.cache.resolve("spigot-empty-package-mappings.csrg") - spigotEmptyMappings.writeText("") - - try { - try { - val logFile = layout.cache.resolve(paperTaskOutput("class.log")) - logFile.deleteForcefully() - launcher.runJar( - objects.fileCollection().from(specialSource2Jar), - workingDir = work, - logFile = logFile, - args = doReplacements(classMapCommand.get(), inputJarPath, classMappingPath, classJarPath) { - // ignore excludes, we actually want to map every class - it != "-e" - } - ) - } catch (e: Exception) { - throw PaperweightException("Failed to apply class mappings", e) - } - - try { - val logFile = layout.cache.resolve(paperTaskOutput("member.log")) - logFile.deleteForcefully() - launcher.runJar( - objects.fileCollection().from(specialSource2Jar), - workingDir = work, - logFile = logFile, - args = doReplacements(memberMapCommand.get(), classJarPath, spigotMembersPath, membersJarPath) - ) - } catch (e: Exception) { - throw PaperweightException("Failed to apply member mappings", e) - } - - try { - val logFile = layout.cache.resolve(paperTaskOutput("final.log")) - logFile.deleteForcefully() - launcher.runJar( - objects.fileCollection().from(specialSourceJar), - workingDir = work, - logFile = logFile, - args = doReplacements( - finalMapCommand.get(), - membersJarPath, - accessTransformersPath, - spigotEmptyMappings.absolutePathString(), - outputJarPath - ) - ) - } catch (e: Exception) { - throw PaperweightException("Failed to create remapped jar", e) - } - } finally { - classJarFile.deleteForcefully() - membersJarFile.deleteForcefully() - spigotEmptyMappings.deleteForcefully() - } - } - - private val indexReg = Regex("\\{(\\d)}") - private fun doReplacements(command: String, vararg values: String, filter: ((String) -> Boolean)? = null): Array { - var skipNext = false - return command.split(" ").let { it.subList(3, it.size) } - .filter { - if (skipNext) { - skipNext = false - return@filter false - } else if (filter != null && !filter(it)) { - skipNext = true - return@filter false - } else { - return@filter true - } - } - .map { - val index = indexReg.matchEntire(it)?.groupValues?.get(1)?.toInt() - return@map if (index != null) { - values[index] - } else { - it - } - }.toTypedArray() - } -} diff --git a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/UnpackSpigotBuildData.kt b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/UnpackSpigotBuildData.kt deleted file mode 100644 index 6fbc2bd20..000000000 --- a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/UnpackSpigotBuildData.kt +++ /dev/null @@ -1,86 +0,0 @@ -/* - * paperweight is a Gradle plugin for the PaperMC project. - * - * Copyright (c) 2023 Kyle Wood (DenWav) - * Contributors - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 only, no later versions. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 - * USA - */ - -package io.papermc.paperweight.tasks - -import io.papermc.paperweight.util.* -import java.nio.file.Path -import kotlin.io.path.* -import org.gradle.api.file.RegularFileProperty -import org.gradle.api.tasks.InputFile -import org.gradle.api.tasks.OutputFile -import org.gradle.api.tasks.TaskAction - -abstract class UnpackSpigotBuildData : BaseTask() { - @get:InputFile - abstract val buildDataZip: RegularFileProperty - - @get:OutputFile - abstract val buildDataInfoFile: RegularFileProperty - - @get:OutputFile - abstract val excludesFile: RegularFileProperty - - @get:OutputFile - abstract val atFile: RegularFileProperty - - @get:OutputFile - abstract val classMappings: RegularFileProperty - - @get:OutputFile - abstract val specialSourceJar: RegularFileProperty - - @get:OutputFile - abstract val specialSource2Jar: RegularFileProperty - - override fun init() { - buildDataInfoFile.convention(defaultOutput("spigot-build-data-info", "json")) - excludesFile.convention(defaultOutput("spigot-excludes", "exclude")) - atFile.convention(defaultOutput("spigot-ats", "at")) - classMappings.convention(defaultOutput("spigot-class-mapping", "csrg")) - specialSourceJar.convention(defaultOutput("special-source", "jar")) - specialSource2Jar.convention(defaultOutput("special-source-2", "jar")) - } - - @TaskAction - fun run() { - buildDataZip.path.openZip().use { - val root = it.getPath("/") - root.resolve("info.json") - .copyTo(buildDataInfoFile.path.createParentDirectories(), overwrite = true) - val mappings = root.resolve("mappings") - bukkitFileFrom(mappings, "exclude") - .copyTo(excludesFile.path.createParentDirectories(), overwrite = true) - bukkitFileFrom(mappings, "at") - .copyTo(atFile.path.createParentDirectories(), overwrite = true) - bukkitFileFrom(mappings, "csrg") - .copyTo(classMappings.path.createParentDirectories(), overwrite = true) - root.resolve("bin/SpecialSource.jar") - .copyTo(specialSourceJar.path.createParentDirectories(), overwrite = true) - root.resolve("bin/SpecialSource-2.jar") - .copyTo(specialSource2Jar.path.createParentDirectories(), overwrite = true) - } - } - - private fun bukkitFileFrom(dir: Path, extension: String): Path = - dir.useDirectoryEntries { it.filter { f -> f.name.endsWith(extension) }.single() } -} diff --git a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/mache/RunCodebook.kt b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/mache/RunCodebook.kt index 70212048d..a1a2ea78c 100644 --- a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/mache/RunCodebook.kt +++ b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/mache/RunCodebook.kt @@ -40,12 +40,8 @@ abstract class RunCodebook : JavaLauncherTask() { @get:InputFile abstract val serverJar: RegularFileProperty - @get:PathSensitive(PathSensitivity.NONE) - @get:InputFile - abstract val serverMappings: RegularFileProperty - @get:Input - abstract val remapperArgs: ListProperty + abstract val codebookArgs: ListProperty @get:Classpath abstract val codebookClasspath: ConfigurableFileCollection @@ -53,13 +49,6 @@ abstract class RunCodebook : JavaLauncherTask() { @get:CompileClasspath abstract val minecraftClasspath: ConfigurableFileCollection - @get:Classpath - abstract val remapperClasspath: ConfigurableFileCollection - - @get:PathSensitive(PathSensitivity.NONE) - @get:InputFiles - abstract val paramMappings: ConfigurableFileCollection - @get:Classpath abstract val constants: ConfigurableFileCollection @@ -72,11 +61,11 @@ abstract class RunCodebook : JavaLauncherTask() { launcher.get(), codebookClasspath, outputJar.path, - remapperArgs.get(), + codebookArgs.get(), temporaryDir.toPath(), - remapperClasspath, - serverMappings.path, - paramMappings.singleFile.toPath(), + null, + null, + null, constants.files.singleOrNull()?.toPath(), serverJar.path, minecraftClasspath.files.map { it.toPath() } @@ -90,9 +79,9 @@ fun runCodebook( outputJar: Path, remapperArgs: List, tempDir: Path, - remapperClasspath: FileCollection, - serverMappings: Path, - paramMappings: Path, + remapperClasspath: FileCollection?, + serverMappings: Path?, + paramMappings: Path?, constants: Path?, serverJar: Path, minecraftClasspath: List, @@ -106,10 +95,18 @@ fun runCodebook( remapperArgs.forEach { arg -> args += arg .replace(Regex("\\{tempDir}")) { tempDir.absolutePathString() } - .replace(Regex("\\{remapperFile}")) { remapperClasspath.singleFile.absolutePath } - .replace(Regex("\\{mappingsFile}")) { serverMappings.absolutePathString() } - .replace(Regex("\\{paramsFile}")) { paramMappings.absolutePathString() } - .replace(Regex("\\{constantsFile}")) { constants!!.absolutePathString() } + .replace(Regex("\\{remapperFile}")) { + remapperClasspath?.singleFile?.absolutePath ?: error("{remapperFile} in args, but no remapperClasspath provided") + } + .replace(Regex("\\{mappingsFile}")) { + serverMappings?.absolutePathString() ?: error("{mappingsFile} in args, but no serverMappings provided") + } + .replace(Regex("\\{paramsFile}")) { + paramMappings?.absolutePathString() ?: error("{paramsFile} in args, but no paramMappings provided") + } + .replace(Regex("\\{constantsFile}")) { + constants?.absolutePathString() ?: error("{constantsFile} in args, but no constants provided") + } .replace(Regex("\\{output}")) { outputJar.absolutePathString() } .replace(Regex("\\{input}")) { serverJar.absolutePathString() } .replace(Regex("\\{inputClasspath}")) { minecraftClasspath.joinToString(":") { it.absolutePathString() } } diff --git a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/data/mache/MacheDependencies.kt b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/data/mache/MacheDependencies.kt index 630003ed0..7aadb4041 100644 --- a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/data/mache/MacheDependencies.kt +++ b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/data/mache/MacheDependencies.kt @@ -24,8 +24,8 @@ package io.papermc.paperweight.util.data.mache data class MacheDependencies( val codebook: List, - val paramMappings: List, + val paramMappings: List?, val constants: List, - val remapper: List, + val remapper: List?, val decompiler: List, ) diff --git a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/data/mache/MacheMeta.kt b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/data/mache/MacheMeta.kt index cb613557c..ee8c0f445 100644 --- a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/data/mache/MacheMeta.kt +++ b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/data/mache/MacheMeta.kt @@ -62,7 +62,7 @@ data class MacheMeta( } named(MACHE_PARAM_MAPPINGS_CONFIG) { defaultDependencies { - macheDeps.paramMappings.forEach { + macheDeps.paramMappings?.forEach { add(project.dependencies.create(it.toMavenString())) } } @@ -76,7 +76,7 @@ data class MacheMeta( } named(MACHE_REMAPPER_CONFIG) { defaultDependencies { - macheDeps.remapper.forEach { + macheDeps.remapper?.forEach { add(project.dependencies.create(it.toMavenString())) } } diff --git a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/utils.kt b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/utils.kt index 4d653072d..2a4246f24 100644 --- a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/utils.kt +++ b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/utils.kt @@ -381,7 +381,7 @@ fun ByteArray.asHexString(): String { fun JavaToolchainService.defaultJavaLauncher(project: Project): Provider { // If the java plugin isn't applied, or no toolchain value was set val fallback = launcherFor { - languageVersion.set(JavaLanguageVersion.of(21)) + languageVersion.set(JavaLanguageVersion.of(25)) } val ext = project.extensions.findByType() ?: return fallback diff --git a/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/internal/setup/DevBundles.kt b/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/internal/setup/DevBundles.kt index 7cf316fa4..e2421ca81 100644 --- a/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/internal/setup/DevBundles.kt +++ b/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/internal/setup/DevBundles.kt @@ -26,6 +26,7 @@ import io.papermc.paperweight.PaperweightException import io.papermc.paperweight.tasks.* import io.papermc.paperweight.userdev.internal.setup.v2.DevBundleV2 import io.papermc.paperweight.userdev.internal.setup.v5.DevBundleV5 +import io.papermc.paperweight.userdev.internal.setup.v7.DevBundleV7 import io.papermc.paperweight.util.* import java.nio.file.Path import kotlin.io.path.* @@ -35,8 +36,9 @@ private val supported = mapOf( 3 to DevBundleV5.Config::class, // up to 1.20.4 4 to DevBundleV5.Config::class, // 1.20.5, early 1.20.6 5 to DevBundleV5.Config::class, // 1.20.6+ (nullable mojangApiCoordinates) - 6 to GenerateDevBundle.DevBundleConfig::class, // Post-repo-restructure 1.21.4, early 1.21.5 - 7 to GenerateDevBundle.DevBundleConfig::class, // 1.21.5+ + 6 to DevBundleV7.Config::class, // Post-repo-restructure 1.21.4, early 1.21.5 + 7 to DevBundleV7.Config::class, // 1.21.5+ + 8 to GenerateDevBundle.DevBundleConfig::class, // 26.1+ ) fun readBundleInfo(bundleZip: Path): BundleInfo { diff --git a/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/internal/setup/SetupHandlerImpl.kt b/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/internal/setup/SetupHandlerImpl.kt index d91eef9c5..07b0617b7 100644 --- a/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/internal/setup/SetupHandlerImpl.kt +++ b/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/internal/setup/SetupHandlerImpl.kt @@ -199,11 +199,7 @@ class SetupHandlerImpl( } override fun extractReobfMappings(output: Path) { - bundle.config.reobfMappingsFile?.let { location -> - bundle.zip.openZipSafe().use { fs -> - fs.getPath(location).copyTo(output, true) - } - } + // TODO } private fun macheMeta(): MacheMeta = requireNotNull(macheMeta) { "Mache meta is not setup yet" } diff --git a/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/internal/setup/v2/SetupHandlerImplV2.kt b/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/internal/setup/v2/SetupHandlerImplV2.kt index a542a48c7..3d168a618 100644 --- a/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/internal/setup/v2/SetupHandlerImplV2.kt +++ b/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/internal/setup/v2/SetupHandlerImplV2.kt @@ -32,6 +32,7 @@ import io.papermc.paperweight.userdev.internal.setup.SetupHandler import io.papermc.paperweight.userdev.internal.setup.UserdevSetup import io.papermc.paperweight.userdev.internal.setup.UserdevSetupTask import io.papermc.paperweight.userdev.internal.setup.action.* +import io.papermc.paperweight.userdev.internal.setup.action.GenerateMappingsAction import io.papermc.paperweight.util.* import io.papermc.paperweight.util.constants.* import java.nio.file.Path diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/SpigotExtension.kt b/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/internal/setup/v7/DevBundleV7.kt similarity index 66% rename from paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/SpigotExtension.kt rename to paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/internal/setup/v7/DevBundleV7.kt index db994117d..48dda242b 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/SpigotExtension.kt +++ b/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/internal/setup/v7/DevBundleV7.kt @@ -20,13 +20,18 @@ * USA */ -package io.papermc.paperweight.core.extension +package io.papermc.paperweight.userdev.internal.setup.v7 -import org.gradle.api.provider.Property +import io.papermc.paperweight.util.MavenDep -interface SpigotExtension { - - val buildDataRef: Property - val packageVersion: Property - val enabled: Property +object DevBundleV7 { + data class Config( + val minecraftVersion: String, + val mache: MavenDep, + val patchDir: String, + val reobfMappingsFile: String?, + val mojangMappedPaperclipFile: String, + val libraryRepositories: List, + val pluginRemapArgs: List, + ) } diff --git a/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/internal/setup/v7/SetupHandlerImplV7.kt b/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/internal/setup/v7/SetupHandlerImplV7.kt new file mode 100644 index 000000000..5acc20395 --- /dev/null +++ b/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/internal/setup/v7/SetupHandlerImplV7.kt @@ -0,0 +1,276 @@ +/* + * paperweight is a Gradle plugin for the PaperMC project. + * + * Copyright (c) 2023 Kyle Wood (DenWav) + * Contributors + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 only, no later versions. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + * USA + */ + +package io.papermc.paperweight.userdev.internal.setup.v7 + +import io.papermc.paperweight.userdev.internal.action.FileCollectionValue +import io.papermc.paperweight.userdev.internal.action.StringValue +import io.papermc.paperweight.userdev.internal.action.WorkDispatcher +import io.papermc.paperweight.userdev.internal.action.fileValue +import io.papermc.paperweight.userdev.internal.action.javaLauncherValue +import io.papermc.paperweight.userdev.internal.action.stringListValue +import io.papermc.paperweight.userdev.internal.setup.BundleInfo +import io.papermc.paperweight.userdev.internal.setup.SetupHandler +import io.papermc.paperweight.userdev.internal.setup.UserdevSetup +import io.papermc.paperweight.userdev.internal.setup.UserdevSetupTask +import io.papermc.paperweight.userdev.internal.setup.action.ApplyDevBundlePatchesAction +import io.papermc.paperweight.userdev.internal.setup.action.ExtractFromBundlerAction +import io.papermc.paperweight.userdev.internal.setup.action.RunCodebookAction +import io.papermc.paperweight.userdev.internal.setup.action.RunPaperclipAction +import io.papermc.paperweight.userdev.internal.setup.action.SetupMacheSourcesAction +import io.papermc.paperweight.userdev.internal.setup.action.VanillaServerDownloads +import io.papermc.paperweight.util.* +import io.papermc.paperweight.util.constants.* +import io.papermc.paperweight.util.data.mache.* +import java.nio.file.Path +import kotlin.io.path.* +import org.gradle.api.artifacts.DependencySet +import org.gradle.api.file.FileCollection +import org.gradle.api.provider.Property +import org.gradle.kotlin.dsl.* + +class SetupHandlerImplV7( + private val parameters: UserdevSetup.Parameters, + private val bundle: BundleInfo, +) : SetupHandler { + private var macheMeta: MacheMeta? = null + + private fun createDispatcher(context: SetupHandler.ExecutionContext): WorkDispatcher { + val dispatcher = WorkDispatcher.create(parameters.cache.path) + dispatcher.overrideTerminalInputHash(parameters.bundleZipHash.get()) + + val javaLauncher = javaLauncherValue(context.javaLauncher) + val mcVer = StringValue(bundle.config.minecraftVersion) + val bundleZip = fileValue(bundle.zip) + dispatcher.provided( + javaLauncher, + mcVer, + bundleZip, + ) + + val vanillaDownloads = dispatcher.register( + "vanillaServerDownloads", + VanillaServerDownloads( + mcVer, + dispatcher.outputFile("vanillaServer.jar"), + dispatcher.outputFile("mojangServerMappings.txt"), + parameters.downloadService.get(), + ) + ) + + val applyPaperclip = dispatcher.register( + "applyPaperclipPatch", + RunPaperclipAction( + javaLauncher, + bundleZip, + StringValue(bundle.config.mojangMappedPaperclipFile), + dispatcher.outputFile("output.jar"), + vanillaDownloads.serverJar, + mcVer, + ) + ) + dispatcher.provided(applyPaperclip.paperclipPath) + + val extract = dispatcher.register( + "extractFromBundler", + ExtractFromBundlerAction( + vanillaDownloads.serverJar, + dispatcher.outputFile("vanillaServer.jar"), + dispatcher.outputDir("minecraftLibraries"), + ) + ) + + val remap = dispatcher.register( + "remapMinecraft", + RunCodebookAction( + javaLauncher, + stringListValue(macheMeta().remapperArgs), + extract.vanillaServerJar, + extract.minecraftLibraryJars, + vanillaDownloads.serverMappings, + FileCollectionValue(context.macheParamMappingsConfig), + FileCollectionValue(context.macheConstantsConfig), + FileCollectionValue(context.macheCodebookConfig), + FileCollectionValue(context.macheRemapperConfig), + dispatcher.outputFile("output.jar"), + ) + ) + dispatcher.provided( + remap.minecraftRemapArgs, + remap.paramMappings, + remap.constants, + remap.codebook, + remap.remapper, + ) + + val macheSources = dispatcher.register( + "setupMacheSources", + SetupMacheSourcesAction( + javaLauncher, + remap.outputJar, + dispatcher.outputFile("output.zip"), + extract.minecraftLibraryJars, + stringListValue(macheMeta().decompilerArgs), + FileCollectionValue(context.macheDecompilerConfig), + FileCollectionValue(context.macheConfig), + ) + ) + dispatcher.provided( + macheSources.decompileArgs, + macheSources.decompiler, + macheSources.mache, + ) + + val applyPatches = dispatcher.register( + "applyDevBundlePatches", + ApplyDevBundlePatchesAction( + macheSources.outputJar, + bundleZip, + StringValue(bundle.config.patchDir), + dispatcher.outputFile("output.jar"), + applyPaperclip.outputJar, + ) + ) + dispatcher.provided(applyPatches.patchesPath) + + return dispatcher + } + + override fun populateCompileConfiguration( + context: SetupHandler.ConfigurationContext, + dependencySet: DependencySet, + injectServerJar: Property + ) { + if (injectServerJar.get()) { + dependencySet.add(context.dependencyFactory.create(context.layout.files(context.setupTask.flatMap { it.mappedServerJar }))) + } + dependencySet.add(context.dependencyFactory.create(context.devBundleCoordinates)) + } + + override fun populateRuntimeConfiguration( + context: SetupHandler.ConfigurationContext, + dependencySet: DependencySet, + injectServerJar: Property + ) { + populateCompileConfiguration(context, dependencySet, injectServerJar) + } + + @Volatile + private var completedOutput: SetupHandler.ArtifactsResult? = null + + @Synchronized + override fun generateArtifacts(context: SetupHandler.ExecutionContext): SetupHandler.ArtifactsResult { + if (completedOutput != null) { + return requireNotNull(completedOutput) + } + + // If the config cache is reused then the mache config may not be populated + setupMacheMeta(context.macheConfig) + + val dispatcher = createDispatcher(context) + val request = if (parameters.genSources.get()) { + dispatcher.registered("applyDevBundlePatches").outputJar + } else { + dispatcher.registered("applyPaperclipPatch").outputJar + } + context.withProgressLogger { progressLogger -> + dispatcher.dispatch(request) { + progressLogger.progress(it) + } + } + return SetupHandler.ArtifactsResult(request.get(), null) + .also { completedOutput = it } + } + + override fun extractReobfMappings(output: Path) { + bundle.config.reobfMappingsFile?.let { location -> + bundle.zip.openZipSafe().use { fs -> + fs.getPath(location).copyTo(output, true) + } + } + } + + private fun macheMeta(): MacheMeta = requireNotNull(macheMeta) { "Mache meta is not setup yet" } + + private fun setupMacheMeta(macheFiles: FileCollection) { + if (macheMeta == null) { + synchronized(this) { + macheMeta = macheFiles.resolveMacheMeta() + } + } + } + + override fun afterEvaluate(context: SetupHandler.ConfigurationContext) { + super.afterEvaluate(context) + setupMacheMeta(context.project.configurations.getByName(MACHE_CONFIG)) + + val configurations = context.project.configurations + + val macheCodebook = configurations.register(MACHE_CODEBOOK_CONFIG) { + isTransitive = false + } + val macheRemapper = configurations.register(MACHE_REMAPPER_CONFIG) { + isTransitive = false + } + val macheDecompiler = configurations.register(MACHE_DECOMPILER_CONFIG) { + isTransitive = false + } + val macheParamMappings = configurations.register(MACHE_PARAM_MAPPINGS_CONFIG) { + isTransitive = false + } + val macheConstants = configurations.register(MACHE_CONSTANTS_CONFIG) { + isTransitive = false + } + + context.project.tasks.withType(UserdevSetupTask::class).configureEach { + macheCodebookConfig.from(macheCodebook) + macheRemapperConfig.from(macheRemapper) + macheDecompilerConfig.from(macheDecompiler) + macheParamMappingsConfig.from(macheParamMappings) + macheConstantsConfig.from(macheConstants) + } + + macheMeta().addDependencies(context.project) + macheMeta().addRepositories(context.project) + } + + override val minecraftVersion: String + get() = bundle.config.minecraftVersion + + override val pluginRemapArgs: List + get() = bundle.config.pluginRemapArgs + + override val paramMappings: MavenDep? + get() = null + + override val decompiler: MavenDep? + get() = null + + override val remapper: MavenDep? + get() = null + + override val mache: MavenDep + get() = bundle.config.mache + + override val libraryRepositories: List + get() = bundle.config.libraryRepositories +} From 79f32cd92250f0594f48eefe0def9f6883a1f6e4 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Wed, 18 Mar 2026 21:01:36 +0100 Subject: [PATCH 191/257] chore: fix tests --- .../test-server/patches/base/0001-Base.patch | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/paperweight-core/src/test/resources/functional_test/test-server/patches/base/0001-Base.patch b/paperweight-core/src/test/resources/functional_test/test-server/patches/base/0001-Base.patch index 67a839b3c..bd30c70b7 100644 --- a/paperweight-core/src/test/resources/functional_test/test-server/patches/base/0001-Base.patch +++ b/paperweight-core/src/test/resources/functional_test/test-server/patches/base/0001-Base.patch @@ -5,15 +5,15 @@ Subject: [PATCH] Base diff --git a/net/minecraft/CrashReport.java b/net/minecraft/CrashReport.java -index 7efc2d1a901ea5708045fc9fddca09caf467762f..659b933e4c077cb6b36c23e9844e314ecb2f1110 100644 +index 01c9784075b31876a0a91e81df0ae50a42d246e3..25837ee13ac83e64c3bea23f5abf4e6dd1e39829 100644 --- a/net/minecraft/CrashReport.java +++ b/net/minecraft/CrashReport.java @@ -29,7 +29,7 @@ public class CrashReport { - private Path saveFile; + private @Nullable Path saveFile; private boolean trackingStackTrace = true; public StackTraceElement[] uncategorizedStackTrace = new StackTraceElement[0]; - public SystemReport systemReport = new SystemReport(); + private SystemReport systemReport = new SystemReport(); // woooo at base test???? who knew - public CrashReport(String title, Throwable exception) { + public CrashReport(final String title, final Throwable t) { this.title = title; From c025e220d1f628c673c45ed1f8f75dd71f304057 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Wed, 18 Mar 2026 21:06:31 +0100 Subject: [PATCH 192/257] if paper has these disabled and they don't feel like updating those, then so be it --- .../test/resources/functional_test/test-server/build.gradle | 6 ------ 1 file changed, 6 deletions(-) diff --git a/paperweight-core/src/test/resources/functional_test/test-server/build.gradle b/paperweight-core/src/test/resources/functional_test/test-server/build.gradle index c0f204edc..fa9919787 100644 --- a/paperweight-core/src/test/resources/functional_test/test-server/build.gradle +++ b/paperweight-core/src/test/resources/functional_test/test-server/build.gradle @@ -42,12 +42,6 @@ paperweight { additionalAts.set(file('../build-data/fake.at')) } } else { - paper { - basePatchDir.set(file('patches/base')) - sourcePatchDir.set(file('patches/sources')) - resourcePatchDir.set(file('patches/resources')) - featurePatchDir.set(file('patches/features')) - } minecraftVersion = '26.1-snapshot-11' } } From bda157ff6cc3180a9a6233a5a12e9c4a2682fad2 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Sun, 22 Mar 2026 15:47:27 +0100 Subject: [PATCH 193/257] chore: bump diffpatch to v2.1.0.43 --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 4c481a7b5..75b783930 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -34,7 +34,7 @@ hypo-mappings = { module = "dev.denwav.hypo:hypo-mappings", version.ref = "hypo" lorenzTiny = "net.fabricmc:lorenz-tiny:3.0.0" jbsdiff = "io.sigpipe:jbsdiff:1.0" -diffpatch = "io.codechicken:DiffPatch:2.1.0.42" +diffpatch = "io.codechicken:DiffPatch:2.1.0.43" serialize-core = { module = "org.jetbrains.kotlinx:kotlinx-serialization-core", version.ref = "serialize" } serialize-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "serialize" } From 1b9b73989ecdc94e2ed0fbbd78ea8caea0d66700 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Fri, 3 Apr 2026 23:26:11 +0200 Subject: [PATCH 194/257] chore!: rip out publisher jar logic --- .../paperweight/core/PaperweightCore.kt | 1 - .../core/taskcontainers/PaperclipTasks.kt | 47 +++++-------------- .../paperweight/tasks/CreatePublisherJar.kt | 30 ------------ 3 files changed, 11 insertions(+), 67 deletions(-) delete mode 100644 paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/CreatePublisherJar.kt diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/PaperweightCore.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/PaperweightCore.kt index 2c2d799b0..702f09441 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/PaperweightCore.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/PaperweightCore.kt @@ -153,7 +153,6 @@ abstract class PaperweightCore : Plugin { tasks.downloadServerJar.flatMap { it.outputJar }, serverJar, ext.minecraftVersion, - ext.activeFork.map { it.name }.orElse("paper") ) target.afterEvaluate { diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PaperclipTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PaperclipTasks.kt index 37b1a3b39..88fc6564a 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PaperclipTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PaperclipTasks.kt @@ -46,23 +46,20 @@ class PaperclipTasks( private val serverLibrariesList: Provider, private val vanillaJar: Provider, serverJar: Provider, - private val mcVersion: Provider, - private val forkName: Provider + private val mcVersion: Provider ) { init { - val (createBundlerJar, createPaperclipJar, createPublisherJar) = project.createTasks() + val (createBundlerJar, createPaperclipJar) = project.createTasks() createBundlerJar.serverJar(serverJar) createPaperclipJar.bundlerJar(createBundlerJar) - createPublisherJar.paperclipJar(createPaperclipJar) } private fun Project.createTasks( classifier: String = "", - ): Triple, TaskProvider, TaskProvider> { + ): Pair, TaskProvider> { val bundlerTaskName = "create${classifier.capitalized()}BundlerJar" val paperclipTaskName = "create${classifier.capitalized()}PaperclipJar" - val publisherTaskName = "create${classifier.capitalized()}PublisherJar" val bundlerJarTask = tasks.register(bundlerTaskName) { group = "bundling" @@ -81,30 +78,17 @@ class PaperclipTasks( libraryChangesJson.set(bundlerJarTask.flatMap { it.libraryChangesJson }) outputZip.set(layout.buildDirectory.file(jarName("paperclip", classifier).map { "libs/$it" })) } - val publisherJarTask = tasks.register(publisherTaskName) { - group = "bundling" - description = "Build a ready-to-publish paperclip jar" - - outputZip.convention(layout.buildDirectory.file(jarName("publisher", classifier).map { "libs/$it" })) - } - return Triple(bundlerJarTask, paperclipJarTask, publisherJarTask) + return bundlerJarTask to paperclipJarTask } private fun Project.jarName(kind: String, classifier: String): Provider { - return if (kind != "publisher") { - bundlerJarName.map { - listOfNotNull( - it, - kind, - project.version, - classifier.takeIf { c -> c.isNotBlank() }, - ).joinToString("-") + ".jar" - } - } else { - val buildNum = providers.environmentVariable("BUILD_NUMBER").orElse("local") - forkName.zip(buildNum) { name, build -> - "$name-build.$build.jar" - } + return bundlerJarName.map { + listOfNotNull( + it, + kind, + project.version, + classifier.takeIf { c -> c.isNotBlank() }, + ).joinToString("-") + ".jar" } } @@ -158,15 +142,6 @@ class PaperclipTasks( op.execute(this) } - private fun TaskProvider.paperclipJar( - createPaperclipJar: TaskProvider, - op: Action = Action {}, - ) = configure { - inputZip.set(createPaperclipJar.flatMap { it.outputZip }) - - op.execute(this) - } - companion object { fun NamedDomainObjectContainer.registerVersionArtifact( name: String, diff --git a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/CreatePublisherJar.kt b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/CreatePublisherJar.kt deleted file mode 100644 index 30bff1c22..000000000 --- a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/CreatePublisherJar.kt +++ /dev/null @@ -1,30 +0,0 @@ -/* - * paperweight is a Gradle plugin for the PaperMC project. - * - * Copyright (c) 2023 Kyle Wood (DenWav) - * Contributors - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 only, no later versions. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 - * USA - */ - -package io.papermc.paperweight.tasks - -import java.nio.file.Path - -// dummy task -abstract class CreatePublisherJar : ZippedTask() { - override fun run(rootDir: Path) {} -} From 103de064f8b542fcd34f2a53a6d2c96884ec19e7 Mon Sep 17 00:00:00 2001 From: "tofik.07" Date: Sat, 4 Apr 2026 00:47:30 +0200 Subject: [PATCH 195/257] Version 2.4.0 --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index cc99def54..f63eb6e72 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ group = io.canvasmc.weaver -version = 2.4.0-SNAPSHOT +version = 2.4.0 org.gradle.caching = true org.gradle.configuration-cache = true From bf79921193a244d39db622e8c42e0655c53e8766 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Thu, 9 Apr 2026 00:09:48 +0200 Subject: [PATCH 196/257] chore: continue development cycle --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index f63eb6e72..f7284bf01 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ group = io.canvasmc.weaver -version = 2.4.0 +version = 2.4.1-SNAPSHOT org.gradle.caching = true org.gradle.configuration-cache = true From 3774747a341ce82bc87cd3fc09371b739233eb5d Mon Sep 17 00:00:00 2001 From: Toffikk Date: Thu, 9 Apr 2026 22:38:53 +0200 Subject: [PATCH 197/257] chore: mark emitRejects as deprecated for removal --- .../papermc/paperweight/core/tasks/patching/ApplyBasePatches.kt | 1 + .../paperweight/core/tasks/patching/ApplyFeaturePatches.kt | 1 + 2 files changed, 2 insertions(+) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyBasePatches.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyBasePatches.kt index 00ad3bfac..33f9a630a 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyBasePatches.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyBasePatches.kt @@ -63,6 +63,7 @@ abstract class ApplyBasePatches : ControllableOutputTask() { @get:Input abstract val verbose: Property + @Deprecated("To be removed in a future version with no replacement") @get:Internal abstract val emitRejects: Property diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyFeaturePatches.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyFeaturePatches.kt index 759845dc3..6115aaa9a 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyFeaturePatches.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyFeaturePatches.kt @@ -55,6 +55,7 @@ abstract class ApplyFeaturePatches : ControllableOutputTask() { @get:Input abstract val verbose: Property + @Deprecated("To be removed in a future version with no replacement") @get:Internal abstract val emitRejects: Property From 6e079f79923dc633b8558d236a1fb42bb0387e40 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Fri, 24 Apr 2026 01:52:57 +0200 Subject: [PATCH 198/257] BREAKING: Rework the old fork commit logic --- .../papermc/paperweight/core/PaperweightCore.kt | 16 +++++++++------- .../core/extension/UpdatingMinecraftExtension.kt | 1 + .../taskcontainers/MinecraftPatchingTasks.kt | 6 ++---- 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/PaperweightCore.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/PaperweightCore.kt index 702f09441..d681a0dc8 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/PaperweightCore.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/PaperweightCore.kt @@ -219,14 +219,16 @@ abstract class PaperweightCore : Plugin { coreExt.paper.rejectsDir, layout.projectDirectory.dir("src/minecraft/java"), ) - } else if (coreExt.activeFork.isPresent && target.hasProperty("old${coreExt.activeFork.get().name.capitalized()}Commit")) { - val name = coreExt.activeFork.get().name + } else if (coreExt.activeFork.isPresent && coreExt.updatingMinecraft.oldForkCommit.isPresent) { // old commit fetching for forks through a gradle property target.tasks.named("applyMinecraftBasePatches").configure { - additionalRemote = layout.cache.resolve( - "$PAPER_PATH/old${name.capitalized()}/${target.providers.gradleProperty("old${name.capitalized()}Commit").get()}" + - "/$name-server/src/minecraft/java" - ).absolutePathString() + additionalRemote = coreExt.activeFork.map { + layout.cache + .resolve( + "$PAPER_PATH/old${name.capitalized()}/${coreExt.updatingMinecraft.oldForkCommit.get()}/${it.name}-server/src/minecraft/java" + ) + .absolutePathString() + } } target.tasks.named("applyMinecraftSourcePatches").configure { emitRejects = false @@ -236,7 +238,7 @@ abstract class PaperweightCore : Plugin { target, name.lowercase(), coreExt.minecraftVersion, - coreExt.activeFork.get().rejectsDir, + coreExt.activeFork.flatMap { it.rejectsDir }, layout.projectDirectory.dir("src/minecraft/java"), ) } diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/UpdatingMinecraftExtension.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/UpdatingMinecraftExtension.kt index d8164ffc1..87f7577a5 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/UpdatingMinecraftExtension.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/UpdatingMinecraftExtension.kt @@ -26,4 +26,5 @@ import org.gradle.api.provider.Property interface UpdatingMinecraftExtension { val oldPaperCommit: Property + val oldForkCommit: Property } diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt index d58b723d2..eb2b976a8 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt @@ -193,10 +193,8 @@ class MinecraftPatchingTasks( outputDir.set(layout.cache.resolve(paperTaskOutput())) identifier.set(configName) - if (project.hasProperty("old${configName.capitalized()}Commit")) { - oldCommit.convention(project.providers.gradleProperty("old${configName.capitalized()}Commit")) - oldOutputDir.set(layout.cache.resolve("$PAPER_PATH/old${configName.capitalized()}")) - } + oldCommit.convention(project.coreExt.updatingMinecraft.oldForkCommit) + oldOutputDir.set(layout.cache.resolve("$PAPER_PATH/old${configName.capitalized()}")) libraryImports.set(importLibFiles.flatMap { it.outputDir }) atFile.set(mergeCollectedAts.flatMap { it.outputFile }) From 33a250c64776e4720c2fae5b90bd141a23973903 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Fri, 24 Apr 2026 02:10:52 +0200 Subject: [PATCH 199/257] fix: guard against setting up old fork in upstream's setup --- .../kotlin/io/papermc/paperweight/core/PaperweightCore.kt | 2 +- .../core/taskcontainers/MinecraftPatchingTasks.kt | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/PaperweightCore.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/PaperweightCore.kt index d681a0dc8..48f1824e3 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/PaperweightCore.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/PaperweightCore.kt @@ -225,7 +225,7 @@ abstract class PaperweightCore : Plugin { additionalRemote = coreExt.activeFork.map { layout.cache .resolve( - "$PAPER_PATH/old${name.capitalized()}/${coreExt.updatingMinecraft.oldForkCommit.get()}/${it.name}-server/src/minecraft/java" + "$PAPER_PATH/old${it.name.capitalized()}/${coreExt.updatingMinecraft.oldForkCommit.get()}/${it.name}-server/src/minecraft/java" ) .absolutePathString() } diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt index eb2b976a8..be95ffd1a 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt @@ -193,8 +193,10 @@ class MinecraftPatchingTasks( outputDir.set(layout.cache.resolve(paperTaskOutput())) identifier.set(configName) - oldCommit.convention(project.coreExt.updatingMinecraft.oldForkCommit) - oldOutputDir.set(layout.cache.resolve("$PAPER_PATH/old${configName.capitalized()}")) + if (namePart == "Minecraft") { // only configure this when we're the target fork + oldCommit.convention(project.coreExt.updatingMinecraft.oldForkCommit) + oldOutputDir.set(layout.cache.resolve("$PAPER_PATH/old${configName.capitalized()}")) + } libraryImports.set(importLibFiles.flatMap { it.outputDir }) atFile.set(mergeCollectedAts.flatMap { it.outputFile }) From 188bb7adfcf2736267c93b0be74a9caba1ba47de Mon Sep 17 00:00:00 2001 From: Toffikk Date: Fri, 24 Apr 2026 19:47:37 +0200 Subject: [PATCH 200/257] feat/fix: declare a dependency bridge plugin to allow resolving api project's dependencies for the JST classpath safely --- .../PaperweightDependencyBridge.kt | 40 +++++++++++++++++++ .../paperweight/core/PaperweightCore.kt | 24 +++++------ .../core/taskcontainers/PatchingTasks.kt | 25 ++---------- .../paperweight/patcher/PaperweightPatcher.kt | 12 ++++++ ...nvasmc.weaver.dependency-bridge.properties | 1 + .../paperweight/util/constants/constants.kt | 4 ++ 6 files changed, 70 insertions(+), 36 deletions(-) create mode 100644 paperweight-core/src/main/kotlin/io/papermc/paperweight/PaperweightDependencyBridge.kt create mode 100644 paperweight-core/src/main/resources/META-INF/gradle-plugins/io.canvasmc.weaver.dependency-bridge.properties diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/PaperweightDependencyBridge.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/PaperweightDependencyBridge.kt new file mode 100644 index 000000000..cda265b6c --- /dev/null +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/PaperweightDependencyBridge.kt @@ -0,0 +1,40 @@ +/* + * paperweight is a Gradle plugin for the PaperMC project. + * + * Copyright (c) 2023 Kyle Wood (DenWav) + * Contributors + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 only, no later versions. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + * USA + */ + +package io.papermc.paperweight + +import io.papermc.paperweight.util.constants.JST_CLASSPATH_ATTRIBUTE +import io.papermc.paperweight.util.constants.JST_CLASSPATH_CONFIG +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.api.plugins.JavaPlugin + +abstract class PaperweightDependencyBridge : Plugin { + override fun apply(target: Project) { + target.configurations.register(JST_CLASSPATH_CONFIG) { + attributes { + attribute(JST_CLASSPATH_ATTRIBUTE, true) + } + extendsFrom(target.configurations.getByName(JavaPlugin.API_CONFIGURATION_NAME)) + } + } +} diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/PaperweightCore.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/PaperweightCore.kt index 48f1824e3..999a72faa 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/PaperweightCore.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/PaperweightCore.kt @@ -28,10 +28,8 @@ import io.papermc.paperweight.core.extension.PaperweightCoreExtension import io.papermc.paperweight.core.taskcontainers.CoreTasks import io.papermc.paperweight.core.taskcontainers.DevBundleTasks import io.papermc.paperweight.core.taskcontainers.PaperclipTasks -import io.papermc.paperweight.core.tasks.SetupForkUpstreamSources import io.papermc.paperweight.core.tasks.patching.ApplyBasePatches import io.papermc.paperweight.core.tasks.patching.ApplyFilePatches -import io.papermc.paperweight.core.tasks.patching.RebuildFilePatches import io.papermc.paperweight.core.tasks.patchroulette.PatchRouletteTasks import io.papermc.paperweight.core.util.coreExt import io.papermc.paperweight.tasks.* @@ -52,7 +50,6 @@ import org.gradle.api.tasks.Delete import org.gradle.api.tasks.SourceSet import org.gradle.api.tasks.bundling.AbstractArchiveTask import org.gradle.kotlin.dsl.* -import org.gradle.kotlin.dsl.withType abstract class PaperweightCore : Plugin { @get:Inject @@ -111,6 +108,16 @@ abstract class PaperweightCore : Plugin { } } + target.configurations.register(JST_CLASSPATH_CONFIG) { + attributes { + attribute(JST_CLASSPATH_ATTRIBUTE, true) + } + extendsFrom( + target.configurations.getByName(MACHE_MINECRAFT_CONFIG), + target.configurations.getByName(JavaPlugin.COMPILE_CLASSPATH_CONFIGURATION_NAME) + ) + } + // impl extends minecraft target.configurations.named(JavaPlugin.IMPLEMENTATION_CONFIGURATION_NAME) { extendsFrom(macheMinecraftLibrariesConfig.get()) @@ -185,17 +192,6 @@ abstract class PaperweightCore : Plugin { mache.get().addRepositories(this) mache.get().addDependencies(this) - // attach the mache minecraft jar so JST can properly create binary representations - target.tasks.withType().configureEach { - dependsOn(target.tasks.named("macheRemapJar")) - ats.jstClasspath.from(target.configurations.named(MACHE_MINECRAFT_CONFIG)) - } - - target.tasks.withType().configureEach { - dependsOn(target.tasks.named("macheRemapJar")) - ats.jstClasspath.from(target.configurations.named(MACHE_MINECRAFT_CONFIG)) - } - tasks.afterEvaluate() devBundleTasks.configureAfterEvaluate( diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt index 75c758148..bb9076f61 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt @@ -22,7 +22,6 @@ package io.papermc.paperweight.core.taskcontainers -import io.papermc.paperweight.core.PaperweightCore import io.papermc.paperweight.core.tasks.SetupForkUpstreamSources import io.papermc.paperweight.core.tasks.patching.ApplyBasePatches import io.papermc.paperweight.core.tasks.patching.ApplyFeaturePatches @@ -32,6 +31,7 @@ import io.papermc.paperweight.core.tasks.patching.FixupFilePatches import io.papermc.paperweight.core.tasks.patching.RebuildFilePatches import io.papermc.paperweight.tasks.* import io.papermc.paperweight.util.* +import io.papermc.paperweight.util.constants.JST_CLASSPATH_CONFIG import io.papermc.paperweight.util.constants.JST_CONFIG import io.papermc.paperweight.util.constants.paperTaskOutput import java.nio.file.Path @@ -40,7 +40,6 @@ import org.gradle.api.Task import org.gradle.api.file.Directory import org.gradle.api.file.DirectoryProperty import org.gradle.api.file.RegularFileProperty -import org.gradle.api.plugins.JavaPlugin import org.gradle.api.provider.Provider import org.gradle.api.tasks.TaskContainer import org.gradle.kotlin.dsl.* @@ -160,16 +159,7 @@ class PatchingTasks( atFile.set(mergeCollectedAts.flatMap { it.outputFile }) ats.jst.from(project.configurations.named(JST_CONFIG)) - ats.jstClasspath.from( - project.configurations.named(JavaPlugin.COMPILE_CLASSPATH_CONFIGURATION_NAME), - project.subprojects.mapNotNull { - if (!it.plugins.hasPlugin(PaperweightCore::class)) { - it.configurations.named(JavaPlugin.COMPILE_CLASSPATH_CONFIGURATION_NAME).map { it.files } - } else { - null - } - } - ) + ats.jstClasspath.from(project.configurations.named(JST_CLASSPATH_CONFIG)) validateAts.set(this@PatchingTasks.validateAts) } @@ -217,16 +207,7 @@ class PatchingTasks( patches.set(filePatchDir) gitFilePatches.set(this@PatchingTasks.gitFilePatches) - ats.jstClasspath.from( - project.configurations.named(JavaPlugin.COMPILE_CLASSPATH_CONFIGURATION_NAME), - project.subprojects.mapNotNull { - if (!it.plugins.hasPlugin(PaperweightCore::class)) { - it.configurations.named(JavaPlugin.COMPILE_CLASSPATH_CONFIGURATION_NAME).map { it.files } - } else { - null - } - } - ) + ats.jstClasspath.from(project.configurations.named(JST_CLASSPATH_CONFIG)) ats.jst.from(project.configurations.named(JST_CONFIG)) atFile.set(additionalAts.fileExists()) atFileOut.set(additionalAts.fileExists()) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/PaperweightPatcher.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/PaperweightPatcher.kt index 4af2193a1..731edfca5 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/PaperweightPatcher.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/PaperweightPatcher.kt @@ -61,12 +61,24 @@ abstract class PaperweightPatcher : Plugin { } } + target.configurations.register(JST_CLASSPATH_CONFIG) { + attributes { + attribute(JST_CLASSPATH_ATTRIBUTE, true) + } + } + target.afterEvaluate { repositories { maven(patcher.jstRepo) { name = JST_REPO_NAME content { onlyForConfigurations(JST_CONFIG) } } + maven(PAPER_MAVEN_REPO_URL) { + content { onlyForConfigurations(JST_CLASSPATH_CONFIG) } + } + mavenCentral { + content { onlyForConfigurations(JST_CLASSPATH_CONFIG) } + } } afterEvaluate(patcher) } diff --git a/paperweight-core/src/main/resources/META-INF/gradle-plugins/io.canvasmc.weaver.dependency-bridge.properties b/paperweight-core/src/main/resources/META-INF/gradle-plugins/io.canvasmc.weaver.dependency-bridge.properties new file mode 100644 index 000000000..8a5fb9178 --- /dev/null +++ b/paperweight-core/src/main/resources/META-INF/gradle-plugins/io.canvasmc.weaver.dependency-bridge.properties @@ -0,0 +1 @@ +implementation-class=io.papermc.paperweight.PaperweightDependencyBridge 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 60f164f31..3c5e7f728 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 @@ -23,6 +23,7 @@ package io.papermc.paperweight.util.constants import org.gradle.api.Task +import org.gradle.api.attributes.Attribute const val PAPERWEIGHT_EXTENSION = "paperweight" const val PAPER_CHECKSTYLE_EXTENSION = "paperCheckstyle" @@ -55,11 +56,14 @@ const val MACHE_MINECRAFT_LIBRARIES_CONFIG = "macheMinecraftLibraries" const val MACHE_MINECRAFT_CONFIG = "macheMinecraft" const val MAPPED_JAR_OUTGOING_CONFIG = "mappedJarOutgoing" const val JST_CONFIG = "javaSourceTransformer" +const val JST_CLASSPATH_CONFIG = "jstClasspath" const val DEV_BUNDLE_CONFIG = "paperweightDevelopmentBundle" const val MOJANG_MAPPED_SERVER_CONFIG = "mojangMappedServer" const val MOJANG_MAPPED_SERVER_RUNTIME_CONFIG = "mojangMappedServerRuntime" const val REOBF_CONFIG = "reobf" +val JST_CLASSPATH_ATTRIBUTE = Attribute.of("io.papermc.paperweight.jst-classpath", Boolean::class.javaObjectType) + const val PARAM_MAPPINGS_REPO_NAME = "paperweightParamMappingsRepository" const val DECOMPILER_REPO_NAME = "paperweightDecompilerRepository" const val REMAPPER_REPO_NAME = "paperweightRemapperRepository" From 30c1f578e5bb734158dab10b178ceb6fea4fdf06 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Fri, 24 Apr 2026 21:18:35 +0200 Subject: [PATCH 201/257] chore: remove unused property --- .../paperweight/core/tasks/SetupForkMinecraftSources.kt | 4 ---- 1 file changed, 4 deletions(-) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupForkMinecraftSources.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupForkMinecraftSources.kt index 4b94ffb95..2effdc478 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupForkMinecraftSources.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupForkMinecraftSources.kt @@ -85,13 +85,9 @@ abstract class SetupForkMinecraftSources : JavaLauncherTask() { @get:Optional abstract val oldCommit: Property - @get:Input - abstract val additionalRemoteName: Property - override fun init() { super.init() atWorkingDir.set(layout.cache.resolve(paperTaskOutput(name = "${name}_atWorkingDir"))) - additionalRemoteName.convention("old") forkName.convention(project.coreExt.activeFork.map { it.name.capitalized() }) } From 581f8a519ddc360890202eb949f3fed6b2df2e73 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Fri, 24 Apr 2026 21:47:46 +0200 Subject: [PATCH 202/257] Provide a more comprehensive classpath for MC AT application to decrease the amount of failed bin representations for forks --- .../paperweight/core/taskcontainers/MinecraftPatchingTasks.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt index be95ffd1a..eb551692b 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt @@ -201,7 +201,7 @@ class MinecraftPatchingTasks( libraryImports.set(importLibFiles.flatMap { it.outputDir }) atFile.set(mergeCollectedAts.flatMap { it.outputFile }) ats.jst.from(project.configurations.named(JST_CONFIG)) - ats.jstClasspath.from(project.configurations.named(MACHE_MINECRAFT_LIBRARIES_CONFIG)) + ats.jstClasspath.from(project.configurations.named(JST_CLASSPATH_CONFIG)) validateATs.set(project.coreExt.validateATs) } From 92e9d53c68818b58a7cff67ae0ac314f2c2a024b Mon Sep 17 00:00:00 2001 From: Toffikk Date: Fri, 24 Apr 2026 22:00:59 +0200 Subject: [PATCH 203/257] Version 2.4.1 --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index f7284bf01..de9492ea2 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ group = io.canvasmc.weaver -version = 2.4.1-SNAPSHOT +version = 2.4.1 org.gradle.caching = true org.gradle.configuration-cache = true From c5166bba1ce68e67eed7cc1a564e236a9470df44 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Fri, 24 Apr 2026 22:02:08 +0200 Subject: [PATCH 204/257] chore: continue development cycle --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index de9492ea2..ebcbe438d 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ group = io.canvasmc.weaver -version = 2.4.1 +version = 2.4.2-SNAPSHOT org.gradle.caching = true org.gradle.configuration-cache = true From e0c8b657428193bcf214370bf9e033c0511ecbf4 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Fri, 24 Apr 2026 22:19:34 +0200 Subject: [PATCH 205/257] feat: enable AT validation by default --- .../paperweight/core/extension/PaperweightCoreExtension.kt | 2 +- .../paperweight/core/tasks/patching/RebuildFilePatches.kt | 2 +- .../patcher/extension/PaperweightPatcherExtension.kt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/PaperweightCoreExtension.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/PaperweightCoreExtension.kt index f79414726..b6a8162d1 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/PaperweightCoreExtension.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/PaperweightCoreExtension.kt @@ -68,5 +68,5 @@ abstract class PaperweightCoreExtension @Inject constructor(objects: ObjectFacto action.execute(updatingMinecraft) } - val validateATs: Property = objects.property().convention(false) + val validateATs: Property = objects.property().convention(true) } diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/RebuildFilePatches.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/RebuildFilePatches.kt index 072f2e43e..8fa92f80b 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/RebuildFilePatches.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/RebuildFilePatches.kt @@ -289,7 +289,7 @@ abstract class RebuildFilePatches : JavaLauncherTask() { at, temporaryDir.toPath().resolve("jst_work"), singleFile = true, - validate = false, + validate = true, ) println("NEW: " + decomp.readText()) } diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/extension/PaperweightPatcherExtension.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/extension/PaperweightPatcherExtension.kt index 1e5209200..adf6dc22b 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/extension/PaperweightPatcherExtension.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/extension/PaperweightPatcherExtension.kt @@ -38,7 +38,7 @@ abstract class PaperweightPatcherExtension @Inject constructor(private val objec val gitFilePatches: Property = objects.property().convention(false) val filterPatches: Property = objects.property().convention(true) - val validateATs: Property = objects.property().convention(false) + val validateATs: Property = objects.property().convention(true) val jstRepo: Property = objects.property().convention(PAPER_MAVEN_REPO_URL) From 4afa4ad0de256c7d48b65da098221c288726bd4b Mon Sep 17 00:00:00 2001 From: Toffikk Date: Sat, 25 Apr 2026 14:00:28 +0200 Subject: [PATCH 206/257] fix task naming in patch roulette --- .../main/kotlin/io/papermc/paperweight/core/PaperweightCore.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/PaperweightCore.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/PaperweightCore.kt index 999a72faa..8cc5d1075 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/PaperweightCore.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/PaperweightCore.kt @@ -232,7 +232,7 @@ abstract class PaperweightCore : Plugin { PatchRouletteTasks( target, - name.lowercase(), + coreExt.activeFork.get().name.lowercase(), coreExt.minecraftVersion, coreExt.activeFork.flatMap { it.rejectsDir }, layout.projectDirectory.dir("src/minecraft/java"), From 2c2ed44fadb1cbbda3b4946f0bbc901332a94425 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Sat, 25 Apr 2026 15:45:53 +0200 Subject: [PATCH 207/257] fix: dont trigger compileJava for the API subproject --- .../io/papermc/paperweight/PaperweightDependencyBridge.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/PaperweightDependencyBridge.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/PaperweightDependencyBridge.kt index cda265b6c..016bf2111 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/PaperweightDependencyBridge.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/PaperweightDependencyBridge.kt @@ -30,7 +30,7 @@ import org.gradle.api.plugins.JavaPlugin abstract class PaperweightDependencyBridge : Plugin { override fun apply(target: Project) { - target.configurations.register(JST_CLASSPATH_CONFIG) { + target.configurations.consumable(JST_CLASSPATH_CONFIG) { attributes { attribute(JST_CLASSPATH_ATTRIBUTE, true) } From a1fb1d2239376ed54c9b20dfaec17e675d576949 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Sat, 25 Apr 2026 22:48:30 +0200 Subject: [PATCH 208/257] another shot at fixing the dep bridge --- .../paperweight/PaperweightDependencyBridge.kt | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/PaperweightDependencyBridge.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/PaperweightDependencyBridge.kt index 016bf2111..39956737e 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/PaperweightDependencyBridge.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/PaperweightDependencyBridge.kt @@ -30,11 +30,18 @@ import org.gradle.api.plugins.JavaPlugin abstract class PaperweightDependencyBridge : Plugin { override fun apply(target: Project) { - target.configurations.consumable(JST_CLASSPATH_CONFIG) { + target.configurations.register(JST_CLASSPATH_CONFIG) { + isCanBeConsumed = true attributes { attribute(JST_CLASSPATH_ATTRIBUTE, true) } - extendsFrom(target.configurations.getByName(JavaPlugin.API_CONFIGURATION_NAME)) + } + target.afterEvaluate { + val api = target.configurations.getByName(JavaPlugin.API_CONFIGURATION_NAME) + + api.allDependencies.forEach { + target.dependencies.add(JST_CLASSPATH_CONFIG, it) + } } } } From e4f4d7b41ba8575e0c4dd663d59023cd3c7e93bc Mon Sep 17 00:00:00 2001 From: Toffikk Date: Sun, 26 Apr 2026 00:39:02 +0200 Subject: [PATCH 209/257] fix: don't fail on clean run --- .../io/papermc/paperweight/PaperweightDependencyBridge.kt | 8 +------- .../io/papermc/paperweight/patcher/PaperweightPatcher.kt | 3 ++- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/PaperweightDependencyBridge.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/PaperweightDependencyBridge.kt index 39956737e..d070f2c9a 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/PaperweightDependencyBridge.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/PaperweightDependencyBridge.kt @@ -35,13 +35,7 @@ abstract class PaperweightDependencyBridge : Plugin { attributes { attribute(JST_CLASSPATH_ATTRIBUTE, true) } - } - target.afterEvaluate { - val api = target.configurations.getByName(JavaPlugin.API_CONFIGURATION_NAME) - - api.allDependencies.forEach { - target.dependencies.add(JST_CLASSPATH_CONFIG, it) - } + extendsFrom(target.configurations.getByName(JavaPlugin.COMPILE_CLASSPATH_CONFIGURATION_NAME)) } } } diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/PaperweightPatcher.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/PaperweightPatcher.kt index 731edfca5..7b781fca0 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/PaperweightPatcher.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/PaperweightPatcher.kt @@ -131,7 +131,8 @@ abstract class PaperweightPatcher : Plugin { description = "Applies all patches defined in the weaver-patcher project and the server project. " + "(equivalent to running '$depend' and then '${tasks.get().single()}' in a second Gradle invocation)" projectDir.set(layout.projectDirectory) - dependsOn(depend) + dependsOn("apply${upstream.name.capitalized()}SingleFilePatches") + finalizedBy(depend) } } patcher.additionalUpstreams.forEach { upstream -> From f294cc719ee27427dc036bc34a82936642c2953b Mon Sep 17 00:00:00 2001 From: Toffikk Date: Sun, 26 Apr 2026 01:04:30 +0200 Subject: [PATCH 210/257] actually fix --- .../paperweight/patcher/PaperweightPatcher.kt | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/PaperweightPatcher.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/PaperweightPatcher.kt index 7b781fca0..8b30569b1 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/PaperweightPatcher.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/PaperweightPatcher.kt @@ -124,15 +124,20 @@ abstract class PaperweightPatcher : Plugin { upstream.directoryPatchSets.names.joinToString(", ") + ", ${upstream.name} single file" ) applyForDownstream { dependsOn("apply${upstream.name.capitalized()}Patches") } + val depend = "apply${upstream.name.capitalized()}Patches" + val depend1 = "apply${upstream.name.capitalized()}SingleFilePatches" + tasks.register("applyAllPatches") { group = "patching" - val depend = "apply${upstream.name.capitalized()}Patches" tasks.addAll("applyAllServerPatches") description = "Applies all patches defined in the weaver-patcher project and the server project. " + - "(equivalent to running '$depend' and then '${tasks.get().single()}' in a second Gradle invocation)" + "(equivalent to running '$depend1' and '$depend', and then ${tasks.get().single()}' in a second Gradle invocation)" projectDir.set(layout.projectDirectory) - dependsOn("apply${upstream.name.capitalized()}SingleFilePatches") - finalizedBy(depend) + dependsOn(depend) + } + + this@afterEvaluate.tasks.named(depend).configure { + dependsOn(depend1) } } patcher.additionalUpstreams.forEach { upstream -> From 21f8fe1f2fecf22788799b265643a763a4f62242 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Sun, 26 Apr 2026 01:49:33 +0200 Subject: [PATCH 211/257] some things --- .../io/papermc/paperweight/patcher/PaperweightPatcher.kt | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/PaperweightPatcher.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/PaperweightPatcher.kt index 8b30569b1..c83f71e2d 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/PaperweightPatcher.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/PaperweightPatcher.kt @@ -124,19 +124,16 @@ abstract class PaperweightPatcher : Plugin { upstream.directoryPatchSets.names.joinToString(", ") + ", ${upstream.name} single file" ) applyForDownstream { dependsOn("apply${upstream.name.capitalized()}Patches") } - val depend = "apply${upstream.name.capitalized()}Patches" - val depend1 = "apply${upstream.name.capitalized()}SingleFilePatches" tasks.register("applyAllPatches") { group = "patching" + val depend = "apply${upstream.name.capitalized()}Patches" + val depend1 = "apply${upstream.name.capitalized()}SingleFilePatches" tasks.addAll("applyAllServerPatches") description = "Applies all patches defined in the weaver-patcher project and the server project. " + "(equivalent to running '$depend1' and '$depend', and then ${tasks.get().single()}' in a second Gradle invocation)" projectDir.set(layout.projectDirectory) dependsOn(depend) - } - - this@afterEvaluate.tasks.named(depend).configure { dependsOn(depend1) } } From 02f70c230f1c3f0228ad426e92bc20bd3f03a7f3 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Tue, 28 Apr 2026 16:24:50 +0200 Subject: [PATCH 212/257] Dirty workaround for upstream ATs --- .../kotlin/io/papermc/paperweight/PaperweightDependencyBridge.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/PaperweightDependencyBridge.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/PaperweightDependencyBridge.kt index d070f2c9a..8a513ee8c 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/PaperweightDependencyBridge.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/PaperweightDependencyBridge.kt @@ -37,5 +37,6 @@ abstract class PaperweightDependencyBridge : Plugin { } extendsFrom(target.configurations.getByName(JavaPlugin.COMPILE_CLASSPATH_CONFIGURATION_NAME)) } + target.rootProject.dependencies.add(JST_CLASSPATH_CONFIG, target) } } From 59d3b81c55db2d830d1dee1cbdb79c7dfab9bf6b Mon Sep 17 00:00:00 2001 From: Toffikk Date: Fri, 1 May 2026 00:02:37 +0200 Subject: [PATCH 213/257] Revert patcher change --- .../io/papermc/paperweight/patcher/PaperweightPatcher.kt | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/PaperweightPatcher.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/PaperweightPatcher.kt index c83f71e2d..4f3e9968d 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/PaperweightPatcher.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/PaperweightPatcher.kt @@ -128,13 +128,11 @@ abstract class PaperweightPatcher : Plugin { tasks.register("applyAllPatches") { group = "patching" val depend = "apply${upstream.name.capitalized()}Patches" - val depend1 = "apply${upstream.name.capitalized()}SingleFilePatches" tasks.addAll("applyAllServerPatches") description = "Applies all patches defined in the weaver-patcher project and the server project. " + - "(equivalent to running '$depend1' and '$depend', and then ${tasks.get().single()}' in a second Gradle invocation)" + "(equivalent to running '$depend' and then ${tasks.get().single()}' in a second Gradle invocation)" projectDir.set(layout.projectDirectory) dependsOn(depend) - dependsOn(depend1) } } patcher.additionalUpstreams.forEach { upstream -> From f790946de7552315e195e6157407b674511c9324 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Fri, 1 May 2026 00:04:17 +0200 Subject: [PATCH 214/257] Version 2.4.2 --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index ebcbe438d..24e583945 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ group = io.canvasmc.weaver -version = 2.4.2-SNAPSHOT +version = 2.4.2 org.gradle.caching = true org.gradle.configuration-cache = true From 95511f03d1702ab00188df2812707fd9ba24e6b7 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Fri, 1 May 2026 01:56:19 +0200 Subject: [PATCH 215/257] chore: continue development cycle --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 24e583945..ef638bd57 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ group = io.canvasmc.weaver -version = 2.4.2 +version = 2.4.3-SNAPSHOT org.gradle.caching = true org.gradle.configuration-cache = true From d96aa2484fc6dec3834d21e1a54efd2e7c35cfa7 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Fri, 1 May 2026 01:56:39 +0200 Subject: [PATCH 216/257] chore: resolve dev bundles from the canvasmc public maven repository --- .../kotlin/io/papermc/paperweight/util/constants/constants.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 3c5e7f728..d6f5538d9 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 @@ -35,7 +35,7 @@ const val MC_LIBRARY_URL = "https://libraries.minecraft.net/" const val MC_MANIFEST_URL = "https://piston-meta.mojang.com/mc/game/version_manifest_v2.json" -const val CANVAS_MAVEN_REPO_URL = "https://maven.canvasmc.io/snapshots" +const val CANVAS_MAVEN_REPO_URL = "https://maven.canvasmc.io/public" const val PAPER_MAVEN_REPO_URL = "https://repo.papermc.io/repository/maven-public/" From 5cef661369b6fdd73e4df9a8f5ffc4cf4576dd21 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Fri, 1 May 2026 18:10:44 +0200 Subject: [PATCH 217/257] Version 2.4.3 --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index ef638bd57..d1392f9d8 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ group = io.canvasmc.weaver -version = 2.4.3-SNAPSHOT +version = 2.4.3 org.gradle.caching = true org.gradle.configuration-cache = true From 5dde1a34b698ca7ee9bcbbb2669fd18e325d9035 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Mon, 4 May 2026 16:53:48 +0200 Subject: [PATCH 218/257] chore: continue development cycle --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index d1392f9d8..3707bb9c8 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ group = io.canvasmc.weaver -version = 2.4.3 +version = 2.4.4-SNAPSHOT org.gradle.caching = true org.gradle.configuration-cache = true From 4532e6d548a66bfcbd9359523883795850c33cb4 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Mon, 4 May 2026 16:54:32 +0200 Subject: [PATCH 219/257] fix: make the patch output flow match paperweight in order to resolve stale data issues --- .../core/taskcontainers/MinecraftPatchingTasks.kt | 4 +--- .../papermc/paperweight/core/taskcontainers/PatchingTasks.kt | 5 +---- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt index eb551692b..a1a3988d8 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt @@ -81,10 +81,8 @@ class MinecraftPatchingTasks( if (readOnly) { input.set(applyBasePatches.flatMap { it.output }) - output.set(outputSrcFile) - } else { - output.set(outputSrc) } + output.set(outputSrc) patches.set(sourcePatchDir.fileExists()) rejectsDir.set(this@MinecraftPatchingTasks.rejectsDir) gitFilePatches.set(this@MinecraftPatchingTasks.gitFilePatches) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt index bb9076f61..599ab060d 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt @@ -71,10 +71,8 @@ class PatchingTasks( if (readOnly) { input.set(applyBasePatches.flatMap { it.output }) - output.set(layout.cache.resolve(paperTaskOutput())) - } else { - output.set(outputDir) } + output.set(outputDir) patches.set(filePatchDir.fileExists()) rejectsDir.set(this@PatchingTasks.rejectsDir) gitFilePatches.set(this@PatchingTasks.gitFilePatches) @@ -91,7 +89,6 @@ class PatchingTasks( } else { output.set(outputDir) } - patches.set(basePatchDir.fileExists()) baseRef.set("base") identifier = "$forkName $patchSetName" From 0aec6f719dd8ff7294d6fdbf77d28fbcdf60f5fa Mon Sep 17 00:00:00 2001 From: Toffikk Date: Thu, 7 May 2026 14:36:29 +0200 Subject: [PATCH 220/257] fix: detach the rebuild worktree --- .../paperweight/core/tasks/patching/RebuildFilePatches.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/RebuildFilePatches.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/RebuildFilePatches.kt index 8fa92f80b..4e3b2ecec 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/RebuildFilePatches.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/RebuildFilePatches.kt @@ -126,7 +126,7 @@ abstract class RebuildFilePatches : JavaLauncherTask() { git("checkout", "file").executeSilently(silenceErr = true) val baseCommit = "file~1" - git("worktree", "add", "--force", baseDir.absolutePathString(), baseCommit).executeSilently(silenceErr = true) + git("worktree", "add", "--detach", "--force", baseDir.absolutePathString(), baseCommit).executeSilently(silenceErr = true) val filesWithNewAts = if (!ats.jst.isEmpty) { handleAts( From deaf030ae4988c832a6ab31309b954f8ee924cd1 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Fri, 15 May 2026 18:37:19 +0200 Subject: [PATCH 221/257] feat: add a base patch fixup task --- .../taskcontainers/MinecraftPatchingTasks.kt | 9 ++ .../core/taskcontainers/PatchingTasks.kt | 10 ++ .../core/tasks/patching/FixupBasePatches.kt | 101 ++++++++++++++++++ 3 files changed, 120 insertions(+) create mode 100644 paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/FixupBasePatches.kt diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt index a1a3988d8..d2e48f605 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt @@ -29,6 +29,7 @@ import io.papermc.paperweight.core.tasks.patching.ApplyBasePatches import io.papermc.paperweight.core.tasks.patching.ApplyFeaturePatches import io.papermc.paperweight.core.tasks.patching.ApplyFilePatches import io.papermc.paperweight.core.tasks.patching.ApplyFilePatchesFuzzy +import io.papermc.paperweight.core.tasks.patching.FixupBasePatches import io.papermc.paperweight.core.tasks.patching.FixupFilePatches import io.papermc.paperweight.core.tasks.patching.RebuildFilePatches import io.papermc.paperweight.core.util.coreExt @@ -290,6 +291,14 @@ class MinecraftPatchingTasks( dependsOn(rebuildBasePatches, rebuildFilePatches, rebuildFeaturePatches) } + val fixupBasePatches = tasks.register("fixup${namePart}BasePatches") { + group() + description = "Puts the latest changes under the $configName Minecraft sources base patches commit" + + repo.set(outputSrc) + identifier = configName + } + val fixupSourcePatches = tasks.register("fixup${namePart}SourcePatches") { group() description = "Puts the currently tracked source changes into the $configName Minecraft sources file patches commit" diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt index 599ab060d..409fdfa2a 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt @@ -27,6 +27,7 @@ import io.papermc.paperweight.core.tasks.patching.ApplyBasePatches import io.papermc.paperweight.core.tasks.patching.ApplyFeaturePatches import io.papermc.paperweight.core.tasks.patching.ApplyFilePatches import io.papermc.paperweight.core.tasks.patching.ApplyFilePatchesFuzzy +import io.papermc.paperweight.core.tasks.patching.FixupBasePatches import io.papermc.paperweight.core.tasks.patching.FixupFilePatches import io.papermc.paperweight.core.tasks.patching.RebuildFilePatches import io.papermc.paperweight.tasks.* @@ -126,6 +127,7 @@ class PatchingTasks( val rebuildBasePatchesName = "rebuild${namePart}BasePatches" val rebuildFilePatchesName = "rebuild${namePart}FilePatches" + val fixupBasePatchesName = "fixup${namePart}BasePatches" val fixupFilePatchesName = "fixup${namePart}FilePatches" val rebuildFeaturePatchesName = "rebuild${namePart}FeaturePatches" val rebuildPatchesName = "rebuild${namePart}Patches" @@ -211,6 +213,14 @@ class PatchingTasks( identifier = "$forkName $patchSetName" } + val fixupBasePatches = tasks.register(fixupBasePatchesName) { + group = taskGroup + description = "Puts the latest changes under the $patchSetName base patches commit" + + repo.set(outputDir) + identifier = "$forkName $patchSetName" + } + val fixupFilePatches = tasks.register(fixupFilePatchesName) { group = taskGroup description = "Puts the currently tracked source changes into the $patchSetName file patches commit" diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/FixupBasePatches.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/FixupBasePatches.kt new file mode 100644 index 000000000..87d5d36d2 --- /dev/null +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/FixupBasePatches.kt @@ -0,0 +1,101 @@ +/* + * paperweight is a Gradle plugin for the PaperMC project. + * + * Copyright (c) 2023 Kyle Wood (DenWav) + * Contributors + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 only, no later versions. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + * USA + */ + +package io.papermc.paperweight.core.tasks.patching + +import io.papermc.paperweight.tasks.* +import io.papermc.paperweight.util.* +import org.gradle.api.file.DirectoryProperty +import org.gradle.api.provider.Property +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.InputDirectory +import org.gradle.api.tasks.Optional +import org.gradle.api.tasks.TaskAction +import org.gradle.api.tasks.UntrackedTask +import org.gradle.api.tasks.options.Option + +@UntrackedTask(because = "Always fixup when requested") +abstract class FixupBasePatches : BaseTask() { + + @get:InputDirectory + abstract val repo: DirectoryProperty + + @get:Input + @get:Optional + @get:Option(option = "message", description = "Commit message") + abstract val message: Property + + @get:Input + abstract val identifier: Property + + @TaskAction + fun run() { + val git = Git(repo) + if (message.isPresent) { + git("add", ".").executeOut() + git("commit", "-m", message.get()).executeOut() + } + val baseCommit = git("rev-parse", "basepatches").getText().trim() + val headCommit = git("rev-parse", "HEAD").getText().trim() + git("branch", "-f", "fixup/basepatches").executeOut() + git("reset", "basepatches~1", "--hard").executeOut() + git("cherry-pick", headCommit).executeOut() + git("cherry-pick", "$baseCommit~1..$headCommit~1", "--keep-redundant-commits").executeOut() + git("switch", "-C", "main", "HEAD").executeOut() + git("branch", "-D", "fixup/basepatches").executeOut() + tagCommits(git) + } + + private fun tagCommits(git: Git) { + val baseCommit = git( + "log", + "--format=%H %s", + "--grep=^${identifier.get()} Base Patches$", + "base..HEAD" + ).getText() + .lineSequence() + .map(String::trim) + .filter(String::isNotEmpty) + .map { it.substringBefore(" ") } + .toList() + + val fileCommit = git( + "log", + "--format=%H %s", + "--grep=^${identifier.get()} File Patches$", + "base..HEAD" + ).getText() + .lineSequence() + .map(String::trim) + .filter(String::isNotEmpty) + .map { it.substringBefore(" ") } + .toList() + + // throw if false, since that means the repo state is corrupted + validateSingleCommit(identifier, "Base", baseCommit) + validateSingleCommit(identifier, "File", fileCommit) + + // retag everything + git("tag", "-f", "basepatches", baseCommit.joinToString()).executeOut() + git("tag", "-f", "file", fileCommit.joinToString()).executeOut() + } +} From 8e7ad564fd9d182781f2162450dd3ff737c8f854 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Fri, 15 May 2026 18:42:20 +0200 Subject: [PATCH 222/257] fix: allow fixing base patches up without a file commit --- .../paperweight/core/tasks/patching/FixupBasePatches.kt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/FixupBasePatches.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/FixupBasePatches.kt index 87d5d36d2..5b59b470d 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/FixupBasePatches.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/FixupBasePatches.kt @@ -92,10 +92,12 @@ abstract class FixupBasePatches : BaseTask() { // throw if false, since that means the repo state is corrupted validateSingleCommit(identifier, "Base", baseCommit) - validateSingleCommit(identifier, "File", fileCommit) + validateSingleOrNullCommit(identifier, "File", fileCommit) // retag everything git("tag", "-f", "basepatches", baseCommit.joinToString()).executeOut() - git("tag", "-f", "file", fileCommit.joinToString()).executeOut() + if (fileCommit.size == 1) { + git("tag", "-f", "file", fileCommit.joinToString()).executeOut() + } } } From 8af0bf0fcb7e2b2ba948f17d53482234a0bc394c Mon Sep 17 00:00:00 2001 From: Toffikk Date: Fri, 15 May 2026 18:55:05 +0200 Subject: [PATCH 223/257] feat: allow specifying commit descriptions too --- .../paperweight/core/tasks/patching/FixupBasePatches.kt | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/FixupBasePatches.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/FixupBasePatches.kt index 5b59b470d..475de4fda 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/FixupBasePatches.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/FixupBasePatches.kt @@ -44,6 +44,11 @@ abstract class FixupBasePatches : BaseTask() { @get:Option(option = "message", description = "Commit message") abstract val message: Property + @get:Input + @get:Optional + @get:Option(option = "description", description = "Commit description") + abstract val description: Property + @get:Input abstract val identifier: Property @@ -51,8 +56,9 @@ abstract class FixupBasePatches : BaseTask() { fun run() { val git = Git(repo) if (message.isPresent) { + val additionalArgs = if (description.isPresent) arrayOf("-m", description.get()) else arrayOf() git("add", ".").executeOut() - git("commit", "-m", message.get()).executeOut() + git("commit", "-m", message.get(), *additionalArgs).executeOut() } val baseCommit = git("rev-parse", "basepatches").getText().trim() val headCommit = git("rev-parse", "HEAD").getText().trim() From 031f88fa3cebd236a519caeed1e49ddf582554d3 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Fri, 15 May 2026 21:14:14 +0200 Subject: [PATCH 224/257] feat: allow for fixing up already existing base patches --- .../taskcontainers/MinecraftPatchingTasks.kt | 14 +++- .../core/taskcontainers/PatchingTasks.kt | 19 ++++- ...FixupBasePatches.kt => CreateBasePatch.kt} | 2 +- .../core/tasks/patching/FixupBasePatch.kt | 84 +++++++++++++++++++ 4 files changed, 113 insertions(+), 6 deletions(-) rename paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/{FixupBasePatches.kt => CreateBasePatch.kt} (98%) create mode 100644 paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/FixupBasePatch.kt diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt index d2e48f605..ffb738923 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt @@ -29,7 +29,8 @@ import io.papermc.paperweight.core.tasks.patching.ApplyBasePatches import io.papermc.paperweight.core.tasks.patching.ApplyFeaturePatches import io.papermc.paperweight.core.tasks.patching.ApplyFilePatches import io.papermc.paperweight.core.tasks.patching.ApplyFilePatchesFuzzy -import io.papermc.paperweight.core.tasks.patching.FixupBasePatches +import io.papermc.paperweight.core.tasks.patching.CreateBasePatch +import io.papermc.paperweight.core.tasks.patching.FixupBasePatch import io.papermc.paperweight.core.tasks.patching.FixupFilePatches import io.papermc.paperweight.core.tasks.patching.RebuildFilePatches import io.papermc.paperweight.core.util.coreExt @@ -291,7 +292,7 @@ class MinecraftPatchingTasks( dependsOn(rebuildBasePatches, rebuildFilePatches, rebuildFeaturePatches) } - val fixupBasePatches = tasks.register("fixup${namePart}BasePatches") { + val createBasePatch = tasks.register("create${namePart}BasePatch") { group() description = "Puts the latest changes under the $configName Minecraft sources base patches commit" @@ -299,6 +300,15 @@ class MinecraftPatchingTasks( identifier = configName } + val fixupBasePatches = tasks.register("fixup${namePart}BasePatch") { + group() + description = "Puts the currently tracked source changes into the specified patch" + + repo.set(outputSrc) + patches.set(basePatchDir) + upstream.set("upstream/main") + } + val fixupSourcePatches = tasks.register("fixup${namePart}SourcePatches") { group() description = "Puts the currently tracked source changes into the $configName Minecraft sources file patches commit" diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt index 409fdfa2a..d33556575 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt @@ -27,7 +27,8 @@ import io.papermc.paperweight.core.tasks.patching.ApplyBasePatches import io.papermc.paperweight.core.tasks.patching.ApplyFeaturePatches import io.papermc.paperweight.core.tasks.patching.ApplyFilePatches import io.papermc.paperweight.core.tasks.patching.ApplyFilePatchesFuzzy -import io.papermc.paperweight.core.tasks.patching.FixupBasePatches +import io.papermc.paperweight.core.tasks.patching.CreateBasePatch +import io.papermc.paperweight.core.tasks.patching.FixupBasePatch import io.papermc.paperweight.core.tasks.patching.FixupFilePatches import io.papermc.paperweight.core.tasks.patching.RebuildFilePatches import io.papermc.paperweight.tasks.* @@ -35,6 +36,7 @@ import io.papermc.paperweight.util.* import io.papermc.paperweight.util.constants.JST_CLASSPATH_CONFIG import io.papermc.paperweight.util.constants.JST_CONFIG import io.papermc.paperweight.util.constants.paperTaskOutput +import io.papermc.paperweight.util.set import java.nio.file.Path import org.gradle.api.Project import org.gradle.api.Task @@ -44,6 +46,7 @@ import org.gradle.api.file.RegularFileProperty import org.gradle.api.provider.Provider import org.gradle.api.tasks.TaskContainer import org.gradle.kotlin.dsl.* +import org.gradle.kotlin.dsl.register class PatchingTasks( private val project: Project, @@ -127,7 +130,8 @@ class PatchingTasks( val rebuildBasePatchesName = "rebuild${namePart}BasePatches" val rebuildFilePatchesName = "rebuild${namePart}FilePatches" - val fixupBasePatchesName = "fixup${namePart}BasePatches" + val createBasePatchName = "create${namePart}BasePatch" + val fixupBasePatchName = "fixup${namePart}BasePatch" val fixupFilePatchesName = "fixup${namePart}FilePatches" val rebuildFeaturePatchesName = "rebuild${namePart}FeaturePatches" val rebuildPatchesName = "rebuild${namePart}Patches" @@ -213,7 +217,7 @@ class PatchingTasks( identifier = "$forkName $patchSetName" } - val fixupBasePatches = tasks.register(fixupBasePatchesName) { + val createBasePatch = tasks.register(createBasePatchName) { group = taskGroup description = "Puts the latest changes under the $patchSetName base patches commit" @@ -221,6 +225,15 @@ class PatchingTasks( identifier = "$forkName $patchSetName" } + val fixupBasePatches = tasks.register(fixupBasePatchName) { + group = taskGroup + description = "Puts the currently tracked source changes into the specified patch" + + repo.set(outputDir) + patches.set(basePatchDir) + upstream.set("basepatches") + } + val fixupFilePatches = tasks.register(fixupFilePatchesName) { group = taskGroup description = "Puts the currently tracked source changes into the $patchSetName file patches commit" diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/FixupBasePatches.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/CreateBasePatch.kt similarity index 98% rename from paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/FixupBasePatches.kt rename to paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/CreateBasePatch.kt index 475de4fda..a5e7dbf7a 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/FixupBasePatches.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/CreateBasePatch.kt @@ -34,7 +34,7 @@ import org.gradle.api.tasks.UntrackedTask import org.gradle.api.tasks.options.Option @UntrackedTask(because = "Always fixup when requested") -abstract class FixupBasePatches : BaseTask() { +abstract class CreateBasePatch : BaseTask() { @get:InputDirectory abstract val repo: DirectoryProperty diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/FixupBasePatch.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/FixupBasePatch.kt new file mode 100644 index 000000000..0d2ed7872 --- /dev/null +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/FixupBasePatch.kt @@ -0,0 +1,84 @@ +/* + * paperweight is a Gradle plugin for the PaperMC project. + * + * Copyright (c) 2023 Kyle Wood (DenWav) + * Contributors + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 only, no later versions. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + * USA + */ + +package io.papermc.paperweight.core.tasks.patching + +import io.papermc.paperweight.tasks.* +import io.papermc.paperweight.util.* +import kotlin.io.path.listDirectoryEntries +import org.gradle.api.file.DirectoryProperty +import org.gradle.api.provider.Property +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.InputDirectory +import org.gradle.api.tasks.Optional +import org.gradle.api.tasks.TaskAction +import org.gradle.api.tasks.UntrackedTask +import org.gradle.api.tasks.options.Option + +@UntrackedTask(because = "Always fixup when requested") +abstract class FixupBasePatch : BaseTask() { + + @get:InputDirectory + abstract val repo: DirectoryProperty + + @get:Input + abstract val upstream: Property + + @get:InputDirectory + abstract val patches: DirectoryProperty + + @get:Input + @get:Optional + @get:Option(option = "patch-number", description = "Select the patch to modify") + abstract val patchNumber: Property + + @TaskAction + fun run() { + val git = Git(repo) + var index = 0 + if (patchNumber.isPresent) { + index = patchNumber.get().minus(1) + } else { + logger.lifecycle("===============================================") + logger.lifecycle("Please enter the patch number into which the current changes should be merged") + logger.lifecycle("===============================================") + val patches = patches.get().path.listDirectoryEntries("*.patch").toList().sortedBy { it.fileName.toString().substringBefore("-").toInt() } + logger.lifecycle("Possible patches:") + for (patch in patches) { + logger.lifecycle(patch.fileName.toString()) + } + logger.lifecycle("===============================================") + while (index == 0) { + index = System.`in`.bufferedReader().readLine().toInt().minus(1) + } + } + val commits = git("rev-list", "base..basepatches").getText().trim().lines().filter { it.isNotBlank() }.reversed() + if (index < 0 || index >= commits.size) { + error("Patch index out of range: $index (size=${commits.size})") + } + val selectedCommit = commits[index] + logger.lifecycle("Selected commit: $selectedCommit") + git("add", ".").executeOut() + git("commit", "--fixup", selectedCommit).executeOut() + git("-c", "sequence.editor=:", "rebase", "-i", "--autosquash", upstream.get()).executeOut() + } +} From f03c6a5d535db65152aa9fc25c303292d5c2eabf Mon Sep 17 00:00:00 2001 From: Toffikk Date: Sat, 16 May 2026 15:37:59 +0200 Subject: [PATCH 225/257] feat: allow fixup to work with failed applies --- .../papermc/paperweight/core/tasks/patching/FixupBasePatch.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/FixupBasePatch.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/FixupBasePatch.kt index 0d2ed7872..f64d0541e 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/FixupBasePatch.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/FixupBasePatch.kt @@ -71,7 +71,9 @@ abstract class FixupBasePatch : BaseTask() { index = System.`in`.bufferedReader().readLine().toInt().minus(1) } } - val commits = git("rev-list", "base..basepatches").getText().trim().lines().filter { it.isNotBlank() }.reversed() + val hasBasePatchesTag = git("rev-parse", "-q", "--verify", "basepatches").getText() + val headTag = if (hasBasePatchesTag.isNotEmpty()) "basepatches" else "HEAD" + val commits = git("rev-list", "base..$headTag").getText().trim().lines().filter { it.isNotBlank() }.reversed() if (index < 0 || index >= commits.size) { error("Patch index out of range: $index (size=${commits.size})") } From 5e318713d33e48b55512efeb65d108ff6d05b488 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Sat, 16 May 2026 15:42:29 +0200 Subject: [PATCH 226/257] chore: rename the base patches fixup task --- .../core/taskcontainers/MinecraftPatchingTasks.kt | 6 +++--- .../paperweight/core/taskcontainers/PatchingTasks.kt | 8 ++++---- .../patching/{FixupBasePatch.kt => FixupBasePatches.kt} | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) rename paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/{FixupBasePatch.kt => FixupBasePatches.kt} (98%) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt index ffb738923..4e54653d1 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt @@ -30,7 +30,7 @@ import io.papermc.paperweight.core.tasks.patching.ApplyFeaturePatches import io.papermc.paperweight.core.tasks.patching.ApplyFilePatches import io.papermc.paperweight.core.tasks.patching.ApplyFilePatchesFuzzy import io.papermc.paperweight.core.tasks.patching.CreateBasePatch -import io.papermc.paperweight.core.tasks.patching.FixupBasePatch +import io.papermc.paperweight.core.tasks.patching.FixupBasePatches import io.papermc.paperweight.core.tasks.patching.FixupFilePatches import io.papermc.paperweight.core.tasks.patching.RebuildFilePatches import io.papermc.paperweight.core.util.coreExt @@ -300,9 +300,9 @@ class MinecraftPatchingTasks( identifier = configName } - val fixupBasePatches = tasks.register("fixup${namePart}BasePatch") { + val fixupBasePatches = tasks.register("fixup${namePart}BasePatches") { group() - description = "Puts the currently tracked source changes into the specified patch" + description = "Puts the currently tracked source changes into the specified patch commit" repo.set(outputSrc) patches.set(basePatchDir) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt index d33556575..b8f428028 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt @@ -28,7 +28,7 @@ import io.papermc.paperweight.core.tasks.patching.ApplyFeaturePatches import io.papermc.paperweight.core.tasks.patching.ApplyFilePatches import io.papermc.paperweight.core.tasks.patching.ApplyFilePatchesFuzzy import io.papermc.paperweight.core.tasks.patching.CreateBasePatch -import io.papermc.paperweight.core.tasks.patching.FixupBasePatch +import io.papermc.paperweight.core.tasks.patching.FixupBasePatches import io.papermc.paperweight.core.tasks.patching.FixupFilePatches import io.papermc.paperweight.core.tasks.patching.RebuildFilePatches import io.papermc.paperweight.tasks.* @@ -131,7 +131,7 @@ class PatchingTasks( val rebuildBasePatchesName = "rebuild${namePart}BasePatches" val rebuildFilePatchesName = "rebuild${namePart}FilePatches" val createBasePatchName = "create${namePart}BasePatch" - val fixupBasePatchName = "fixup${namePart}BasePatch" + val fixupBasePatchesName = "fixup${namePart}BasePatches" val fixupFilePatchesName = "fixup${namePart}FilePatches" val rebuildFeaturePatchesName = "rebuild${namePart}FeaturePatches" val rebuildPatchesName = "rebuild${namePart}Patches" @@ -225,9 +225,9 @@ class PatchingTasks( identifier = "$forkName $patchSetName" } - val fixupBasePatches = tasks.register(fixupBasePatchName) { + val fixupBasePatches = tasks.register(fixupBasePatchesName) { group = taskGroup - description = "Puts the currently tracked source changes into the specified patch" + description = "Puts the currently tracked source changes into the specified patch commit" repo.set(outputDir) patches.set(basePatchDir) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/FixupBasePatch.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/FixupBasePatches.kt similarity index 98% rename from paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/FixupBasePatch.kt rename to paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/FixupBasePatches.kt index f64d0541e..d99bde8e8 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/FixupBasePatch.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/FixupBasePatches.kt @@ -35,7 +35,7 @@ import org.gradle.api.tasks.UntrackedTask import org.gradle.api.tasks.options.Option @UntrackedTask(because = "Always fixup when requested") -abstract class FixupBasePatch : BaseTask() { +abstract class FixupBasePatches : BaseTask() { @get:InputDirectory abstract val repo: DirectoryProperty From 6a6741c55246cb2a236fddc0fc55c78ad18be46e Mon Sep 17 00:00:00 2001 From: Toffikk Date: Sat, 16 May 2026 20:02:31 +0200 Subject: [PATCH 227/257] feat: add a fixup feature patches task --- .../taskcontainers/MinecraftPatchingTasks.kt | 10 +++ .../core/taskcontainers/PatchingTasks.kt | 11 +++ .../tasks/patching/FixupFeaturePatches.kt | 83 +++++++++++++++++++ 3 files changed, 104 insertions(+) create mode 100644 paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/FixupFeaturePatches.kt diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt index 8de9acb5a..d05118bd5 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt @@ -28,6 +28,7 @@ import io.papermc.paperweight.core.tasks.SetupForkMinecraftSources import io.papermc.paperweight.core.tasks.patching.ApplyFeaturePatches import io.papermc.paperweight.core.tasks.patching.ApplyFilePatches import io.papermc.paperweight.core.tasks.patching.ApplyFilePatchesFuzzy +import io.papermc.paperweight.core.tasks.patching.FixupFeaturePatches import io.papermc.paperweight.core.tasks.patching.FixupFilePatches import io.papermc.paperweight.core.tasks.patching.RebuildFilePatches import io.papermc.paperweight.tasks.* @@ -263,6 +264,15 @@ class MinecraftPatchingTasks( upstream.set("upstream/main") } + val fixupFeaturePatches = tasks.register("fixup${namePart}FeaturePatches") { + group() + description = "Puts the currently tracked source changes into the specified $configName Minecraft feature patch commit" + + repo.set(outputSrc) + upstream.set("upstream/main") + patches.set(featurePatchDir) + } + val applyOrMoveSourcePatches = tasks.register("applyOrMove${namePart}SourcePatches") { configureApplyFilePatches() description = "Applies $configName file patches to the Minecraft sources as Git patches, moving any failed patches to the rejects dir. " + diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt index d9cf03739..2e58bde5c 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt @@ -25,6 +25,7 @@ package io.papermc.paperweight.core.taskcontainers import io.papermc.paperweight.core.tasks.patching.ApplyFeaturePatches import io.papermc.paperweight.core.tasks.patching.ApplyFilePatches import io.papermc.paperweight.core.tasks.patching.ApplyFilePatchesFuzzy +import io.papermc.paperweight.core.tasks.patching.FixupFeaturePatches import io.papermc.paperweight.core.tasks.patching.FixupFilePatches import io.papermc.paperweight.core.tasks.patching.RebuildFilePatches import io.papermc.paperweight.tasks.* @@ -101,6 +102,7 @@ class PatchingTasks( val rebuildFilePatchesName = "rebuild${namePart}FilePatches" val fixupFilePatchesName = "fixup${namePart}FilePatches" + val fixupFeaturePatchesName = "fixup${namePart}FeaturePatches" val rebuildFeaturePatchesName = "rebuild${namePart}FeaturePatches" val rebuildPatchesName = "rebuild${namePart}Patches" @@ -139,6 +141,15 @@ class PatchingTasks( upstream.set("base") } + val fixupFeaturePatches = tasks.register(fixupFeaturePatchesName) { + group = taskGroup + description = "Puts the currently tracked source changes into the specified $patchSetName feature patch commit" + + repo.set(outputDir) + upstream.set("base") + patches.set(featurePatchDir) + } + val rebuildFeaturePatches = tasks.register(rebuildFeaturePatchesName) { group = taskGroup description = "Rebuilds $patchSetName feature patches" diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/FixupFeaturePatches.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/FixupFeaturePatches.kt new file mode 100644 index 000000000..78bd04bf3 --- /dev/null +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/FixupFeaturePatches.kt @@ -0,0 +1,83 @@ +/* + * paperweight is a Gradle plugin for the PaperMC project. + * + * Copyright (c) 2023 Kyle Wood (DenWav) + * Contributors + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 only, no later versions. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + * USA + */ + +package io.papermc.paperweight.core.tasks.patching + +import io.papermc.paperweight.tasks.* +import io.papermc.paperweight.util.* +import kotlin.io.path.listDirectoryEntries +import org.gradle.api.file.DirectoryProperty +import org.gradle.api.provider.Property +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.InputDirectory +import org.gradle.api.tasks.Optional +import org.gradle.api.tasks.TaskAction +import org.gradle.api.tasks.UntrackedTask +import org.gradle.api.tasks.options.Option + +@UntrackedTask(because = "Always fixup when requested") +abstract class FixupFeaturePatches : BaseTask() { + + @get:InputDirectory + abstract val repo: DirectoryProperty + + @get:Input + abstract val upstream: Property + + @get:InputDirectory + abstract val patches: DirectoryProperty + + @get:Input + @get:Optional + @get:Option(option = "patch-number", description = "Select the patch to modify") + abstract val patchNumber: Property + + @TaskAction + fun run() { + val git = Git(repo) + var index = 0 + if (patchNumber.isPresent) { + index = patchNumber.get() - 1 // -1 as the commits index starts from 0 whereas patches start from 1 + } else { + logger.lifecycle("===============================================") + logger.lifecycle("Please enter the patch number into which the current changes should be merged") + logger.lifecycle("===============================================") + val patches = patches.get().path.listDirectoryEntries("*.patch").toList().sortedBy { it.fileName.toString().substringBefore("-").toInt() } + logger.lifecycle("Possible patches:") + for (patch in patches) { + logger.lifecycle(patch.fileName.toString()) + } + logger.lifecycle("===============================================") + while (index == 0) { + index = System.`in`.bufferedReader().readLine().toInt() - 1 + } + } + val commits = git("rev-list", "file..HEAD").getText().trim().lines().filter { it.isNotBlank() }.reversed() + if (index < 0 || index >= commits.size) { + error("Patch index out of range: $index (size=${commits.size})") + } + val selectedCommit = commits[index] + git("add", ".").executeOut() + git("commit", "--fixup", selectedCommit).executeOut() + git("-c", "sequence.editor=:", "rebase", "-i", "--autosquash", upstream.get()).executeOut() + } +} From aef80bee56353e4b26b6e53beb09eaddb8dee648 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Sat, 16 May 2026 20:21:41 +0200 Subject: [PATCH 228/257] Use gradle's lifecycle logger wherever applicable This is done for consistency between different tasks and so that CLI log level args such as --quiet get respected. --- .../paperweight/core/tasks/SetupForkMinecraftSources.kt | 2 +- .../paperweight/core/tasks/SetupMinecraftSources.kt | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupForkMinecraftSources.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupForkMinecraftSources.kt index 673a0b94d..eb449d732 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupForkMinecraftSources.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupForkMinecraftSources.kt @@ -79,7 +79,7 @@ abstract class SetupForkMinecraftSources : JavaLauncherTask() { val git = Git.open(outputDir.path.toFile()) if (atFile.isPresent && atFile.path.readText().isNotBlank()) { - println("Applying access transformers...") + logger.lifecycle("Applying access transformers...") ats.run( launcher.get(), inputDir.path, diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupMinecraftSources.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupMinecraftSources.kt index 70110a9c3..512a00964 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupMinecraftSources.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/SetupMinecraftSources.kt @@ -123,7 +123,7 @@ abstract class SetupMinecraftSources : JavaLauncherZippedTask() { git.tag().setName("ROOT").setTagger(rootIdent).setSigned(false).call() } - println("Copy initial sources...") + logger.lifecycle("Copy initial sources...") inputFile.path.openZip().use { inputFileFs -> inputFileFs.walkSequence() .filter(predicate.get()::test) @@ -144,13 +144,13 @@ abstract class SetupMinecraftSources : JavaLauncherZippedTask() { } } - println("Setup git repo...") + logger.lifecycle("Setup git repo...") if (!oldPaperCommit.isPresent) { commitAndTag(git, "Vanilla") } if (!mache.isEmpty) { - println("Applying mache patches...") + logger.lifecycle("Applying mache patches...") val result = PatchOperation.builder() .logTo(LoggingOutputStream(logger, LogLevel.LIFECYCLE)) @@ -175,7 +175,7 @@ abstract class SetupMinecraftSources : JavaLauncherZippedTask() { } if (atFile.isPresent) { - println("Applying access transformers...") + logger.lifecycle("Applying access transformers...") ats.run( launcher.get(), outputPath, From 1457ded44dc46bfe0eca8ac8466f23e0f9508d0a Mon Sep 17 00:00:00 2001 From: Toffikk Date: Sat, 16 May 2026 20:27:19 +0200 Subject: [PATCH 229/257] chore: remove debug log line --- .../papermc/paperweight/core/tasks/patching/FixupBasePatches.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/FixupBasePatches.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/FixupBasePatches.kt index 3a80382a8..d598de009 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/FixupBasePatches.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/FixupBasePatches.kt @@ -78,7 +78,6 @@ abstract class FixupBasePatches : BaseTask() { error("Patch index out of range: $index (size=${commits.size})") } val selectedCommit = commits[index] - logger.lifecycle("Selected commit: $selectedCommit") git("add", ".").executeOut() git("commit", "--fixup", selectedCommit).executeOut() git("-c", "sequence.editor=:", "rebase", "-i", "--autosquash", upstream.get()).executeOut() From b5701a6a1b8f4a127c191e895d8521403a248a55 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Sat, 16 May 2026 22:05:02 +0200 Subject: [PATCH 230/257] use int min value --- .../paperweight/core/tasks/patching/FixupBasePatches.kt | 4 ++-- .../paperweight/core/tasks/patching/FixupFeaturePatches.kt | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/FixupBasePatches.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/FixupBasePatches.kt index d598de009..09e89022b 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/FixupBasePatches.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/FixupBasePatches.kt @@ -54,7 +54,7 @@ abstract class FixupBasePatches : BaseTask() { @TaskAction fun run() { val git = Git(repo) - var index = -1 + var index = Int.MIN_VALUE if (patchNumber.isPresent) { index = patchNumber.get().minus(1) } else { @@ -67,7 +67,7 @@ abstract class FixupBasePatches : BaseTask() { logger.lifecycle(patch.fileName.toString()) } logger.lifecycle("===============================================") - while (index == -1) { + while (index == Int.MIN_VALUE) { index = System.`in`.bufferedReader().readLine().toInt().minus(1) } } diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/FixupFeaturePatches.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/FixupFeaturePatches.kt index 9e5bc3943..6567d595c 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/FixupFeaturePatches.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/FixupFeaturePatches.kt @@ -54,7 +54,7 @@ abstract class FixupFeaturePatches : BaseTask() { @TaskAction fun run() { val git = Git(repo) - var index = -1 + var index = Int.MIN_VALUE if (patchNumber.isPresent) { index = patchNumber.get() - 1 // -1 as the commits index starts from 0 whereas patches start from 1 } else { @@ -67,7 +67,7 @@ abstract class FixupFeaturePatches : BaseTask() { logger.lifecycle(patch.fileName.toString()) } logger.lifecycle("===============================================") - while (index == -1) { + while (index == Int.MIN_VALUE) { index = System.`in`.bufferedReader().readLine().toInt() - 1 } } From ecdd01f65f2b3edf74d5049054dd1476a398fd3b Mon Sep 17 00:00:00 2001 From: Toffikk Date: Sun, 17 May 2026 22:50:13 +0200 Subject: [PATCH 231/257] fix: set correct upstreams for fixup tasks --- .../core/taskcontainers/MinecraftPatchingTasks.kt | 4 ++-- .../paperweight/core/taskcontainers/PatchingTasks.kt | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt index 16410c8b1..feeb7add4 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt @@ -315,7 +315,7 @@ class MinecraftPatchingTasks( description = "Puts the currently tracked source changes into the $configName Minecraft sources file patches commit" repo.set(outputSrc) - upstream.set("upstream/main") + upstream.set("basepatches") } val fixupResourcePatches = tasks.register("fixup${namePart}ResourcePatches") { @@ -331,7 +331,7 @@ class MinecraftPatchingTasks( description = "Puts the currently tracked source changes into the specified $configName Minecraft feature patch commit" repo.set(outputSrc) - upstream.set("upstream/main") + upstream.set("file") patches.set(featurePatchDir) } diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt index 444c3398e..6679bab37 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt @@ -229,11 +229,11 @@ class PatchingTasks( val fixupBasePatches = tasks.register(fixupBasePatchesName) { group = taskGroup - description = "Puts the currently tracked source changes into the specified $patchSetName feature patch commit" + description = "Puts the currently tracked source changes into the specified $patchSetName base patch commit" repo.set(outputDir) patches.set(basePatchDir) - upstream.set("basepatches") + upstream.set("base") } val fixupFilePatches = tasks.register(fixupFilePatchesName) { @@ -249,7 +249,7 @@ class PatchingTasks( description = "Puts the currently tracked source changes into the specified $patchSetName feature patch commit" repo.set(outputDir) - upstream.set("base") + upstream.set("file") patches.set(featurePatchDir) } From c5f601cccfadc787615ab44f8403a24dfbf6554e Mon Sep 17 00:00:00 2001 From: Toffikk Date: Sun, 17 May 2026 23:06:44 +0200 Subject: [PATCH 232/257] fix: properly retag in base fixups --- .../taskcontainers/MinecraftPatchingTasks.kt | 1 + .../core/taskcontainers/PatchingTasks.kt | 1 + .../core/tasks/patching/CreateBasePatch.kt | 20 ++-------------- .../core/tasks/patching/FixupBasePatches.kt | 24 +++++++++++++++++++ 4 files changed, 28 insertions(+), 18 deletions(-) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt index feeb7add4..6605da21c 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt @@ -308,6 +308,7 @@ class MinecraftPatchingTasks( repo.set(outputSrc) patches.set(basePatchDir) upstream.set("upstream/main") + identifier = configName } val fixupSourcePatches = tasks.register("fixup${namePart}SourcePatches") { diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt index 6679bab37..4f659f341 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt @@ -234,6 +234,7 @@ class PatchingTasks( repo.set(outputDir) patches.set(basePatchDir) upstream.set("base") + identifier = "$forkName $patchSetName" } val fixupFilePatches = tasks.register(fixupFilePatchesName) { diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/CreateBasePatch.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/CreateBasePatch.kt index a5e7dbf7a..4d62af648 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/CreateBasePatch.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/CreateBasePatch.kt @@ -71,7 +71,7 @@ abstract class CreateBasePatch : BaseTask() { tagCommits(git) } - private fun tagCommits(git: Git) { + fun tagCommits(git: Git) { val baseCommit = git( "log", "--format=%H %s", @@ -84,26 +84,10 @@ abstract class CreateBasePatch : BaseTask() { .map { it.substringBefore(" ") } .toList() - val fileCommit = git( - "log", - "--format=%H %s", - "--grep=^${identifier.get()} File Patches$", - "base..HEAD" - ).getText() - .lineSequence() - .map(String::trim) - .filter(String::isNotEmpty) - .map { it.substringBefore(" ") } - .toList() - // throw if false, since that means the repo state is corrupted validateSingleCommit(identifier, "Base", baseCommit) - validateSingleOrNullCommit(identifier, "File", fileCommit) - // retag everything + // retag git("tag", "-f", "basepatches", baseCommit.joinToString()).executeOut() - if (fileCommit.size == 1) { - git("tag", "-f", "file", fileCommit.joinToString()).executeOut() - } } } diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/FixupBasePatches.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/FixupBasePatches.kt index 09e89022b..dc60e39f2 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/FixupBasePatches.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/FixupBasePatches.kt @@ -46,6 +46,9 @@ abstract class FixupBasePatches : BaseTask() { @get:InputDirectory abstract val patches: DirectoryProperty + @get:Input + abstract val identifier: Property + @get:Input @get:Optional @get:Option(option = "patch-number", description = "Select the patch to modify") @@ -81,5 +84,26 @@ abstract class FixupBasePatches : BaseTask() { git("add", ".").executeOut() git("commit", "--fixup", selectedCommit).executeOut() git("-c", "sequence.editor=:", "rebase", "-i", "--autosquash", upstream.get()).executeOut() + tagCommits(git) + } + + fun tagCommits(git: Git) { + val baseCommit = git( + "log", + "--format=%H %s", + "--grep=^${identifier.get()} Base Patches$", + "base..HEAD" + ).getText() + .lineSequence() + .map(String::trim) + .filter(String::isNotEmpty) + .map { it.substringBefore(" ") } + .toList() + + // throw if false, since that means the repo state is corrupted + validateSingleCommit(identifier, "Base", baseCommit) + + // retag + git("tag", "-f", "basepatches", baseCommit.joinToString()).executeOut() } } From c6d756699b42d054ca93b08e5e0aafdcfa7cc692 Mon Sep 17 00:00:00 2001 From: "tofik.07" Date: Mon, 18 May 2026 18:34:20 +0200 Subject: [PATCH 233/257] fix: resolve CreateBasePatch task creation issue --- .../paperweight/core/tasks/patching/CreateBasePatch.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/CreateBasePatch.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/CreateBasePatch.kt index 4d62af648..df17cf2ff 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/CreateBasePatch.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/CreateBasePatch.kt @@ -47,7 +47,7 @@ abstract class CreateBasePatch : BaseTask() { @get:Input @get:Optional @get:Option(option = "description", description = "Commit description") - abstract val description: Property + abstract val commitDescription: Property @get:Input abstract val identifier: Property @@ -56,7 +56,7 @@ abstract class CreateBasePatch : BaseTask() { fun run() { val git = Git(repo) if (message.isPresent) { - val additionalArgs = if (description.isPresent) arrayOf("-m", description.get()) else arrayOf() + val additionalArgs = if (commitDescription.isPresent) arrayOf("-m", commitDescription.get()) else arrayOf() git("add", ".").executeOut() git("commit", "-m", message.get(), *additionalArgs).executeOut() } From c0f5cdaa05d6bce309f2afe6f4d9ee6d41e5c77e Mon Sep 17 00:00:00 2001 From: Toffikk Date: Mon, 18 May 2026 18:40:02 +0200 Subject: [PATCH 234/257] try to fix tests --- .../src/test/resources/functional_test/test-server/build.gradle | 1 + 1 file changed, 1 insertion(+) diff --git a/paperweight-core/src/test/resources/functional_test/test-server/build.gradle b/paperweight-core/src/test/resources/functional_test/test-server/build.gradle index fa9919787..19bf3b4cc 100644 --- a/paperweight-core/src/test/resources/functional_test/test-server/build.gradle +++ b/paperweight-core/src/test/resources/functional_test/test-server/build.gradle @@ -28,6 +28,7 @@ dependencies { } paperweight { + validateATs = false if (fake) { // use fake mojang data for testing minecraftVersion = 'fake' From 488c6548aa399dd03badcefeaa6336118435b333 Mon Sep 17 00:00:00 2001 From: "tofik.07" Date: Mon, 18 May 2026 18:48:37 +0200 Subject: [PATCH 235/257] chore: correct the dependency notation in libs.versions.toml --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 6cccbf2ac..a40cda600 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -39,7 +39,7 @@ diffpatch = "io.codechicken:DiffPatch:2.1.0.43" serialize-core = { module = "org.jetbrains.kotlinx:kotlinx-serialization-core", version.ref = "serialize" } serialize-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "serialize" } -jst = { module = "io.canvasmc.jst:jst-cli-bundle", version.ref = "jst" } +jst = { module = "io.papermc.jst:jst-cli-bundle", version.ref = "jst" } # jst = { module = "net.neoforged.jst:jst-cli-bundle", version.ref = "jst" } tinyRemapper = { module = "net.fabricmc:tiny-remapper", version.ref = "tinyRemapper" } From 59235d22ddf40c6a0c06798b516a7ee00764cccb Mon Sep 17 00:00:00 2001 From: Toffikk Date: Mon, 18 May 2026 18:49:58 +0200 Subject: [PATCH 236/257] Version 2.4.4 --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 3707bb9c8..19be3a148 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ group = io.canvasmc.weaver -version = 2.4.4-SNAPSHOT +version = 2.4.4 org.gradle.caching = true org.gradle.configuration-cache = true From 5cbd118430bd3ca831bf8b99c98a2f9a096345d0 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Wed, 10 Jun 2026 20:29:49 +0200 Subject: [PATCH 237/257] feat(rebuild): Detach rebuild tasks from the apply process and removed deprecated properties We are able to do that as we don't operate on directories but on commits due to the base patch logic, testing on Canvas 26.1.2 showed a reduction in rebuilding all server patches on canvas of a minute (1m+ -> 3s) on my machine --- gradle.properties | 2 +- .../taskcontainers/MinecraftPatchingTasks.kt | 12 ++-- .../core/taskcontainers/PatchingTasks.kt | 11 ++-- .../core/tasks/patching/ApplyBasePatches.kt | 61 ++----------------- .../tasks/patching/ApplyFeaturePatches.kt | 61 ++----------------- .../core/tasks/patching/RebuildFilePatches.kt | 4 -- .../paperweight/GitMutationLockService.kt | 28 +++++++++ .../tasks/RebuildBaseGitPatches.kt | 3 - .../paperweight/util/constants/constants.kt | 1 + .../io/papermc/paperweight/util/utils.kt | 6 ++ 10 files changed, 56 insertions(+), 133 deletions(-) create mode 100644 paperweight-lib/src/main/kotlin/io/papermc/paperweight/GitMutationLockService.kt diff --git a/gradle.properties b/gradle.properties index 19be3a148..e42d805e6 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ group = io.canvasmc.weaver -version = 2.4.4 +version = 2.4.5-SNAPSHOT org.gradle.caching = true org.gradle.configuration-cache = true diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt index 6605da21c..e5831d8e8 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt @@ -76,6 +76,7 @@ class MinecraftPatchingTasks( } private val namePart = if (readOnly) "${configName.capitalized()}Minecraft" else if (paper) "" else "Minecraft" + private val gitMutationLockService = project.gitMutationLockService private fun ApplyFilePatches.configureApplyFilePatches() { group() @@ -209,12 +210,6 @@ class MinecraftPatchingTasks( applyBasePatches.configure { input.set(setup.flatMap { it.outputDir }) } - val name = "rebuild${namePart}BasePatches" - if (name in tasks.names) { - tasks.named(name) { - base.set(setup.flatMap { it.outputDir }) - } - } } private fun setupWritable() { @@ -233,8 +228,8 @@ class MinecraftPatchingTasks( val rebuildBasePatches = tasks.register(rebuildBasePatchesName) { group() description = "Rebuilds $configName base patches to the Minecraft source" + usesService(gitMutationLockService) - base.set(baseSources) // workaround to achieve the correct task execution order inputDir.set(outputSrc) patchDir.set(basePatchDir) baseRef.set("base") @@ -246,6 +241,7 @@ class MinecraftPatchingTasks( val rebuildSourcePatches = tasks.register(rebuildSourcePatchesName) { group() description = "Rebuilds $configName file patches to the Minecraft sources" + usesService(gitMutationLockService) dependsOn(rebuildBasePatches) input.set(outputSrc) @@ -263,7 +259,6 @@ class MinecraftPatchingTasks( group() description = "Rebuilds $configName file patches to the Minecraft resources" - base.set(baseResources) input.set(outputResources) patches.set(resourcePatchDir) gitFilePatches.set(this@MinecraftPatchingTasks.gitFilePatches) @@ -279,6 +274,7 @@ class MinecraftPatchingTasks( val rebuildFeaturePatches = tasks.register(rebuildFeaturePatchesName) { group() description = "Rebuilds all $configName feature patches to the Minecraft sources" + usesService(gitMutationLockService) dependsOn(rebuildFilePatches) inputDir.set(outputSrc) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt index 4f659f341..c7a71da14 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt @@ -68,6 +68,7 @@ class PatchingTasks( private val tasks: TaskContainer = project.tasks, ) { private val namePart: String = if (readOnly) "${forkName.capitalized()}${patchSetName.capitalized()}" else patchSetName.capitalized() + private val gitMutationLockService = project.gitMutationLockService private fun ApplyFilePatches.configureApplyFilePatches() { group = taskGroup @@ -171,12 +172,6 @@ class PatchingTasks( applyBasePatches.configure { input.set(setup.flatMap { it.outputDir }) } - val name = "rebuild${namePart}BasePatches" - if (name in tasks.names) { - tasks.named(name) { - base.set(setup.flatMap { it.outputDir }) - } - } } private fun setupWritable() { @@ -194,8 +189,8 @@ class PatchingTasks( val rebuildBasePatches = tasks.register(rebuildBasePatchesName) { group = taskGroup description = "Rebuilds $patchSetName base patches" + usesService(gitMutationLockService) - base.set(baseDir) // correct task execution order -> unused inputDir.set(outputDir) patchDir.set(basePatchDir) baseRef.set("base") @@ -206,6 +201,7 @@ class PatchingTasks( val rebuildFilePatches = tasks.register(rebuildFilePatchesName) { group = taskGroup description = "Rebuilds $patchSetName file patches" + usesService(gitMutationLockService) dependsOn(rebuildBasePatches) input.set(outputDir) @@ -257,6 +253,7 @@ class PatchingTasks( val rebuildFeaturePatches = tasks.register(rebuildFeaturePatchesName) { group = taskGroup description = "Rebuilds $patchSetName feature patches" + usesService(gitMutationLockService) dependsOn(rebuildFilePatches) inputDir.set(outputDir) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyBasePatches.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyBasePatches.kt index 33f9a630a..d03e4a8b0 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyBasePatches.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyBasePatches.kt @@ -63,10 +63,6 @@ abstract class ApplyBasePatches : ControllableOutputTask() { @get:Input abstract val verbose: Property - @Deprecated("To be removed in a future version with no replacement") - @get:Internal - abstract val emitRejects: Property - @get:Input @get:Optional abstract val identifier: Property @@ -83,7 +79,6 @@ abstract class ApplyBasePatches : ControllableOutputTask() { printOutput.convention(false).finalizeValueOnRead() additionalRemoteName.convention("old") verbose.convention(false) - emitRejects.convention(false) } @TaskAction @@ -209,61 +204,17 @@ abstract class ApplyBasePatches : ControllableOutputTask() { } val gitOut = printOutput && verbose - val result = if (emitRejects.get()) { - git( - "am", - "--3way", - "--reject", - "--ignore-whitespace", - tempDir.absolutePathString() - ).captureOut(gitOut) - } else { - git("am", "--3way", "--ignore-whitespace", tempDir.absolutePathString()).captureOut(gitOut) - } + val result = git("am", "--3way", "--ignore-whitespace", tempDir.absolutePathString()).captureOut(gitOut) if (result.exit != 0) { statusFile.writeText("1") - if (emitRejects.get()) { - logFile.writeText(result.out) - - val filtered = result.out.lineSequence() - .map { line -> - val index = line.indexOf("Patch failed at") - if (index >= 0) line.substring(index) else line - } - .filterNot { line -> - line.startsWith("error: while") || - line.startsWith("Checking patch") || - line.startsWith("Applied patch") || - line.startsWith("Applying patch") || - line.endsWith("reject...") || - line.endsWith("cleanly.") || - line.startsWith(" ") || - line.startsWith("Hunk #") || - line.startsWith("Rejected hunk") || - line.contains(";") || - line.isEmpty() - } - .joinToString("\n") - if (!gitOut) { - // Log the output anyway on failure - logger.error(filtered) - } - - logger.error("*** Please review above details and finish the apply") - logger.error("*** by manually applying the rejected hunks and deleting all `.rej` files") - logger.error("*** then running `git am --continue` and saving the changes with `./gradlew rebuildPatches`") - logger.error("*** For full logs refer to the `.git/patch-apply-logs.log` log file") - } else { - if (!gitOut) { - // Log the output anyway on failure - logger.error(result.out) - } - - logger.error("*** Please review above details and finish the apply then") - logger.error("*** save the changes with `./gradlew rebuildPatches`") + if (!gitOut) { + // Log the output anyway on failure + logger.error(result.out) } + logger.error("*** Please review above details and finish the apply then") + logger.error("*** save the changes with `./gradlew rebuildPatches`") throw PaperweightException("Failed to apply patches") } else { diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyFeaturePatches.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyFeaturePatches.kt index 6115aaa9a..58214c993 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyFeaturePatches.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyFeaturePatches.kt @@ -55,14 +55,9 @@ abstract class ApplyFeaturePatches : ControllableOutputTask() { @get:Input abstract val verbose: Property - @Deprecated("To be removed in a future version with no replacement") - @get:Internal - abstract val emitRejects: Property - override fun init() { printOutput.convention(false).finalizeValueOnRead() verbose.convention(false) - emitRejects.convention(false) } @TaskAction @@ -139,61 +134,17 @@ abstract class ApplyFeaturePatches : ControllableOutputTask() { } val gitOut = printOutput && verbose - val result = if (emitRejects.get()) { - git( - "am", - "--3way", - "--reject", - "--ignore-whitespace", - tempDir.absolutePathString() - ).captureOut(gitOut) - } else { - git("am", "--3way", "--ignore-whitespace", tempDir.absolutePathString()).captureOut(gitOut) - } + val result = git("am", "--3way", "--ignore-whitespace", tempDir.absolutePathString()).captureOut(gitOut) if (result.exit != 0) { statusFile.writeText("1") - if (emitRejects.get()) { - logFile.writeText(result.out) - - val filtered = result.out.lineSequence() - .map { line -> - val index = line.indexOf("Patch failed at") - if (index >= 0) line.substring(index) else line - } - .filterNot { line -> - line.startsWith("error: while") || - line.startsWith("Checking patch") || - line.startsWith("Applied patch") || - line.startsWith("Applying patch") || - line.endsWith("reject...") || - line.endsWith("cleanly.") || - line.startsWith(" ") || - line.startsWith("Hunk #") || - line.startsWith("Rejected hunk") || - line.contains(";") || - line.isEmpty() - } - .joinToString("\n") - if (!gitOut) { - // Log the output anyway on failure - logger.error(filtered) - } - - logger.error("*** Please review above details and finish the apply") - logger.error("*** by manually applying the rejected hunks and deleting all `.rej` files") - logger.error("*** then running `git am --continue` and saving the changes with `./gradlew rebuildPatches`") - logger.error("*** For full logs refer to the `.git/patch-apply-logs.log` log file") - } else { - if (!gitOut) { - // Log the output anyway on failure - logger.error(result.out) - } - - logger.error("*** Please review above details and finish the apply then") - logger.error("*** save the changes with `./gradlew rebuildPatches`") + if (!gitOut) { + // Log the output anyway on failure + logger.error(result.out) } + logger.error("*** Please review above details and finish the apply then") + logger.error("*** save the changes with `./gradlew rebuildPatches`") throw PaperweightException("Failed to apply patches") } else { diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/RebuildFilePatches.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/RebuildFilePatches.kt index 4e3b2ecec..08f838762 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/RebuildFilePatches.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/RebuildFilePatches.kt @@ -57,10 +57,6 @@ abstract class RebuildFilePatches : JavaLauncherTask() { @get:InputDirectory abstract val input: DirectoryProperty - @get:Optional - @get:InputDirectory - abstract val base: DirectoryProperty - @get:Internal abstract val baseDir: DirectoryProperty diff --git a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/GitMutationLockService.kt b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/GitMutationLockService.kt new file mode 100644 index 000000000..46051e7b0 --- /dev/null +++ b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/GitMutationLockService.kt @@ -0,0 +1,28 @@ +/* + * paperweight is a Gradle plugin for the PaperMC project. + * + * Copyright (c) 2023 Kyle Wood (DenWav) + * Contributors + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 only, no later versions. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + * USA + */ + +package io.papermc.paperweight + +import org.gradle.api.services.BuildService +import org.gradle.api.services.BuildServiceParameters + +abstract class GitMutationLockService : BuildService diff --git a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RebuildBaseGitPatches.kt b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RebuildBaseGitPatches.kt index 03177ecb2..1fda23e81 100644 --- a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RebuildBaseGitPatches.kt +++ b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RebuildBaseGitPatches.kt @@ -40,9 +40,6 @@ abstract class RebuildBaseGitPatches : ControllableOutputTask() { @get:InputDirectory abstract val inputDir: DirectoryProperty - @get:InputDirectory - abstract val base: DirectoryProperty // we dont need this at all but we use this to trick gradle into the correct task execution order - @get:Input abstract val baseRef: Property 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 d6f5538d9..1b3e05615 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 @@ -121,6 +121,7 @@ const val FINAL_REMAPPED_CODEBOOK_JAR = "$TASK_CACHE/codebook-minecraft.jar" const val FINAL_DECOMPILE_JAR = "$TASK_CACHE/decompileJar.jar" const val DOWNLOAD_SERVICE_NAME = "paperweightDownloadService" +const val GIT_MUTATION_LOCK_SERVICE_NAME = "paperweightGitMutationLockService" private const val MACHE_PATH = "$PAPER_PATH/mache" const val BASE_PROJECT = "$MACHE_PATH/base" diff --git a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/utils.kt b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/utils.kt index 2a4246f24..6acf4525b 100644 --- a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/utils.kt +++ b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/utils.kt @@ -26,6 +26,7 @@ import com.github.salomonbrys.kotson.fromJson import com.google.gson.* import dev.denwav.hypo.model.ClassProviderRoot import io.papermc.paperweight.DownloadService +import io.papermc.paperweight.GitMutationLockService import io.papermc.paperweight.PaperweightException import io.papermc.paperweight.tasks.* import io.papermc.paperweight.util.constants.* @@ -114,6 +115,11 @@ fun Provider.fileExists(): Provider { val Project.download: Provider get() = gradle.sharedServices.registrations.getByName(DOWNLOAD_SERVICE_NAME).service as Provider +val Project.gitMutationLockService: Provider + get() = gradle.sharedServices.registerIfAbsent(GIT_MUTATION_LOCK_SERVICE_NAME, GitMutationLockService::class) { + maxParallelUsages.set(1) + } + fun commentRegex(): Regex { return Regex("\\s*#.*") } From 6b3a12cee084ac43b86fcfb51dcb4b4e9c71e094 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Wed, 10 Jun 2026 20:36:06 +0200 Subject: [PATCH 238/257] fix(userdev): Inject Canvas releases maven repo explicitly to fix issues with gradle automatic version selection --- .../io/papermc/paperweight/util/constants/constants.kt | 2 ++ .../kotlin/io/papermc/paperweight/userdev/PaperweightUser.kt | 5 ++++- .../papermc/paperweight/userdev/PaperweightUserExtension.kt | 4 ++-- 3 files changed, 8 insertions(+), 3 deletions(-) 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 1b3e05615..8e146d2ba 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 @@ -37,6 +37,8 @@ const val MC_MANIFEST_URL = "https://piston-meta.mojang.com/mc/game/version_mani const val CANVAS_MAVEN_REPO_URL = "https://maven.canvasmc.io/public" +const val CANVAS_MAVEN_RELEASES_REPO_URL = "https://maven.canvasmc.io/releases" + const val PAPER_MAVEN_REPO_URL = "https://repo.papermc.io/repository/maven-public/" const val MAVEN_CENTRAL_URL = "https://repo.maven.apache.org/maven2/" diff --git a/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/PaperweightUser.kt b/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/PaperweightUser.kt index 302e17323..b5ce5aefe 100644 --- a/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/PaperweightUser.kt +++ b/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/PaperweightUser.kt @@ -178,7 +178,10 @@ abstract class PaperweightUser : Plugin { } } - if (userdev.injectCanvasRepository.get()) { + if (userdev.injectCanvasRepositories.get()) { + repositories.maven(CANVAS_MAVEN_RELEASES_REPO_URL) { + content { onlyForConfigurations(DEV_BUNDLE_CONFIG) } + } repositories.maven(CANVAS_MAVEN_REPO_URL) { content { onlyForConfigurations(DEV_BUNDLE_CONFIG) } } diff --git a/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/PaperweightUserExtension.kt b/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/PaperweightUserExtension.kt index 922a4c18c..7bbc4ac68 100644 --- a/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/PaperweightUserExtension.kt +++ b/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/PaperweightUserExtension.kt @@ -50,11 +50,11 @@ abstract class PaperweightUserExtension( val injectPaperRepository: Property = objects.property().convention(true) /** - * Whether to inject the Canvas maven repository for use by the dev bundle configuration. + * Whether to inject the Canvas maven repositories for use by the dev bundle configuration. * * True by default to allow easily resolving Canvas development bundles. */ - val injectCanvasRepository: Property = objects.property().convention(true) + val injectCanvasRepositories: Property = objects.property().convention(true) /** * Configurations to add the Minecraft server dependency to. From 378818d2de8a9d90a9f9359210f6e5937fe97782 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Wed, 10 Jun 2026 20:39:08 +0200 Subject: [PATCH 239/257] chore: spotless --- .../papermc/paperweight/core/tasks/patching/ApplyBasePatches.kt | 1 - .../paperweight/core/tasks/patching/ApplyFeaturePatches.kt | 1 - 2 files changed, 2 deletions(-) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyBasePatches.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyBasePatches.kt index d03e4a8b0..21473d42e 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyBasePatches.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyBasePatches.kt @@ -35,7 +35,6 @@ import org.gradle.api.file.DirectoryProperty import org.gradle.api.provider.Property import org.gradle.api.tasks.Input import org.gradle.api.tasks.InputDirectory -import org.gradle.api.tasks.Internal import org.gradle.api.tasks.Optional import org.gradle.api.tasks.OutputDirectory import org.gradle.api.tasks.PathSensitive diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyFeaturePatches.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyFeaturePatches.kt index 58214c993..c87f3f8a1 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyFeaturePatches.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/ApplyFeaturePatches.kt @@ -31,7 +31,6 @@ import org.gradle.api.file.DirectoryProperty import org.gradle.api.provider.Property import org.gradle.api.tasks.Input import org.gradle.api.tasks.InputDirectory -import org.gradle.api.tasks.Internal import org.gradle.api.tasks.Optional import org.gradle.api.tasks.OutputDirectory import org.gradle.api.tasks.PathSensitive From a98bf4047486c6870c37959de8686ed1505d6e5c Mon Sep 17 00:00:00 2001 From: Toffikk Date: Wed, 10 Jun 2026 21:01:05 +0200 Subject: [PATCH 240/257] fix(patcher): make compatible with Gradle 9.5.0 Resolves PaperMC/Paperweight#389\nAlthough the proper solution would be bumping the minimum Gradle version, I chose to keep compatibility with older builds until upstream makes the switch --- gradle/wrapper/gradle-wrapper.jar | Bin 46175 -> 48462 bytes gradle/wrapper/gradle-wrapper.properties | 4 ++- gradlew | 2 +- gradlew.bat | 31 ++++++------------ .../paperweight/core/tasks/RunNestedBuild.kt | 30 +++++++++++++---- 5 files changed, 38 insertions(+), 29 deletions(-) diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 61285a659d17295f1de7c53e24fdf13ad755c379..b1b8ef56b44f16b14dc800fa8103a6d89abb526f 100644 GIT binary patch delta 39773 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%0D6K65^m-0x6bZ9rDjF>In@OH( z-}n(gbEW`7N;Uw_nvT;^Dka<4HW`~@d4Ar1vP6&Q8=(0P)eG+4q=iBy>>s6#LLaiI z8Ius$PjnUlOR@A0RT&$X@hAoJ70tH@R`t4bsi-gdvtDkF(|IT~X#VhfF}CW+LQ7FW zSCNg@0d5;A>uzYV4~NGgNQxY^mrkR5QK&vnGw3CnNw3furU7!AZlW^NZ8XbtJC&P% zUZvTlH)xLOZJKMIO)m3%nrB`_^Ubxiz~?+yp|iN&T=EwTkfQDE%!hMSq#BO znPfZowh7rr_-LTI6JC5QuwEpv41d*sTb<#r!bL{Oe)x&GLyY# zLIBZRGLO=gqKSY}SBMZm*Ua_TQ9^uh;QMA2wwOAK9o3H6%iT8%>4Q0Pe|TBUf%$0VOHR=*E~+%SzZrDd+t#Ea7=v2I9{w8WcjX}z#b;jQh&*4=4IZK>gAkr&I%Q-uf7#`b@v z445^+&wQ(+H4C;az4Zb~B9#ysl|-y|$yh#%^l+I5GKSgjYy2pU*>B>c zTd{C8PsNu@i6PRUvsF;PGHb-8HBi`z0ayr&YC?+eT?{WbUxFJB6jmX3I0_AI-2=7(f^3?W&}|!N zOYhPXc4q+M`adSf=X)-NHXLhyh%EbDTX3O48 zY-q^Lv~;AhxmYr3kdvwU>e!xGOEQ+))v~$wYBQcQ$j#(Vrg!Z!GfXFUR+!(Z2UjzB z`qFz-$#krbskRd1I(rzbGlpWhfwkGJIoO*N!HX*KUtG?ENej$<@nk-m*rPXpvo;<# zv)Qg#DyCICuUtYRl`}J`ShA%bj4jD@d^fDrvVPj>5bi!VkxJy&FkMl1f=!-qTW`FF z`b(J{b@i9}shZ~a$e*4GQ>Z9@Y41Ce8Aai{|^uLtgo|z)wD^l5eN@$xk!n z+o??R;3$dy;dO~@E|ciZi+^r^NvCs6o9a6C*(cI1vdrqv2~X(BiIc6aue3kgd6w9; zUA!9B^q0#rXc6pd?!%f{z5mPnw1k%WXfd5W$>pqt460)a=w9fTY-vv?lkh}nnl-3_ z$!iV{<%T;UjcHC@edh$H^sV+&n3{alNX=kPqDNG~y0h!*FQ2?cKb=W`%S4OiOtYqp z8FV&N&4lQ+nM_QtX;7<=R?5LuC9>-h8EBTy4EyOEYeNxiFxH0~5UA^%dY4wq#?Phm zWIh~i=48bUx`4?@WY^2?{M1HEt7t8?GhKC|HhHM8?94)EEX|$~>Pws1P%51Z#nY)= zERhPuQo~@gbV``n=Fs|oeM4xNeW6@B)SpQ8h0J}icrG~{T2|s4ZuZc6oKJr`GZ=$O zɵ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 Hv&Q@%MAWx6 delta 37584 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 echo Please set the JAVA_HOME variable in your environment to match the 1>&2 echo location of your Java installation. 1>&2 -goto fail +"%COMSPEC%" /c exit 1 :findJavaFromJavaHome set JAVA_HOME=%JAVA_HOME:"=% @@ -65,7 +65,7 @@ echo. 1>&2 echo Please set the JAVA_HOME variable in your environment to match the 1>&2 echo location of your Java installation. 1>&2 -goto fail +"%COMSPEC%" /c exit 1 :execute @rem Setup the command line @@ -73,21 +73,10 @@ goto fail @rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* +@rem endlocal doesn't take effect until after the line is parsed and variables are expanded +@rem which allows us to clear the local environment before executing the java command +endlocal & "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* & call :exitWithErrorLevel -:end -@rem End local scope for the variables with windows NT shell -if %ERRORLEVEL% equ 0 goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -set EXIT_CODE=%ERRORLEVEL% -if %EXIT_CODE% equ 0 set EXIT_CODE=1 -if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% -exit /b %EXIT_CODE% - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega +:exitWithErrorLevel +@rem Use "%COMSPEC%" /c exit to allow operators to work properly in scripts +"%COMSPEC%" /c exit %ERRORLEVEL% diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/RunNestedBuild.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/RunNestedBuild.kt index 71a75ee27..97b625496 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/RunNestedBuild.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/RunNestedBuild.kt @@ -38,7 +38,9 @@ import org.gradle.api.tasks.Internal import org.gradle.api.tasks.TaskAction import org.gradle.api.tasks.UntrackedTask import org.gradle.internal.build.NestedRootBuildRunner +import org.gradle.internal.classpath.ClassPath import org.gradle.internal.service.ServiceRegistry +import org.gradle.util.GradleVersion @UntrackedTask(because = "Nested build does it's own up-to-date checking") abstract class RunNestedBuild : BaseTask() { @@ -68,11 +70,27 @@ abstract class RunNestedBuild : BaseTask() { params.systemPropertiesArgs[PAPERWEIGHT_DEBUG] = System.getProperty(PAPERWEIGHT_DEBUG, "false") - NestedRootBuildRunner::class.java.getDeclaredMethod( - "runNestedRootBuild", - String::class.java, - StartParameterInternal::class.java, - ServiceRegistry::class.java - ).invoke(null, null, params, services) + val legacy = GradleVersion.current() < GradleVersion.version("9.5.0") // Gradle changed the parameters in 9.5.0 + val method = when { + legacy -> NestedRootBuildRunner::class.java.getDeclaredMethod( + "runNestedRootBuild", + String::class.java, + StartParameterInternal::class.java, + ServiceRegistry::class.java + ) + else -> NestedRootBuildRunner::class.java.getDeclaredMethod( + "runNestedRootBuild", + String::class.java, + StartParameterInternal::class.java, + ServiceRegistry::class.java, + ClassPath::class.java + ) + } + val args = when { + legacy -> arrayOf(null, params, services) + else -> arrayOf(null, params, services, ClassPath.EMPTY) + } + + method.invoke(null, *args) } } From bdcb4b15f0c75a4326191e243ba4beff5f2fec00 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Wed, 10 Jun 2026 21:03:20 +0200 Subject: [PATCH 241/257] Version 2.4.5 --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index e42d805e6..a20ff807e 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ group = io.canvasmc.weaver -version = 2.4.5-SNAPSHOT +version = 2.4.5 org.gradle.caching = true org.gradle.configuration-cache = true From 20c02995ed51b840aa36c296772f058194f0d608 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Thu, 11 Jun 2026 21:18:14 +0200 Subject: [PATCH 242/257] chore: continue development cycle --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index a20ff807e..1dad93922 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ group = io.canvasmc.weaver -version = 2.4.5 +version = 2.4.6-SNAPSHOT org.gradle.caching = true org.gradle.configuration-cache = true From f47d0b0a13ee5b86178cdec3fb72f93e4f2c05fa Mon Sep 17 00:00:00 2001 From: Toffikk Date: Thu, 11 Jun 2026 21:18:32 +0200 Subject: [PATCH 243/257] chore: move commit logic to util --- .../paperweight/core/PaperweightCore.kt | 4 +-- .../core/tasks/patching/CreateBasePatch.kt | 19 +++----------- .../core/tasks/patching/FixupBasePatches.kt | 19 +++----------- .../core/tasks/patching/RebuildFilePatches.kt | 19 +++----------- .../tasks/RebuildBaseGitPatches.kt | 17 +++--------- .../kotlin/io/papermc/paperweight/util/git.kt | 26 +++++++++++++++++++ 6 files changed, 41 insertions(+), 63 deletions(-) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/PaperweightCore.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/PaperweightCore.kt index 8cc5d1075..ee7fa0f05 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/PaperweightCore.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/PaperweightCore.kt @@ -206,7 +206,7 @@ abstract class PaperweightCore : Plugin { } tasks.paperPatchingTasks.applySourcePatches.configure { emitRejects = false - } // if we ever make features/base emit rejects by default, we should update this. + } PatchRouletteTasks( target, @@ -228,7 +228,7 @@ abstract class PaperweightCore : Plugin { } target.tasks.named("applyMinecraftSourcePatches").configure { emitRejects = false - } // if we ever make features/base emit rejects by default, we should update this. + } PatchRouletteTasks( target, diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/CreateBasePatch.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/CreateBasePatch.kt index df17cf2ff..759234020 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/CreateBasePatch.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/CreateBasePatch.kt @@ -72,22 +72,9 @@ abstract class CreateBasePatch : BaseTask() { } fun tagCommits(git: Git) { - val baseCommit = git( - "log", - "--format=%H %s", - "--grep=^${identifier.get()} Base Patches$", - "base..HEAD" - ).getText() - .lineSequence() - .map(String::trim) - .filter(String::isNotEmpty) - .map { it.substringBefore(" ") } - .toList() - - // throw if false, since that means the repo state is corrupted - validateSingleCommit(identifier, "Base", baseCommit) - + // single commit, since if its more than that it means the repo state is corrupted and we can't rebuild safely + val baseCommit = getCommitByIdentifier(git, identifier, "Base", "single").joinToString() // retag - git("tag", "-f", "basepatches", baseCommit.joinToString()).executeOut() + git("tag", "-f", "basepatches", baseCommit).executeOut() } } diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/FixupBasePatches.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/FixupBasePatches.kt index dc60e39f2..99925d799 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/FixupBasePatches.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/FixupBasePatches.kt @@ -88,22 +88,9 @@ abstract class FixupBasePatches : BaseTask() { } fun tagCommits(git: Git) { - val baseCommit = git( - "log", - "--format=%H %s", - "--grep=^${identifier.get()} Base Patches$", - "base..HEAD" - ).getText() - .lineSequence() - .map(String::trim) - .filter(String::isNotEmpty) - .map { it.substringBefore(" ") } - .toList() - - // throw if false, since that means the repo state is corrupted - validateSingleCommit(identifier, "Base", baseCommit) - + // single commit, since if its more than that it means the repo state is corrupted and we can't rebuild safely + val baseCommit = getCommitByIdentifier(git, identifier, "Base", "single").joinToString() // retag - git("tag", "-f", "basepatches", baseCommit.joinToString()).executeOut() + git("tag", "-f", "basepatches", baseCommit).executeOut() } } diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/RebuildFilePatches.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/RebuildFilePatches.kt index 08f838762..9380e541e 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/RebuildFilePatches.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/patching/RebuildFilePatches.kt @@ -99,23 +99,12 @@ abstract class RebuildFilePatches : JavaLauncherTask() { val baseDir = baseDir.convertToPath() val git = Git(inputDir) - val fileCommit = git( - "log", - "--format=%H %s", - "--grep=^${identifier.get()} File Patches$", - "base..HEAD" - ).getText() - .lineSequence() - .map(String::trim) - .filter(String::isNotEmpty) - .map { it.substringBefore(" ") } - .toList() - - // throw if false, since that means the repo state is corrupted and we can't rebuild safely - validateSingleCommit(identifier, "File", fileCommit) + + // single commit, since if its more than that it means the repo state is corrupted and we can't rebuild safely + val fileCommit = getCommitByIdentifier(git, identifier, "File", "single").joinToString() // we update the appropriate tag to reflect the new repo state - git("tag", "-f", "file", fileCommit.joinToString()).executeSilently(silenceErr = true) + git("tag", "-f", "file", fileCommit).executeSilently(silenceErr = true) // now we can safely work with the repo, having updated the tag git("stash", "push").executeSilently(silenceErr = true) diff --git a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RebuildBaseGitPatches.kt b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RebuildBaseGitPatches.kt index 1fda23e81..8de250ff2 100644 --- a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RebuildBaseGitPatches.kt +++ b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/tasks/RebuildBaseGitPatches.kt @@ -70,20 +70,9 @@ abstract class RebuildBaseGitPatches : ControllableOutputTask() { @TaskAction fun run() { val git = Git(inputDir.path) - val basepatchesCommit = git( - "log", - "--format=%H %s", - "--grep=^${identifier.get()} Base Patches$", - "base..HEAD" - ).getText() - .lineSequence() - .map(String::trim) - .filter(String::isNotEmpty) - .map { it.substringBefore(" ") } - .toList() - - // throw if above 1, since that means the repo state is corrupted and we can't rebuild safely - val commitNumber = validateSingleOrNullCommit(identifier, "Base", basepatchesCommit) + // single or null commit, since if its more than that it means the repo state is corrupted and we can't rebuild safely + val basepatchesCommit = getCommitByIdentifier(git, identifier, "Base", "singleOrNull") + val commitNumber = basepatchesCommit.size // we update the tag to reflect the new repo state if (commitNumber == 1) { diff --git a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/git.kt b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/git.kt index dece9b1b8..474d59755 100644 --- a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/git.kt +++ b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/git.kt @@ -296,6 +296,32 @@ fun checkoutRepoFromUpstream( git("gc").runSilently(silenceErr = true) } +fun getCommitsByIdentifier(git: Git, identifier: Provider, kind: String): List { + return git( + "log", + "--format=%H %s", + "--grep=^${identifier.get()} $kind Patches$", + "base..HEAD" + ).getText() + .lineSequence() + .map(String::trim) + .filter(String::isNotEmpty) + .map { it.substringBefore(" ") } + .toList() +} + +fun getCommitByIdentifier(git: Git, identifier: Provider, kind: String, validation: String): List { + val commits = getCommitsByIdentifier(git, identifier, kind) + if (validation == "single") { + validateSingleCommit(identifier, kind, commits) + } else if (validation == "singleOrNull") { + validateSingleOrNullCommit(identifier, kind, commits) + } else { + throw PaperweightException("Invalid commit validation strategy passed: $validation\nSupported strategies: single, singleOrNull") + } + return commits +} + fun validateSingleOrNullCommit(identifier: Provider, kind: String, commits: List): Int { // > 1 hack bc its possible we encountered an apply error so the marker commit wasn't generated if (commits.size > 1) { From 125bab08d069ebb10d13adb781c40f97852d3a96 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Sun, 5 Jul 2026 21:46:12 +0200 Subject: [PATCH 244/257] feat: support patch roulette for upstream sources --- .../io/papermc/paperweight/core/PaperweightCore.kt | 12 ++++++++++++ .../paperweight/patcher/PaperweightPatcher.kt | 11 +++++++++++ 2 files changed, 23 insertions(+) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/PaperweightCore.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/PaperweightCore.kt index ee7fa0f05..2206dff34 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/PaperweightCore.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/PaperweightCore.kt @@ -237,6 +237,18 @@ abstract class PaperweightCore : Plugin { coreExt.activeFork.flatMap { it.rejectsDir }, layout.projectDirectory.dir("src/minecraft/java"), ) + + coreExt.activeFork.get().upstream { + directoryPatchSets.forEach { + PatchRouletteTasks( + target, + it.name, + coreExt.minecraftVersion.map { ver -> "${it.name}-$ver" }, + it.rejectsDir, + it.outputDir.get(), + ) + } + } } } } diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/PaperweightPatcher.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/PaperweightPatcher.kt index 4f3e9968d..63617340f 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/PaperweightPatcher.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/patcher/PaperweightPatcher.kt @@ -28,6 +28,7 @@ import io.papermc.paperweight.core.tasks.GeneratePatches import io.papermc.paperweight.core.tasks.GenerateSources import io.papermc.paperweight.core.tasks.PrepareForPatchGeneration import io.papermc.paperweight.core.tasks.RunNestedBuild +import io.papermc.paperweight.core.tasks.patchroulette.PatchRouletteTasks import io.papermc.paperweight.patcher.extension.PaperweightPatcherExtension import io.papermc.paperweight.util.* import io.papermc.paperweight.util.constants.* @@ -134,6 +135,16 @@ abstract class PaperweightPatcher : Plugin { projectDir.set(layout.projectDirectory) dependsOn(depend) } + // TODO: maybe lock it behind a property like the server tasks? + upstream.directoryPatchSets.configureEach { + PatchRouletteTasks( + this@afterEvaluate, + name, + upstream.ref.map { ref -> "$name-git-${ref.take(7)}" }, + rejectsDir, + outputDir.get(), + ) + } } patcher.additionalUpstreams.forEach { upstream -> val checkoutTask = tasks.register("checkout${upstream.name.capitalized()}RepoForGeneration") { From 6607eea798450012bff0fa0f36abf4d98b84e657 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Sun, 5 Jul 2026 22:33:39 +0200 Subject: [PATCH 245/257] chore: fix tests --- .../paperweight/core/tasks/patching/RebuildFilePatchesTest.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/paperweight-core/src/test/kotlin/io/papermc/paperweight/core/tasks/patching/RebuildFilePatchesTest.kt b/paperweight-core/src/test/kotlin/io/papermc/paperweight/core/tasks/patching/RebuildFilePatchesTest.kt index dcc491c59..34de497c4 100644 --- a/paperweight-core/src/test/kotlin/io/papermc/paperweight/core/tasks/patching/RebuildFilePatchesTest.kt +++ b/paperweight-core/src/test/kotlin/io/papermc/paperweight/core/tasks/patching/RebuildFilePatchesTest.kt @@ -59,7 +59,6 @@ class RebuildFilePatchesTest : TaskTest() { val atFileOut = tempDir.resolve("ats.at").toFile() task.input.set(source) - task.base.set(base) task.patches.set(patches) task.atFile.set(atFile) task.atFileOut.set(atFileOut) From b386deb52ada69dfdb6d9315aa58d2c791eb72f1 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Sun, 5 Jul 2026 23:18:29 +0200 Subject: [PATCH 246/257] chore: use gradle 9.3.1 --- gradle/wrapper/gradle-wrapper.jar | Bin 48462 -> 46175 bytes gradle/wrapper/gradle-wrapper.properties | 4 +-- gradlew | 2 +- gradlew.bat | 31 +++++++++++++++-------- 4 files changed, 23 insertions(+), 14 deletions(-) diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index b1b8ef56b44f16b14dc800fa8103a6d89abb526f..61285a659d17295f1de7c53e24fdf13ad755c379 100644 GIT binary patch delta 37584 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%0D6K65^m-0x6bZ9rDjF>In@OH( z-}n(gbEW`7N;Uw_nvT;^Dka<4HW`~@d4Ar1vP6&Q8=(0P)eG+4q=iBy>>s6#LLaiI z8Ius$PjnUlOR@A0RT&$X@hAoJ70tH@R`t4bsi-gdvtDkF(|IT~X#VhfF}CW+LQ7FW zSCNg@0d5;A>uzYV4~NGgNQxY^mrkR5QK&vnGw3CnNw3furU7!AZlW^NZ8XbtJC&P% zUZvTlH)xLOZJKMIO)m3%nrB`_^Ubxiz~?+yp|iN&T=EwTkfQDE%!hMSq#BO znPfZowh7rr_-LTI6JC5QuwEpv41d*sTb<#r!bL{Oe)x&GLyY# zLIBZRGLO=gqKSY}SBMZm*Ua_TQ9^uh;QMA2wwOAK9o3H6%iT8%>4Q0Pe|TBUf%$0VOHR=*E~+%SzZrDd+t#Ea7=v2I9{w8WcjX}z#b;jQh&*4=4IZK>gAkr&I%Q-uf7#`b@v z445^+&wQ(+H4C;az4Zb~B9#ysl|-y|$yh#%^l+I5GKSgjYy2pU*>B>c zTd{C8PsNu@i6PRUvsF;PGHb-8HBi`z0ayr&YC?+eT?{WbUxFJB6jmX3I0_AI-2=7(f^3?W&}|!N zOYhPXc4q+M`adSf=X)-NHXLhyh%EbDTX3O48 zY-q^Lv~;AhxmYr3kdvwU>e!xGOEQ+))v~$wYBQcQ$j#(Vrg!Z!GfXFUR+!(Z2UjzB z`qFz-$#krbskRd1I(rzbGlpWhfwkGJIoO*N!HX*KUtG?ENej$<@nk-m*rPXpvo;<# zv)Qg#DyCICuUtYRl`}J`ShA%bj4jD@d^fDrvVPj>5bi!VkxJy&FkMl1f=!-qTW`FF z`b(J{b@i9}shZ~a$e*4GQ>Z9@Y41Ce8Aai{|^uLtgo|z)wD^l5eN@$xk!n z+o??R;3$dy;dO~@E|ciZi+^r^NvCs6o9a6C*(cI1vdrqv2~X(BiIc6aue3kgd6w9; zUA!9B^q0#rXc6pd?!%f{z5mPnw1k%WXfd5W$>pqt460)a=w9fTY-vv?lkh}nnl-3_ z$!iV{<%T;UjcHC@edh$H^sV+&n3{alNX=kPqDNG~y0h!*FQ2?cKb=W`%S4OiOtYqp z8FV&N&4lQ+nM_QtX;7<=R?5LuC9>-h8EBTy4EyOEYeNxiFxH0~5UA^%dY4wq#?Phm zWIh~i=48bUx`4?@WY^2?{M1HEt7t8?GhKC|HhHM8?94)EEX|$~>Pws1P%51Z#nY)= zERhPuQo~@gbV``n=Fs|oeM4xNeW6@B)SpQ8h0J}icrG~{T2|s4ZuZc6oKJr`GZ=$O zɵ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 Hv&Q@%MAWx6 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index e74c8700b..5f38436fc 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,9 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-9.5.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.3.1-all.zip networkTimeout=10000 -retries=0 -retryBackOffMs=500 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index b9bb139f7..adff685a0 100755 --- a/gradlew +++ b/gradlew @@ -57,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/3d91ce3b8caaf77ad09f381f43615b715b53f72c/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. diff --git a/gradlew.bat b/gradlew.bat index 24c62d56f..c4bdd3ab8 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -23,8 +23,8 @@ @rem @rem ########################################################################## -@rem Set local scope for the variables, and ensure extensions are enabled -setlocal EnableExtensions +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal set DIRNAME=%~dp0 if "%DIRNAME%"=="" set DIRNAME=. @@ -51,7 +51,7 @@ echo. 1>&2 echo Please set the JAVA_HOME variable in your environment to match the 1>&2 echo location of your Java installation. 1>&2 -"%COMSPEC%" /c exit 1 +goto fail :findJavaFromJavaHome set JAVA_HOME=%JAVA_HOME:"=% @@ -65,7 +65,7 @@ echo. 1>&2 echo Please set the JAVA_HOME variable in your environment to match the 1>&2 echo location of your Java installation. 1>&2 -"%COMSPEC%" /c exit 1 +goto fail :execute @rem Setup the command line @@ -73,10 +73,21 @@ echo location of your Java installation. 1>&2 @rem Execute Gradle -@rem endlocal doesn't take effect until after the line is parsed and variables are expanded -@rem which allows us to clear the local environment before executing the java command -endlocal & "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* & call :exitWithErrorLevel +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* -:exitWithErrorLevel -@rem Use "%COMSPEC%" /c exit to allow operators to work properly in scripts -"%COMSPEC%" /c exit %ERRORLEVEL% +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega From 62dcc60f0e6d58efed3312f3cf8ffd4103628f21 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Sun, 5 Jul 2026 23:40:28 +0200 Subject: [PATCH 247/257] fix(rebuild): use git mutation lock service for resource patches rebuild to prevent race conditions from happening --- .../paperweight/core/taskcontainers/MinecraftPatchingTasks.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt index e5831d8e8..bdc6179e5 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt @@ -258,6 +258,7 @@ class MinecraftPatchingTasks( val rebuildResourcePatches = tasks.register(rebuildResourcePatchesName) { group() description = "Rebuilds $configName file patches to the Minecraft resources" + usesService(gitMutationLockService) input.set(outputResources) patches.set(resourcePatchDir) From d7d9f1af4b793da4896405373747feb42714dd14 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Thu, 17 Sep 2026 20:38:46 +0200 Subject: [PATCH 248/257] Bump to 2.5.0-SNAPSHOT --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 1dad93922..5c074317e 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ group = io.canvasmc.weaver -version = 2.4.6-SNAPSHOT +version = 2.5.0-SNAPSHOT org.gradle.caching = true org.gradle.configuration-cache = true From 17f36dfca157689f335c27a942b4c147b1ef26f3 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Fri, 18 Sep 2026 00:03:50 +0200 Subject: [PATCH 249/257] Isolated Projects support Reworks the build scripts and the plugin code to support IP and resolve deprecation warnings from using kotlin `by` accessors. userdev has not been tested yet. --- build-logic/build.gradle.kts | 26 +++++++++++ build-logic/settings.gradle.kts | 13 ++++++ .../src/main/kotlin/config-kotlin.gradle.kts | 12 ++++- .../src/main/kotlin/config-publish.gradle.kts | 43 ++++++++++++------ .../src/main/kotlin/utils.kt | 5 +- build.gradle.kts | 5 ++ buildSrc/build.gradle.kts | 22 --------- buildSrc/settings.gradle.kts | 9 ---- gradle.properties | 2 + gradle/libs.versions.toml | 15 +++--- gradle/wrapper/gradle-wrapper.jar | Bin 46175 -> 47505 bytes gradle/wrapper/gradle-wrapper.properties | 4 +- gradlew | 6 +-- gradlew.bat | 35 +++++--------- license/copyright.txt | 39 ++++++++-------- paperweight-core/build.gradle.kts | 4 +- .../PaperweightDependencyBridge.kt | 13 ++++-- .../PaperweightSourceGeneratorHelper.kt | 6 +-- .../paperweight/checkstyle/PaperCheckstyle.kt | 2 +- .../paperweight/core/PaperweightCore.kt | 14 +++--- .../paperweight/core/extension/ForkConfig.kt | 2 +- .../core/extension/PaperExtension.kt | 2 +- .../core/taskcontainers/AllTasks.kt | 11 ++--- .../core/taskcontainers/CoreTasks.kt | 40 ++++++++-------- .../core/taskcontainers/DevBundleTasks.kt | 13 +++--- .../core/taskcontainers/InitialTasks.kt | 9 ++-- .../taskcontainers/MinecraftPatchingTasks.kt | 10 ++-- .../core/taskcontainers/PatchingTasks.kt | 10 ++-- .../paperweight/core/tasks/RunNestedBuild.kt | 30 +++--------- paperweight-lib/build.gradle.kts | 19 ++++++-- .../papermc/paperweight/util/dependencies.kt | 4 ++ .../io/papermc/paperweight/util/utils.kt | 13 +++--- paperweight-userdev/build.gradle.kts | 4 +- .../paperweight/userdev/PaperweightUser.kt | 4 +- .../userdev/internal/setup/SetupHandler.kt | 5 +- .../internal/setup/SetupHandlerImpl.kt | 2 +- .../internal/setup/v7/SetupHandlerImplV7.kt | 2 +- settings.gradle.kts | 4 ++ 38 files changed, 250 insertions(+), 209 deletions(-) create mode 100644 build-logic/build.gradle.kts create mode 100644 build-logic/settings.gradle.kts rename {buildSrc => build-logic}/src/main/kotlin/config-kotlin.gradle.kts (90%) rename {buildSrc => build-logic}/src/main/kotlin/config-publish.gradle.kts (79%) rename {buildSrc => build-logic}/src/main/kotlin/utils.kt (84%) delete mode 100644 buildSrc/build.gradle.kts delete mode 100644 buildSrc/settings.gradle.kts diff --git a/build-logic/build.gradle.kts b/build-logic/build.gradle.kts new file mode 100644 index 000000000..e1370f466 --- /dev/null +++ b/build-logic/build.gradle.kts @@ -0,0 +1,26 @@ +plugins { + `kotlin-dsl` +} + +repositories { + mavenCentral() + gradlePluginPortal() +} + +dependencies { + implementation(libs.gradle.licenser) + implementation(libs.gradle.spotless) + implementation(libs.gradle.shadow) + implementation(libs.gradle.kotlin.dsl) + implementation(kotlin("gradle-plugin", embeddedKotlinVersion)) + implementation(libs.gradle.plugin.publish) + + constraints { + // spotless carries 1.9.10 and kotlin-dsl plugin has a strictly 2.4.0 constraint + implementation("org.jetbrains.kotlin:kotlin-stdlib") { + version { + strictly(embeddedKotlinVersion) + } + } + } +} diff --git a/build-logic/settings.gradle.kts b/build-logic/settings.gradle.kts new file mode 100644 index 000000000..c450d63d2 --- /dev/null +++ b/build-logic/settings.gradle.kts @@ -0,0 +1,13 @@ +plugins { + id("org.gradle.toolchains.foojay-resolver-convention") version "1.0.0" +} + +dependencyResolutionManagement { + versionCatalogs { + register("libs") { + from(files("../gradle/libs.versions.toml")) + } + } +} + +rootProject.name = "build-logic" diff --git a/buildSrc/src/main/kotlin/config-kotlin.gradle.kts b/build-logic/src/main/kotlin/config-kotlin.gradle.kts similarity index 90% rename from buildSrc/src/main/kotlin/config-kotlin.gradle.kts rename to build-logic/src/main/kotlin/config-kotlin.gradle.kts index 3ad3abd39..81acf6b4e 100644 --- a/buildSrc/src/main/kotlin/config-kotlin.gradle.kts +++ b/build-logic/src/main/kotlin/config-kotlin.gradle.kts @@ -61,7 +61,7 @@ dependencies { testing { suites { - val test by getting(JvmTestSuite::class) { + named("test") { useKotlinTest(embeddedKotlinVersion) dependencies { implementation("org.junit.jupiter:junit-jupiter-engine:6.0.3") @@ -96,6 +96,10 @@ tasks.jar { } } +tasks.validatePlugins { + enableStricterValidation = false // TODO: re-enable this +} + // The following is to work around https://github.com/diffplug/spotless/issues/1599 // Ensure the ktlint step is before the license header step @@ -116,20 +120,24 @@ extensions.configure { ) val ktlintVer = "1.5.0" + val headerFile = isolated.rootProject.projectDirectory.file("license/copyright.txt") kotlin { ktlint(ktlintVer).editorConfigOverride(overrides) + licenseHeaderFile(headerFile) // until indra works } kotlinGradle { ktlint(ktlintVer).editorConfigOverride(overrides) } } +/* Not compatible with IP plugins.apply("net.kyori.indra.licenser.spotless") extensions.configure { - licenseHeaderFile(rootProject.file("license/copyright.txt")) + licenseHeaderFile(isolated.rootProject.projectDirectory.file("license/copyright.txt")) newLine(true) } +*/ tasks.register("format") { group = "formatting" diff --git a/buildSrc/src/main/kotlin/config-publish.gradle.kts b/build-logic/src/main/kotlin/config-publish.gradle.kts similarity index 79% rename from buildSrc/src/main/kotlin/config-publish.gradle.kts rename to build-logic/src/main/kotlin/config-publish.gradle.kts index b846ff5b5..490f183f5 100644 --- a/buildSrc/src/main/kotlin/config-publish.gradle.kts +++ b/build-logic/src/main/kotlin/config-publish.gradle.kts @@ -1,7 +1,8 @@ import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar +import org.gradle.kotlin.dsl.named +import org.gradle.kotlin.dsl.withType plugins { - id("org.jetbrains.kotlin.jvm") id("com.gradleup.shadow") id("com.gradle.plugin-publish") } @@ -16,18 +17,31 @@ if (noRelocate) { } } -val shade: Configuration by configurations.creating +val sourcesJar = configurations.dependencyScope("sourcesJar") +val sourcesJarResolvable = configurations.resolvable("sourcesJarResolvable") { + extendsFrom(sourcesJar) +} + +dependencies { + sourcesJar(project(":paperweight-lib", "sourcesJar")) +} + +val shade = configurations.dependencyScope("shade") +val shadeResolvable = configurations.resolvable("shadeResolvable") { + extendsFrom(shade) +} + configurations.implementation { extendsFrom(shade) } configurations.shadowRuntimeElements { - compatibilityAttributes(objects) + compatibilityAttributes() } fun ShadowJar.configureStandard() { - configurations = listOf(shade) - filesMatching("META-INF/services/**") { + configurations.setFrom(listOf(shadeResolvable)) + filesMatching("META-INF/**") { duplicatesStrategy = DuplicatesStrategy.INCLUDE } @@ -41,12 +55,13 @@ fun ShadowJar.configureStandard() { mergeServiceFiles() } -val sourcesJar by tasks.existing(AbstractArchiveTask::class) { - from( - zipTree(project(":paperweight-lib").tasks - .named("sourcesJar", AbstractArchiveTask::class) - .flatMap { it.archiveFile }) - ) { +private fun SetProperty.setFrom(configurations: List>) { + empty() + configurations.forEach { add(it) } +} + +val libSourcesJar = tasks.named("sourcesJar") { + from(zipTree(sourcesJarResolvable.map { it.singleFile })) { exclude("META-INF/**") } } @@ -56,13 +71,13 @@ gradlePlugin { vcsUrl.set("https://github.com/CraftCanvasMC/weaver/") } -val shadowJar by tasks.existing(ShadowJar::class) { - archiveClassifier.set(null as String?) +val shadowJar = tasks.named("shadowJar") { + archiveClassifier.set("") configureStandard() inputs.property("noRelocate", noRelocate) if (noRelocate) { - return@existing + return@named } val prefix = "paper.libs" diff --git a/buildSrc/src/main/kotlin/utils.kt b/build-logic/src/main/kotlin/utils.kt similarity index 84% rename from buildSrc/src/main/kotlin/utils.kt rename to build-logic/src/main/kotlin/utils.kt index 9b0921e13..167705ae0 100644 --- a/buildSrc/src/main/kotlin/utils.kt +++ b/build-logic/src/main/kotlin/utils.kt @@ -2,15 +2,14 @@ import org.gradle.api.Action import org.gradle.api.artifacts.Configuration import org.gradle.api.attributes.java.TargetJvmVersion import org.gradle.api.attributes.plugin.GradlePluginApiVersion -import org.gradle.api.model.ObjectFactory import org.gradle.kotlin.dsl.* import org.gradle.plugin.devel.GradlePluginDevelopmentExtension import org.gradle.plugin.devel.PluginDeclaration -fun Configuration.compatibilityAttributes(objects: ObjectFactory) { +fun Configuration.compatibilityAttributes() { attributes { attribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, 17) - attribute(GradlePluginApiVersion.GRADLE_PLUGIN_API_VERSION_ATTRIBUTE, objects.named("9.0.0")) + attribute(GradlePluginApiVersion.GRADLE_PLUGIN_API_VERSION_ATTRIBUTE, named("9.5.0")) } } diff --git a/build.gradle.kts b/build.gradle.kts index addb5ebc4..2a58056ef 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,3 +1,8 @@ +plugins { + `kotlin-dsl` apply false + id("com.diffplug.spotless") version "8.10.2" apply false +} + tasks.register("printVersion") { val ver = project.version doFirst { diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts deleted file mode 100644 index 204fb296b..000000000 --- a/buildSrc/build.gradle.kts +++ /dev/null @@ -1,22 +0,0 @@ -plugins { - `kotlin-dsl` - `kotlin-dsl-precompiled-script-plugins` -} - -repositories { - mavenCentral() - gradlePluginPortal() -} - -dependencies { - implementation(libs.gradle.licenser) - implementation(libs.gradle.spotless) - implementation(libs.gradle.shadow) - implementation(libs.gradle.kotlin.dsl) - implementation(libs.gradle.plugin.kotlin.withVersion(embeddedKotlinVersion)) - implementation(libs.gradle.plugin.publish) -} - -fun Provider.withVersion(version: String): Provider { - return map { "${it.module.group}:${it.module.name}:$version" } -} diff --git a/buildSrc/settings.gradle.kts b/buildSrc/settings.gradle.kts deleted file mode 100644 index 62991c1ef..000000000 --- a/buildSrc/settings.gradle.kts +++ /dev/null @@ -1,9 +0,0 @@ -rootProject.name = "buildSrc" - -dependencyResolutionManagement { - versionCatalogs { - create("libs") { - from(files("../gradle/libs.versions.toml")) - } - } -} diff --git a/gradle.properties b/gradle.properties index 5c074317e..9078c9079 100644 --- a/gradle.properties +++ b/gradle.properties @@ -4,3 +4,5 @@ version = 2.5.0-SNAPSHOT org.gradle.caching = true org.gradle.configuration-cache = true org.gradle.parallel = true +org.gradle.tooling.parallel = true +org.gradle.isolated-projects = true diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index d34732d18..9146f30d2 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,8 +1,8 @@ [versions] -asm = "9.9.1" +asm = "9.10.1" lorenz = "0.5.8" hypo = "1.2.4" -serialize = "1.9.0" +serialize = "2.4.0" jst = "1.0.68+paper.2-SNAPSHOT" tinyRemapper = "0.12.0" # For paper-checkstyle plugin @@ -47,12 +47,11 @@ tinyRemapper = { module = "net.fabricmc:tiny-remapper", version.ref = "tinyRemap mockk = "io.mockk:mockk:1.14.9" # Gradle -gradle-licenser = "net.kyori:indra-licenser-spotless:4.0.0" -gradle-spotless = "com.diffplug.spotless:spotless-plugin-gradle:8.1.0" -gradle-shadow = "com.gradleup.shadow:com.gradleup.shadow.gradle.plugin:9.3.2" -gradle-kotlin-dsl = "org.gradle.kotlin.kotlin-dsl:org.gradle.kotlin.kotlin-dsl.gradle.plugin:6.4.2" -gradle-plugin-kotlin = { module = "org.jetbrains.kotlin.jvm:org.jetbrains.kotlin.jvm.gradle.plugin" } -gradle-plugin-publish = "com.gradle.publish:plugin-publish-plugin:2.0.0" +gradle-licenser = "net.kyori:indra-licenser-spotless:4.1.0" +gradle-spotless = "com.diffplug.spotless:spotless-plugin-gradle:8.10.2" +gradle-shadow = "com.gradleup.shadow:com.gradleup.shadow.gradle.plugin:9.6.1" +gradle-kotlin-dsl = "org.gradle.kotlin.kotlin-dsl:org.gradle.kotlin.kotlin-dsl.gradle.plugin:6.7.3" +gradle-plugin-publish = "com.gradle.publish:plugin-publish-plugin:2.1.0" # for renovate zCheckstyle = { module = "com.puppycrawl.tools:checkstyle", version.ref = "checkstyle" } diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 61285a659d17295f1de7c53e24fdf13ad755c379..eddabd2eef8d94a5437d6168ff9c87a78ff725b3 100644 GIT binary patch delta 39228 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 echo Please set the JAVA_HOME variable in your environment to match the 1>&2 echo location of your Java installation. 1>&2 -goto fail +"%COMSPEC%" /c exit 1 :findJavaFromJavaHome set JAVA_HOME=%JAVA_HOME:"=% @@ -65,29 +65,18 @@ echo. 1>&2 echo Please set the JAVA_HOME variable in your environment to match the 1>&2 echo location of your Java installation. 1>&2 -goto fail +"%COMSPEC%" /c exit 1 :execute @rem Setup the command line -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* +@rem Execute gradlew +@rem endlocal doesn't take effect until after the line is parsed and variables are expanded +@rem which allows us to clear the local environment before executing the java command +endlocal & "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* & call :exitWithErrorLevel -:end -@rem End local scope for the variables with windows NT shell -if %ERRORLEVEL% equ 0 goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -set EXIT_CODE=%ERRORLEVEL% -if %EXIT_CODE% equ 0 set EXIT_CODE=1 -if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% -exit /b %EXIT_CODE% - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega +:exitWithErrorLevel +@rem Use "%COMSPEC%" /c exit to allow operators to work properly in scripts +"%COMSPEC%" /c exit %ERRORLEVEL% diff --git a/license/copyright.txt b/license/copyright.txt index c78a29b46..5c88a4148 100644 --- a/license/copyright.txt +++ b/license/copyright.txt @@ -1,19 +1,22 @@ -paperweight is a Gradle plugin for the PaperMC project. +/* + * paperweight is a Gradle plugin for the PaperMC project. + * + * Copyright (c) 2023 Kyle Wood (DenWav) + * Contributors + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 only, no later versions. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + * USA + */ -Copyright (c) 2023 Kyle Wood (DenWav) - Contributors - -This library is free software; you can redistribute it and/or -modify it under the terms of the GNU Lesser General Public -License as published by the Free Software Foundation; -version 2.1 only, no later versions. - -This library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public -License along with this library; if not, write to the Free Software -Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 -USA diff --git a/paperweight-core/build.gradle.kts b/paperweight-core/build.gradle.kts index b89f06804..09aa7ebef 100644 --- a/paperweight-core/build.gradle.kts +++ b/paperweight-core/build.gradle.kts @@ -1,6 +1,6 @@ plugins { - `config-kotlin` - `config-publish` + id("config-kotlin") + id("config-publish") } dependencies { diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/PaperweightDependencyBridge.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/PaperweightDependencyBridge.kt index 8a513ee8c..da1b36ef5 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/PaperweightDependencyBridge.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/PaperweightDependencyBridge.kt @@ -24,19 +24,24 @@ package io.papermc.paperweight import io.papermc.paperweight.util.constants.JST_CLASSPATH_ATTRIBUTE import io.papermc.paperweight.util.constants.JST_CLASSPATH_CONFIG +import javax.inject.Inject import org.gradle.api.Plugin import org.gradle.api.Project +import org.gradle.api.artifacts.ConfigurationContainer import org.gradle.api.plugins.JavaPlugin abstract class PaperweightDependencyBridge : Plugin { + + @get:Inject + abstract val configurations: ConfigurationContainer + override fun apply(target: Project) { - target.configurations.register(JST_CLASSPATH_CONFIG) { - isCanBeConsumed = true + configurations.consumable(JST_CLASSPATH_CONFIG) { attributes { attribute(JST_CLASSPATH_ATTRIBUTE, true) } - extendsFrom(target.configurations.getByName(JavaPlugin.COMPILE_CLASSPATH_CONFIGURATION_NAME)) + extendsFrom(configurations.named(JavaPlugin.COMPILE_CLASSPATH_CONFIGURATION_NAME)) } - target.rootProject.dependencies.add(JST_CLASSPATH_CONFIG, target) } + // TODO } diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/PaperweightSourceGeneratorHelper.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/PaperweightSourceGeneratorHelper.kt index aff375947..accf6eecb 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/PaperweightSourceGeneratorHelper.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/PaperweightSourceGeneratorHelper.kt @@ -34,9 +34,9 @@ abstract class PaperweightSourceGeneratorHelper : Plugin { override fun apply(target: Project) = with(target) { val ext = extensions.create(PAPERWEIGHT_EXTENSION, PaperweightSourceGeneratorExt::class) - val minecraftJar by configurations.registering + val minecraftJar = configurations.register("minecraftJar") - val applyAts by tasks.registering { + val applyAts = tasks.register("applyAts") { inputJar.set(layout.file(minecraftJar.flatMap { it.elements }.map { it.single().asFile })) atFile.set(ext.atFile) } @@ -50,7 +50,7 @@ abstract class PaperweightSourceGeneratorHelper : Plugin { afterEvaluate { if (ext.addVanillaServerToImplementation.get()) { configurations.named("implementation") { - extendsFrom(vanilla.get()) + extendsFrom(vanilla) } } } diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/checkstyle/PaperCheckstyle.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/checkstyle/PaperCheckstyle.kt index 4f61a4ce5..11bfd005c 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/checkstyle/PaperCheckstyle.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/checkstyle/PaperCheckstyle.kt @@ -50,7 +50,7 @@ abstract class PaperCheckstyle : Plugin { toolVersion = LibraryVersions.CHECKSTYLE } - val mergeCheckstyleConfigs by target.tasks.registering() + val mergeCheckstyleConfigs = target.tasks.register("mergeCheckstyleConfigs") target.tasks.withType(PaperCheckstyleTask::class.java).configureEach { rootPath.convention(layout.settingsDirectory.asFile.path) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/PaperweightCore.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/PaperweightCore.kt index 2206dff34..cbfbc47f2 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/PaperweightCore.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/PaperweightCore.kt @@ -77,7 +77,7 @@ abstract class PaperweightCore : Plugin { delete(layout.cache) } - target.configurations.create(REMAPPER_CONFIG) { + target.configurations.register(REMAPPER_CONFIG) { defaultDependencies { // Join list to avoid relocations breaking the string val coordinates = "${listOf("net", "fabricmc").joinToString(".")}:tiny-remapper:${LibraryVersions.TINY_REMAPPER}:fat" @@ -85,8 +85,8 @@ abstract class PaperweightCore : Plugin { add(remapper) } } - target.configurations.create(PAPERCLIP_CONFIG) - val macheConfig = target.configurations.create(MACHE_CONFIG) { + target.configurations.register(PAPERCLIP_CONFIG) + val macheConfig = target.configurations.register(MACHE_CONFIG) { attributes.attribute(MacheOutput.ATTRIBUTE, objects.named(MacheOutput.ZIP)) } target.configurations.register(MACHE_CODEBOOK_CONFIG) { isTransitive = false } @@ -98,7 +98,7 @@ abstract class PaperweightCore : Plugin { extendsFrom(macheConfig) } target.configurations.register(MACHE_MINECRAFT_CONFIG) { - extendsFrom(macheMinecraftLibrariesConfig.get()) + extendsFrom(macheMinecraftLibrariesConfig) } target.configurations.consumable(MAPPED_JAR_OUTGOING_CONFIG) // For source generator modules target.configurations.register(JST_CONFIG) { @@ -113,14 +113,14 @@ abstract class PaperweightCore : Plugin { attribute(JST_CLASSPATH_ATTRIBUTE, true) } extendsFrom( - target.configurations.getByName(MACHE_MINECRAFT_CONFIG), - target.configurations.getByName(JavaPlugin.COMPILE_CLASSPATH_CONFIGURATION_NAME) + target.configurations.named(MACHE_MINECRAFT_CONFIG), + target.configurations.named(JavaPlugin.COMPILE_CLASSPATH_CONFIGURATION_NAME) ) } // impl extends minecraft target.configurations.named(JavaPlugin.IMPLEMENTATION_CONFIGURATION_NAME) { - extendsFrom(macheMinecraftLibrariesConfig.get()) + extendsFrom(macheMinecraftLibrariesConfig) } if (target.providers.gradleProperty("paperweight.dev").orNull == "true") { diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/ForkConfig.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/ForkConfig.kt index 737859282..ef0a668b0 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/ForkConfig.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/ForkConfig.kt @@ -45,7 +45,7 @@ abstract class ForkConfig @Inject constructor( return configName } - val rootDirectory: DirectoryProperty = objects.directoryProperty().convention(project.rootProject.layout.projectDirectory).finalizedOnRead() + val rootDirectory: DirectoryProperty = objects.directoryProperty().convention(project.isolated.rootProject.projectDirectory).finalizedOnRead() val serverDirectory: DirectoryProperty = objects.dirFrom(rootDirectory, providers.provider { "$name-server" }) val serverPatchesDir: DirectoryProperty = objects.dirFrom(serverDirectory, "minecraft-patches") val rejectsDir: DirectoryProperty = objects.dirFrom(serverPatchesDir, "rejected") diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/PaperExtension.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/PaperExtension.kt index a6b670f81..fb7f3ea81 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/PaperExtension.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/extension/PaperExtension.kt @@ -31,7 +31,7 @@ import org.gradle.api.model.ObjectFactory abstract class PaperExtension @Inject constructor(objects: ObjectFactory, project: Project) { - val rootDirectory: DirectoryProperty = objects.directoryProperty().convention(project.rootProject.layout.projectDirectory) + val rootDirectory: DirectoryProperty = objects.directoryProperty().convention(project.isolated.rootProject.projectDirectory) val paperServerDir: DirectoryProperty = objects.dirFrom(rootDirectory, "paper-server") val serverPatchesDir: DirectoryProperty = objects.dirFrom(paperServerDir, "patches") val rejectsDir: DirectoryProperty = objects.dirFrom(serverPatchesDir, "rejected") diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/AllTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/AllTasks.kt index a859c0a6b..e6e7d9bb0 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/AllTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/AllTasks.kt @@ -23,13 +23,12 @@ package io.papermc.paperweight.core.taskcontainers import io.papermc.paperweight.DownloadService -import io.papermc.paperweight.core.extension.PaperweightCoreExtension -import io.papermc.paperweight.core.util.coreExt import io.papermc.paperweight.tasks.* import io.papermc.paperweight.util.* import io.papermc.paperweight.util.constants.* import java.nio.file.Path import org.gradle.api.Project +import org.gradle.api.artifacts.ConfigurationContainer import org.gradle.api.artifacts.component.ModuleComponentIdentifier import org.gradle.api.plugins.JavaPlugin import org.gradle.api.provider.Provider @@ -39,13 +38,13 @@ import org.gradle.kotlin.dsl.* @Suppress("MemberVisibilityCanBePrivate") open class AllTasks( project: Project, + configurations: ConfigurationContainer = project.configurations, tasks: TaskContainer = project.tasks, cache: Path = project.layout.cache, - extension: PaperweightCoreExtension = project.coreExt, downloadService: Provider = project.download ) : InitialTasks(project) { - val downloadMcLibrariesSources by tasks.registering { + val downloadMcLibrariesSources = tasks.register("downloadMcLibrariesSources") { mcLibrariesFile.set(extractFromBundler.flatMap { it.serverLibrariesTxt }) repositories.set(listOf(MC_LIBRARY_URL, MAVEN_CENTRAL_URL)) outputDir.set(cache.resolve(MINECRAFT_SOURCES_PATH)) @@ -54,9 +53,9 @@ open class AllTasks( downloader.set(downloadService) } - val downloadRuntimeClasspathSources by tasks.registering { + val downloadRuntimeClasspathSources = tasks.register("downloadRuntimeClasspathSources") { paperDependencies.set( - project.configurations.named(JavaPlugin.RUNTIME_CLASSPATH_CONFIGURATION_NAME).map { configuration -> + configurations.named(JavaPlugin.RUNTIME_CLASSPATH_CONFIGURATION_NAME).map { configuration -> val view = configuration.incoming.artifactView { componentFilter { it is ModuleComponentIdentifier } } diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/CoreTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/CoreTasks.kt index 5190bc1d1..754e02360 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/CoreTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/CoreTasks.kt @@ -38,6 +38,7 @@ import io.papermc.paperweight.util.constants.* import io.papermc.paperweight.util.data.mache.* import java.nio.file.Files import org.gradle.api.Project +import org.gradle.api.artifacts.ConfigurationContainer import org.gradle.api.provider.Property import org.gradle.api.tasks.TaskContainer import org.gradle.kotlin.dsl.* @@ -45,37 +46,38 @@ import org.gradle.kotlin.dsl.* class CoreTasks( val project: Project, val mache: Property, + val configurations: ConfigurationContainer = project.configurations, tasks: TaskContainer = project.tasks ) : AllTasks(project) { lateinit var paperPatchingTasks: MinecraftPatchingTasks - val macheRemapJar by tasks.registering(RunCodebook::class) { + val macheRemapJar = tasks.register("macheRemapJar") { serverJar.set(extractFromBundler.flatMap { it.serverJar }) codebookArgs.set(mache.map { it.remapperArgs }) - codebookClasspath.from(project.configurations.named(MACHE_CODEBOOK_CONFIG)) - minecraftClasspath.from(project.configurations.named(MACHE_MINECRAFT_LIBRARIES_CONFIG)) - constants.from(project.configurations.named(MACHE_CONSTANTS_CONFIG)) + codebookClasspath.from(configurations.named(MACHE_CODEBOOK_CONFIG)) + minecraftClasspath.from(configurations.named(MACHE_MINECRAFT_LIBRARIES_CONFIG)) + constants.from(configurations.named(MACHE_CONSTANTS_CONFIG)) outputJar.set(layout.cache.resolve(FINAL_REMAPPED_CODEBOOK_JAR)) } - val macheDecompileJar by tasks.registering(DecompileJar::class) { + val macheDecompileJar = tasks.register("macheDecompileJar") { inputJar.set(macheRemapJar.flatMap { it.outputJar }) decompilerArgs.set(mache.map { it.decompilerArgs }) - minecraftClasspath.from(project.configurations.named(MACHE_MINECRAFT_LIBRARIES_CONFIG)) - decompiler.from(project.configurations.named(MACHE_DECOMPILER_CONFIG)) + minecraftClasspath.from(configurations.named(MACHE_MINECRAFT_LIBRARIES_CONFIG)) + decompiler.from(configurations.named(MACHE_DECOMPILER_CONFIG)) outputJar.set(layout.cache.resolve(FINAL_DECOMPILE_JAR)) } - val collectPaperATsFromPatches by tasks.registering(CollectATsFromPatches::class) { + val collectPaperATsFromPatches = tasks.register("collectPaperATsFromPatches") { basePatchDir.set(project.coreExt.paper.basePatchDir.fileExists()) featurePatchDir.set(project.coreExt.paper.featurePatchDir.fileExists()) } - val mergePaperATs by tasks.registering { + val mergePaperATs = tasks.register("mergePaperATs") { firstFile.set(project.coreExt.paper.additionalAts.fileExists()) secondFile.set(collectPaperATsFromPatches.flatMap { it.outputFile }) } @@ -95,41 +97,41 @@ class CoreTasks( } private fun SetupMinecraftSources.configureSetupMacheSources() { - mache.from(project.configurations.named(MACHE_CONFIG)) + mache.from(configurations.named(MACHE_CONFIG)) oldPaperCommit.convention(project.coreExt.updatingMinecraft.oldPaperCommit) inputFile.set(macheDecompileJar.flatMap { it.outputJar }) predicate.set { Files.isRegularFile(it) && it.toString().endsWith(".java") } } - val setupMacheSources by tasks.registering(SetupMinecraftSources::class) { + val setupMacheSources = tasks.register("setupMacheSources") { description = "Setup Minecraft source dir (applying mache patches and paper ATs)." configureSetupMacheSources() libraryImports.set(importLibraryFiles.flatMap { it.outputDir }) outputZip.set(layout.cache.resolve(BASE_PROJECT).resolve("sources.zip")) atFile.set(mergePaperATs.flatMap { it.outputFile }) - ats.jstClasspath.from(project.configurations.named(MACHE_MINECRAFT_LIBRARIES_CONFIG)) - ats.jst.from(project.configurations.named(JST_CONFIG)) + ats.jstClasspath.from(configurations.named(MACHE_MINECRAFT_LIBRARIES_CONFIG)) + ats.jst.from(configurations.named(JST_CONFIG)) validateATs.set(project.coreExt.validateATs) } - val extractMacheSources by tasks.registering(ExtractMinecraftSources::class) { + val extractMacheSources = tasks.register("extractMacheSources") { zip.set(setupMacheSources.flatMap { it.outputZip }) outputDir.set(layout.cache.resolve(BASE_PROJECT).resolve("sources")) } - val setupMacheSourcesForDevBundle by tasks.registering(SetupMinecraftSources::class) { + val setupMacheSourcesForDevBundle = tasks.register("setupMacheSourcesForDevBundle") { description = "Setup Minecraft source dir (applying mache patches)." configureSetupMacheSources() outputZip.set(layout.cache.resolve(BASE_PROJECT).resolve("sources_dev_bundle.zip")) } - val extractMacheSourcesForDevBundle by tasks.registering(ExtractMinecraftSources::class) { + val extractMacheSourcesForDevBundle = tasks.register("extractMacheSourcesForDevBundle") { zip.set(setupMacheSourcesForDevBundle.flatMap { it.outputZip }) outputDir.set(layout.cache.resolve(BASE_PROJECT).resolve("sources_dev_bundle")) } - val setupMacheResources by tasks.registering(SetupMinecraftSources::class) { + val setupMacheResources = tasks.register("setupMacheResources") { description = "Setup Minecraft resources dir" inputFile.set(extractFromBundler.flatMap { it.serverJar }) @@ -137,7 +139,7 @@ class CoreTasks( outputZip.set(layout.cache.resolve(BASE_PROJECT).resolve("resources.zip")) } - val extractMacheResources by tasks.registering(ExtractMinecraftSources::class) { + val extractMacheResources = tasks.register("extractMacheResources") { zip.set(setupMacheResources.flatMap { it.outputZip }) outputDir.set(layout.cache.resolve(BASE_PROJECT).resolve("resources")) } @@ -164,7 +166,7 @@ class CoreTasks( } if (!hasFork) { - val setupPaperScript by project.tasks.registering(SetupPaperScript::class) { + val setupPaperScript = project.tasks.register("setupPaperScript") { group = GENERAL_TASK_GROUP description = "Creates a util script and installs it into path" diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/DevBundleTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/DevBundleTasks.kt index fa54119e1..a080493d6 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/DevBundleTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/DevBundleTasks.kt @@ -28,6 +28,7 @@ import io.papermc.paperweight.tasks.* import io.papermc.paperweight.util.* import io.papermc.paperweight.util.constants.* import org.gradle.api.Project +import org.gradle.api.artifacts.ConfigurationContainer import org.gradle.api.artifacts.repositories.MavenArtifactRepository import org.gradle.api.file.RegularFile import org.gradle.api.plugins.JavaPluginExtension @@ -35,27 +36,27 @@ import org.gradle.api.provider.Provider import org.gradle.api.tasks.TaskContainer import org.gradle.kotlin.dsl.* -@Suppress("MemberVisibilityCanBePrivate") class DevBundleTasks( project: Project, private val coreTasks: CoreTasks, + private val configurations: ConfigurationContainer = project.configurations, tasks: TaskContainer = project.tasks, ) { - val serverBundlerForDevBundle by tasks.registering { + val serverBundlerForDevBundle = tasks.register("serverBundlerForDevBundle") { mainClass.set(project.coreExt.mainClass) - paperclip.from(project.configurations.named(PAPERCLIP_CONFIG)) + paperclip.from(configurations.named(PAPERCLIP_CONFIG)) serverLibrariesList.set(coreTasks.extractFromBundler.flatMap { it.serverLibrariesList }) vanillaBundlerJar.set(coreTasks.downloadServerJar.flatMap { it.outputJar }) } - val paperclipForDevBundle by tasks.registering { + val paperclipForDevBundle = tasks.register("paperclipForDevBundle") { bundlerJar.set(serverBundlerForDevBundle.flatMap { it.outputZip }) libraryChangesJson.set(serverBundlerForDevBundle.flatMap { it.libraryChangesJson }) originalBundlerJar.set(coreTasks.downloadServerJar.flatMap { it.outputJar }) mcVersion.set(project.coreExt.minecraftVersion) } - val generateDevelopmentBundle by tasks.registering { + val generateDevelopmentBundle = tasks.register("generateDevelopmentBundle") { group = "bundling" devBundleFile.set(project.layout.buildDirectory.file("libs/paperweight-development-bundle-${project.version}.zip")) @@ -82,7 +83,7 @@ class DevBundleTasks( } generateDevelopmentBundle { macheUrl.set(project.repositories.named(MACHE_REPO_NAME).map { it.url.toString() }) - macheDep.set(determineArtifactCoordinates(project.configurations.getByName(MACHE_CONFIG)).single()) + macheDep.set(determineArtifactCoordinates(configurations.named(MACHE_CONFIG)).map { it.single() }) } } } diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/InitialTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/InitialTasks.kt index 54641e0f0..8ead8bf62 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/InitialTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/InitialTasks.kt @@ -35,7 +35,6 @@ import org.gradle.api.provider.Provider import org.gradle.api.tasks.TaskContainer import org.gradle.kotlin.dsl.* -@Suppress("MemberVisibilityCanBePrivate") open class InitialTasks( project: Project, tasks: TaskContainer = project.tasks, @@ -44,7 +43,7 @@ open class InitialTasks( downloadService: Provider = project.download ) { - val downloadMcManifest by tasks.registering { + val downloadMcManifest = tasks.register("downloadMcManifest") { url.set(project.coreExt.minecraftManifestUrl) outputFile.set(cache.resolve(MC_MANIFEST)) @@ -54,7 +53,7 @@ open class InitialTasks( } private val mcManifest = downloadMcManifest.flatMap { it.outputFile }.map { gson.fromJson(it) } - val downloadMcVersionManifest by tasks.registering { + val downloadMcVersionManifest = tasks.register("downloadMcVersionManifest") { url.set( mcManifest.zip(extension.minecraftVersion) { manifest, version -> manifest.versions.first { it.id == version }.url @@ -71,14 +70,14 @@ open class InitialTasks( } private val versionManifest = downloadMcVersionManifest.flatMap { it.outputFile }.map { gson.fromJson(it) } - val downloadServerJar by tasks.registering { + val downloadServerJar = tasks.register("downloadServerJar") { downloadUrl.set(versionManifest.map { version -> version.serverDownload().url }) expectedHash.set(versionManifest.map { version -> version.serverDownload().hash() }) downloader.set(downloadService) } - val extractFromBundler by tasks.registering { + val extractFromBundler = tasks.register("extractFromBundler") { bundlerJar.set(downloadServerJar.flatMap { it.outputJar }) versionJson.set(cache.resolve(SERVER_VERSION_JSON)) diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt index bdc6179e5..8656f398b 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/MinecraftPatchingTasks.kt @@ -41,6 +41,7 @@ import io.papermc.paperweight.util.constants.* import java.nio.file.Path import org.gradle.api.Project import org.gradle.api.Task +import org.gradle.api.artifacts.ConfigurationContainer import org.gradle.api.file.Directory import org.gradle.api.file.DirectoryProperty import org.gradle.api.file.RegularFileProperty @@ -68,6 +69,7 @@ class MinecraftPatchingTasks( private val outputSrc: Path = outputRoot.resolve("src/minecraft/java"), private val outputResources: Path = outputRoot.resolve("src/minecraft/resources"), private val outputSrcFile: Path = outputRoot.resolve("file/src/minecraft/java"), + private val configurations: ConfigurationContainer = project.configurations, private val tasks: TaskContainer = project.tasks ) { private val taskGroup = if (readOnly) "upstream minecraft patching" else "minecraft patching" @@ -202,8 +204,8 @@ class MinecraftPatchingTasks( libraryImports.set(importLibFiles.flatMap { it.outputDir }) atFile.set(mergeCollectedAts.flatMap { it.outputFile }) - ats.jst.from(project.configurations.named(JST_CONFIG)) - ats.jstClasspath.from(project.configurations.named(JST_CLASSPATH_CONFIG)) + ats.jst.from(configurations.named(JST_CONFIG)) + ats.jstClasspath.from(configurations.named(JST_CLASSPATH_CONFIG)) validateATs.set(project.coreExt.validateATs) } @@ -248,8 +250,8 @@ class MinecraftPatchingTasks( patches.set(sourcePatchDir) gitFilePatches.set(this@MinecraftPatchingTasks.gitFilePatches) - ats.jstClasspath.from(project.configurations.named(MACHE_MINECRAFT_CONFIG)) - ats.jst.from(project.configurations.named(JST_CONFIG)) + ats.jstClasspath.from(configurations.named(MACHE_MINECRAFT_CONFIG)) + ats.jst.from(configurations.named(JST_CONFIG)) atFile.set(additionalAts.fileExists()) atFileOut.set(additionalAts.fileExists()) identifier = configName diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt index c7a71da14..7bc6f5ba5 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/taskcontainers/PatchingTasks.kt @@ -41,6 +41,7 @@ import io.papermc.paperweight.util.set import java.nio.file.Path import org.gradle.api.Project import org.gradle.api.Task +import org.gradle.api.artifacts.ConfigurationContainer import org.gradle.api.file.Directory import org.gradle.api.file.DirectoryProperty import org.gradle.api.file.RegularFileProperty @@ -65,6 +66,7 @@ class PatchingTasks( private val gitFilePatches: Provider, private val filterPatches: Provider, private val outputDir: Path, + private val configurations: ConfigurationContainer = project.configurations, private val tasks: TaskContainer = project.tasks, ) { private val namePart: String = if (readOnly) "${forkName.capitalized()}${patchSetName.capitalized()}" else patchSetName.capitalized() @@ -164,8 +166,8 @@ class PatchingTasks( identifier.set(namePart) atFile.set(mergeCollectedAts.flatMap { it.outputFile }) - ats.jst.from(project.configurations.named(JST_CONFIG)) - ats.jstClasspath.from(project.configurations.named(JST_CLASSPATH_CONFIG)) + ats.jst.from(configurations.named(JST_CONFIG)) + ats.jstClasspath.from(configurations.named(JST_CLASSPATH_CONFIG)) validateAts.set(this@PatchingTasks.validateAts) } @@ -208,8 +210,8 @@ class PatchingTasks( patches.set(filePatchDir) gitFilePatches.set(this@PatchingTasks.gitFilePatches) - ats.jstClasspath.from(project.configurations.named(JST_CLASSPATH_CONFIG)) - ats.jst.from(project.configurations.named(JST_CONFIG)) + ats.jstClasspath.from(configurations.named(JST_CLASSPATH_CONFIG)) + ats.jst.from(configurations.named(JST_CONFIG)) atFile.set(additionalAts.fileExists()) atFileOut.set(additionalAts.fileExists()) identifier = "$forkName $patchSetName" diff --git a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/RunNestedBuild.kt b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/RunNestedBuild.kt index 97b625496..f4d8449e8 100644 --- a/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/RunNestedBuild.kt +++ b/paperweight-core/src/main/kotlin/io/papermc/paperweight/core/tasks/RunNestedBuild.kt @@ -40,7 +40,6 @@ import org.gradle.api.tasks.UntrackedTask import org.gradle.internal.build.NestedRootBuildRunner import org.gradle.internal.classpath.ClassPath import org.gradle.internal.service.ServiceRegistry -import org.gradle.util.GradleVersion @UntrackedTask(because = "Nested build does it's own up-to-date checking") abstract class RunNestedBuild : BaseTask() { @@ -70,27 +69,12 @@ abstract class RunNestedBuild : BaseTask() { params.systemPropertiesArgs[PAPERWEIGHT_DEBUG] = System.getProperty(PAPERWEIGHT_DEBUG, "false") - val legacy = GradleVersion.current() < GradleVersion.version("9.5.0") // Gradle changed the parameters in 9.5.0 - val method = when { - legacy -> NestedRootBuildRunner::class.java.getDeclaredMethod( - "runNestedRootBuild", - String::class.java, - StartParameterInternal::class.java, - ServiceRegistry::class.java - ) - else -> NestedRootBuildRunner::class.java.getDeclaredMethod( - "runNestedRootBuild", - String::class.java, - StartParameterInternal::class.java, - ServiceRegistry::class.java, - ClassPath::class.java - ) - } - val args = when { - legacy -> arrayOf(null, params, services) - else -> arrayOf(null, params, services, ClassPath.EMPTY) - } - - method.invoke(null, *args) + NestedRootBuildRunner::class.java.getDeclaredMethod( + "runNestedRootBuild", + String::class.java, + StartParameterInternal::class.java, + ServiceRegistry::class.java, + ClassPath::class.java + ).invoke(null, null, params, services, ClassPath.EMPTY) } } diff --git a/paperweight-lib/build.gradle.kts b/paperweight-lib/build.gradle.kts index 99717f9a5..5a3e4f26a 100644 --- a/paperweight-lib/build.gradle.kts +++ b/paperweight-lib/build.gradle.kts @@ -1,5 +1,5 @@ plugins { - `config-kotlin` + id("config-kotlin") id("net.kyori.blossom") version "2.2.0" } @@ -40,15 +40,24 @@ dependencies { testImplementation(libs.mockk) } +configurations.consumable("sourcesJar") { + attributes { + attribute(Usage.USAGE_ATTRIBUTE, named(Usage.JAVA_RUNTIME)) + attribute(Category.CATEGORY_ATTRIBUTE, named(Category.DOCUMENTATION)) + attribute(DocsType.DOCS_TYPE_ATTRIBUTE, named(DocsType.SOURCES)) + } + outgoing.artifact(tasks.named("sourcesJar")) +} + val testClassesJar = tasks.register("testClassesJar") { archiveClassifier.set("test-classes") - from(sourceSets.test.get().output.classesDirs) - dependsOn(sourceSets.test.get().classesTaskName) + from(sourceSets.test.map { it.output.classesDirs }) + dependsOn(sourceSets.test.map { it.classesTaskName }) } configurations.consumable("testClassesJar") { attributes { - attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage.JAVA_RUNTIME)) - attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, objects.named(LibraryElements.JAR)) + attribute(Usage.USAGE_ATTRIBUTE, named(Usage.JAVA_RUNTIME)) + attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, named(LibraryElements.JAR)) } outgoing.artifact(testClassesJar) } diff --git a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/dependencies.kt b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/dependencies.kt index edcc39ac6..093d2616a 100644 --- a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/dependencies.kt +++ b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/dependencies.kt @@ -25,9 +25,13 @@ package io.papermc.paperweight.util import org.gradle.api.artifacts.Configuration import org.gradle.api.artifacts.ModuleDependency import org.gradle.api.artifacts.repositories.RepositoryContentDescriptor +import org.gradle.api.provider.Provider data class MavenDep(val url: String, val coordinates: List) +fun determineArtifactCoordinates(configuration: Provider): Provider> = + configuration.map { config -> determineArtifactCoordinates(config) } + fun determineArtifactCoordinates(configuration: Configuration): List { return configuration.dependencies.filterIsInstance().map { dep -> sequenceOf( diff --git a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/utils.kt b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/utils.kt index 6acf4525b..d1a3c3f68 100644 --- a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/utils.kt +++ b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/utils.kt @@ -58,7 +58,6 @@ import kotlinx.coroutines.ExecutorCoroutineDispatcher import kotlinx.coroutines.asCoroutineDispatcher import org.cadixdev.lorenz.merge.MergeResult import org.gradle.api.Project -import org.gradle.api.Task import org.gradle.api.artifacts.ConfigurationContainer import org.gradle.api.attributes.Attribute import org.gradle.api.file.Directory @@ -72,12 +71,12 @@ import org.gradle.api.logging.LogLevel import org.gradle.api.logging.Logging import org.gradle.api.model.ObjectFactory import org.gradle.api.plugins.JavaPluginExtension +import org.gradle.api.project.IsolatedProject import org.gradle.api.provider.Property import org.gradle.api.provider.Provider import org.gradle.api.provider.ProviderFactory import org.gradle.api.tasks.Input import org.gradle.api.tasks.Internal -import org.gradle.api.tasks.TaskContainer import org.gradle.jvm.toolchain.JavaLanguageVersion import org.gradle.jvm.toolchain.JavaLauncher import org.gradle.jvm.toolchain.JavaToolchainService @@ -104,6 +103,7 @@ val ProjectLayout.cache: Path get() = projectDirectory.dir(".gradle/$CACHE_PATH").path fun ProjectLayout.cacheDir(path: String) = projectDirectory.dir(".gradle/$CACHE_PATH").dir(path) +fun IsolatedProject.cacheDir(path: String) = projectDirectory.dir(".gradle/$CACHE_PATH").dir(path) fun Project.offlineMode(): Boolean = gradle.startParameter.isOffline @@ -113,7 +113,9 @@ fun Provider.fileExists(): Provider { @Suppress("UNCHECKED_CAST") val Project.download: Provider - get() = gradle.sharedServices.registrations.getByName(DOWNLOAD_SERVICE_NAME).service as Provider + get() = gradle.sharedServices.registerIfAbsent(DOWNLOAD_SERVICE_NAME, DownloadService::class) { + parameters.projectPath.set(isolated.rootProject.projectDirectory) + } val Project.gitMutationLockService: Provider get() = gradle.sharedServices.registerIfAbsent(GIT_MUTATION_LOCK_SERVICE_NAME, GitMutationLockService::class) { @@ -296,9 +298,6 @@ fun emptyMergeResult(): MergeResult { return emptyMergeResult as MergeResult } -inline fun TaskContainer.registering(noinline configuration: T.() -> Unit) = registering(T::class, configuration) -inline fun TaskContainer.registering() = registering(T::class) - enum class HashingAlgorithm(val algorithmName: String) { SHA256("SHA-256"), SHA1("SHA-1"); @@ -483,7 +482,7 @@ inline fun ObjectFactory.providerSet( fun Project.upstreamsDirectory(): Provider { val workDirProp = providers.gradleProperty(UPSTREAM_WORK_DIR_PROPERTY) val workDirFromProp = layout.dir(workDirProp.map { File(it) }) - return workDirFromProp.orElse(rootProject.layout.cacheDir(UPSTREAMS)) + return workDirFromProp.orElse(isolated.rootProject.cacheDir(UPSTREAMS)) } private val ioDispatcherCount = AtomicInteger(0) diff --git a/paperweight-userdev/build.gradle.kts b/paperweight-userdev/build.gradle.kts index 391576bed..51293a3cf 100644 --- a/paperweight-userdev/build.gradle.kts +++ b/paperweight-userdev/build.gradle.kts @@ -1,6 +1,6 @@ plugins { - `config-kotlin` - `config-publish` + id("config-kotlin") + id("config-publish") } dependencies { diff --git a/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/PaperweightUser.kt b/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/PaperweightUser.kt index b5ce5aefe..8641bbf2b 100644 --- a/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/PaperweightUser.kt +++ b/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/PaperweightUser.kt @@ -84,7 +84,7 @@ abstract class PaperweightUser : Plugin { parameters.projectPath.set(target.projectDir) } - val cleanCache by target.tasks.registering { + val cleanCache = target.tasks.register("cleanCache") { group = GENERAL_TASK_GROUP description = "Delete the project-local paperweight-userdev setup cache." delete(target.layout.cache) @@ -127,7 +127,7 @@ abstract class PaperweightUser : Plugin { createConfigurations(target, target.provider { userdevSetup }, setupTask, userdev) - val reobfJar by target.tasks.registering { + val reobfJar = target.tasks.register("reobfJar") { group = GENERAL_TASK_GROUP description = "Remap the compiled plugin jar to Spigot's obfuscated runtime names." diff --git a/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/internal/setup/SetupHandler.kt b/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/internal/setup/SetupHandler.kt index 61577287e..1eae1c233 100644 --- a/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/internal/setup/SetupHandler.kt +++ b/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/internal/setup/SetupHandler.kt @@ -40,6 +40,7 @@ import org.gradle.api.file.FileCollection import org.gradle.api.file.ProjectLayout import org.gradle.api.logging.Logger import org.gradle.api.provider.Property +import org.gradle.api.provider.Provider import org.gradle.api.tasks.TaskProvider import org.gradle.internal.logging.progress.ProgressLogger import org.gradle.internal.logging.progress.ProgressLoggerFactory @@ -97,7 +98,7 @@ interface SetupHandler { val project: Project, val dependencyFactory: DependencyFactory, val javaToolchainService: JavaToolchainService, - val devBundleCoordinates: String, + val devBundleCoordinates: Provider, val setupTask: TaskProvider, val layout: ProjectLayout = project.layout, ) { @@ -110,7 +111,7 @@ interface SetupHandler { project, dependencyFactory, javaToolchainService, - determineArtifactCoordinates(project.configurations.getByName(DEV_BUNDLE_CONFIG)).single(), + determineArtifactCoordinates(project.configurations.named(DEV_BUNDLE_CONFIG)).map { it.single() }, setupTask, ) } diff --git a/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/internal/setup/SetupHandlerImpl.kt b/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/internal/setup/SetupHandlerImpl.kt index dad66371c..dd9da62e9 100644 --- a/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/internal/setup/SetupHandlerImpl.kt +++ b/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/internal/setup/SetupHandlerImpl.kt @@ -153,7 +153,7 @@ class SetupHandlerImpl( if (injectServerJar.get()) { dependencySet.add(context.dependencyFactory.create(context.layout.files(context.setupTask.flatMap { it.mappedServerJar }))) } - dependencySet.add(context.dependencyFactory.create(context.devBundleCoordinates)) + dependencySet.add(context.dependencyFactory.create(context.devBundleCoordinates.get())) } override fun populateRuntimeConfiguration( diff --git a/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/internal/setup/v7/SetupHandlerImplV7.kt b/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/internal/setup/v7/SetupHandlerImplV7.kt index d9152f382..9840bf163 100644 --- a/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/internal/setup/v7/SetupHandlerImplV7.kt +++ b/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/internal/setup/v7/SetupHandlerImplV7.kt @@ -163,7 +163,7 @@ class SetupHandlerImplV7( if (injectServerJar.get()) { dependencySet.add(context.dependencyFactory.create(context.layout.files(context.setupTask.flatMap { it.mappedServerJar }))) } - dependencySet.add(context.dependencyFactory.create(context.devBundleCoordinates)) + dependencySet.add(context.dependencyFactory.create(context.devBundleCoordinates.get())) } override fun populateRuntimeConfiguration( diff --git a/settings.gradle.kts b/settings.gradle.kts index 8788af058..0d64c4099 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,3 +1,7 @@ +pluginManagement { + includeBuild("build-logic") +} + plugins { id("org.gradle.toolchains.foojay-resolver-convention") version "1.0.0" } From 2e33c41452f55bf8c02b9e25fe6cbacdaf50f5df Mon Sep 17 00:00:00 2001 From: Toffikk Date: Sun, 20 Sep 2026 19:16:26 +0200 Subject: [PATCH 250/257] Backport fix --- build-logic/src/main/kotlin/config-publish.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build-logic/src/main/kotlin/config-publish.gradle.kts b/build-logic/src/main/kotlin/config-publish.gradle.kts index 490f183f5..325aeda05 100644 --- a/build-logic/src/main/kotlin/config-publish.gradle.kts +++ b/build-logic/src/main/kotlin/config-publish.gradle.kts @@ -61,7 +61,7 @@ private fun SetProperty.setFrom(configurations: List("sourcesJar") { - from(zipTree(sourcesJarResolvable.map { it.singleFile })) { + from(zipTree(sourcesJarResolvable.flatMap { it.elements.map { it.single().asFile } })) { exclude("META-INF/**") } } From b22cf13a22f91f4fcb89c3d6b5677f3f371c7d59 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Sun, 20 Sep 2026 19:39:20 +0200 Subject: [PATCH 251/257] backport more changes from the PR branch --- build-logic/src/main/kotlin/config-publish.gradle.kts | 2 -- paperweight-lib/build.gradle.kts | 4 ++-- .../main/kotlin/io/papermc/paperweight/util/dependencies.kt | 3 ++- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/build-logic/src/main/kotlin/config-publish.gradle.kts b/build-logic/src/main/kotlin/config-publish.gradle.kts index 325aeda05..7717bc4a7 100644 --- a/build-logic/src/main/kotlin/config-publish.gradle.kts +++ b/build-logic/src/main/kotlin/config-publish.gradle.kts @@ -1,6 +1,4 @@ import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar -import org.gradle.kotlin.dsl.named -import org.gradle.kotlin.dsl.withType plugins { id("com.gradleup.shadow") diff --git a/paperweight-lib/build.gradle.kts b/paperweight-lib/build.gradle.kts index 5a3e4f26a..941c6af44 100644 --- a/paperweight-lib/build.gradle.kts +++ b/paperweight-lib/build.gradle.kts @@ -46,13 +46,13 @@ configurations.consumable("sourcesJar") { attribute(Category.CATEGORY_ATTRIBUTE, named(Category.DOCUMENTATION)) attribute(DocsType.DOCS_TYPE_ATTRIBUTE, named(DocsType.SOURCES)) } - outgoing.artifact(tasks.named("sourcesJar")) + outgoing.artifact(tasks.sourcesJar) } val testClassesJar = tasks.register("testClassesJar") { archiveClassifier.set("test-classes") from(sourceSets.test.map { it.output.classesDirs }) - dependsOn(sourceSets.test.map { it.classesTaskName }) + dependsOn(sourceSets.test.get().classesTaskName) } configurations.consumable("testClassesJar") { attributes { diff --git a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/dependencies.kt b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/dependencies.kt index 093d2616a..3001cbf1e 100644 --- a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/dependencies.kt +++ b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/dependencies.kt @@ -22,6 +22,7 @@ package io.papermc.paperweight.util +import org.gradle.api.NamedDomainObjectProvider import org.gradle.api.artifacts.Configuration import org.gradle.api.artifacts.ModuleDependency import org.gradle.api.artifacts.repositories.RepositoryContentDescriptor @@ -29,7 +30,7 @@ import org.gradle.api.provider.Provider data class MavenDep(val url: String, val coordinates: List) -fun determineArtifactCoordinates(configuration: Provider): Provider> = +fun determineArtifactCoordinates(configuration: NamedDomainObjectProvider): Provider> = configuration.map { config -> determineArtifactCoordinates(config) } fun determineArtifactCoordinates(configuration: Configuration): List { From fcc1350e68de584201a2d5593476b7dc000c0e34 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Sun, 20 Sep 2026 21:19:14 +0200 Subject: [PATCH 252/257] [backport] Fix kotlin compiler deprecation warning --- build-logic/src/main/kotlin/config-kotlin.gradle.kts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/build-logic/src/main/kotlin/config-kotlin.gradle.kts b/build-logic/src/main/kotlin/config-kotlin.gradle.kts index 81acf6b4e..5f3ad3637 100644 --- a/build-logic/src/main/kotlin/config-kotlin.gradle.kts +++ b/build-logic/src/main/kotlin/config-kotlin.gradle.kts @@ -2,6 +2,7 @@ import com.diffplug.gradle.spotless.SpotlessExtension import net.kyori.indra.licenser.spotless.IndraSpotlessLicenserExtension import org.gradle.api.tasks.testing.logging.TestExceptionFormat import org.gradle.api.tasks.testing.logging.TestLogEvent +import org.jetbrains.kotlin.gradle.dsl.JvmDefaultMode import org.jetbrains.kotlin.gradle.dsl.JvmTarget plugins { @@ -23,7 +24,8 @@ kotlin { } compilerOptions { jvmTarget = JvmTarget.JVM_21 - freeCompilerArgs = listOf("-Xjvm-default=all", "-Xjdk-release=21") + jvmDefault = JvmDefaultMode.NO_COMPATIBILITY + freeCompilerArgs = listOf("-Xjdk-release=21") } } From ca54b54a0c30790480749a676d8b97e8af8096ce Mon Sep 17 00:00:00 2001 From: Toffikk Date: Sun, 20 Sep 2026 21:35:38 +0200 Subject: [PATCH 253/257] Version 2.4.6 Reverts the change of CI to namespace --- .github/workflows/deploy-snapshot.yml | 20 +++++--------------- .github/workflows/deploy.yml | 18 ++++-------------- .github/workflows/test.yml | 20 +++++--------------- gradle.properties | 2 +- 4 files changed, 15 insertions(+), 45 deletions(-) diff --git a/.github/workflows/deploy-snapshot.yml b/.github/workflows/deploy-snapshot.yml index 225939f8b..1e9311636 100644 --- a/.github/workflows/deploy-snapshot.yml +++ b/.github/workflows/deploy-snapshot.yml @@ -12,25 +12,15 @@ on: jobs: deploy: name: Deploy Snapshot - runs-on: namespace-profile-linux-default + runs-on: 'ubuntu-latest' steps: - - uses: namespacelabs/nscloud-checkout-action@v9 - - uses: actions/setup-java@v6 + - uses: actions/checkout@v5 + - uses: actions/setup-java@v5 with: - distribution: 'temurin' java-version: 21 - cache: "" # namespace handles this - - name: Set up cache - uses: namespacelabs/nscloud-cache-action@v1 - with: - cache: gradle - - name: Setup gradle build cache - run: | - nsc cache gradle setup --init-gradle /tmp/init.gradle + distribution: 'temurin' - name: Setup Gradle uses: gradle/actions/setup-gradle@v5 - with: - cache-disabled: true # namespace handles this - name: Get project version id: get_version shell: bash @@ -39,7 +29,7 @@ jobs: echo version=$project_version >> $GITHUB_OUTPUT - name: Deploy snapshot version if: endsWith(steps.get_version.outputs.version, '-SNAPSHOT') - run: ./gradlew --init-script=/tmp/init.gradle -Dorg.gradle.parallel=true publishAllPublicationsToCanvasMC_SnapshotsRepository --no-daemon --stacktrace -Dorg.gradle.internal.http.socketTimeout=90000 -Dorg.gradle.internal.http.connectionTimeout=90000 + run: ./gradlew -Dorg.gradle.parallel=true publishAllPublicationsToCanvasMC_SnapshotsRepository --no-daemon --stacktrace -Dorg.gradle.internal.http.socketTimeout=90000 -Dorg.gradle.internal.http.connectionTimeout=90000 env: PUBLISH_USER: ${{ secrets.PUBLISH_USER }} PUBLISH_TOKEN: ${{ secrets.PUBLISH_TOKEN }} diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 28dbf7917..8b7f3db28 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -8,25 +8,15 @@ jobs: name: Deploy runs-on: 'ubuntu-latest' steps: - - uses: namespacelabs/nscloud-checkout-action@v9 - - uses: actions/setup-java@v6 + - uses: actions/checkout@v5 + - uses: actions/setup-java@v5 with: - distribution: 'temurin' java-version: 21 - cache: "" # namespace handles this - - name: Set up cache - uses: namespacelabs/nscloud-cache-action@v1 - with: - cache: gradle - - name: Setup gradle build cache - run: | - nsc cache gradle setup --init-gradle /tmp/init.gradle + distribution: 'temurin' - name: Setup Gradle uses: gradle/actions/setup-gradle@v5 - with: - cache-disabled: true # namespace handles this - name: Deploy release - run: ./gradlew --init-script=/tmp/init.gradle publishAllPublicationsToCanvasMC_ReleasesRepository --no-daemon --stacktrace + run: ./gradlew publishAllPublicationsToCanvasMC_ReleasesRepository --no-daemon --stacktrace env: PUBLISH_USER: ${{ secrets.PUBLISH_USER }} PUBLISH_TOKEN: ${{ secrets.PUBLISH_TOKEN }} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 2cfe2c7e7..eb5618af8 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -9,30 +9,20 @@ jobs: # Only run on PRs if the source branch is on someone else's repo if: ${{ github.event_name != 'pull_request' || github.repository != github.event.pull_request.head.repo.full_name }} name: Test - runs-on: namespace-profile-linux-default + runs-on: 'ubuntu-latest' steps: - - uses: namespacelabs/nscloud-checkout-action@v9 - - uses: actions/setup-java@v6 + - uses: actions/checkout@v5 + - uses: actions/setup-java@v5 with: - distribution: 'temurin' java-version: 21 - cache: "" # namespace handles this - - name: Set up cache - uses: namespacelabs/nscloud-cache-action@v1 - with: - cache: gradle - - name: Setup gradle build cache - run: | - nsc cache gradle setup --init-gradle /tmp/init.gradle + distribution: 'temurin' - name: Setup Gradle uses: gradle/actions/setup-gradle@v5 - with: - cache-disabled: true # namespace handles this - name: Execute Gradle build run: | git config --global user.email "no-reply@github.com" git config --global user.name "GitHub Actions" - ./gradlew --init-script=/tmp/init.gradle build --no-daemon --stacktrace + ./gradlew build --no-daemon --stacktrace - name: Publish Test Report uses: mikepenz/action-junit-report@v6 if: always() diff --git a/gradle.properties b/gradle.properties index 1dad93922..16fd37b84 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ group = io.canvasmc.weaver -version = 2.4.6-SNAPSHOT +version = 2.4.6 org.gradle.caching = true org.gradle.configuration-cache = true From 86be7d972ad71cdf93ddfa3c580333a02478b646 Mon Sep 17 00:00:00 2001 From: Toffikk Date: Sun, 20 Sep 2026 22:14:26 +0200 Subject: [PATCH 254/257] Fix cleanCache for IP --- .../src/main/kotlin/io/papermc/paperweight/util/utils.kt | 3 +++ .../io/papermc/paperweight/userdev/PaperweightUser.kt | 6 +++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/utils.kt b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/utils.kt index d1a3c3f68..e5b55c3f6 100644 --- a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/utils.kt +++ b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/utils.kt @@ -102,6 +102,9 @@ inline fun Gson.fromJson(any: Any): T = when (any) { val ProjectLayout.cache: Path get() = projectDirectory.dir(".gradle/$CACHE_PATH").path +val IsolatedProject.cache: Path + get() = projectDirectory.dir(".gradle/$CACHE_PATH").path + fun ProjectLayout.cacheDir(path: String) = projectDirectory.dir(".gradle/$CACHE_PATH").dir(path) fun IsolatedProject.cacheDir(path: String) = projectDirectory.dir(".gradle/$CACHE_PATH").dir(path) diff --git a/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/PaperweightUser.kt b/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/PaperweightUser.kt index 8641bbf2b..8f0d1485e 100644 --- a/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/PaperweightUser.kt +++ b/paperweight-userdev/src/main/kotlin/io/papermc/paperweight/userdev/PaperweightUser.kt @@ -88,7 +88,7 @@ abstract class PaperweightUser : Plugin { group = GENERAL_TASK_GROUP description = "Delete the project-local paperweight-userdev setup cache." delete(target.layout.cache) - delete(target.rootProject.layout.cache.resolve("paperweight-userdev")) + delete(target.isolated.rootProject.cache.resolve("paperweight-userdev")) } val cleanAll = target.tasks.register("cleanAllPaperweightUserdevCaches") { group = GENERAL_TASK_GROUP @@ -207,7 +207,7 @@ abstract class PaperweightUser : Plugin { userdevSetup.afterEvaluate(createContext(this, setupTask)) userdev.addServerDependencyTo.get().forEach { - it.extendsFrom(configurations.getByName(MOJANG_MAPPED_SERVER_CONFIG)) + it.extendsFrom(configurations.named(MOJANG_MAPPED_SERVER_CONFIG)) } // Clean v1 shared caches @@ -384,7 +384,7 @@ abstract class PaperweightUser : Plugin { val devBundleZip = bundleConfig.map { it.singleFile }.convertToPath() val bundleHash = devBundleZip.sha256asHex() val cacheDir = if (!target.sharedCaches) { - target.rootProject.layout.cache.resolve("paperweight-userdev/v2/work") + target.isolated.rootProject.cache.resolve("paperweight-userdev/v2/work") } else { target.gradle.gradleUserHomeDir.toPath().resolve("caches/paperweight-userdev/v2/work") } From ad3353d66c941b717fe1e0b1fcf8402b7ce8253b Mon Sep 17 00:00:00 2001 From: Toffikk Date: Sun, 20 Sep 2026 22:16:57 +0200 Subject: [PATCH 255/257] Version 2.5.1 --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index d61430e7f..006af7966 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ group = io.canvasmc.weaver -version = 2.5.0 +version = 2.5.1 org.gradle.caching = true org.gradle.configuration-cache = true From ff41211d95d7aba57de83d52ed88abf3482db07f Mon Sep 17 00:00:00 2001 From: Toffikk Date: Sun, 20 Sep 2026 23:13:31 +0200 Subject: [PATCH 256/257] Register download service in the relevant project --- gradle.properties | 2 +- .../src/main/kotlin/io/papermc/paperweight/util/utils.kt | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/gradle.properties b/gradle.properties index 006af7966..65c21c1f3 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ group = io.canvasmc.weaver -version = 2.5.1 +version = 2.5.2-SNAPSHOT org.gradle.caching = true org.gradle.configuration-cache = true diff --git a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/utils.kt b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/utils.kt index e5b55c3f6..053357b58 100644 --- a/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/utils.kt +++ b/paperweight-lib/src/main/kotlin/io/papermc/paperweight/util/utils.kt @@ -106,6 +106,7 @@ val IsolatedProject.cache: Path get() = projectDirectory.dir(".gradle/$CACHE_PATH").path fun ProjectLayout.cacheDir(path: String) = projectDirectory.dir(".gradle/$CACHE_PATH").dir(path) + fun IsolatedProject.cacheDir(path: String) = projectDirectory.dir(".gradle/$CACHE_PATH").dir(path) fun Project.offlineMode(): Boolean = gradle.startParameter.isOffline @@ -117,7 +118,7 @@ fun Provider.fileExists(): Provider { @Suppress("UNCHECKED_CAST") val Project.download: Provider get() = gradle.sharedServices.registerIfAbsent(DOWNLOAD_SERVICE_NAME, DownloadService::class) { - parameters.projectPath.set(isolated.rootProject.projectDirectory) + parameters.projectPath.set(isolated.projectDirectory) } val Project.gitMutationLockService: Provider From 08737cf78c639ffc1a1e5449a0e3cbd14658321f Mon Sep 17 00:00:00 2001 From: Toffikk Date: Sat, 26 Sep 2026 12:16:31 +0200 Subject: [PATCH 257/257] chore: fix bad merge --- .github/workflows/deploy-snapshot.yml | 2 +- .github/workflows/deploy.yml | 2 +- .github/workflows/test.yml | 6 ------ 3 files changed, 2 insertions(+), 8 deletions(-) diff --git a/.github/workflows/deploy-snapshot.yml b/.github/workflows/deploy-snapshot.yml index 1e9311636..a70e593b5 100644 --- a/.github/workflows/deploy-snapshot.yml +++ b/.github/workflows/deploy-snapshot.yml @@ -20,7 +20,7 @@ jobs: java-version: 21 distribution: 'temurin' - name: Setup Gradle - uses: gradle/actions/setup-gradle@v5 + uses: gradle/actions/setup-gradle@v6 - name: Get project version id: get_version shell: bash diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 8b7f3db28..3fd2756d1 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -14,7 +14,7 @@ jobs: java-version: 21 distribution: 'temurin' - name: Setup Gradle - uses: gradle/actions/setup-gradle@v5 + uses: gradle/actions/setup-gradle@v6 - name: Deploy release run: ./gradlew publishAllPublicationsToCanvasMC_ReleasesRepository --no-daemon --stacktrace env: diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index b5c224cc3..75617ce4c 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -17,13 +17,7 @@ jobs: java-version: 21 distribution: 'temurin' - name: Setup Gradle -<<<<<<< HEAD - uses: gradle/actions/setup-gradle@v5 -======= uses: gradle/actions/setup-gradle@v6 - with: - cache-disabled: true # namespace handles this ->>>>>>> papermc/main - name: Execute Gradle build run: | git config --global user.email "no-reply@github.com"