Implement paperclip support

This commit is contained in:
Kyle Wood 2021-01-01 19:28:45 -08:00
parent 49b386f15f
commit fba55d744f
No known key found for this signature in database
GPG key ID: D74E80413907E2D3
4 changed files with 200 additions and 26 deletions

View file

@ -77,6 +77,8 @@ dependencies {
shade("org.cadixdev:mercury:0.1.0-rc2-PW-SNAPSHOT")
shade("net.fabricmc:lorenz-tiny:3.0.0")
shade("io.sigpipe:jbsdiff:1.0")
}
ktlint {

View file

@ -74,6 +74,7 @@ class Paperweight : Plugin<Project> {
target.configurations.create(Constants.PARAM_MAPPINGS_CONFIG)
target.configurations.create(Constants.REMAPPER_CONFIG)
target.configurations.create(Constants.DECOMPILER_CONFIG)
target.configurations.create(Constants.PAPERCLIP_CONFIG)
target.repositories.apply {
maven(Constants.FABRIC_MAVEN_URL) {
@ -88,38 +89,48 @@ class Paperweight : Plugin<Project> {
}
}
val spigotTasks = target.createTasks()
val tasks = target.createTasks()
// Setup the server jar
target.afterEvaluate {
val serverProj = target.ext.serverProject.orNull ?: return@afterEvaluate
if (!serverProj.projectDir.exists()) {
return@afterEvaluate
}
val cache = target.layout.cache
serverProj.plugins.apply("java")
serverProj.dependencies.apply {
val remappedJar = cache.resolve(Constants.FINAL_REMAPPED_JAR)
if (remappedJar.exists()) {
add("implementation", target.files(remappedJar))
}
val libsFile = cache.resolve(Constants.SERVER_LIBRARIES)
if (libsFile.exists()) {
libsFile.forEachLine { line ->
add("implementation", line)
}
target.ext.serverProject.forUseAtConfigurationTime().orNull?.setupServerProject(target, tasks.spigotTasks)?.let { reobfJar ->
val generatePaperclipPatch by target.tasks.registering<CreatePaperclipPatch> {
paperclipJar.fileProvider(target.configurations.named(Constants.PAPERCLIP_CONFIG).map { it.singleFile })
originalJar.set(tasks.generalTasks.downloadServerJar.flatMap { it.outputJar })
patchedJar.set(reobfJar.flatMap { it.outputJar })
mcVersion.set(target.ext.minecraftVersion)
}
}
serverProj.apply(plugin = "com.github.johnrengelman.shadow")
serverProj.createBuildTasks(target.ext, spigotTasks)
}
}
private fun Project.createTasks(): SpigotTasks {
private fun Project.setupServerProject(parent: Project, spigotTasks: SpigotTasks): TaskProvider<RemapJarAtlas>? {
if (!projectDir.exists()) {
return null
}
val cache = parent.layout.cache
plugins.apply("java")
dependencies.apply {
val remappedJar = cache.resolve(Constants.FINAL_REMAPPED_JAR)
if (remappedJar.exists()) {
add("implementation", parent.files(remappedJar))
}
val libsFile = cache.resolve(Constants.SERVER_LIBRARIES)
if (libsFile.exists()) {
libsFile.forEachLine { line ->
add("implementation", line)
}
}
}
apply(plugin = "com.github.johnrengelman.shadow")
return createBuildTasks(parent.ext, spigotTasks)
}
private fun Project.createTasks(): TaskContainers {
val extension = ext
val cache: File = layout.cache
@ -196,7 +207,7 @@ class Paperweight : Plugin<Project> {
createPatchRemapTasks(generalTasks, vanillaTasks, spigotTasks, applyMergedAt)
return spigotTasks
return TaskContainers(initialTasks, generalTasks, vanillaTasks, spigotTasks)
}
// Shared task containers
@ -226,6 +237,13 @@ class Paperweight : Plugin<Project> {
val mergeGeneratedAts: TaskProvider<MergeAccessTransforms>
)
data class TaskContainers(
val initialTasks: InitialTasks,
val generalTasks: GeneralTasks,
val vanillaTasks: VanillaTasks,
val spigotTasks: SpigotTasks
)
private fun Project.createInitialTasks(): InitialTasks {
val cache: File = layout.cache
val extension: PaperweightExtension = ext
@ -485,7 +503,7 @@ class Paperweight : Plugin<Project> {
)
}
private fun Project.createBuildTasks(ext: PaperweightExtension, spigotTasks: SpigotTasks) {
private fun Project.createBuildTasks(ext: PaperweightExtension, spigotTasks: SpigotTasks): TaskProvider<RemapJarAtlas> {
val shadowJar: TaskProvider<Jar> = tasks.named("shadowJar", Jar::class.java)
val reobfJar by tasks.registering<RemapJarAtlas> {
@ -499,6 +517,8 @@ class Paperweight : Plugin<Project> {
outputJar.set(buildDir.resolve("libs/${shadowJar.get().archiveBaseName.get()}-reobf.jar"))
}
return reobfJar
}
private fun Project.createPatchRemapTasks(

View file

@ -0,0 +1,151 @@
/*
* paperweight is a Gradle plugin for the PaperMC project.
*
* Copyright (c) 2020 Kyle Wood (DemonWav)
* 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.file
import io.papermc.paperweight.util.path
import io.papermc.paperweight.util.unzip
import io.sigpipe.jbsdiff.Diff
import java.io.File
import java.io.IOException
import java.net.URI
import java.net.URISyntaxException
import java.nio.file.FileSystems
import java.nio.file.Files
import java.nio.file.StandardCopyOption
import java.security.MessageDigest
import java.security.NoSuchAlgorithmException
import java.util.Properties
import kotlin.experimental.and
import org.gradle.api.file.RegularFileProperty
import org.gradle.api.provider.Property
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.InputFile
abstract class CreatePaperclipPatch : ZippedTask() {
@get:InputFile
abstract val paperclipJar: RegularFileProperty
@get:InputFile
abstract val originalJar: RegularFileProperty
@get:InputFile
abstract val patchedJar: RegularFileProperty
@get:Input
abstract val mcVersion: Property<String>
override fun init() {
outputZip.set(
mcVersion.zip(layout.buildDirectory) { version, dir ->
dir.file("libs/Paper-$version.jar")
}
)
}
override fun run(rootDir: File) {
val patchFile = rootDir.resolve("paperMC.patch").toPath()
val propFile = rootDir.resolve("patch.properties").toPath()
val protocol = rootDir.resolve("META-INF/$PROTOCOL_FILE").toPath()
unzip(paperclipJar, rootDir)
val zipUri = try {
val jarUri = patchedJar.file.toURI()
URI("jar:${jarUri.scheme}", jarUri.path, null)
} catch (e: URISyntaxException) {
throw PaperweightException("Failed to create jar URI for $patchedJar", e)
}
try {
FileSystems.newFileSystem(zipUri, mapOf<String, Any>()).use { zipFs ->
val protocolPath = zipFs.getPath("META-INF", PROTOCOL_FILE)
if (Files.notExists(protocolPath)) {
Files.deleteIfExists(protocol)
return@use
}
Files.createDirectories(protocol.parent)
Files.copy(protocolPath, protocol, StandardCopyOption.REPLACE_EXISTING)
}
} catch (e: IOException) {
throw PaperweightException("Failed to read $patchedJar contents", e)
}
// Read the files into memory
println("Reading jars into memory")
val originalBytes = Files.readAllBytes(originalJar.path)
val patchedBytes = Files.readAllBytes(patchedJar.path)
println("Creating Paperclip patch")
try {
Files.newOutputStream(patchFile).use { patchOutput ->
Diff.diff(originalBytes, patchedBytes, patchOutput)
}
} catch (e: Exception) {
throw PaperweightException("Error creating patch between ${originalJar.path} and ${patchedJar.path}", e)
}
// Add the SHA-256 hashes for the files
val digestSha256 = try {
MessageDigest.getInstance("SHA-256")
} catch (e: NoSuchAlgorithmException) {
throw PaperweightException("Could not create SHA-256 hasher", e)
}
// Vanilla's URL uses a SHA1 hash of the vanilla server jar
val digestSha1 = try {
MessageDigest.getInstance("SHA1")
} catch (e: NoSuchAlgorithmException) {
throw PaperweightException("Could not create SHA1 hasher", e)
}
println("Hashing files")
val originalSha1 = digestSha1.digest(originalBytes)
val originalSha256 = digestSha256.digest(originalBytes)
val patchedSha256 = digestSha256.digest(patchedBytes)
val prop = Properties()
prop["originalHash"] = toHex(originalSha256)
prop["patchedHash"] = toHex(patchedSha256)
prop["patch"] = "paperMC.patch"
prop["sourceUrl"] = "https://launcher.mojang.com/v1/objects/" + toHex(originalSha1).toLowerCase() + "/server.jar"
prop["version"] = mcVersion.get()
println("Writing properties file")
Files.newBufferedWriter(propFile).use { writer ->
prop.store(writer, "Default Paperclip launch values. Can be overridden by placing a paperclip.properties file in the server directory.")
}
}
private fun toHex(hash: ByteArray): String {
val sb: StringBuilder = StringBuilder(hash.size * 2)
for (aHash in hash) {
sb.append("%02X".format(aHash and 0xFF.toByte()))
}
return sb.toString()
}
companion object {
const val PROTOCOL_FILE = "io.papermc.paper.daemon.protocol"
}
}

View file

@ -36,6 +36,7 @@ object Constants {
const val PARAM_MAPPINGS_CONFIG = "paramMappings"
const val REMAPPER_CONFIG = "remapper"
const val DECOMPILER_CONFIG = "decompiler"
const val PAPERCLIP_CONFIG = "paperclip"
const val CACHE_PATH = "caches"
private const val PAPER_PATH = "paperweight"