Implement paperclip support
This commit is contained in:
parent
49b386f15f
commit
fba55d744f
4 changed files with 200 additions and 26 deletions
|
@ -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 {
|
||||
|
|
|
@ -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(
|
||||
|
|
151
src/main/kotlin/tasks/CreatePaperclipPatch.kt
Normal file
151
src/main/kotlin/tasks/CreatePaperclipPatch.kt
Normal 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"
|
||||
}
|
||||
}
|
|
@ -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"
|
||||
|
|
Loading…
Reference in a new issue