Compare commits
1 commit
main
...
feature/im
Author | SHA1 | Date | |
---|---|---|---|
|
210a8346a2 |
189 changed files with 4418 additions and 13406 deletions
|
@ -1,85 +1,4 @@
|
|||
# noinspection EditorConfigKeyCorrectness
|
||||
[*.{kt,kts}]
|
||||
max_line_length = 150
|
||||
ij_kotlin_imports_layout = *
|
||||
no-wildcard-imports = no-wildcard-imports
|
||||
ij_continuation_indent_size = 4
|
||||
ij_kotlin_packages_to_use_import_on_demand = io.papermc.paperweight.util.**, io.papermc.paperweight.tasks.*, org.gradle.kotlin.dsl.*, kotlin.io.path.*
|
||||
ij_kotlin_parameter_annotation_wrap = off
|
||||
ij_kotlin_space_after_comma = true
|
||||
ij_kotlin_space_after_extend_colon = true
|
||||
ij_kotlin_space_after_type_colon = true
|
||||
ij_kotlin_space_before_catch_parentheses = true
|
||||
ij_kotlin_space_before_comma = false
|
||||
ij_kotlin_space_before_extend_colon = true
|
||||
ij_kotlin_space_before_for_parentheses = true
|
||||
ij_kotlin_space_before_if_parentheses = true
|
||||
ij_kotlin_space_before_lambda_arrow = true
|
||||
ij_kotlin_space_before_type_colon = false
|
||||
ij_kotlin_space_before_when_parentheses = true
|
||||
ij_kotlin_space_before_while_parentheses = true
|
||||
ij_kotlin_spaces_around_additive_operators = true
|
||||
ij_kotlin_spaces_around_assignment_operators = true
|
||||
ij_kotlin_spaces_around_equality_operators = true
|
||||
ij_kotlin_spaces_around_function_type_arrow = true
|
||||
ij_kotlin_spaces_around_logical_operators = true
|
||||
ij_kotlin_spaces_around_multiplicative_operators = true
|
||||
ij_kotlin_spaces_around_range = false
|
||||
ij_kotlin_spaces_around_relational_operators = true
|
||||
ij_kotlin_spaces_around_unary_operator = false
|
||||
ij_kotlin_spaces_around_when_arrow = true
|
||||
ij_kotlin_variable_annotation_wrap = off
|
||||
ij_kotlin_while_on_new_line = false
|
||||
ij_kotlin_wrap_elvis_expressions = 1
|
||||
ij_kotlin_wrap_expression_body_functions = 1
|
||||
ij_kotlin_wrap_first_method_in_call_chain = false
|
||||
ij_kotlin_name_count_to_use_star_import = 8
|
||||
ij_kotlin_name_count_to_use_star_import_for_members = 5
|
||||
ij_kotlin_insert_whitespaces_in_simple_one_line_method = true
|
||||
ij_kotlin_keep_blank_lines_before_right_brace = 2
|
||||
ij_kotlin_keep_blank_lines_in_code = 2
|
||||
ij_kotlin_keep_blank_lines_in_declarations = 2
|
||||
ij_kotlin_keep_first_column_comment = true
|
||||
ij_kotlin_keep_indents_on_empty_lines = false
|
||||
ij_kotlin_keep_line_breaks = true
|
||||
ij_kotlin_lbrace_on_next_line = false
|
||||
ij_kotlin_line_comment_add_space = false
|
||||
ij_kotlin_line_comment_at_first_column = true
|
||||
ij_kotlin_method_annotation_wrap = split_into_lines
|
||||
ij_kotlin_method_call_chain_wrap = normal
|
||||
ij_kotlin_method_parameters_new_line_after_left_paren = true
|
||||
ij_kotlin_method_parameters_right_paren_on_new_line = true
|
||||
ij_kotlin_method_parameters_wrap = on_every_item
|
||||
ij_kotlin_align_in_columns_case_branch = false
|
||||
ij_kotlin_align_multiline_binary_operation = false
|
||||
ij_kotlin_align_multiline_extends_list = false
|
||||
ij_kotlin_align_multiline_method_parentheses = false
|
||||
ij_kotlin_align_multiline_parameters = true
|
||||
ij_kotlin_align_multiline_parameters_in_calls = false
|
||||
ij_kotlin_allow_trailing_comma = false
|
||||
ij_kotlin_allow_trailing_comma_on_call_site = false
|
||||
ij_kotlin_assignment_wrap = normal
|
||||
ij_kotlin_blank_lines_after_class_header = 0
|
||||
ij_kotlin_blank_lines_around_block_when_branches = 0
|
||||
ij_kotlin_blank_lines_before_declaration_with_comment_or_annotation_on_separate_line = 1
|
||||
ij_kotlin_block_comment_at_first_column = true
|
||||
ij_kotlin_call_parameters_new_line_after_left_paren = true
|
||||
ij_kotlin_call_parameters_right_paren_on_new_line = true
|
||||
ij_kotlin_call_parameters_wrap = on_every_item
|
||||
ij_kotlin_catch_on_new_line = false
|
||||
ij_kotlin_class_annotation_wrap = split_into_lines
|
||||
ij_kotlin_code_style_defaults = KOTLIN_OFFICIAL
|
||||
ij_kotlin_continuation_indent_for_chained_calls = false
|
||||
ij_kotlin_continuation_indent_for_expression_bodies = false
|
||||
ij_kotlin_continuation_indent_in_argument_lists = false
|
||||
ij_kotlin_continuation_indent_in_elvis = false
|
||||
ij_kotlin_continuation_indent_in_if_conditions = false
|
||||
ij_kotlin_continuation_indent_in_parameter_lists = false
|
||||
ij_kotlin_continuation_indent_in_supertype_lists = false
|
||||
ij_kotlin_else_on_new_line = false
|
||||
ij_kotlin_enum_constants_wrap = off
|
||||
ij_kotlin_extends_list_wrap = normal
|
||||
ij_kotlin_field_annotation_wrap = split_into_lines
|
||||
ij_kotlin_finally_on_new_line = false
|
||||
ij_kotlin_if_rparen_on_new_line = true
|
||||
ij_kotlin_import_nested_classes = false
|
||||
max_line_length=150
|
||||
# noinspection EditorConfigKeyCorrectness
|
||||
kotlin_imports_layout=ascii
|
||||
|
|
22
.github/workflows/build.yml
vendored
Normal file
22
.github/workflows/build.yml
vendored
Normal file
|
@ -0,0 +1,22 @@
|
|||
name: Build
|
||||
on: [push, pull_request]
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-20.04
|
||||
container:
|
||||
image: adoptopenjdk:11-jdk-hotspot-focal
|
||||
options: --user root
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- uses: actions/cache@v2
|
||||
with:
|
||||
path: |
|
||||
~/.gradle/caches
|
||||
~/.gradle/jdks
|
||||
~/.gradle/native
|
||||
~/.gradle/wrapper
|
||||
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-gradle-
|
||||
- uses: gradle/wrapper-validation-action@v1
|
||||
- run: ./gradlew build --no-daemon --stacktrace
|
35
.github/workflows/deploy-snapshot.yml
vendored
35
.github/workflows/deploy-snapshot.yml
vendored
|
@ -1,35 +0,0 @@
|
|||
name: Deploy Snapshot
|
||||
on:
|
||||
push:
|
||||
branches: [ 'main' ]
|
||||
paths-ignore:
|
||||
- 'license/*'
|
||||
- 'readme.md'
|
||||
- '.gitignore'
|
||||
- '.gitattributes'
|
||||
- '.editorconfig'
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
name: Deploy Snapshot
|
||||
runs-on: 'ubuntu-latest'
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-java@v3
|
||||
with:
|
||||
java-version: 17
|
||||
distribution: 'temurin'
|
||||
- name: Setup Gradle
|
||||
uses: gradle/gradle-build-action@v2
|
||||
- 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 --no-daemon --stacktrace
|
||||
env:
|
||||
ORG_GRADLE_PROJECT_paperUsername: ${{ secrets.DEPLOY_USER }}
|
||||
ORG_GRADLE_PROJECT_paperPassword: ${{ secrets.DEPLOY_PASS }}
|
42
.github/workflows/deploy.yml
vendored
42
.github/workflows/deploy.yml
vendored
|
@ -1,30 +1,28 @@
|
|||
name: Deploy
|
||||
on:
|
||||
push:
|
||||
tags: [ 'v*' ]
|
||||
|
||||
branches:
|
||||
- main
|
||||
jobs:
|
||||
deploy:
|
||||
name: Deploy
|
||||
runs-on: 'ubuntu-latest'
|
||||
runs-on: ubuntu-20.04
|
||||
container:
|
||||
image: adoptopenjdk:11-jdk-hotspot-focal
|
||||
options: --user root
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-java@v3
|
||||
- uses: actions/checkout@v1
|
||||
- uses: actions/cache@v2
|
||||
with:
|
||||
java-version: 17
|
||||
distribution: 'temurin'
|
||||
- name: Setup Gradle
|
||||
uses: gradle/gradle-build-action@v2
|
||||
- name: Deploy release
|
||||
run: ./gradlew publishPlugins --no-daemon --stacktrace
|
||||
path: |
|
||||
~/.gradle/caches
|
||||
~/.gradle/jdks
|
||||
~/.gradle/native
|
||||
~/.gradle/wrapper
|
||||
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-gradle-
|
||||
- uses: gradle/wrapper-validation-action@v1
|
||||
- run: ./gradlew publish --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_wavJfrogUsername: ${{ secrets.DEPLOY_USER }}
|
||||
ORG_GRADLE_PROJECT_wavJfrogPassword: ${{ secrets.DEPLOY_PASS }}
|
||||
|
|
22
.github/workflows/test.yml
vendored
22
.github/workflows/test.yml
vendored
|
@ -1,22 +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@v3
|
||||
- uses: actions/setup-java@v3
|
||||
with:
|
||||
java-version: 17
|
||||
distribution: 'temurin'
|
||||
- name: Setup Gradle
|
||||
uses: gradle/gradle-build-action@v2
|
||||
- name: Execute Gradle build
|
||||
run: ./gradlew build --no-daemon --stacktrace
|
|
@ -1,9 +0,0 @@
|
|||
tasks.register("printVersion") {
|
||||
doFirst {
|
||||
println(version)
|
||||
}
|
||||
}
|
||||
|
||||
tasks.wrapper {
|
||||
distributionType = Wrapper.DistributionType.ALL
|
||||
}
|
|
@ -1,3 +1,5 @@
|
|||
import org.jetbrains.kotlin.gradle.plugin.getKotlinPluginVersion
|
||||
|
||||
plugins {
|
||||
`kotlin-dsl`
|
||||
`kotlin-dsl-precompiled-script-plugins`
|
||||
|
@ -10,11 +12,10 @@ repositories {
|
|||
|
||||
dependencies {
|
||||
implementation(libs.gradle.licenser)
|
||||
implementation(libs.gradle.spotless)
|
||||
implementation(libs.gradle.ktlint)
|
||||
implementation(libs.gradle.shadow)
|
||||
implementation(libs.gradle.kotlin.dsl)
|
||||
implementation(libs.gradle.plugin.kotlin.withVersion(embeddedKotlinVersion))
|
||||
implementation(libs.gradle.plugin.publish)
|
||||
implementation(libs.gradle.kotlin.plugin.withVersion(embeddedKotlinVersion))
|
||||
}
|
||||
|
||||
fun Provider<MinimalExternalModuleDependency>.withVersion(version: String): Provider<String> {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
rootProject.name = "buildSrc"
|
||||
enableFeaturePreview("VERSION_CATALOGS")
|
||||
|
||||
dependencyResolutionManagement {
|
||||
versionCatalogs {
|
||||
|
|
|
@ -1,61 +1,29 @@
|
|||
import com.diffplug.gradle.spotless.SpotlessExtension
|
||||
import net.kyori.indra.licenser.spotless.IndraSpotlessLicenserExtension
|
||||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||
|
||||
plugins {
|
||||
idea
|
||||
id("org.gradle.kotlin.kotlin-dsl")
|
||||
id("org.cadixdev.licenser")
|
||||
id("org.jlleitschuh.gradle.ktlint")
|
||||
}
|
||||
|
||||
java {
|
||||
toolchain {
|
||||
languageVersion.set(JavaLanguageVersion.of(11))
|
||||
}
|
||||
withSourcesJar()
|
||||
}
|
||||
|
||||
tasks.withType(JavaCompile::class).configureEach {
|
||||
options.release = 11
|
||||
}
|
||||
|
||||
kotlin {
|
||||
jvmToolchain {
|
||||
languageVersion = JavaLanguageVersion.of(17)
|
||||
}
|
||||
target {
|
||||
compilations.configureEach {
|
||||
kotlinOptions {
|
||||
jvmTarget = "11"
|
||||
freeCompilerArgs = listOf("-Xjvm-default=all", "-Xjdk-release=11")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
repositories {
|
||||
maven("https://repo.papermc.io/repository/maven-snapshots/") {
|
||||
mavenCentral()
|
||||
maven("https://oss.sonatype.org/content/repositories/snapshots/") {
|
||||
mavenContent {
|
||||
includeModule("org.cadixdev", "mercury")
|
||||
}
|
||||
}
|
||||
maven("https://repo.papermc.io/repository/maven-public/") {
|
||||
maven("https://maven.quiltmc.org/repository/release/") {
|
||||
mavenContent {
|
||||
includeGroup("codechicken")
|
||||
includeGroup("net.fabricmc")
|
||||
}
|
||||
}
|
||||
mavenCentral()
|
||||
gradlePluginPortal()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly(gradleApi())
|
||||
compileOnly(kotlin("stdlib-jdk8"))
|
||||
}
|
||||
|
||||
testing {
|
||||
suites {
|
||||
val test by getting(JvmTestSuite::class) {
|
||||
useKotlinTest(embeddedKotlinVersion)
|
||||
dependencies {
|
||||
implementation("org.junit.jupiter:junit-jupiter-engine:5.10.1")
|
||||
}
|
||||
includeGroup("org.quiltmc")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -68,46 +36,38 @@ configurations.all {
|
|||
dependencies.removeIf { it.group == "org.jetbrains.kotlin" }
|
||||
}
|
||||
|
||||
tasks.jar {
|
||||
manifest {
|
||||
attributes(
|
||||
"Implementation-Version" to project.version
|
||||
)
|
||||
dependencies {
|
||||
compileOnly(gradleApi())
|
||||
compileOnly(kotlin("stdlib-jdk8"))
|
||||
}
|
||||
|
||||
gradlePlugin {
|
||||
isAutomatedPublishing = false
|
||||
}
|
||||
|
||||
tasks.withType<KotlinCompile> {
|
||||
kotlinOptions {
|
||||
jvmTarget = "1.8"
|
||||
useIR = true
|
||||
freeCompilerArgs = listOf("-Xopt-in=kotlin.io.path.ExperimentalPathApi")
|
||||
}
|
||||
}
|
||||
|
||||
// The following is to work around https://github.com/diffplug/spotless/issues/1599
|
||||
// Ensure the ktlint step is before the license header step
|
||||
ktlint {
|
||||
enableExperimentalRules.set(true)
|
||||
|
||||
plugins.apply("com.diffplug.spotless")
|
||||
extensions.configure<SpotlessExtension> {
|
||||
val overrides = mapOf(
|
||||
"ktlint_standard_no-wildcard-imports" to "disabled",
|
||||
"ktlint_standard_filename" to "disabled",
|
||||
"ktlint_standard_trailing-comma-on-call-site" to "disabled",
|
||||
"ktlint_standard_trailing-comma-on-declaration-site" to "disabled",
|
||||
)
|
||||
|
||||
val ktlintVer = "0.50.0"
|
||||
|
||||
kotlin {
|
||||
ktlint(ktlintVer).editorConfigOverride(overrides)
|
||||
}
|
||||
kotlinGradle {
|
||||
ktlint(ktlintVer).editorConfigOverride(overrides)
|
||||
}
|
||||
}
|
||||
|
||||
plugins.apply("net.kyori.indra.licenser.spotless")
|
||||
extensions.configure<IndraSpotlessLicenserExtension> {
|
||||
licenseHeaderFile(rootProject.file("license/copyright.txt"))
|
||||
newLine(true)
|
||||
disabledRules.add("no-wildcard-imports")
|
||||
}
|
||||
|
||||
tasks.register("format") {
|
||||
group = "formatting"
|
||||
description = "Formats source code according to project style"
|
||||
dependsOn(tasks.named("spotlessApply"))
|
||||
dependsOn(tasks.licenseFormat, tasks.ktlintFormat)
|
||||
}
|
||||
|
||||
license {
|
||||
header.set(resources.text.fromFile(rootProject.file("license/copyright.txt")))
|
||||
include("**/*.kt")
|
||||
}
|
||||
|
||||
idea {
|
||||
|
|
|
@ -1,19 +1,22 @@
|
|||
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
|
||||
import org.gradle.api.artifacts.repositories.PasswordCredentials
|
||||
import org.gradle.api.publish.maven.MavenPom
|
||||
import org.gradle.api.publish.maven.MavenPublication
|
||||
import org.gradle.api.publish.maven.tasks.PublishToMavenLocal
|
||||
import org.gradle.api.publish.maven.tasks.PublishToMavenRepository
|
||||
import org.gradle.kotlin.dsl.credentials
|
||||
import org.gradle.kotlin.dsl.existing
|
||||
import org.gradle.kotlin.dsl.get
|
||||
import org.gradle.kotlin.dsl.getValue
|
||||
import org.gradle.kotlin.dsl.maven
|
||||
import org.gradle.kotlin.dsl.provideDelegate
|
||||
import org.gradle.kotlin.dsl.registering
|
||||
import org.gradle.kotlin.dsl.withType
|
||||
|
||||
plugins {
|
||||
`maven-publish`
|
||||
id("org.jetbrains.kotlin.jvm")
|
||||
id("com.github.johnrengelman.shadow")
|
||||
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
|
||||
|
@ -33,38 +36,12 @@ fun ShadowJar.configureStandard() {
|
|||
mergeServiceFiles()
|
||||
}
|
||||
|
||||
val sourcesJar by tasks.existing(AbstractArchiveTask::class) {
|
||||
from(
|
||||
zipTree(project(":nugget-lib").tasks
|
||||
.named("sourcesJar", AbstractArchiveTask::class)
|
||||
.flatMap { it.archiveFile })
|
||||
) {
|
||||
exclude("META-INF/**")
|
||||
}
|
||||
}
|
||||
|
||||
val prefix = project.name.substringAfter("nugget-")
|
||||
|
||||
gradlePlugin {
|
||||
website.set("https://git.zontreck.com/ObsidianMC/nugget")
|
||||
vcsUrl.set("https://git.zontreck.com/ObsidianMC/nugget")
|
||||
plugins.create("nugget-$prefix") {
|
||||
id = "io.papermc.paperweight." + prefix
|
||||
displayName = "nugget $prefix"
|
||||
tags.set(listOf("obsidian", "minecraft"))
|
||||
}
|
||||
}
|
||||
val sourcesJar by tasks.existing
|
||||
|
||||
val shadowJar by tasks.existing(ShadowJar::class) {
|
||||
archiveClassifier.set(null as String?)
|
||||
configureStandard()
|
||||
|
||||
inputs.property("noRelocate", noRelocate)
|
||||
if (noRelocate) {
|
||||
return@existing
|
||||
}
|
||||
|
||||
val prefix = "obsidian.libs"
|
||||
val prefix = "paper.libs"
|
||||
listOf(
|
||||
"com.github.salomonbrys.kotson",
|
||||
"com.google.errorprone.annotations",
|
||||
|
@ -83,49 +60,101 @@ val shadowJar by tasks.existing(ShadowJar::class) {
|
|||
"org.objectweb.asm",
|
||||
"org.osgi",
|
||||
"org.tukaani.xz",
|
||||
"org.slf4j",
|
||||
"codechicken.diffpatch",
|
||||
"codechicken.repack"
|
||||
).forEach { pack ->
|
||||
relocate(pack, "$prefix.$pack")
|
||||
}
|
||||
}
|
||||
|
||||
val devShadowJar by tasks.registering(ShadowJar::class) {
|
||||
configureStandard()
|
||||
|
||||
val MAVEN_PASSWORD = "AriasCreationsMavenPassword"
|
||||
val MAVEN_USER = "AriasCreationsMavenUser"
|
||||
from(project.sourceSets.main.get().output)
|
||||
|
||||
archiveClassifier.set("dev")
|
||||
}
|
||||
|
||||
val isSnapshot = version().endsWith("-SNAPSHOT")
|
||||
|
||||
publishing {
|
||||
repositories {
|
||||
maven {
|
||||
url = uri("https://git.zontreck.com/api/packages/ObsidianMC/maven")
|
||||
name = "obsidian"
|
||||
|
||||
credentials {
|
||||
username = project.findProperty(MAVEN_USER)?.toString()
|
||||
password = project.findProperty(MAVEN_PASSWORD)?.toString()
|
||||
publications {
|
||||
register<MavenPublication>("shadow") {
|
||||
pluginConfig(version())
|
||||
artifact(shadowJar) {
|
||||
classifier = null
|
||||
}
|
||||
}
|
||||
}
|
||||
register<MavenPublication>("maven") {
|
||||
standardConfig(version())
|
||||
}
|
||||
register<MavenPublication>("shadowLocal") {
|
||||
pluginConfig(localVersion())
|
||||
artifact(devShadowJar) {
|
||||
classifier = null
|
||||
}
|
||||
}
|
||||
register<MavenPublication>("mavenLocal") {
|
||||
standardConfig(localVersion())
|
||||
}
|
||||
|
||||
publications {
|
||||
withType(MavenPublication::class).configureEach {
|
||||
pom {
|
||||
pomConfig()
|
||||
repositories {
|
||||
val url = "https://wav.jfrog.io/artifactory/repo/"
|
||||
maven(url) {
|
||||
credentials(PasswordCredentials::class)
|
||||
name = "wavJfrog"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun MavenPom.pomConfig() {
|
||||
val repoPath = "ObsidianMC/nugget"
|
||||
val repoUrl = "https://git.zontreck.com/$repoPath"
|
||||
tasks.withType(PublishToMavenRepository::class).configureEach {
|
||||
onlyIf {
|
||||
!publication.name.endsWith("Local")
|
||||
}
|
||||
}
|
||||
tasks.withType(PublishToMavenLocal::class).configureEach {
|
||||
onlyIf {
|
||||
publication.name.endsWith("Local")
|
||||
}
|
||||
}
|
||||
|
||||
name.set("nugget")
|
||||
description.set("Gradle plugin for the ObsidianMC project")
|
||||
fun MavenPublication.standardConfig(versionName: String) {
|
||||
groupId = project.group.toString()
|
||||
artifactId = project.name
|
||||
version = versionName
|
||||
|
||||
from(components["java"])
|
||||
artifact(devShadowJar)
|
||||
|
||||
withoutBuildIdentifier()
|
||||
pom {
|
||||
pomConfig()
|
||||
}
|
||||
}
|
||||
|
||||
fun MavenPublication.pluginConfig(versionName: String) {
|
||||
val baseName = project.group.toString() + "." + project.name.substringAfter('-')
|
||||
|
||||
groupId = baseName
|
||||
artifactId = "$baseName.gradle.plugin"
|
||||
version = versionName
|
||||
|
||||
artifact(sourcesJar)
|
||||
|
||||
withoutBuildIdentifier()
|
||||
pom {
|
||||
pomConfig()
|
||||
}
|
||||
}
|
||||
|
||||
fun MavenPom.pomConfig() {
|
||||
val repoPath = "PaperMC/paperweight"
|
||||
val repoUrl = "https://github.com/$repoPath"
|
||||
|
||||
name.set("paperweight")
|
||||
description.set("Gradle plugin for the PaperMC project")
|
||||
url.set(repoUrl)
|
||||
inceptionYear.set("2020")
|
||||
packaging = "jar"
|
||||
|
||||
licenses {
|
||||
license {
|
||||
|
@ -142,10 +171,10 @@ fun MavenPom.pomConfig() {
|
|||
|
||||
developers {
|
||||
developer {
|
||||
id.set("DenWav")
|
||||
id.set("DemonWav")
|
||||
name.set("Kyle Wood")
|
||||
email.set("kyle@denwav.dev")
|
||||
url.set("https://github.com/DenWav")
|
||||
email.set("demonwav@gmail.com")
|
||||
url.set("https://github.com/DemonWav")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -155,3 +184,14 @@ fun MavenPom.pomConfig() {
|
|||
developerConnection.set("scm:git:git@github.com:$repoPath.git")
|
||||
}
|
||||
}
|
||||
|
||||
fun version(): String {
|
||||
return project.version.toString()
|
||||
}
|
||||
fun localVersion(): String {
|
||||
return if (isSnapshot) {
|
||||
version().substringBefore('-') + "-LOCAL-SNAPSHOT"
|
||||
} else {
|
||||
version() + "-LOCAL"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,2 @@
|
|||
group = com.zontreck.nugget
|
||||
version = 1.7.4-SNAPSHOT
|
||||
|
||||
org.gradle.caching = true
|
||||
org.gradle.parallel = true
|
||||
group = io.papermc.paperweight
|
||||
version = 1.0.0-SNAPSHOT
|
||||
|
|
|
@ -1,22 +1,21 @@
|
|||
[versions]
|
||||
asm = "9.7"
|
||||
lorenz = "0.5.8"
|
||||
hypo = "1.2.4"
|
||||
asm = "9.1"
|
||||
lorenz = "0.5.6"
|
||||
hypo = "1.0.1"
|
||||
|
||||
[libraries]
|
||||
asm-core = { module = "org.ow2.asm:asm", version.ref = "asm" }
|
||||
asm-tree = { module = "org.ow2.asm:asm-tree", version.ref = "asm" }
|
||||
|
||||
httpclient = "org.apache.httpcomponents:httpclient:4.5.14"
|
||||
httpclient = "org.apache.httpcomponents:httpclient:4.5.13"
|
||||
kotson = "com.github.salomonbrys.kotson:kotson:2.5.0"
|
||||
gson = "com.google.code.gson:gson:2.10.1"
|
||||
|
||||
cadix-lorenz-core = { module = "org.cadixdev:lorenz", version.ref = "lorenz" }
|
||||
cadix-lorenz-asm = { module = "org.cadixdev:lorenz-asm", version.ref = "lorenz" }
|
||||
cadix-lorenz-proguard = { module = "org.cadixdev:lorenz-io-proguard", version.ref = "lorenz" }
|
||||
cadix-atlas = "org.cadixdev:atlas:0.2.1"
|
||||
cadix-atlas = "org.cadixdev:atlas:0.2.0"
|
||||
cadix-at = "org.cadixdev:at:0.1.0-rc1"
|
||||
cadix-mercury = "org.cadixdev:mercury:0.1.2-paperweight-SNAPSHOT"
|
||||
cadix-mercury = "org.cadixdev:mercury:0.1.0-rc2-SNAPSHOT"
|
||||
|
||||
hypo-model = { module = "dev.denwav.hypo:hypo-model", version.ref = "hypo" }
|
||||
hypo-core = { module = "dev.denwav.hypo:hypo-core", version.ref = "hypo" }
|
||||
|
@ -24,23 +23,18 @@ hypo-hydrate = { module = "dev.denwav.hypo:hypo-hydrate", version.ref = "hypo" }
|
|||
hypo-asm-core = { module = "dev.denwav.hypo:hypo-asm", version.ref = "hypo" }
|
||||
hypo-asm-hydrate = { module = "dev.denwav.hypo:hypo-asm-hydrate", version.ref = "hypo" }
|
||||
hypo-mappings = { module = "dev.denwav.hypo:hypo-mappings", version.ref = "hypo" }
|
||||
slf4j-jdk14 = "org.slf4j:slf4j-jdk14:1.7.32"
|
||||
|
||||
lorenzTiny = "net.fabricmc:lorenz-tiny:3.0.0"
|
||||
lorenzTiny = "org.quiltmc:lorenz-tiny:3.0.0"
|
||||
jbsdiff = "io.sigpipe:jbsdiff:1.0"
|
||||
|
||||
diffpatch = "codechicken:DiffPatch:1.5.0.29"
|
||||
|
||||
# Gradle
|
||||
gradle-licenser = "net.kyori:indra-licenser-spotless:3.1.3"
|
||||
gradle-spotless = "com.diffplug.spotless:spotless-plugin-gradle:6.23.1"
|
||||
gradle-shadow = "com.github.johnrengelman.shadow:com.github.johnrengelman.shadow.gradle.plugin:8.1.1"
|
||||
gradle-kotlin-dsl = "org.gradle.kotlin.kotlin-dsl:org.gradle.kotlin.kotlin-dsl.gradle.plugin:4.3.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.2.1"
|
||||
gradle-licenser = "org.cadixdev.licenser:org.cadixdev.licenser.gradle.plugin:0.6.0"
|
||||
gradle-shadow = "com.github.johnrengelman.shadow:com.github.johnrengelman.shadow.gradle.plugin:7.0.0"
|
||||
gradle-ktlint = "org.jlleitschuh.gradle.ktlint:org.jlleitschuh.gradle.ktlint.gradle.plugin:10.0.0"
|
||||
gradle-kotlin-dsl = "org.gradle.kotlin.kotlin-dsl:org.gradle.kotlin.kotlin-dsl.gradle.plugin:2.1.4"
|
||||
gradle-kotlin-plugin = { module = "org.jetbrains.kotlin.jvm:org.jetbrains.kotlin.jvm.gradle.plugin" }
|
||||
|
||||
[bundles]
|
||||
asm = ["asm-core", "asm-tree"]
|
||||
cadix = ["cadix-lorenz-core", "cadix-lorenz-asm", "cadix-lorenz-proguard", "cadix-atlas", "cadix-at", "cadix-mercury"]
|
||||
hypo = ["hypo-model", "hypo-core", "hypo-hydrate", "hypo-asm-core", "hypo-asm-hydrate", "hypo-mappings"]
|
||||
kotson = ["kotson", "gson"]
|
||||
|
|
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
4
gradle/wrapper/gradle-wrapper.properties
vendored
4
gradle/wrapper/gradle-wrapper.properties
vendored
|
@ -1,7 +1,5 @@
|
|||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-all.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-bin.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
|
282
gradlew
vendored
282
gradlew
vendored
|
@ -1,7 +1,7 @@
|
|||
#!/bin/sh
|
||||
#!/usr/bin/env sh
|
||||
|
||||
#
|
||||
# Copyright © 2015-2021 the original authors.
|
||||
# Copyright 2015 the original author or authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
|
@ -17,99 +17,67 @@
|
|||
#
|
||||
|
||||
##############################################################################
|
||||
#
|
||||
# Gradle start up script for POSIX generated by Gradle.
|
||||
#
|
||||
# Important for running:
|
||||
#
|
||||
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
|
||||
# noncompliant, but you have some other compliant shell such as ksh or
|
||||
# bash, then to run this script, type that shell name before the whole
|
||||
# command line, like:
|
||||
#
|
||||
# ksh Gradle
|
||||
#
|
||||
# Busybox and similar reduced shells will NOT work, because this script
|
||||
# requires all of these POSIX shell features:
|
||||
# * functions;
|
||||
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
|
||||
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
|
||||
# * compound commands having a testable exit status, especially «case»;
|
||||
# * various built-in commands including «command», «set», and «ulimit».
|
||||
#
|
||||
# Important for patching:
|
||||
#
|
||||
# (2) This script targets any POSIX shell, so it avoids extensions provided
|
||||
# by Bash, Ksh, etc; in particular arrays are avoided.
|
||||
#
|
||||
# The "traditional" practice of packing multiple parameters into a
|
||||
# space-separated string is a well documented source of bugs and security
|
||||
# problems, so this is (mostly) avoided, by progressively accumulating
|
||||
# options in "$@", and eventually passing that to Java.
|
||||
#
|
||||
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
|
||||
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
|
||||
# see the in-line comments for details.
|
||||
#
|
||||
# There are tweaks for specific operating systems such as AIX, CygWin,
|
||||
# Darwin, MinGW, and NonStop.
|
||||
#
|
||||
# (3) This script is generated from the Groovy template
|
||||
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# within the Gradle project.
|
||||
#
|
||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||
#
|
||||
##
|
||||
## Gradle start up script for UN*X
|
||||
##
|
||||
##############################################################################
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
|
||||
# Resolve links: $0 may be a link
|
||||
app_path=$0
|
||||
|
||||
# Need this for daisy-chained symlinks.
|
||||
while
|
||||
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
|
||||
[ -h "$app_path" ]
|
||||
do
|
||||
ls=$( ls -ld "$app_path" )
|
||||
link=${ls#*' -> '}
|
||||
case $link in #(
|
||||
/*) app_path=$link ;; #(
|
||||
*) app_path=$APP_HOME$link ;;
|
||||
esac
|
||||
PRG="$0"
|
||||
# Need this for relative symlinks.
|
||||
while [ -h "$PRG" ] ; do
|
||||
ls=`ls -ld "$PRG"`
|
||||
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||
if expr "$link" : '/.*' > /dev/null; then
|
||||
PRG="$link"
|
||||
else
|
||||
PRG=`dirname "$PRG"`"/$link"
|
||||
fi
|
||||
done
|
||||
SAVED="`pwd`"
|
||||
cd "`dirname \"$PRG\"`/" >/dev/null
|
||||
APP_HOME="`pwd -P`"
|
||||
cd "$SAVED" >/dev/null
|
||||
|
||||
# This is normally unused
|
||||
# shellcheck disable=SC2034
|
||||
APP_BASE_NAME=${0##*/}
|
||||
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
|
||||
APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
|
||||
APP_NAME="Gradle"
|
||||
APP_BASE_NAME=`basename "$0"`
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD=maximum
|
||||
MAX_FD="maximum"
|
||||
|
||||
warn () {
|
||||
echo "$*"
|
||||
} >&2
|
||||
}
|
||||
|
||||
die () {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
} >&2
|
||||
}
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
nonstop=false
|
||||
case "$( uname )" in #(
|
||||
CYGWIN* ) cygwin=true ;; #(
|
||||
Darwin* ) darwin=true ;; #(
|
||||
MSYS* | MINGW* ) msys=true ;; #(
|
||||
NONSTOP* ) nonstop=true ;;
|
||||
case "`uname`" in
|
||||
CYGWIN* )
|
||||
cygwin=true
|
||||
;;
|
||||
Darwin* )
|
||||
darwin=true
|
||||
;;
|
||||
MINGW* )
|
||||
msys=true
|
||||
;;
|
||||
NONSTOP* )
|
||||
nonstop=true
|
||||
;;
|
||||
esac
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
|
@ -119,9 +87,9 @@ CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
|||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD=$JAVA_HOME/jre/sh/java
|
||||
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||
else
|
||||
JAVACMD=$JAVA_HOME/bin/java
|
||||
JAVACMD="$JAVA_HOME/bin/java"
|
||||
fi
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||
|
@ -130,120 +98,88 @@ Please set the JAVA_HOME variable in your environment to match the
|
|||
location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD=java
|
||||
if ! command -v java >/dev/null 2>&1
|
||||
then
|
||||
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
JAVACMD="java"
|
||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||
case $MAX_FD in #(
|
||||
max*)
|
||||
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC2039,SC3045
|
||||
MAX_FD=$( ulimit -H -n ) ||
|
||||
warn "Could not query maximum file descriptor limit"
|
||||
esac
|
||||
case $MAX_FD in #(
|
||||
'' | soft) :;; #(
|
||||
*)
|
||||
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC2039,SC3045
|
||||
ulimit -n "$MAX_FD" ||
|
||||
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||
esac
|
||||
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
||||
MAX_FD_LIMIT=`ulimit -H -n`
|
||||
if [ $? -eq 0 ] ; then
|
||||
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||
MAX_FD="$MAX_FD_LIMIT"
|
||||
fi
|
||||
ulimit -n $MAX_FD
|
||||
if [ $? -ne 0 ] ; then
|
||||
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
||||
fi
|
||||
else
|
||||
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Collect all arguments for the java command, stacking in reverse order:
|
||||
# * args from the command line
|
||||
# * the main class name
|
||||
# * -classpath
|
||||
# * -D...appname settings
|
||||
# * --module-path (only if needed)
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
|
||||
# For Darwin, add options to specify how the application appears in the dock
|
||||
if $darwin; then
|
||||
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||
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" )
|
||||
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
|
||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||
|
||||
JAVACMD=$( cygpath --unix "$JAVACMD" )
|
||||
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
for arg do
|
||||
if
|
||||
case $arg in #(
|
||||
-*) false ;; # don't mess with options #(
|
||||
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
|
||||
[ -e "$t" ] ;; #(
|
||||
*) false ;;
|
||||
esac
|
||||
then
|
||||
arg=$( cygpath --path --ignore --mixed "$arg" )
|
||||
fi
|
||||
# Roll the args list around exactly as many times as the number of
|
||||
# args, so each arg winds up back in the position where it started, but
|
||||
# possibly modified.
|
||||
#
|
||||
# NB: a `for` loop captures its iteration list before it begins, so
|
||||
# changing the positional parameters here affects neither the number of
|
||||
# iterations, nor the values presented in `arg`.
|
||||
shift # remove old arg
|
||||
set -- "$@" "$arg" # push replacement arg
|
||||
# We build the pattern for arguments to be converted via cygpath
|
||||
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||
SEP=""
|
||||
for dir in $ROOTDIRSRAW ; do
|
||||
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
||||
SEP="|"
|
||||
done
|
||||
OURCYGPATTERN="(^($ROOTDIRS))"
|
||||
# Add a user-defined pattern to the cygpath arguments
|
||||
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
||||
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
||||
fi
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
i=0
|
||||
for arg in "$@" ; do
|
||||
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
||||
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
||||
|
||||
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
||||
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
||||
else
|
||||
eval `echo args$i`="\"$arg\""
|
||||
fi
|
||||
i=`expr $i + 1`
|
||||
done
|
||||
case $i in
|
||||
0) set -- ;;
|
||||
1) set -- "$args0" ;;
|
||||
2) set -- "$args0" "$args1" ;;
|
||||
3) set -- "$args0" "$args1" "$args2" ;;
|
||||
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
# Escape application args
|
||||
save () {
|
||||
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||
echo " "
|
||||
}
|
||||
APP_ARGS=`save "$@"`
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# Collect all arguments for the java command:
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
|
||||
# and any embedded shellness will be escaped.
|
||||
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
|
||||
# treated as '${Hostname}' itself on the command line.
|
||||
|
||||
set -- \
|
||||
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||
-classpath "$CLASSPATH" \
|
||||
org.gradle.wrapper.GradleWrapperMain \
|
||||
"$@"
|
||||
|
||||
# Stop when "xargs" is not available.
|
||||
if ! command -v xargs >/dev/null 2>&1
|
||||
then
|
||||
die "xargs is not available"
|
||||
fi
|
||||
|
||||
# Use "xargs" to parse quoted args.
|
||||
#
|
||||
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
|
||||
#
|
||||
# In Bash we could simply go:
|
||||
#
|
||||
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
|
||||
# set -- "${ARGS[@]}" "$@"
|
||||
#
|
||||
# but POSIX shell has neither arrays nor command substitution, so instead we
|
||||
# post-process each arg (as a line of input to sed) to backslash-escape any
|
||||
# character that might be a shell metacharacter, then use eval to reverse
|
||||
# that process (while maintaining the separation between arguments), and wrap
|
||||
# the whole thing up as a single "set" statement.
|
||||
#
|
||||
# This will of course break if any of these variables contains a newline or
|
||||
# an unmatched quote.
|
||||
#
|
||||
|
||||
eval "set -- $(
|
||||
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
|
||||
xargs -n1 |
|
||||
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
|
||||
tr '\n' ' '
|
||||
)" '"$@"'
|
||||
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
||||
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
||||
|
||||
exec "$JAVACMD" "$@"
|
||||
|
|
35
gradlew.bat
vendored
35
gradlew.bat
vendored
|
@ -14,7 +14,7 @@
|
|||
@rem limitations under the License.
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%"=="" @echo off
|
||||
@if "%DEBUG%" == "" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
|
@ -25,8 +25,7 @@
|
|||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%"=="" set DIRNAME=.
|
||||
@rem This is normally unused
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
|
@ -41,13 +40,13 @@ if defined JAVA_HOME goto findJavaFromJavaHome
|
|||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if %ERRORLEVEL% equ 0 goto execute
|
||||
if "%ERRORLEVEL%" == "0" goto execute
|
||||
|
||||
echo. 1>&2
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
|
||||
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
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
|
@ -57,11 +56,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
|||
|
||||
if exist "%JAVA_EXE%" goto execute
|
||||
|
||||
echo. 1>&2
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
|
||||
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
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
|
@ -76,15 +75,13 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
|||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if %ERRORLEVEL% equ 0 goto mainEnd
|
||||
if "%ERRORLEVEL%"=="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%
|
||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||
exit /b 1
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
paperweight is a Gradle plugin for the PaperMC project.
|
||||
|
||||
Copyright (c) 2023 Kyle Wood (DenWav)
|
||||
Copyright (c) 2021 Kyle Wood (DemonWav)
|
||||
Contributors
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
|
|
|
@ -1,17 +0,0 @@
|
|||
plugins {
|
||||
`config-kotlin`
|
||||
`config-publish`
|
||||
}
|
||||
|
||||
dependencies {
|
||||
shade(projects.nuggetLib)
|
||||
|
||||
implementation(libs.bundles.kotson)
|
||||
}
|
||||
|
||||
gradlePlugin {
|
||||
plugins.all {
|
||||
description = "Gradle plugin for developing Paper"
|
||||
implementationClass = "io.papermc.paperweight.core.PaperweightCore"
|
||||
}
|
||||
}
|
|
@ -1,244 +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
|
||||
|
||||
import io.papermc.paperweight.DownloadService
|
||||
import io.papermc.paperweight.core.extension.PaperweightCoreExtension
|
||||
import io.papermc.paperweight.core.taskcontainers.AllTasks
|
||||
import io.papermc.paperweight.core.tasks.PaperweightCorePrepareForDownstream
|
||||
import io.papermc.paperweight.taskcontainers.BundlerJarTasks
|
||||
import io.papermc.paperweight.taskcontainers.DevBundleTasks
|
||||
import io.papermc.paperweight.tasks.*
|
||||
import io.papermc.paperweight.tasks.patchremap.RemapPatches
|
||||
import io.papermc.paperweight.util.*
|
||||
import io.papermc.paperweight.util.constants.*
|
||||
import java.io.File
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.logging.Logging
|
||||
import org.gradle.api.tasks.Delete
|
||||
import org.gradle.api.tasks.TaskProvider
|
||||
import org.gradle.api.tasks.bundling.Zip
|
||||
import org.gradle.kotlin.dsl.*
|
||||
|
||||
class PaperweightCore : Plugin<Project> {
|
||||
companion object {
|
||||
private val logger = Logging.getLogger(PaperweightCore::class.java)
|
||||
}
|
||||
|
||||
override fun apply(target: Project) {
|
||||
checkJavaVersion()
|
||||
Git.checkForGit()
|
||||
printId<PaperweightCore>("nugget-core", target.gradle)
|
||||
|
||||
val ext = target.extensions.create(PAPERWEIGHT_EXTENSION, PaperweightCoreExtension::class, target)
|
||||
|
||||
target.gradle.sharedServices.registerIfAbsent(DOWNLOAD_SERVICE_NAME, DownloadService::class) {}
|
||||
|
||||
target.tasks.register<Delete>("cleanCache") {
|
||||
group = "paper"
|
||||
description = "Delete the project setup cache and task outputs."
|
||||
delete(target.layout.cache)
|
||||
}
|
||||
|
||||
// Make sure the submodules are initialized, since there are files there
|
||||
// which are required for configuration
|
||||
target.layout.maybeInitSubmodules(target.offlineMode(), logger)
|
||||
|
||||
target.configurations.create(PARAM_MAPPINGS_CONFIG)
|
||||
target.configurations.create(REMAPPER_CONFIG)
|
||||
target.configurations.create(DECOMPILER_CONFIG)
|
||||
target.configurations.create(PAPERCLIP_CONFIG)
|
||||
|
||||
if (target.providers.gradleProperty("nugget.dev").orNull == "true") {
|
||||
target.tasks.register<CreateDiffOutput>("diff") {
|
||||
inputDir.convention(ext.paper.paperServerDir.map { it.dir("src/main/java") })
|
||||
val prop = target.providers.gradleProperty("paperweight.diff.output")
|
||||
if (prop.isPresent) {
|
||||
baseDir.convention(target.layout.projectDirectory.dir(prop))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val tasks = AllTasks(target)
|
||||
|
||||
val devBundleTasks = DevBundleTasks(target)
|
||||
|
||||
val bundlerJarTasks = BundlerJarTasks(
|
||||
target,
|
||||
ext.bundlerJarName,
|
||||
ext.mainClass
|
||||
)
|
||||
|
||||
target.createPatchRemapTask(tasks)
|
||||
|
||||
target.tasks.register<PaperweightCorePrepareForDownstream>(PAPERWEIGHT_PREPARE_DOWNSTREAM) {
|
||||
dependsOn(tasks.applyPatches)
|
||||
vanillaJar.set(tasks.downloadServerJar.flatMap { it.outputJar })
|
||||
remappedJar.set(tasks.lineMapJar.flatMap { it.outputJar })
|
||||
decompiledJar.set(tasks.decompileJar.flatMap { it.outputJar })
|
||||
mcVersion.set(target.ext.minecraftVersion)
|
||||
mcLibrariesFile.set(tasks.extractFromBundler.flatMap { it.serverLibrariesTxt })
|
||||
mcLibrariesDir.set(tasks.extractFromBundler.flatMap { it.serverLibraryJars })
|
||||
mcLibrariesSourcesDir.set(tasks.downloadMcLibrariesSources.flatMap { it.outputDir })
|
||||
spigotLibrariesSourcesDir.set(tasks.downloadSpigotDependencies.flatMap { it.outputSourcesDir })
|
||||
mappings.set(tasks.patchMappings.flatMap { it.outputMappings })
|
||||
notchToSpigotMappings.set(tasks.generateSpigotMappings.flatMap { it.notchToSpigotMappings })
|
||||
sourceMappings.set(tasks.generateMappings.flatMap { it.outputMappings })
|
||||
reobfPackagesToFix.set(ext.paper.reobfPackagesToFix)
|
||||
reobfMappingsPatch.set(ext.paper.reobfMappingsPatch)
|
||||
vanillaJarIncludes.set(ext.vanillaJarIncludes)
|
||||
paramMappingsUrl.set(ext.paramMappingsRepo)
|
||||
paramMappingsConfig.set(target.configurations.named(PARAM_MAPPINGS_CONFIG))
|
||||
atFile.set(tasks.mergeAdditionalAts.flatMap { it.outputFile })
|
||||
spigotRecompiledClasses.set(tasks.remapSpigotSources.flatMap { it.spigotRecompiledClasses })
|
||||
bundlerVersionJson.set(tasks.extractFromBundler.flatMap { it.versionJson })
|
||||
serverLibrariesTxt.set(tasks.extractFromBundler.flatMap { it.serverLibrariesTxt })
|
||||
serverLibrariesList.set(tasks.extractFromBundler.flatMap { it.serverLibrariesList })
|
||||
|
||||
dataFile.set(
|
||||
target.layout.file(
|
||||
providers.gradleProperty(PAPERWEIGHT_DOWNSTREAM_FILE_PROPERTY).map { File(it) }
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
target.afterEvaluate {
|
||||
target.repositories {
|
||||
maven(ext.paramMappingsRepo) {
|
||||
name = PARAM_MAPPINGS_REPO_NAME
|
||||
content { onlyForConfigurations(PARAM_MAPPINGS_CONFIG) }
|
||||
}
|
||||
maven(ext.remapRepo) {
|
||||
name = REMAPPER_REPO_NAME
|
||||
content { onlyForConfigurations(REMAPPER_CONFIG) }
|
||||
}
|
||||
maven(ext.decompileRepo) {
|
||||
name = DECOMPILER_REPO_NAME
|
||||
content { onlyForConfigurations(DECOMPILER_CONFIG) }
|
||||
}
|
||||
}
|
||||
|
||||
// Setup the server jar
|
||||
val cache = target.layout.cache
|
||||
|
||||
val serverProj = target.ext.serverProject.orNull ?: return@afterEvaluate
|
||||
val serverJar = serverProj.tasks.register("serverJar", Zip::class)
|
||||
|
||||
tasks.generateReobfMappings {
|
||||
inputJar.set(serverJar.flatMap { it.archiveFile })
|
||||
}
|
||||
tasks.generateRelocatedReobfMappings {
|
||||
inputJar.set(serverJar.flatMap { it.archiveFile })
|
||||
}
|
||||
|
||||
val (includeMappings, reobfJar) = serverProj.setupServerProject(
|
||||
target,
|
||||
tasks.lineMapJar.flatMap { it.outputJar },
|
||||
tasks.decompileJar.flatMap { it.outputJar },
|
||||
ext.mcDevSourceDir.path,
|
||||
cache.resolve(SERVER_LIBRARIES_TXT),
|
||||
ext.paper.reobfPackagesToFix,
|
||||
tasks.generateRelocatedReobfMappings,
|
||||
serverJar
|
||||
) ?: return@afterEvaluate
|
||||
|
||||
devBundleTasks.configure(
|
||||
ext.serverProject.get(),
|
||||
ext.bundlerJarName.get(),
|
||||
ext.mainClass,
|
||||
ext.minecraftVersion,
|
||||
tasks.decompileJar.map { it.outputJar.path },
|
||||
tasks.extractFromBundler.map { it.serverLibrariesTxt.path },
|
||||
tasks.extractFromBundler.map { it.serverLibrariesList.path },
|
||||
tasks.downloadServerJar.map { it.outputJar.path },
|
||||
tasks.mergeAdditionalAts.map { it.outputFile.path },
|
||||
tasks.extractFromBundler.map { it.versionJson.path }.convertToFileProvider(layout, providers)
|
||||
) {
|
||||
vanillaJarIncludes.set(ext.vanillaJarIncludes)
|
||||
reobfMappingsFile.set(tasks.generateRelocatedReobfMappings.flatMap { it.outputMappings })
|
||||
|
||||
paramMappingsCoordinates.set(
|
||||
target.provider {
|
||||
determineArtifactCoordinates(target.configurations.getByName(PARAM_MAPPINGS_CONFIG)).single()
|
||||
}
|
||||
)
|
||||
paramMappingsUrl.set(ext.paramMappingsRepo)
|
||||
}
|
||||
devBundleTasks.configureAfterEvaluate()
|
||||
|
||||
bundlerJarTasks.configureBundlerTasks(
|
||||
tasks.extractFromBundler.flatMap { it.versionJson },
|
||||
tasks.extractFromBundler.flatMap { it.serverLibrariesList },
|
||||
tasks.downloadServerJar.flatMap { it.outputJar },
|
||||
includeMappings.flatMap { it.outputJar },
|
||||
reobfJar,
|
||||
ext.minecraftVersion
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun Project.createPatchRemapTask(allTasks: AllTasks) {
|
||||
val extension: PaperweightCoreExtension = ext
|
||||
|
||||
/*
|
||||
* To ease the waiting time for debugging this task, all of the task dependencies have been removed (notice all
|
||||
* of those .get() calls). This means when you make changes to paperweight Gradle won't know that this task
|
||||
* technically depends on the output of all of those other tasks.
|
||||
*
|
||||
* In order to run all of the other necessary tasks before running this task in order to setup the inputs, run:
|
||||
*
|
||||
* ./gradlew patchPaper applyVanillaSrgAt
|
||||
*
|
||||
* Then you should be able to run `./gradlew remapPatches` without having to worry about all of the other tasks
|
||||
* running whenever you make changes to paperweight.
|
||||
*/
|
||||
|
||||
@Suppress("UNUSED_VARIABLE")
|
||||
val remapPatches: TaskProvider<RemapPatches> by tasks.registering<RemapPatches> {
|
||||
group = "paperweight"
|
||||
description = "NOT FOR TYPICAL USE: Attempt to remap Paper's patches from Spigot mappings to Mojang mappings."
|
||||
|
||||
inputPatchDir.set(extension.paper.unmappedSpigotServerPatchDir)
|
||||
apiPatchDir.set(extension.paper.spigotApiPatchDir)
|
||||
|
||||
mappingsFile.set(allTasks.patchMappings.flatMap { it.outputMappings }.get())
|
||||
ats.set(allTasks.remapSpigotSources.flatMap { it.generatedAt }.get())
|
||||
|
||||
// Pull in as many jars as possible to reduce the possibility of type bindings not resolving
|
||||
classpathJars.from(allTasks.applyMergedAt.flatMap { it.outputJar }.get()) // final remapped jar
|
||||
classpathJars.from(allTasks.remapSpigotSources.flatMap { it.vanillaRemappedSpigotJar }.get()) // Spigot remapped jar
|
||||
classpathJars.from(allTasks.extractFromBundler.flatMap { it.serverJar }.get()) // pure vanilla jar
|
||||
|
||||
spigotApiDir.set(allTasks.patchSpigotApi.flatMap { it.outputDir }.get())
|
||||
spigotServerDir.set(allTasks.patchSpigotServer.flatMap { it.outputDir }.get())
|
||||
spigotDecompJar.set(allTasks.spigotDecompileJar.flatMap { it.outputJar }.get())
|
||||
|
||||
// library class imports
|
||||
mcLibrarySourcesDir.set(allTasks.downloadMcLibrariesSources.flatMap { it.outputDir }.get())
|
||||
devImports.set(extension.paper.devImports)
|
||||
|
||||
outputPatchDir.set(extension.paper.remappedSpigotServerPatchDir)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,183 +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.DownloadService
|
||||
import io.papermc.paperweight.core.ext
|
||||
import io.papermc.paperweight.core.extension.PaperweightCoreExtension
|
||||
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.Task
|
||||
import org.gradle.api.provider.Provider
|
||||
import org.gradle.api.tasks.TaskContainer
|
||||
import org.gradle.kotlin.dsl.*
|
||||
|
||||
@Suppress("MemberVisibilityCanBePrivate")
|
||||
open class AllTasks(
|
||||
project: Project,
|
||||
tasks: TaskContainer = project.tasks,
|
||||
cache: Path = project.layout.cache,
|
||||
extension: PaperweightCoreExtension = project.ext,
|
||||
downloadService: Provider<DownloadService> = project.download
|
||||
) : SpigotTasks(project) {
|
||||
|
||||
val mergeAdditionalAts by tasks.registering<MergeAccessTransforms> {
|
||||
firstFile.set(mergeGeneratedAts.flatMap { it.outputFile })
|
||||
secondFile.set(mergePaperAts.flatMap { it.outputFile })
|
||||
}
|
||||
|
||||
val applyMergedAt by tasks.registering<ApplyAccessTransform> {
|
||||
inputJar.set(fixJar.flatMap { it.outputJar })
|
||||
atFile.set(mergeAdditionalAts.flatMap { it.outputFile })
|
||||
}
|
||||
|
||||
val copyResources by tasks.registering<CopyResources> {
|
||||
inputJar.set(applyMergedAt.flatMap { it.outputJar })
|
||||
vanillaJar.set(extractFromBundler.flatMap { it.serverJar })
|
||||
}
|
||||
|
||||
val decompileJar by tasks.registering<RunVineFlower> {
|
||||
executable.from(project.configurations.named(DECOMPILER_CONFIG))
|
||||
|
||||
inputJar.set(copyResources.flatMap { it.outputJar })
|
||||
libraries.from(extractFromBundler.map { it.serverLibraryJars.asFileTree })
|
||||
|
||||
outputJar.set(cache.resolve(FINAL_DECOMPILE_JAR))
|
||||
}
|
||||
|
||||
val lineMapJar by tasks.registering<LineMapJar> {
|
||||
inputJar.set(copyResources.flatMap { it.outputJar })
|
||||
outputJar.set(cache.resolve(FINAL_REMAPPED_JAR))
|
||||
decompiledJar.set(decompileJar.flatMap { it.outputJar })
|
||||
}
|
||||
|
||||
val applyApiPatches by tasks.registering<ApplyGitPatches> {
|
||||
group = "paper"
|
||||
description = "Setup the Paper-API project"
|
||||
|
||||
if (project.isBaseExecution) {
|
||||
doNotTrackState("$name should always run when requested as part of the base execution.")
|
||||
}
|
||||
printOutput.set(project.isBaseExecution)
|
||||
|
||||
branch.set("HEAD")
|
||||
upstreamBranch.set("upstream")
|
||||
upstream.set(patchSpigotApi.flatMap { it.outputDir })
|
||||
patchDir.set(extension.paper.spigotApiPatchDir)
|
||||
unneededFiles.value(listOf("README.md"))
|
||||
|
||||
outputDir.set(extension.paper.paperApiDir)
|
||||
}
|
||||
|
||||
val downloadMcLibrariesSources by tasks.registering<DownloadMcLibraries> {
|
||||
mcLibrariesFile.set(extractFromBundler.flatMap { it.serverLibrariesTxt })
|
||||
repositories.set(listOf(MC_LIBRARY_URL, MAVEN_CENTRAL_URL))
|
||||
outputDir.set(cache.resolve(MINECRAFT_SOURCES_PATH))
|
||||
sources.set(true)
|
||||
|
||||
downloader.set(downloadService)
|
||||
}
|
||||
|
||||
@Suppress("DuplicatedCode")
|
||||
val applyServerPatches by tasks.registering<ApplyPaperPatches> {
|
||||
group = "paper"
|
||||
description = "Setup the Paper-Server project"
|
||||
|
||||
if (project.isBaseExecution) {
|
||||
doNotTrackState("$name should always run when requested as part of the base execution.")
|
||||
}
|
||||
printOutput.set(project.isBaseExecution)
|
||||
|
||||
patchDir.set(extension.paper.spigotServerPatchDir)
|
||||
remappedSource.set(remapSpigotSources.flatMap { it.sourcesOutputZip })
|
||||
remappedTests.set(remapSpigotSources.flatMap { it.testsOutputZip })
|
||||
caseOnlyClassNameChanges.set(cleanupSourceMappings.flatMap { it.caseOnlyNameChanges })
|
||||
upstreamDir.set(patchSpigotServer.flatMap { it.outputDir })
|
||||
sourceMcDevJar.set(decompileJar.flatMap { it.outputJar })
|
||||
mcLibrariesDir.set(downloadMcLibrariesSources.flatMap { it.outputDir })
|
||||
spigotLibrariesDir.set(downloadSpigotDependencies.flatMap { it.outputSourcesDir })
|
||||
devImports.set(extension.paper.devImports.fileExists(project))
|
||||
unneededFiles.value(listOf("nms-patches", "applyPatches.sh", "CONTRIBUTING.md", "makePatches.sh", "README.md"))
|
||||
|
||||
outputDir.set(extension.paper.paperServerDir)
|
||||
mcDevSources.set(extension.mcDevSourceDir)
|
||||
}
|
||||
|
||||
val applyPatches by tasks.registering<Task> {
|
||||
group = "paper"
|
||||
description = "Set up the Paper development environment"
|
||||
dependsOn(applyApiPatches, applyServerPatches)
|
||||
}
|
||||
|
||||
val rebuildApiPatches by tasks.registering<RebuildGitPatches> {
|
||||
group = "paper"
|
||||
description = "Rebuilds patches to api"
|
||||
inputDir.set(extension.paper.paperApiDir)
|
||||
baseRef.set("base")
|
||||
|
||||
patchDir.set(extension.paper.spigotApiPatchDir)
|
||||
}
|
||||
|
||||
val rebuildServerPatches by tasks.registering<RebuildGitPatches> {
|
||||
group = "paper"
|
||||
description = "Rebuilds patches to server"
|
||||
inputDir.set(extension.paper.paperServerDir)
|
||||
baseRef.set("base")
|
||||
|
||||
patchDir.set(extension.paper.spigotServerPatchDir)
|
||||
}
|
||||
|
||||
@Suppress("unused")
|
||||
val rebuildPatches by tasks.registering<Task> {
|
||||
group = "paper"
|
||||
description = "Rebuilds patches to api and server"
|
||||
dependsOn(rebuildApiPatches, rebuildServerPatches)
|
||||
}
|
||||
|
||||
val generateReobfMappings by tasks.registering<GenerateReobfMappings> {
|
||||
inputMappings.set(patchMappings.flatMap { it.outputMappings })
|
||||
notchToSpigotMappings.set(generateSpigotMappings.flatMap { it.notchToSpigotMappings })
|
||||
sourceMappings.set(generateMappings.flatMap { it.outputMappings })
|
||||
spigotRecompiledClasses.set(remapSpigotSources.flatMap { it.spigotRecompiledClasses })
|
||||
|
||||
reobfMappings.set(cache.resolve(REOBF_MOJANG_SPIGOT_MAPPINGS))
|
||||
}
|
||||
|
||||
val patchReobfMappings by tasks.registering<PatchMappings> {
|
||||
inputMappings.set(generateReobfMappings.flatMap { it.reobfMappings })
|
||||
patch.set(extension.paper.reobfMappingsPatch.fileExists(project))
|
||||
|
||||
fromNamespace.set(DEOBF_NAMESPACE)
|
||||
toNamespace.set(SPIGOT_NAMESPACE)
|
||||
|
||||
outputMappings.set(cache.resolve(PATCHED_REOBF_MOJANG_SPIGOT_MAPPINGS))
|
||||
}
|
||||
|
||||
val generateRelocatedReobfMappings by tasks.registering<GenerateRelocatedReobfMappings> {
|
||||
inputMappings.set(patchReobfMappings.flatMap { it.outputMappings })
|
||||
outputMappings.set(cache.resolve(RELOCATED_PATCHED_REOBF_MOJANG_SPIGOT_MAPPINGS))
|
||||
}
|
||||
}
|
|
@ -1,74 +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
|
||||
|
||||
import io.papermc.paperweight.extension.PaperweightSourceGeneratorExt
|
||||
import io.papermc.paperweight.tasks.*
|
||||
import io.papermc.paperweight.util.*
|
||||
import io.papermc.paperweight.util.constants.*
|
||||
import kotlin.io.path.*
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.kotlin.dsl.*
|
||||
|
||||
abstract class PaperweightSourceGeneratorHelper : Plugin<Project> {
|
||||
override fun apply(target: Project) = with(target) {
|
||||
val ext = extensions.create("paperweight", PaperweightSourceGeneratorExt::class)
|
||||
|
||||
val applyAts by tasks.registering<ApplyAccessTransform> {
|
||||
inputJar.set(rootProject.tasks.named<FixJarTask>("fixJar").flatMap { it.outputJar })
|
||||
atFile.set(ext.atFile)
|
||||
}
|
||||
|
||||
val copyResources by tasks.registering<CopyResources> {
|
||||
inputJar.set(applyAts.flatMap { it.outputJar })
|
||||
vanillaJar.set(rootProject.tasks.named<ExtractFromBundler>("extractFromBundler").flatMap { it.serverJar })
|
||||
}
|
||||
|
||||
val libsFile = rootProject.layout.cache.resolve(SERVER_LIBRARIES_TXT)
|
||||
val vanilla = configurations.register("vanillaServer") {
|
||||
withDependencies {
|
||||
dependencies {
|
||||
val libs = libsFile.convertToPathOrNull()
|
||||
if (libs != null && libs.exists()) {
|
||||
libs.forEachLine { line ->
|
||||
add(create(line))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
vanilla.name(files(copyResources.flatMap { it.outputJar }))
|
||||
}
|
||||
|
||||
afterEvaluate {
|
||||
if (ext.addVanillaServerToImplementation.get()) {
|
||||
configurations.named("implementation") {
|
||||
extendsFrom(vanilla.get())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,31 +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.extension
|
||||
|
||||
import org.gradle.api.model.ObjectFactory
|
||||
import org.gradle.api.provider.Property
|
||||
import org.gradle.kotlin.dsl.property
|
||||
|
||||
abstract class PaperweightServerExtension(objects: ObjectFactory) {
|
||||
val craftBukkitPackageVersion: Property<String> = objects.property()
|
||||
}
|
|
@ -1,36 +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.extension
|
||||
|
||||
import org.gradle.api.file.RegularFileProperty
|
||||
import org.gradle.api.provider.Property
|
||||
|
||||
@Suppress("LeakingThis")
|
||||
abstract class PaperweightSourceGeneratorExt {
|
||||
abstract val atFile: RegularFileProperty
|
||||
abstract val addVanillaServerToImplementation: Property<Boolean>
|
||||
|
||||
init {
|
||||
addVanillaServerToImplementation.convention(true)
|
||||
}
|
||||
}
|
|
@ -1,156 +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.taskcontainers
|
||||
|
||||
import com.google.gson.JsonObject
|
||||
import io.papermc.paperweight.tasks.*
|
||||
import io.papermc.paperweight.util.*
|
||||
import io.papermc.paperweight.util.constants.*
|
||||
import org.gradle.api.NamedDomainObjectContainer
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.file.RegularFile
|
||||
import org.gradle.api.provider.Provider
|
||||
import org.gradle.api.tasks.TaskProvider
|
||||
import org.gradle.kotlin.dsl.*
|
||||
|
||||
@Suppress("MemberVisibilityCanBePrivate")
|
||||
class BundlerJarTasks(
|
||||
project: Project,
|
||||
private val bundlerJarName: Provider<String>,
|
||||
private val mainClassString: Provider<String>,
|
||||
) {
|
||||
val createBundlerJar: TaskProvider<CreateBundlerJar>
|
||||
val createPaperclipJar: TaskProvider<CreatePaperclipJar>
|
||||
|
||||
val createReobfBundlerJar: TaskProvider<CreateBundlerJar>
|
||||
val createReobfPaperclipJar: TaskProvider<CreatePaperclipJar>
|
||||
|
||||
init {
|
||||
val (createBundlerJar, createPaperclipJar) = project.createBundlerJarTask("mojmap")
|
||||
val (createReobfBundlerJar, createReobfPaperclipJar) = project.createBundlerJarTask("reobf")
|
||||
this.createBundlerJar = createBundlerJar
|
||||
this.createPaperclipJar = createPaperclipJar
|
||||
|
||||
this.createReobfBundlerJar = createReobfBundlerJar
|
||||
this.createReobfPaperclipJar = createReobfPaperclipJar
|
||||
}
|
||||
|
||||
fun configureBundlerTasks(
|
||||
bundlerVersionJson: Provider<RegularFile>,
|
||||
serverLibrariesList: Provider<RegularFile>,
|
||||
vanillaJar: Provider<RegularFile>,
|
||||
mojangJar: Provider<RegularFile>,
|
||||
reobfJar: TaskProvider<RemapJar>,
|
||||
mcVersion: Provider<String>
|
||||
) {
|
||||
createBundlerJar.configureWith(
|
||||
bundlerVersionJson,
|
||||
serverLibrariesList,
|
||||
vanillaJar,
|
||||
mojangJar,
|
||||
)
|
||||
createReobfBundlerJar.configureWith(
|
||||
bundlerVersionJson,
|
||||
serverLibrariesList,
|
||||
vanillaJar,
|
||||
reobfJar.flatMap { it.outputJar },
|
||||
)
|
||||
|
||||
createPaperclipJar.configureWith(vanillaJar, createBundlerJar, mcVersion)
|
||||
createReobfPaperclipJar.configureWith(vanillaJar, createReobfBundlerJar, mcVersion)
|
||||
}
|
||||
|
||||
private fun Project.createBundlerJarTask(
|
||||
classifier: String = "",
|
||||
): Pair<TaskProvider<CreateBundlerJar>, TaskProvider<CreatePaperclipJar>> {
|
||||
val bundlerTaskName = "create${classifier.capitalized()}BundlerJar"
|
||||
val paperclipTaskName = "create${classifier.capitalized()}PaperclipJar"
|
||||
|
||||
val bundlerJarTask = tasks.register<CreateBundlerJar>(bundlerTaskName) {
|
||||
group = "paperweight"
|
||||
description = "Build a runnable bundler jar"
|
||||
|
||||
paperclip.from(configurations.named(PAPERCLIP_CONFIG))
|
||||
mainClass.set(mainClassString)
|
||||
|
||||
outputZip.set(layout.buildDirectory.file("libs/${jarName("bundler", classifier)}"))
|
||||
}
|
||||
val paperclipJarTask = tasks.register<CreatePaperclipJar>(paperclipTaskName) {
|
||||
group = "paperweight"
|
||||
description = "Build a runnable paperclip jar"
|
||||
|
||||
libraryChangesJson.set(bundlerJarTask.flatMap { it.libraryChangesJson })
|
||||
outputZip.set(layout.buildDirectory.file("libs/${jarName("paperclip", classifier)}"))
|
||||
}
|
||||
return bundlerJarTask to paperclipJarTask
|
||||
}
|
||||
|
||||
private fun Project.jarName(kind: String, classifier: String): String {
|
||||
return listOfNotNull(
|
||||
project.name,
|
||||
kind,
|
||||
project.version,
|
||||
classifier.takeIf { it.isNotBlank() },
|
||||
).joinToString("-") + ".jar"
|
||||
}
|
||||
|
||||
private fun TaskProvider<CreateBundlerJar>.configureWith(
|
||||
bundlerVersionJson: Provider<RegularFile>,
|
||||
serverLibrariesListFile: Provider<RegularFile>,
|
||||
vanillaJar: Provider<RegularFile>,
|
||||
serverJar: Provider<RegularFile>,
|
||||
) = this {
|
||||
libraryArtifacts.set(project.configurations.named(SERVER_RUNTIME_CLASSPATH))
|
||||
serverLibrariesList.set(serverLibrariesListFile)
|
||||
vanillaBundlerJar.set(vanillaJar)
|
||||
|
||||
versionArtifacts {
|
||||
registerVersionArtifact(
|
||||
bundlerJarName.get(),
|
||||
bundlerVersionJson,
|
||||
serverJar
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun TaskProvider<CreatePaperclipJar>.configureWith(
|
||||
vanillaJar: Provider<RegularFile>,
|
||||
createBundlerJar: TaskProvider<CreateBundlerJar>,
|
||||
mcVers: Provider<String>
|
||||
) = this {
|
||||
originalBundlerJar.set(vanillaJar)
|
||||
bundlerJar.set(createBundlerJar.flatMap { it.outputZip })
|
||||
mcVersion.set(mcVers)
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun NamedDomainObjectContainer<CreateBundlerJar.VersionArtifact>.registerVersionArtifact(
|
||||
name: String,
|
||||
versionJson: Provider<RegularFile>,
|
||||
serverJar: Provider<RegularFile>
|
||||
) = register(name) {
|
||||
id.set(versionJson.map { gson.fromJson<JsonObject>(it)["id"].asString })
|
||||
file.set(serverJar)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,123 +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.taskcontainers
|
||||
|
||||
import io.papermc.paperweight.taskcontainers.BundlerJarTasks.Companion.registerVersionArtifact
|
||||
import 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.Project
|
||||
import org.gradle.api.artifacts.repositories.MavenArtifactRepository
|
||||
import org.gradle.api.file.RegularFile
|
||||
import org.gradle.api.provider.Property
|
||||
import org.gradle.api.provider.Provider
|
||||
import org.gradle.api.tasks.TaskContainer
|
||||
import org.gradle.kotlin.dsl.*
|
||||
|
||||
@Suppress("MemberVisibilityCanBePrivate")
|
||||
class DevBundleTasks(
|
||||
project: Project,
|
||||
tasks: TaskContainer = project.tasks,
|
||||
) {
|
||||
val serverBundlerForDevBundle by tasks.registering<CreateBundlerJar> {
|
||||
paperclip.from(project.configurations.named(PAPERCLIP_CONFIG))
|
||||
}
|
||||
|
||||
val paperclipForDevBundle by tasks.registering<CreatePaperclipJar> {
|
||||
bundlerJar.set(serverBundlerForDevBundle.flatMap { it.outputZip })
|
||||
libraryChangesJson.set(serverBundlerForDevBundle.flatMap { it.libraryChangesJson })
|
||||
}
|
||||
|
||||
val generateDevelopmentBundle by tasks.registering<GenerateDevBundle> {
|
||||
group = "paperweight"
|
||||
|
||||
remapperConfig.set(project.configurations.named(REMAPPER_CONFIG))
|
||||
decompilerConfig.set(project.configurations.named(DECOMPILER_CONFIG))
|
||||
|
||||
devBundleFile.set(project.layout.buildDirectory.file("libs/paperweight-development-bundle-${project.version}.zip"))
|
||||
|
||||
ignoreUnsupportedEnvironment.set(project.providers.gradleProperty(GenerateDevBundle.unsupportedEnvironmentPropName).map { it.toBoolean() })
|
||||
}
|
||||
|
||||
fun configure(
|
||||
serverProj: Project,
|
||||
bundlerJarName: String,
|
||||
mainClassName: Property<String>,
|
||||
minecraftVer: Provider<String>,
|
||||
decompileJar: Provider<Path>,
|
||||
serverLibrariesTxt: Provider<Path>,
|
||||
serverLibrariesListFile: Provider<Path>,
|
||||
vanillaBundlerJarFile: Provider<Path>,
|
||||
accessTransformFile: Provider<Path>,
|
||||
versionJsonFile: Provider<RegularFile>,
|
||||
devBundleConfiguration: GenerateDevBundle.() -> Unit
|
||||
) {
|
||||
serverBundlerForDevBundle {
|
||||
mainClass.set(mainClassName)
|
||||
serverLibrariesList.pathProvider(serverLibrariesListFile)
|
||||
vanillaBundlerJar.pathProvider(vanillaBundlerJarFile)
|
||||
versionArtifacts {
|
||||
registerVersionArtifact(
|
||||
bundlerJarName,
|
||||
versionJsonFile,
|
||||
serverProj.tasks.named<IncludeMappings>("includeMappings").flatMap { it.outputJar }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
paperclipForDevBundle {
|
||||
originalBundlerJar.pathProvider(vanillaBundlerJarFile)
|
||||
mcVersion.set(minecraftVer)
|
||||
}
|
||||
|
||||
generateDevelopmentBundle {
|
||||
sourceDir.set(serverProj.layout.projectDirectory.dir("src/main/java"))
|
||||
minecraftVersion.set(minecraftVer)
|
||||
mojangMappedPaperclipFile.set(paperclipForDevBundle.flatMap { it.outputZip })
|
||||
vanillaServerLibraries.set(
|
||||
serverLibrariesTxt.map { txt ->
|
||||
txt.readLines(Charsets.UTF_8).filter { it.isNotBlank() }
|
||||
}
|
||||
)
|
||||
|
||||
serverVersion.set(serverProj.version.toString())
|
||||
serverCoordinates.set(GenerateDevBundle.createCoordinatesFor(serverProj))
|
||||
serverProject.set(serverProj)
|
||||
runtimeConfiguration.set(project.configurations.named(SERVER_RUNTIME_CLASSPATH))
|
||||
|
||||
decompiledJar.pathProvider(decompileJar)
|
||||
atFile.pathProvider(accessTransformFile)
|
||||
|
||||
devBundleConfiguration(this)
|
||||
}
|
||||
}
|
||||
|
||||
fun configureAfterEvaluate() {
|
||||
generateDevelopmentBundle {
|
||||
remapperUrl.set(project.repositories.named<MavenArtifactRepository>(REMAPPER_REPO_NAME).map { it.url.toString() })
|
||||
decompilerUrl.set(project.repositories.named<MavenArtifactRepository>(DECOMPILER_REPO_NAME).map { it.url.toString() })
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,135 +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.HypoContext
|
||||
import dev.denwav.hypo.hydrate.HydrationManager
|
||||
import dev.denwav.hypo.mappings.ChangeChain
|
||||
import dev.denwav.hypo.mappings.MappingsCompletionManager
|
||||
import dev.denwav.hypo.mappings.contributors.CopyMappingsDown
|
||||
import dev.denwav.hypo.mappings.contributors.PropagateMappingsUp
|
||||
import dev.denwav.hypo.mappings.contributors.RemoveUnusedMappings
|
||||
import dev.denwav.hypo.model.ClassProviderRoot
|
||||
import io.papermc.paperweight.util.*
|
||||
import io.papermc.paperweight.util.constants.*
|
||||
import javax.inject.Inject
|
||||
import kotlin.io.path.*
|
||||
import org.gradle.api.file.ConfigurableFileCollection
|
||||
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
|
||||
|
||||
@CacheableTask
|
||||
abstract class CleanupMappings : JavaLauncherTask() {
|
||||
|
||||
@get:Classpath
|
||||
abstract val sourceJar: RegularFileProperty
|
||||
|
||||
@get:CompileClasspath
|
||||
abstract val libraries: ConfigurableFileCollection
|
||||
|
||||
@get:InputFile
|
||||
@get:PathSensitive(PathSensitivity.NONE)
|
||||
abstract val inputMappings: RegularFileProperty
|
||||
|
||||
@get:OutputFile
|
||||
abstract val outputMappings: RegularFileProperty
|
||||
|
||||
@get:Internal
|
||||
abstract val jvmargs: ListProperty<String>
|
||||
|
||||
@get:Inject
|
||||
abstract val workerExecutor: WorkerExecutor
|
||||
|
||||
override fun init() {
|
||||
super.init()
|
||||
|
||||
jvmargs.convention(listOf("-Xmx1G"))
|
||||
}
|
||||
|
||||
@TaskAction
|
||||
fun run() {
|
||||
val queue = workerExecutor.processIsolation {
|
||||
forkOptions.jvmArgs(jvmargs.get())
|
||||
forkOptions.executable(launcher.get().executablePath.path.absolutePathString())
|
||||
}
|
||||
|
||||
queue.submit(CleanupMappingsAction::class) {
|
||||
inputMappings.set(this@CleanupMappings.inputMappings.path)
|
||||
libraries.from(this@CleanupMappings.libraries.files)
|
||||
sourceJar.set(this@CleanupMappings.sourceJar.path)
|
||||
|
||||
outputMappings.set(this@CleanupMappings.outputMappings.path)
|
||||
}
|
||||
}
|
||||
|
||||
abstract class CleanupMappingsAction : WorkAction<CleanupMappingsAction.Parameters> {
|
||||
|
||||
interface Parameters : WorkParameters {
|
||||
val inputMappings: RegularFileProperty
|
||||
val libraries: ConfigurableFileCollection
|
||||
val sourceJar: RegularFileProperty
|
||||
|
||||
val outputMappings: RegularFileProperty
|
||||
}
|
||||
|
||||
override fun execute() {
|
||||
val mappings = MappingFormats.TINY.read(
|
||||
parameters.inputMappings.path,
|
||||
SPIGOT_NAMESPACE,
|
||||
DEOBF_NAMESPACE
|
||||
)
|
||||
|
||||
val cleanedMappings = HypoContext.builder()
|
||||
.withProvider(AsmClassDataProvider.of(ClassProviderRoot.fromJar(parameters.sourceJar.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(mappings, MappingsCompletionManager.create(hypoContext))
|
||||
}
|
||||
|
||||
MappingFormats.TINY.write(
|
||||
cleanedMappings,
|
||||
parameters.outputMappings.path,
|
||||
SPIGOT_NAMESPACE,
|
||||
DEOBF_NAMESPACE
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,92 +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.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.Optional
|
||||
import org.gradle.api.tasks.OutputFile
|
||||
import org.gradle.api.tasks.TaskAction
|
||||
|
||||
abstract class CollectATsFromPatches : BaseTask() {
|
||||
companion object {
|
||||
private const val PATCH_CONTENT_START = "diff --git a/"
|
||||
private const val CO_AUTHOR_LINE = "Co-authored-by: "
|
||||
}
|
||||
|
||||
@get:Input
|
||||
abstract val header: Property<String>
|
||||
|
||||
@get:InputDirectory
|
||||
abstract val patchDir: DirectoryProperty
|
||||
|
||||
@get:InputDirectory
|
||||
@get:Optional
|
||||
abstract val extraPatchDir: DirectoryProperty
|
||||
|
||||
@get:OutputFile
|
||||
abstract val outputFile: RegularFileProperty
|
||||
|
||||
override fun init() {
|
||||
header.convention("== AT ==")
|
||||
outputFile.convention(defaultOutput("at"))
|
||||
}
|
||||
|
||||
@TaskAction
|
||||
fun run() {
|
||||
outputFile.path.deleteForcefully()
|
||||
val patches = patchDir.path.listDirectoryEntries("*.patch") +
|
||||
(extraPatchDir.pathOrNull?.listDirectoryEntries("*.patch") ?: emptyList())
|
||||
outputFile.path.writeLines(readAts(patches))
|
||||
}
|
||||
|
||||
private fun readAts(patches: Iterable<Path>): List<String> {
|
||||
val result = hashSetOf<String>()
|
||||
|
||||
val start = header.get()
|
||||
for (patch in patches) {
|
||||
patch.useLines {
|
||||
var reading = false
|
||||
for (line in it) {
|
||||
if (line.startsWith(PATCH_CONTENT_START) || line.startsWith(CO_AUTHOR_LINE, true)) {
|
||||
break
|
||||
}
|
||||
if (reading && line.isNotBlank() && !line.startsWith('#')) {
|
||||
result.add(line)
|
||||
}
|
||||
if (line.startsWith(start)) {
|
||||
reading = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result.sorted()
|
||||
}
|
||||
}
|
|
@ -1,210 +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.data.*
|
||||
import java.nio.file.Path
|
||||
import kotlin.io.path.*
|
||||
import org.gradle.api.NamedDomainObjectContainer
|
||||
import org.gradle.api.artifacts.Configuration
|
||||
import org.gradle.api.artifacts.component.ModuleComponentIdentifier
|
||||
import org.gradle.api.artifacts.component.ProjectComponentIdentifier
|
||||
import org.gradle.api.artifacts.result.ResolvedArtifactResult
|
||||
import org.gradle.api.file.ConfigurableFileCollection
|
||||
import org.gradle.api.file.RegularFileProperty
|
||||
import org.gradle.api.provider.Property
|
||||
import org.gradle.api.tasks.*
|
||||
import org.gradle.kotlin.dsl.*
|
||||
|
||||
@CacheableTask
|
||||
abstract class CreateBundlerJar : ZippedTask() {
|
||||
|
||||
interface VersionArtifact {
|
||||
@get:Input
|
||||
val name: String
|
||||
|
||||
@get:Input
|
||||
val id: Property<String>
|
||||
|
||||
@get:Classpath
|
||||
val file: RegularFileProperty
|
||||
}
|
||||
|
||||
@get:Classpath
|
||||
abstract val paperclip: ConfigurableFileCollection
|
||||
|
||||
@get:Input
|
||||
abstract val mainClass: Property<String>
|
||||
|
||||
@get:Nested
|
||||
val versionArtifacts: NamedDomainObjectContainer<VersionArtifact> = createVersionArtifactContainer()
|
||||
|
||||
@get:Classpath
|
||||
@get:Optional
|
||||
abstract val libraryArtifacts: Property<Configuration>
|
||||
|
||||
@get:PathSensitive(PathSensitivity.NONE)
|
||||
@get:InputFile
|
||||
abstract val serverLibrariesList: RegularFileProperty
|
||||
|
||||
@get:Classpath
|
||||
abstract val vanillaBundlerJar: RegularFileProperty
|
||||
|
||||
@get:OutputFile
|
||||
abstract val libraryChangesJson: RegularFileProperty
|
||||
|
||||
private fun createVersionArtifactContainer(): NamedDomainObjectContainer<VersionArtifact> =
|
||||
objects.domainObjectContainer(VersionArtifact::class) { objects.newInstance(it) }
|
||||
|
||||
override fun init() {
|
||||
super.init()
|
||||
|
||||
libraryChangesJson.convention(defaultOutput("$name-library-changes", "json"))
|
||||
}
|
||||
|
||||
override fun run(rootDir: Path) {
|
||||
paperclip.singleFile.toPath().openZip().use { zip ->
|
||||
zip.getPath("/").copyRecursivelyTo(rootDir)
|
||||
}
|
||||
|
||||
val versions = handleVersions(rootDir)
|
||||
val libraries = handleServerDependencies(rootDir)
|
||||
|
||||
val versionsFile = rootDir.resolve(FileEntry.VERSIONS_LIST).also { it.parent.createDirectories() }
|
||||
val librariesFile = rootDir.resolve(FileEntry.LIBRARIES_LIST).also { it.parent.createDirectories() }
|
||||
|
||||
versionsFile.bufferedWriter().use { writer ->
|
||||
for (v in versions.sortedBy { it.id }) {
|
||||
writer.append(v.toString()).append('\n')
|
||||
}
|
||||
}
|
||||
librariesFile.bufferedWriter().use { writer ->
|
||||
for (l in libraries.sortedBy { it.id }) {
|
||||
writer.append(l.toString()).append('\n')
|
||||
}
|
||||
}
|
||||
|
||||
rootDir.resolve("META-INF/main-class").writeText(mainClass.get())
|
||||
|
||||
// copy version.json file
|
||||
vanillaBundlerJar.path.openZip().use { fs ->
|
||||
fs.getPath("/").resolve(FileEntry.VERSION_JSON).copyTo(rootDir.resolve("version.json"))
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleServerDependencies(rootDir: Path): List<FileEntry<ModuleId>> {
|
||||
val libraries = mutableListOf<FileEntry<ModuleId>>()
|
||||
val changedLibraries = mutableListOf<LibraryChange>()
|
||||
|
||||
val serverLibraryEntries = FileEntry.parse(serverLibrariesList.path, ModuleId::parse)
|
||||
|
||||
val outputDir = rootDir.resolve("META-INF/libraries")
|
||||
|
||||
val dependencies = collectDependencies()
|
||||
for (dep in dependencies) {
|
||||
val serverLibrary = serverLibraryEntries.firstOrNull {
|
||||
it.id.group == dep.module.group &&
|
||||
it.id.name == dep.module.name &&
|
||||
it.id.classifier == dep.module.classifier
|
||||
}
|
||||
if (serverLibrary != null) {
|
||||
if (serverLibrary.id.version == dep.module.version) {
|
||||
// nothing to do
|
||||
libraries += serverLibrary
|
||||
|
||||
dep.copyTo(outputDir.resolve(dep.module.toPath()))
|
||||
} else {
|
||||
// we have a different version of this library
|
||||
val newId = dep.module
|
||||
val newPath = newId.toPath()
|
||||
changedLibraries += LibraryChange(serverLibrary.id, serverLibrary.path, newId, newPath)
|
||||
|
||||
val jarFile = dep.copyTo(outputDir.resolve(newPath))
|
||||
|
||||
libraries += FileEntry(jarFile.sha256asHex(), newId, newPath)
|
||||
}
|
||||
} else {
|
||||
// New dependency
|
||||
val id = dep.module
|
||||
val path = id.toPath()
|
||||
val jarFile = dep.copyTo(outputDir.resolve(path))
|
||||
|
||||
libraries += FileEntry(jarFile.sha256asHex(), id, path)
|
||||
}
|
||||
}
|
||||
|
||||
// This file will be used to check library changes in the generatePaperclipPatches step
|
||||
ensureParentExists(libraryChangesJson)
|
||||
libraryChangesJson.path.bufferedWriter().use { writer ->
|
||||
gson.toJson(changedLibraries, writer)
|
||||
}
|
||||
|
||||
return libraries
|
||||
}
|
||||
|
||||
private fun handleVersions(rootDir: Path): List<FileEntry<String>> {
|
||||
val outputDir = rootDir.resolve("META-INF/versions")
|
||||
|
||||
return versionArtifacts.map { versionArtifact ->
|
||||
val id = versionArtifact.id.get()
|
||||
val versionPath = "$id/${versionArtifact.name}-$id.jar"
|
||||
|
||||
val inputFile = versionArtifact.file.path
|
||||
|
||||
val outputFile = outputDir.resolve(versionPath)
|
||||
ensureParentExists(outputFile)
|
||||
inputFile.copyTo(outputFile)
|
||||
|
||||
FileEntry(inputFile.sha256asHex(), id, versionPath)
|
||||
}
|
||||
}
|
||||
|
||||
private fun collectDependencies(): Set<ResolvedArtifactResult> {
|
||||
return libraryArtifacts.map { config ->
|
||||
config.incoming.artifacts.artifacts.filterTo(HashSet()) {
|
||||
val id = it.id.componentIdentifier
|
||||
id is ModuleComponentIdentifier || id is ProjectComponentIdentifier
|
||||
}
|
||||
}.getOrElse(hashSetOf<ResolvedArtifactResult>())
|
||||
}
|
||||
|
||||
private fun ResolvedArtifactResult.copyTo(path: Path): Path {
|
||||
ensureParentExists(path)
|
||||
return file.toPath().copyTo(path, overwrite = true)
|
||||
}
|
||||
|
||||
private val ResolvedArtifactResult.module: ModuleId
|
||||
get() {
|
||||
return when (val ident = id.componentIdentifier) {
|
||||
is ModuleComponentIdentifier -> ModuleId.fromIdentifier(id)
|
||||
is ProjectComponentIdentifier -> {
|
||||
val capability = variant.capabilities.first()
|
||||
val version = capability.version ?: throw PaperweightException("Unknown version for ${capability.group}:${capability.name}")
|
||||
ModuleId(capability.group, capability.name, version)
|
||||
}
|
||||
else -> throw PaperweightException("Unknown artifact result type: ${ident::class.java.name}")
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,63 +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.DirectoryProperty
|
||||
import org.gradle.api.provider.Property
|
||||
import org.gradle.api.tasks.InputDirectory
|
||||
import org.gradle.api.tasks.Internal
|
||||
import org.gradle.api.tasks.OutputDirectory
|
||||
import org.gradle.api.tasks.TaskAction
|
||||
import org.gradle.api.tasks.UntrackedTask
|
||||
import org.gradle.api.tasks.options.Option
|
||||
|
||||
@UntrackedTask(because = "Always copy files")
|
||||
abstract class CreateDiffOutput : BaseTask() {
|
||||
|
||||
@get:InputDirectory
|
||||
abstract val inputDir: DirectoryProperty
|
||||
|
||||
@get:OutputDirectory
|
||||
abstract val outputDir: DirectoryProperty
|
||||
|
||||
@get:Internal
|
||||
abstract val baseDir: DirectoryProperty
|
||||
|
||||
@get:Internal
|
||||
@get:Option(option = "target", description = "Directory name of the diff output")
|
||||
abstract val target: Property<String>
|
||||
|
||||
override fun init() {
|
||||
baseDir.convention(layout.cacheDir("paperweight/diff"))
|
||||
outputDir.convention(baseDir.flatMap { it.dir(target) })
|
||||
}
|
||||
|
||||
@TaskAction
|
||||
fun run() {
|
||||
val output = outputDir.path
|
||||
|
||||
output.deleteRecursive()
|
||||
inputDir.path.copyRecursivelyTo(output)
|
||||
}
|
||||
}
|
|
@ -1,325 +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.data.*
|
||||
import io.sigpipe.jbsdiff.Diff
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.Paths
|
||||
import java.util.StringJoiner
|
||||
import javax.inject.Inject
|
||||
import kotlin.io.path.*
|
||||
import org.gradle.api.file.RegularFileProperty
|
||||
import org.gradle.api.provider.ListProperty
|
||||
import org.gradle.api.provider.Property
|
||||
import org.gradle.api.tasks.CacheableTask
|
||||
import org.gradle.api.tasks.Classpath
|
||||
import org.gradle.api.tasks.Input
|
||||
import org.gradle.api.tasks.InputFile
|
||||
import org.gradle.api.tasks.Internal
|
||||
import org.gradle.api.tasks.PathSensitive
|
||||
import org.gradle.api.tasks.PathSensitivity
|
||||
import org.gradle.kotlin.dsl.*
|
||||
import org.gradle.workers.WorkAction
|
||||
import org.gradle.workers.WorkParameters
|
||||
import org.gradle.workers.WorkQueue
|
||||
import org.gradle.workers.WorkerExecutor
|
||||
|
||||
@CacheableTask
|
||||
abstract class CreatePaperclipJar : JavaLauncherZippedTask() {
|
||||
|
||||
@get:Classpath
|
||||
abstract val originalBundlerJar: RegularFileProperty
|
||||
|
||||
@get:Classpath
|
||||
abstract val bundlerJar: RegularFileProperty
|
||||
|
||||
@get:Input
|
||||
abstract val mcVersion: Property<String>
|
||||
|
||||
@get:PathSensitive(PathSensitivity.NONE)
|
||||
@get:InputFile
|
||||
abstract val libraryChangesJson: RegularFileProperty
|
||||
|
||||
@get:Internal
|
||||
abstract val jvmargs: ListProperty<String>
|
||||
|
||||
@get:Inject
|
||||
abstract val workerExecutor: WorkerExecutor
|
||||
|
||||
override fun init() {
|
||||
super.init()
|
||||
|
||||
jvmargs.convention(listOf("-Xmx1G"))
|
||||
}
|
||||
|
||||
override fun run(rootDir: Path) {
|
||||
// Vanilla's URL uses a SHA1 hash of the vanilla server jar
|
||||
val patchEntries: List<PatchEntry>
|
||||
bundlerJar.path.openZip().use { newBundlerFs ->
|
||||
originalBundlerJar.path.openZip().use { originalBundlerFs ->
|
||||
val originalBundlerRoot = originalBundlerFs.getPath("/")
|
||||
val newBundlerRoot = newBundlerFs.getPath("/")
|
||||
|
||||
patchEntries = createPatches(rootDir, newBundlerRoot, originalBundlerRoot)
|
||||
}
|
||||
}
|
||||
|
||||
rootDir.resolve(PatchEntry.PATCHES_LIST).bufferedWriter().use { writer ->
|
||||
for (entry in patchEntries) {
|
||||
writer.append(entry.toString()).append('\n')
|
||||
}
|
||||
}
|
||||
|
||||
val originalJar = originalBundlerJar.path
|
||||
val vanillaSha256Hash = originalJar.sha256asHex()
|
||||
val vanillaSha1Hash = originalJar.hashFile(HashingAlgorithm.SHA1).asHexString()
|
||||
val vanillaUrl = "https://piston-data.mojang.com/v1/objects/$vanillaSha1Hash/server.jar"
|
||||
val vanillaFileName = "mojang_${mcVersion.get()}.jar"
|
||||
|
||||
val context = DownloadContext(vanillaSha256Hash, vanillaUrl, vanillaFileName)
|
||||
rootDir.resolve(DownloadContext.FILE).writeText(context.toString())
|
||||
}
|
||||
|
||||
private fun createPatches(rootDir: Path, newBundlerRoot: Path, originalBundlerRoot: Path): List<PatchEntry> {
|
||||
val queue = workerExecutor.processIsolation {
|
||||
forkOptions.jvmArgs(jvmargs.get())
|
||||
forkOptions.executable(launcher.get().executablePath.path.absolutePathString())
|
||||
}
|
||||
|
||||
val patchJobs = mutableListOf<PatchJob>()
|
||||
|
||||
val originalVersions = FileEntry.parse(originalBundlerRoot.resolve(FileEntry.VERSIONS_LIST))
|
||||
val originalLibraries = FileEntry.parse(originalBundlerRoot.resolve(FileEntry.LIBRARIES_LIST), ModuleId::parse)
|
||||
|
||||
// Copy all files, we will only replace the files which need to be patched
|
||||
newBundlerRoot.copyRecursivelyTo(rootDir)
|
||||
|
||||
// We will generate patches for library versions which have changed, assuming the changes will be small
|
||||
val libraryChanges = gson.fromJson<List<LibraryChange>>(libraryChangesJson)
|
||||
|
||||
val newVersions = FileEntry.parse(newBundlerRoot.resolve(FileEntry.VERSIONS_LIST))
|
||||
val newLibraries = FileEntry.parse(newBundlerRoot.resolve(FileEntry.LIBRARIES_LIST), ModuleId::parse)
|
||||
|
||||
// First, create paperclip patches for any changed versions
|
||||
for (newVersion in newVersions) {
|
||||
// If there is no original version, then we have nothing to do
|
||||
val originalVersion = originalVersions.firstOrNull { it.id == newVersion.id } ?: continue
|
||||
// If the hashes match we'll be able to pull this file from the original jar
|
||||
if (newVersion.hash == originalVersion.hash) {
|
||||
EntryLocation.VERSION.removeEntry(rootDir, newVersion.path)
|
||||
continue
|
||||
}
|
||||
|
||||
// Both jars have these versions, but they are different, so we need to create a patch
|
||||
patchJobs += queue.submitPatchJob(rootDir, originalBundlerRoot, newBundlerRoot, originalVersion, newVersion, EntryLocation.VERSION)
|
||||
}
|
||||
|
||||
// Remove library jars we don't need
|
||||
for (newLibrary in newLibraries) {
|
||||
val originalLibrary = originalLibraries.firstOrNull { it.id == newLibrary.id } ?: continue
|
||||
if (newLibrary.path != originalLibrary.path && newLibrary.hash == originalLibrary.hash) {
|
||||
throw PaperweightException("Paperclip cannot currently handle non-patch libraries with new paths")
|
||||
}
|
||||
|
||||
if (newLibrary.hash != originalLibrary.hash) {
|
||||
// Create patch for this library as well
|
||||
patchJobs += queue.submitPatchJob(rootDir, originalBundlerRoot, newBundlerRoot, originalLibrary, newLibrary, EntryLocation.LIBRARY)
|
||||
} else {
|
||||
// The original bundler contains the right file, we don't need ours
|
||||
EntryLocation.LIBRARY.removeEntry(rootDir, newLibrary.path)
|
||||
}
|
||||
}
|
||||
|
||||
// Now check for any library changes
|
||||
for (libraryChange in libraryChanges) {
|
||||
val originalLibrary = originalLibraries.firstOrNull { it.id == libraryChange.inputId }
|
||||
?: throw PaperweightException("Unmatched library change, original id: ${libraryChange.inputId}")
|
||||
val newLibrary = newLibraries.firstOrNull { it.id == libraryChange.outputId }
|
||||
?: throw PaperweightException("Unmatched library change, new id: ${libraryChange.outputId}")
|
||||
|
||||
patchJobs += queue.submitPatchJob(rootDir, originalBundlerRoot, newBundlerRoot, originalLibrary, newLibrary, EntryLocation.LIBRARY)
|
||||
}
|
||||
|
||||
queue.await()
|
||||
|
||||
// Find the patch files so we can hash them
|
||||
return patchJobs.map { job ->
|
||||
val patchLocation = job.entryLocation.resolve(rootDir)
|
||||
|
||||
val patchHash = job.patchFile.sha256asHex()
|
||||
PatchEntry(
|
||||
job.entryLocation,
|
||||
job.originalEntry.hash,
|
||||
patchHash,
|
||||
job.newEntry.hash,
|
||||
job.originalEntry.path,
|
||||
job.patchFile.relativeTo(patchLocation).invariantSeparatorsPathString,
|
||||
job.newEntry.path
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun WorkQueue.submitPatchJob(
|
||||
rootDir: Path,
|
||||
originalRoot: Path,
|
||||
newRoot: Path,
|
||||
originalEntry: FileEntry<*>,
|
||||
newEntry: FileEntry<*>,
|
||||
location: EntryLocation
|
||||
): PatchJob {
|
||||
val outputFile = location.resolve(rootDir, newEntry.path)
|
||||
outputFile.deleteForcefully()
|
||||
val patchFile = outputFile.resolveSibling(Paths.get(originalEntry.path).name + ".patch")
|
||||
|
||||
// The original files are in a zip file system, which can't be serialized as that is going outside the JVM
|
||||
// So we copy it out to a real file
|
||||
val originalFile = location.resolve(originalRoot, originalEntry.path)
|
||||
val tempOriginal = createTempFile()
|
||||
originalFile.copyTo(tempOriginal, overwrite = true)
|
||||
|
||||
val newFile = location.resolve(newRoot, newEntry.path)
|
||||
val tempNew = createTempFile()
|
||||
newFile.copyTo(tempNew, overwrite = true)
|
||||
|
||||
submit(PaperclipAction::class) {
|
||||
this.originalFile.set(tempOriginal)
|
||||
this.patchedFile.set(tempNew)
|
||||
this.outputFile.set(patchFile)
|
||||
}
|
||||
|
||||
return PatchJob(originalEntry, newEntry, patchFile, location)
|
||||
}
|
||||
|
||||
abstract class PaperclipAction : WorkAction<PaperclipParameters> {
|
||||
override fun execute() {
|
||||
val outputFile = parameters.outputFile.path
|
||||
val originalFile = parameters.originalFile.path
|
||||
val patchedFile = parameters.patchedFile.path
|
||||
|
||||
// Read the files into memory
|
||||
val originalBytes = parameters.originalFile.path.readBytes()
|
||||
val patchedBytes = parameters.patchedFile.path.readBytes()
|
||||
|
||||
try {
|
||||
outputFile.parent.createDirectories()
|
||||
outputFile.outputStream().use { patchOutput ->
|
||||
Diff.diff(originalBytes, patchedBytes, patchOutput)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
throw PaperweightException("Error creating patch between $originalFile and $patchedFile", e)
|
||||
} finally {
|
||||
runCatching { originalFile.deleteForcefully() }
|
||||
runCatching { patchedFile.deleteForcefully() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface PaperclipParameters : WorkParameters {
|
||||
val originalFile: RegularFileProperty
|
||||
val patchedFile: RegularFileProperty
|
||||
val outputFile: RegularFileProperty
|
||||
}
|
||||
|
||||
data class PatchJob(
|
||||
val originalEntry: FileEntry<*>,
|
||||
val newEntry: FileEntry<*>,
|
||||
val patchFile: Path,
|
||||
val entryLocation: EntryLocation
|
||||
)
|
||||
|
||||
data class PatchEntry(
|
||||
val location: EntryLocation,
|
||||
val originalHash: String,
|
||||
val patchHash: String,
|
||||
val outputHash: String,
|
||||
val originalPath: String,
|
||||
val patchPath: String,
|
||||
val outputPath: String
|
||||
) {
|
||||
override fun toString(): String {
|
||||
val joiner = StringJoiner("\t")
|
||||
joiner.add(location.value)
|
||||
joiner.add(originalHash)
|
||||
joiner.add(patchHash)
|
||||
joiner.add(outputHash)
|
||||
joiner.add(originalPath)
|
||||
joiner.add(patchPath)
|
||||
joiner.add(outputPath)
|
||||
return joiner.toString()
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val PATCHES_LIST = "META-INF/patches.list"
|
||||
}
|
||||
}
|
||||
|
||||
enum class EntryLocation(val value: String) {
|
||||
VERSION("versions") {
|
||||
override fun resolve(dir: Path, path: String?): Path {
|
||||
val base = dir.resolve(FileEntry.VERSIONS_DIR)
|
||||
if (path == null) {
|
||||
return base
|
||||
}
|
||||
return base.resolve(path)
|
||||
}
|
||||
},
|
||||
LIBRARY("libraries") {
|
||||
override fun resolve(dir: Path, path: String?): Path {
|
||||
val base = dir.resolve(FileEntry.LIBRARIES_DIR)
|
||||
if (path == null) {
|
||||
return base
|
||||
}
|
||||
return base.resolve(path)
|
||||
}
|
||||
};
|
||||
|
||||
abstract fun resolve(dir: Path, path: String? = null): Path
|
||||
|
||||
fun removeEntry(dir: Path, path: String) {
|
||||
val entryDir = resolve(dir)
|
||||
|
||||
var file = entryDir.resolve(path)
|
||||
while (file.exists() && file != entryDir) {
|
||||
file.deleteForcefully()
|
||||
file = file.parent
|
||||
|
||||
if (file.listDirectoryEntries().isNotEmpty()) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data class DownloadContext(val hash: String, val url: String, val fileName: String) {
|
||||
override fun toString(): String {
|
||||
return "$hash\t$url\t$fileName"
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val FILE = "META-INF/download-context"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,130 +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 com.google.gson.JsonObject
|
||||
import io.papermc.paperweight.util.*
|
||||
import io.papermc.paperweight.util.data.*
|
||||
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.tasks.CacheableTask
|
||||
import org.gradle.api.tasks.Classpath
|
||||
import org.gradle.api.tasks.OutputDirectory
|
||||
import org.gradle.api.tasks.OutputFile
|
||||
import org.gradle.api.tasks.TaskAction
|
||||
|
||||
@CacheableTask
|
||||
abstract class ExtractFromBundler : BaseTask() {
|
||||
|
||||
@get:Classpath
|
||||
abstract val bundlerJar: RegularFileProperty
|
||||
|
||||
@get:OutputFile
|
||||
abstract val serverJar: RegularFileProperty
|
||||
|
||||
@get:OutputFile
|
||||
abstract val serverLibrariesTxt: RegularFileProperty
|
||||
|
||||
@get:OutputDirectory
|
||||
abstract val serverLibraryJars: DirectoryProperty
|
||||
|
||||
@get:OutputFile
|
||||
abstract val versionJson: RegularFileProperty
|
||||
|
||||
@get:OutputFile
|
||||
abstract val serverLibrariesList: RegularFileProperty
|
||||
|
||||
@get:OutputFile
|
||||
abstract val serverVersionsList: RegularFileProperty
|
||||
|
||||
override fun init() {
|
||||
super.init()
|
||||
serverJar.set(defaultOutput())
|
||||
}
|
||||
|
||||
@TaskAction
|
||||
fun run() {
|
||||
ServerBundler.extractFromBundler(
|
||||
bundlerJar.path,
|
||||
serverJar.path,
|
||||
serverLibraryJars.path,
|
||||
serverLibrariesTxt.path,
|
||||
serverLibrariesList.path,
|
||||
serverVersionsList.path,
|
||||
versionJson.path
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
object ServerBundler {
|
||||
fun extractFromBundler(
|
||||
bundlerJar: Path,
|
||||
serverJar: Path,
|
||||
serverLibraryJars: Path,
|
||||
serverLibrariesTxt: Path?,
|
||||
serverLibrariesList: Path?,
|
||||
serverVersionsList: Path?,
|
||||
versionJson: Path?
|
||||
) {
|
||||
bundlerJar.openZip().use { bundlerFs ->
|
||||
val root = bundlerFs.rootDirectories.first()
|
||||
extractServerJar(root, serverJar, versionJson)
|
||||
extractLibraryJars(root, serverLibraryJars)
|
||||
serverLibrariesTxt?.let { writeLibrariesTxt(root, it) }
|
||||
serverLibrariesList?.let { root.resolve(FileEntry.LIBRARIES_LIST).copyTo(it, overwrite = true) }
|
||||
serverVersionsList?.let { root.resolve(FileEntry.VERSIONS_LIST).copyTo(it, overwrite = true) }
|
||||
}
|
||||
}
|
||||
|
||||
private fun extractServerJar(bundlerZip: Path, serverJar: Path, outputVersionJson: Path?) {
|
||||
val serverVersionJson = bundlerZip.resolve("version.json")
|
||||
outputVersionJson?.let { output ->
|
||||
serverVersionJson.copyTo(output, overwrite = true)
|
||||
}
|
||||
|
||||
val versionId = gson.fromJson<JsonObject>(serverVersionJson)["id"].asString
|
||||
val versions = bundlerZip.resolve("/META-INF/versions.list").readLines()
|
||||
.map { it.split('\t') }
|
||||
.associate { it[1] to it[2] }
|
||||
val serverJarPath = bundlerZip.resolve("/META-INF/versions/${versions[versionId]}")
|
||||
|
||||
serverJar.parent.createDirectories()
|
||||
serverJarPath.copyTo(serverJar, overwrite = true)
|
||||
}
|
||||
|
||||
private fun extractLibraryJars(bundlerZip: Path, serverLibraryJars: Path) {
|
||||
serverLibraryJars.deleteRecursive()
|
||||
serverLibraryJars.parent.createDirectories()
|
||||
bundlerZip.resolve("/META-INF/libraries").copyRecursivelyTo(serverLibraryJars)
|
||||
}
|
||||
|
||||
private fun writeLibrariesTxt(bundlerZip: Path, serverLibrariesTxt: Path) {
|
||||
val libs = bundlerZip.resolve(FileEntry.LIBRARIES_LIST).readLines()
|
||||
.map { it.split('\t')[1] }
|
||||
|
||||
serverLibrariesTxt.parent.createDirectories()
|
||||
serverLibrariesTxt.writeLines(libs)
|
||||
}
|
||||
}
|
|
@ -1,161 +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.Files
|
||||
import java.nio.file.Path
|
||||
import java.util.stream.Collectors
|
||||
import kotlin.io.path.*
|
||||
import org.gradle.api.file.DirectoryProperty
|
||||
import org.gradle.api.file.RegularFileProperty
|
||||
import org.gradle.api.tasks.InputDirectory
|
||||
import org.gradle.api.tasks.InputFile
|
||||
import org.gradle.api.tasks.OutputFile
|
||||
import org.gradle.api.tasks.TaskAction
|
||||
import org.gradle.work.ChangeType
|
||||
import org.gradle.work.Incremental
|
||||
import org.gradle.work.InputChanges
|
||||
|
||||
abstract class FilterPatchedFiles : BaseTask() {
|
||||
|
||||
@get:InputDirectory
|
||||
@get:Incremental
|
||||
abstract val inputSrcDir: DirectoryProperty
|
||||
|
||||
@get:InputDirectory
|
||||
@get:Incremental
|
||||
abstract val inputResourcesDir: DirectoryProperty
|
||||
|
||||
@get:InputFile
|
||||
abstract val vanillaJar: RegularFileProperty
|
||||
|
||||
@get:OutputFile
|
||||
abstract val outputJar: RegularFileProperty
|
||||
|
||||
override fun init() {
|
||||
outputJar.convention(defaultOutput())
|
||||
}
|
||||
|
||||
@TaskAction
|
||||
fun run(changes: InputChanges) {
|
||||
if (!changes.isIncremental) {
|
||||
return runFull()
|
||||
}
|
||||
val srcAdded = changes.added(inputSrcDir)
|
||||
val srcRemoved = changes.removed(inputSrcDir)
|
||||
val resourceAdded = changes.added(inputResourcesDir)
|
||||
val resourceRemoved = changes.removed(inputResourcesDir)
|
||||
if (srcAdded.isEmpty() && srcRemoved.isEmpty() && resourceAdded.isEmpty() && resourceRemoved.isEmpty()) {
|
||||
logger.info("No adds or removes, not doing work.")
|
||||
didWork = false
|
||||
return
|
||||
}
|
||||
vanillaJar.path.openZip().use { vanillaFs ->
|
||||
val vanillaRoot = vanillaFs.rootDirectories.single()
|
||||
outputJar.path.openZip().use { outputFs ->
|
||||
val outputRoot = outputFs.rootDirectories.single()
|
||||
|
||||
for (add in resourceAdded) {
|
||||
if (vanillaRoot.resolve(add).exists()) {
|
||||
outputRoot.resolve(add).deleteIfExists()
|
||||
}
|
||||
}
|
||||
for (del in resourceRemoved) {
|
||||
val vanilla = vanillaRoot.resolve(del)
|
||||
if (vanilla.exists()) {
|
||||
val out = outputRoot.resolve(del)
|
||||
out.parent.createDirectories()
|
||||
out.deleteIfExists()
|
||||
vanilla.copyTo(out)
|
||||
}
|
||||
}
|
||||
|
||||
for (add in srcAdded) {
|
||||
val p = add.removeSuffix(".java") + ".class"
|
||||
val vanilla = vanillaRoot.resolve(p)
|
||||
if (vanilla.exists()) {
|
||||
val outFile = outputRoot.resolve(p)
|
||||
val outDir = outFile.parent
|
||||
val paths = outDir.listDirectoryEntries("${vanilla.name.removeSuffix(".class")}$*.class").toMutableList()
|
||||
paths.add(outFile)
|
||||
paths.forEach { it.deleteIfExists() }
|
||||
}
|
||||
}
|
||||
for (del in srcRemoved) {
|
||||
val p = del.removeSuffix(".java") + ".class"
|
||||
val vanillaFile = vanillaRoot.resolve(p)
|
||||
if (vanillaFile.exists()) {
|
||||
val paths = vanillaFile.parent.listDirectoryEntries("${vanillaFile.name.removeSuffix(".class")}$*.class").toMutableList()
|
||||
paths.add(vanillaFile)
|
||||
paths.forEach {
|
||||
val out = outputRoot.resolve(it.relativeTo(vanillaRoot))
|
||||
out.parent.createDirectories()
|
||||
out.deleteIfExists()
|
||||
it.copyTo(out)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun runFull() {
|
||||
val srcFiles = collectFiles(inputSrcDir.path)
|
||||
val resourceFiles = collectFiles(inputResourcesDir.path)
|
||||
filterJar(
|
||||
vanillaJar.path,
|
||||
outputJar.path,
|
||||
listOf()
|
||||
) {
|
||||
if (!it.isRegularFile()) {
|
||||
false
|
||||
} else if (it.nameCount > 1) {
|
||||
val path = it.subpath(0, it.nameCount - 1).resolve(it.fileName.toString().split("$")[0].removeSuffix(".class")).toString()
|
||||
!srcFiles.contains("$path.java") && !resourceFiles.contains(path)
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun collectFiles(dir: Path): Set<String> {
|
||||
return Files.walk(dir).use { stream ->
|
||||
stream.filter { it.isRegularFile() }
|
||||
.map { it.relativeTo(dir).invariantSeparatorsPathString }
|
||||
.collect(Collectors.toUnmodifiableSet())
|
||||
}
|
||||
}
|
||||
|
||||
private fun InputChanges.added(baseDir: DirectoryProperty): Set<String> {
|
||||
return getFileChanges(baseDir).filter { it.changeType == ChangeType.ADDED }
|
||||
.map { it.file.toPath().relativeTo(baseDir.path).invariantSeparatorsPathString }
|
||||
.toSet()
|
||||
}
|
||||
|
||||
private fun InputChanges.removed(baseDir: DirectoryProperty): Set<String> {
|
||||
return getFileChanges(baseDir).filter { it.changeType == ChangeType.REMOVED }
|
||||
.map { it.file.toPath().relativeTo(baseDir.path).invariantSeparatorsPathString }
|
||||
.toSet()
|
||||
}
|
||||
}
|
|
@ -1,229 +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.CacheableTask
|
||||
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
|
||||
|
||||
@CacheableTask
|
||||
abstract class FixJarForReobf : JavaLauncherTask() {
|
||||
|
||||
@get:Classpath
|
||||
abstract val inputJar: RegularFileProperty
|
||||
|
||||
@get:Optional
|
||||
@get:Input
|
||||
abstract val packagesToProcess: ListProperty<String>
|
||||
|
||||
@get:OutputFile
|
||||
abstract val outputJar: RegularFileProperty
|
||||
|
||||
@get:Internal
|
||||
abstract val jvmargs: ListProperty<String>
|
||||
|
||||
@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<String>
|
||||
val outputJar: RegularFileProperty
|
||||
}
|
||||
|
||||
abstract class FixJarForReobfWorker : WorkAction<FixJarForReobfParams> {
|
||||
|
||||
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<String>) : 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<String>): List<String> {
|
||||
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
|
||||
}
|
||||
}
|
|
@ -1,218 +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.ParameterAnnotationFixer
|
||||
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.provider.Property
|
||||
import org.gradle.api.tasks.CacheableTask
|
||||
import org.gradle.api.tasks.Classpath
|
||||
import org.gradle.api.tasks.Internal
|
||||
import org.gradle.api.tasks.OutputFile
|
||||
import org.gradle.api.tasks.TaskAction
|
||||
import org.gradle.jvm.toolchain.JavaLauncher
|
||||
import org.gradle.kotlin.dsl.*
|
||||
import org.gradle.workers.WorkAction
|
||||
import org.gradle.workers.WorkParameters
|
||||
import org.gradle.workers.WorkQueue
|
||||
import org.gradle.workers.WorkerExecutor
|
||||
import org.objectweb.asm.Opcodes
|
||||
import org.objectweb.asm.tree.AnnotationNode
|
||||
import org.objectweb.asm.tree.ClassNode
|
||||
|
||||
fun fixJar(
|
||||
workerExecutor: WorkerExecutor,
|
||||
jvmArgs: List<String> = arrayListOf("-Xmx512m"),
|
||||
launcher: JavaLauncher,
|
||||
vanillaJarPath: Path,
|
||||
inputJarPath: Path,
|
||||
outputJarPath: Path,
|
||||
useLegacyParameterAnnotationFixer: Boolean = false,
|
||||
): WorkQueue {
|
||||
ensureParentExists(outputJarPath)
|
||||
ensureDeleted(outputJarPath)
|
||||
|
||||
val queue = workerExecutor.processIsolation {
|
||||
forkOptions.jvmArgs(jvmArgs)
|
||||
forkOptions.executable(launcher.executablePath.path.absolutePathString())
|
||||
}
|
||||
|
||||
queue.submit(FixJarTask.FixJarAction::class) {
|
||||
inputJar.set(inputJarPath)
|
||||
vanillaJar.set(vanillaJarPath)
|
||||
outputJar.set(outputJarPath)
|
||||
useLegacyParamAnnotationFixer.set(useLegacyParameterAnnotationFixer)
|
||||
}
|
||||
|
||||
return queue
|
||||
}
|
||||
|
||||
@CacheableTask
|
||||
abstract class FixJarTask : JavaLauncherTask() {
|
||||
|
||||
@get:Classpath
|
||||
abstract val inputJar: RegularFileProperty
|
||||
|
||||
@get:Classpath
|
||||
abstract val vanillaJar: RegularFileProperty
|
||||
|
||||
@get:OutputFile
|
||||
abstract val outputJar: RegularFileProperty
|
||||
|
||||
@get:Internal
|
||||
abstract val jvmArgs: ListProperty<String>
|
||||
|
||||
@get:Inject
|
||||
abstract val workerExecutor: WorkerExecutor
|
||||
|
||||
override fun init() {
|
||||
super.init()
|
||||
|
||||
jvmArgs.convention(listOf("-Xmx512m"))
|
||||
outputJar.convention(defaultOutput())
|
||||
}
|
||||
|
||||
@TaskAction
|
||||
fun run() {
|
||||
fixJar(
|
||||
workerExecutor = workerExecutor,
|
||||
jvmArgs = jvmArgs.get(),
|
||||
launcher = launcher.get(),
|
||||
vanillaJarPath = vanillaJar.path,
|
||||
inputJarPath = inputJar.path,
|
||||
outputJarPath = outputJar.path
|
||||
)
|
||||
}
|
||||
|
||||
interface FixJarParams : WorkParameters {
|
||||
val inputJar: RegularFileProperty
|
||||
val vanillaJar: RegularFileProperty
|
||||
val outputJar: RegularFileProperty
|
||||
val useLegacyParamAnnotationFixer: Property<Boolean>
|
||||
}
|
||||
|
||||
abstract class FixJarAction : WorkAction<FixJarParams> {
|
||||
|
||||
override fun execute() {
|
||||
parameters.vanillaJar.path.openZip().use { vanillaJar ->
|
||||
parameters.outputJar.path.writeZip().use { out ->
|
||||
parameters.inputJar.path.openZip().use { jarFile ->
|
||||
JarProcessing.processJar(
|
||||
jarFile,
|
||||
vanillaJar,
|
||||
out,
|
||||
FixJarClassProcessor(parameters.useLegacyParamAnnotationFixer.get())
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class FixJarClassProcessor(private val legacy: Boolean) : JarProcessing.ClassProcessor.NodeBased, AsmUtil {
|
||||
override fun processClass(node: ClassNode, classNodeCache: ClassNodeCache) {
|
||||
if (legacy) {
|
||||
ParameterAnnotationFixer(node).visitNode()
|
||||
}
|
||||
|
||||
OverrideAnnotationAdder(node, classNodeCache).visitNode()
|
||||
|
||||
if (Opcodes.ACC_RECORD in node.access) {
|
||||
RecordFieldAccessFixer(node).visitNode()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fix proguard changing access of record fields
|
||||
class RecordFieldAccessFixer(private val node: ClassNode) : AsmUtil {
|
||||
fun visitNode() {
|
||||
for (field in node.fields) {
|
||||
if (Opcodes.ACC_STATIC !in field.access && Opcodes.ACC_FINAL in field.access && Opcodes.ACC_PRIVATE !in field.access) {
|
||||
field.access = field.access and AsmUtil.RESET_ACCESS or Opcodes.ACC_PRIVATE
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class OverrideAnnotationAdder(private val node: ClassNode, private val classNodeCache: ClassNodeCache) : AsmUtil {
|
||||
|
||||
fun visitNode() {
|
||||
val superMethods = collectSuperMethods(node)
|
||||
|
||||
val disqualifiedMethods = Opcodes.ACC_STATIC or Opcodes.ACC_PRIVATE
|
||||
for (method in node.methods) {
|
||||
if (method.access in disqualifiedMethods) {
|
||||
continue
|
||||
}
|
||||
|
||||
if (method.name == "<init>" || method.name == "<clinit>") {
|
||||
continue
|
||||
}
|
||||
val (name, desc) = SyntheticUtil.findBaseMethod(method, node.name)
|
||||
|
||||
if (method.name + method.desc in superMethods) {
|
||||
val targetMethod = node.methods.firstOrNull { it.name == name && it.desc == desc } ?: method
|
||||
|
||||
if (targetMethod.invisibleAnnotations == null) {
|
||||
targetMethod.invisibleAnnotations = arrayListOf()
|
||||
}
|
||||
val annoClass = "Ljava/lang/Override;"
|
||||
if (targetMethod.invisibleAnnotations.none { it.desc == annoClass }) {
|
||||
targetMethod.invisibleAnnotations.add(AnnotationNode(annoClass))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun collectSuperMethods(node: ClassNode): Set<String> {
|
||||
fun collectSuperMethods(node: ClassNode, superMethods: HashSet<String>) {
|
||||
val supers = listOfNotNull(node.superName, *node.interfaces.toTypedArray())
|
||||
if (supers.isEmpty()) {
|
||||
return
|
||||
}
|
||||
|
||||
val disqualifiedMethods = Opcodes.ACC_STATIC or Opcodes.ACC_PRIVATE
|
||||
val superNodes = supers.mapNotNull { classNodeCache.findClass(it) }
|
||||
superNodes.asSequence()
|
||||
.flatMap { classNode -> classNode.methods.asSequence() }
|
||||
.filter { method -> method.access !in disqualifiedMethods }
|
||||
.filter { method -> method.name != "<init>" && method.name != "<clinit>" }
|
||||
.map { method -> method.name + method.desc }
|
||||
.toCollection(superMethods)
|
||||
|
||||
for (superNode in superNodes) {
|
||||
collectSuperMethods(superNode, superMethods)
|
||||
}
|
||||
}
|
||||
|
||||
val result = hashSetOf<String>()
|
||||
collectSuperMethods(node, result)
|
||||
return result
|
||||
}
|
||||
}
|
|
@ -1,396 +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 io.papermc.paperweight.util.data.*
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.nio.charset.Charset
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Path
|
||||
import java.util.Locale
|
||||
import java.util.concurrent.TimeUnit
|
||||
import java.util.regex.Pattern
|
||||
import javax.inject.Inject
|
||||
import kotlin.io.path.*
|
||||
import org.gradle.api.DefaultTask
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.artifacts.Configuration
|
||||
import org.gradle.api.artifacts.ExternalModuleDependency
|
||||
import org.gradle.api.artifacts.component.ModuleComponentIdentifier
|
||||
import org.gradle.api.artifacts.result.ResolvedArtifactResult
|
||||
import org.gradle.api.file.DirectoryProperty
|
||||
import org.gradle.api.file.ProjectLayout
|
||||
import org.gradle.api.file.RegularFileProperty
|
||||
import org.gradle.api.plugins.JavaPlugin
|
||||
import org.gradle.api.provider.ListProperty
|
||||
import org.gradle.api.provider.Property
|
||||
import org.gradle.api.tasks.Classpath
|
||||
import org.gradle.api.tasks.Input
|
||||
import org.gradle.api.tasks.InputDirectory
|
||||
import org.gradle.api.tasks.InputFile
|
||||
import org.gradle.api.tasks.Internal
|
||||
import org.gradle.api.tasks.Optional
|
||||
import org.gradle.api.tasks.OutputFile
|
||||
import org.gradle.api.tasks.TaskAction
|
||||
|
||||
abstract class GenerateDevBundle : DefaultTask() {
|
||||
|
||||
@get:InputFile
|
||||
abstract val decompiledJar: RegularFileProperty
|
||||
|
||||
@get:InputDirectory
|
||||
abstract val sourceDir: DirectoryProperty
|
||||
|
||||
@get:Input
|
||||
abstract val minecraftVersion: Property<String>
|
||||
|
||||
@get:InputFile
|
||||
abstract val mojangMappedPaperclipFile: RegularFileProperty
|
||||
|
||||
@get:Input
|
||||
abstract val serverVersion: Property<String>
|
||||
|
||||
@get:Input
|
||||
abstract val serverCoordinates: Property<String>
|
||||
|
||||
@get:Input
|
||||
abstract val apiCoordinates: Property<String>
|
||||
|
||||
@get:Input
|
||||
abstract val vanillaJarIncludes: ListProperty<String>
|
||||
|
||||
@get:Input
|
||||
abstract val vanillaServerLibraries: ListProperty<String>
|
||||
|
||||
@get:Input
|
||||
abstract val libraryRepositories: ListProperty<String>
|
||||
|
||||
@get:Internal
|
||||
abstract val serverProject: Property<Project>
|
||||
|
||||
@get:Classpath
|
||||
abstract val runtimeConfiguration: Property<Configuration>
|
||||
|
||||
@get:Input
|
||||
abstract val paramMappingsUrl: Property<String>
|
||||
|
||||
@get:Input
|
||||
abstract val paramMappingsCoordinates: Property<String>
|
||||
|
||||
@get:Input
|
||||
abstract val decompilerUrl: Property<String>
|
||||
|
||||
@get:Classpath
|
||||
abstract val decompilerConfig: Property<Configuration>
|
||||
|
||||
@get:Input
|
||||
abstract val remapperUrl: Property<String>
|
||||
|
||||
@get:Classpath
|
||||
abstract val remapperConfig: Property<Configuration>
|
||||
|
||||
@get:InputFile
|
||||
abstract val reobfMappingsFile: RegularFileProperty
|
||||
|
||||
@get:InputFile
|
||||
abstract val atFile: RegularFileProperty
|
||||
|
||||
@get:OutputFile
|
||||
abstract val devBundleFile: RegularFileProperty
|
||||
|
||||
@get:Inject
|
||||
abstract val layout: ProjectLayout
|
||||
|
||||
@get:Input
|
||||
@get:Optional
|
||||
abstract val ignoreUnsupportedEnvironment: Property<Boolean>
|
||||
|
||||
@TaskAction
|
||||
fun run() {
|
||||
checkEnvironment()
|
||||
|
||||
val devBundle = devBundleFile.path
|
||||
devBundle.deleteForcefully()
|
||||
devBundle.parent.createDirectories()
|
||||
|
||||
val tempPatchDir = createTempDirectory("devBundlePatches")
|
||||
try {
|
||||
generatePatches(tempPatchDir)
|
||||
|
||||
val dataDir = "data"
|
||||
val patchesDir = "patches"
|
||||
val config = createBundleConfig(dataDir, patchesDir)
|
||||
|
||||
devBundle.writeZip().use { zip ->
|
||||
zip.getPath("config.json").bufferedWriter(Charsets.UTF_8).use { writer ->
|
||||
gson.toJson(config, writer)
|
||||
}
|
||||
zip.getPath("data-version.txt").writeText(currentDataVersion.toString())
|
||||
|
||||
val dataZip = zip.getPath(dataDir)
|
||||
dataZip.createDirectories()
|
||||
reobfMappingsFile.path.copyTo(dataZip.resolve(reobfMappingsFileName))
|
||||
mojangMappedPaperclipFile.path.copyTo(dataZip.resolve(mojangMappedPaperclipFileName))
|
||||
atFile.path.copyTo(dataZip.resolve(atFileName))
|
||||
|
||||
val patchesZip = zip.getPath(patchesDir)
|
||||
tempPatchDir.copyRecursivelyTo(patchesZip)
|
||||
}
|
||||
} finally {
|
||||
tempPatchDir.deleteRecursive()
|
||||
}
|
||||
}
|
||||
|
||||
private fun generatePatches(output: Path) {
|
||||
val workingDir = layout.cache.resolve(paperTaskOutput("tmpdir"))
|
||||
workingDir.deleteRecursive()
|
||||
workingDir.createDirectories()
|
||||
sourceDir.path.copyRecursivelyTo(workingDir)
|
||||
|
||||
Files.walk(workingDir).use { stream ->
|
||||
decompiledJar.path.openZip().use { decompJar ->
|
||||
val decompRoot = decompJar.rootDirectories.single()
|
||||
|
||||
for (file in stream) {
|
||||
if (file.isDirectory()) {
|
||||
continue
|
||||
}
|
||||
val relativeFile = file.relativeTo(workingDir)
|
||||
val relativeFilePath = relativeFile.invariantSeparatorsPathString
|
||||
val decompFile = decompRoot.resolve(relativeFilePath)
|
||||
|
||||
if (decompFile.notExists()) {
|
||||
val outputFile = output.resolve(relativeFilePath)
|
||||
outputFile.parent.createDirectories()
|
||||
file.copyTo(outputFile)
|
||||
} else {
|
||||
val diffText = diffFiles(relativeFilePath, decompFile, file)
|
||||
val patchName = relativeFile.name + ".patch"
|
||||
val outputFile = output.resolve(relativeFilePath).resolveSibling(patchName)
|
||||
if (diffText.isNotBlank()) {
|
||||
outputFile.parent.createDirectories()
|
||||
outputFile.writeText(diffText)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
workingDir.deleteRecursive()
|
||||
}
|
||||
|
||||
private fun diffFiles(fileName: String, original: Path, patched: Path): String {
|
||||
val dir = createTempDirectory("diff")
|
||||
try {
|
||||
val oldFile = dir.resolve("old.java")
|
||||
val newFile = dir.resolve("new.java")
|
||||
original.copyTo(oldFile)
|
||||
patched.copyTo(newFile)
|
||||
|
||||
val args = listOf(
|
||||
"--color=never",
|
||||
"-ud",
|
||||
"--label",
|
||||
"a/$fileName",
|
||||
oldFile.absolutePathString(),
|
||||
"--label",
|
||||
"b/$fileName",
|
||||
newFile.absolutePathString(),
|
||||
)
|
||||
|
||||
return runDiff(dir, args)
|
||||
} finally {
|
||||
dir.deleteRecursive()
|
||||
}
|
||||
}
|
||||
|
||||
private fun runDiff(dir: Path?, args: List<String>): String {
|
||||
val cmd = listOf("diff") + args
|
||||
val process = ProcessBuilder(cmd)
|
||||
.directory(dir)
|
||||
.start()
|
||||
|
||||
val errBytes = ByteArrayOutputStream()
|
||||
val errFuture = redirect(process.errorStream, errBytes)
|
||||
val outBytes = ByteArrayOutputStream()
|
||||
val outFuture = redirect(process.inputStream, outBytes)
|
||||
|
||||
if (!process.waitFor(10L, TimeUnit.SECONDS)) {
|
||||
process.destroyForcibly()
|
||||
throw PaperweightException("Command '${cmd.joinToString(" ")}' did not finish after 10 seconds, killed process")
|
||||
}
|
||||
errFuture.get(500L, TimeUnit.MILLISECONDS)
|
||||
outFuture.get(500L, TimeUnit.MILLISECONDS)
|
||||
val err = asString(errBytes)
|
||||
val exit = process.exitValue()
|
||||
if (exit != 0 && exit != 1 || err.isNotBlank()) {
|
||||
throw PaperweightException("Error (exit code $exit) executing '${cmd.joinToString(" ")}':\n$err")
|
||||
}
|
||||
|
||||
return asString(outBytes)
|
||||
}
|
||||
|
||||
private fun asString(out: ByteArrayOutputStream) = String(out.toByteArray(), Charset.defaultCharset())
|
||||
.replace(System.lineSeparator(), "\n")
|
||||
|
||||
@Suppress("SameParameterValue")
|
||||
private fun createBundleConfig(dataTargetDir: String, patchTargetDir: String): DevBundleConfig {
|
||||
return DevBundleConfig(
|
||||
minecraftVersion = minecraftVersion.get(),
|
||||
mappedServerCoordinates = serverCoordinates.get(),
|
||||
apiCoordinates = "${apiCoordinates.get()}:${serverVersion.get()}",
|
||||
buildData = createBuildDataConfig(dataTargetDir),
|
||||
decompile = createDecompileRunner(),
|
||||
remapper = createRemapDep(),
|
||||
patchDir = patchTargetDir
|
||||
)
|
||||
}
|
||||
|
||||
private fun createBuildDataConfig(targetDir: String): BuildData {
|
||||
return BuildData(
|
||||
paramMappings = MavenDep(paramMappingsUrl.get(), listOf(paramMappingsCoordinates.get())),
|
||||
reobfMappingsFile = "$targetDir/$reobfMappingsFileName",
|
||||
accessTransformFile = "$targetDir/$atFileName",
|
||||
mojangMappedPaperclipFile = "$targetDir/$mojangMappedPaperclipFileName",
|
||||
vanillaJarIncludes = vanillaJarIncludes.get(),
|
||||
compileDependencies = determineLibraries(vanillaServerLibraries.get()).sorted(),
|
||||
runtimeDependencies = collectRuntimeDependencies().map { it.coordinates }.sorted(),
|
||||
libraryRepositories = libraryRepositories.get(),
|
||||
relocations = emptyList(), // Nothing is relocated in the dev bundle as of 1.20.5
|
||||
minecraftRemapArgs = TinyRemapper.minecraftRemapArgs,
|
||||
pluginRemapArgs = TinyRemapper.pluginRemapArgs,
|
||||
)
|
||||
}
|
||||
|
||||
private fun determineLibraries(vanillaServerLibraries: List<String>): Set<String> {
|
||||
val new = arrayListOf<ModuleId>()
|
||||
|
||||
// yes this is not configuration cache compatible, but the task isn't even without this,
|
||||
// and what we want here are the dependencies declared in the server build file,
|
||||
// not the runtime classpath, which would flatten transitive deps of the api for example.
|
||||
for (dependency in serverProject.get().configurations.getByName(JavaPlugin.IMPLEMENTATION_CONFIGURATION_NAME).dependencies) {
|
||||
// don't want project dependencies
|
||||
if (dependency !is ExternalModuleDependency) {
|
||||
continue
|
||||
}
|
||||
val version = listOfNotNull(
|
||||
dependency.versionConstraint.strictVersion,
|
||||
dependency.versionConstraint.requiredVersion,
|
||||
dependency.versionConstraint.preferredVersion,
|
||||
dependency.version
|
||||
).first { it.isNotBlank() }
|
||||
new += ModuleId(dependency.group ?: error("Missing group for $dependency"), dependency.name, version)
|
||||
}
|
||||
|
||||
for (vanillaLib in vanillaServerLibraries) {
|
||||
val vanilla = ModuleId.parse(vanillaLib)
|
||||
if (new.none { it.group == vanilla.group && it.name == vanilla.name && it.classifier == vanilla.classifier }) {
|
||||
new += vanilla
|
||||
}
|
||||
}
|
||||
|
||||
return new.map { it.toString() }.toSet()
|
||||
}
|
||||
|
||||
private val ResolvedArtifactResult.coordinates: String
|
||||
get() = ModuleId.fromIdentifier(id).toString()
|
||||
|
||||
private fun collectRuntimeDependencies(): Set<ResolvedArtifactResult> =
|
||||
runtimeConfiguration.get().incoming.artifacts.artifacts.filterTo(HashSet()) {
|
||||
it.id.componentIdentifier is ModuleComponentIdentifier
|
||||
}
|
||||
|
||||
private fun createDecompileRunner(): Runner {
|
||||
return Runner(
|
||||
dep = determineMavenDep(decompilerUrl, decompilerConfig),
|
||||
args = vineFlowerArgList
|
||||
)
|
||||
}
|
||||
|
||||
private fun createRemapDep(): MavenDep =
|
||||
determineMavenDep(remapperUrl, remapperConfig)
|
||||
|
||||
data class DevBundleConfig(
|
||||
val minecraftVersion: String,
|
||||
val mappedServerCoordinates: String,
|
||||
val apiCoordinates: String,
|
||||
val mojangApiCoordinates: String? = null,
|
||||
val buildData: BuildData,
|
||||
val decompile: Runner,
|
||||
val remapper: MavenDep,
|
||||
val patchDir: String
|
||||
)
|
||||
|
||||
data class BuildData(
|
||||
val paramMappings: MavenDep,
|
||||
val reobfMappingsFile: String,
|
||||
val accessTransformFile: String,
|
||||
val mojangMappedPaperclipFile: String,
|
||||
val vanillaJarIncludes: List<String>,
|
||||
val compileDependencies: List<String>,
|
||||
val runtimeDependencies: List<String>,
|
||||
val libraryRepositories: List<String>,
|
||||
val relocations: List<Relocation>,
|
||||
val minecraftRemapArgs: List<String>,
|
||||
val pluginRemapArgs: List<String>,
|
||||
)
|
||||
|
||||
data class Runner(val dep: MavenDep, val args: List<String>)
|
||||
|
||||
companion object {
|
||||
const val unsupportedEnvironmentPropName: String = "paperweight.generateDevBundle.ignoreUnsupportedEnvironment"
|
||||
|
||||
const val atFileName = "transform.at"
|
||||
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 = 5
|
||||
|
||||
fun createCoordinatesFor(project: Project): String =
|
||||
sequenceOf(project.group, project.name.lowercase(Locale.ENGLISH), "userdev-" + project.version).joinToString(":")
|
||||
}
|
||||
|
||||
private fun checkEnvironment() {
|
||||
val diffVersion = runDiff(null, listOf("--version")) + " " // add whitespace so pattern still works even with eol
|
||||
val matcher = Pattern.compile("diff \\(GNU diffutils\\) (.*?)\\s").matcher(diffVersion)
|
||||
if (matcher.find()) {
|
||||
logger.lifecycle("Using 'diff (GNU diffutils) {}'.", matcher.group(1))
|
||||
return
|
||||
}
|
||||
|
||||
logger.warn("Non-GNU diffutils diff detected, '--version' returned:\n{}", diffVersion)
|
||||
if (this.ignoreUnsupportedEnvironment.getOrElse(false)) {
|
||||
logger.warn("Ignoring unsupported environment as per user configuration.")
|
||||
} else {
|
||||
throw PaperweightException(
|
||||
"Dev bundle generation is running in an unsupported environment (see above log messages).\n" +
|
||||
"You can ignore this and attempt to generate a dev bundle anyways by setting the '$unsupportedEnvironmentPropName' Gradle " +
|
||||
"property to 'true'."
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,156 +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
|
||||
|
||||
@CacheableTask
|
||||
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<String>
|
||||
|
||||
@get:Inject
|
||||
abstract val workerExecutor: WorkerExecutor
|
||||
|
||||
@get:Input
|
||||
abstract val craftBukkitPackageVersion: Property<String>
|
||||
|
||||
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<Action.Parameters> {
|
||||
interface Parameters : WorkParameters {
|
||||
val inputMappings: RegularFileProperty
|
||||
val inputJar: RegularFileProperty
|
||||
val craftBukkitPackageVersion: Property<String>
|
||||
|
||||
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)}"
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
||||
@CacheableTask
|
||||
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<String>
|
||||
|
||||
@get:Inject
|
||||
abstract val workerExecutor: WorkerExecutor
|
||||
|
||||
@get:InputFile
|
||||
@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.path)
|
||||
|
||||
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<String>) : 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<ClassData> = ArrayList()): List<ClassData> {
|
||||
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<GenerateReobfMappingsParams> {
|
||||
|
||||
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.path.readLines().toSet()
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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<String>
|
||||
|
||||
@get:OutputFile
|
||||
abstract val outputJar: RegularFileProperty
|
||||
|
||||
override fun init() {
|
||||
super.init()
|
||||
outputJar.convention(defaultOutput())
|
||||
}
|
||||
|
||||
@TaskAction
|
||||
private 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())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,225 +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 java.nio.ByteBuffer
|
||||
import java.nio.ByteOrder
|
||||
import java.nio.file.Path
|
||||
import java.util.NavigableMap
|
||||
import java.util.TreeMap
|
||||
import java.util.zip.ZipEntry
|
||||
import java.util.zip.ZipInputStream
|
||||
import javax.inject.Inject
|
||||
import kotlin.collections.set
|
||||
import kotlin.io.path.*
|
||||
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.Internal
|
||||
import org.gradle.api.tasks.OutputFile
|
||||
import org.gradle.api.tasks.TaskAction
|
||||
import org.gradle.jvm.toolchain.JavaLauncher
|
||||
import org.gradle.kotlin.dsl.*
|
||||
import org.gradle.workers.WorkAction
|
||||
import org.gradle.workers.WorkParameters
|
||||
import org.gradle.workers.WorkQueue
|
||||
import org.gradle.workers.WorkerExecutor
|
||||
import org.objectweb.asm.ClassVisitor
|
||||
import org.objectweb.asm.Label
|
||||
import org.objectweb.asm.MethodVisitor
|
||||
import org.objectweb.asm.Opcodes
|
||||
import org.objectweb.asm.tree.ClassNode
|
||||
|
||||
@CacheableTask
|
||||
abstract class LineMapJar : JavaLauncherTask() {
|
||||
@get:Classpath
|
||||
abstract val inputJar: RegularFileProperty
|
||||
|
||||
@get:OutputFile
|
||||
abstract val outputJar: RegularFileProperty
|
||||
|
||||
@get:Classpath
|
||||
abstract val decompiledJar: RegularFileProperty
|
||||
|
||||
@get:Internal
|
||||
abstract val jvmArgs: ListProperty<String>
|
||||
|
||||
@get:Inject
|
||||
abstract val workerExecutor: WorkerExecutor
|
||||
|
||||
override fun init() {
|
||||
super.init()
|
||||
|
||||
jvmArgs.convention(listOf("-Xmx512m"))
|
||||
outputJar.convention(defaultOutput())
|
||||
}
|
||||
|
||||
@TaskAction
|
||||
fun run() {
|
||||
lineMapJar(
|
||||
workerExecutor = workerExecutor,
|
||||
jvmArgs = jvmArgs.get(),
|
||||
launcher = launcher.get(),
|
||||
inputJarPath = inputJar.path,
|
||||
outputJarPath = outputJar.path,
|
||||
decompileJarPath = decompiledJar.path
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun lineMapJar(
|
||||
workerExecutor: WorkerExecutor,
|
||||
jvmArgs: List<String> = arrayListOf("-Xmx512m"),
|
||||
launcher: JavaLauncher,
|
||||
inputJarPath: Path,
|
||||
outputJarPath: Path,
|
||||
decompileJarPath: Path,
|
||||
): WorkQueue {
|
||||
ensureParentExists(outputJarPath)
|
||||
ensureDeleted(outputJarPath)
|
||||
|
||||
val queue = workerExecutor.processIsolation {
|
||||
forkOptions.jvmArgs(jvmArgs)
|
||||
forkOptions.executable(launcher.executablePath.path.absolutePathString())
|
||||
}
|
||||
|
||||
queue.submit(LineMapJarAction::class) {
|
||||
inputJar.set(inputJarPath)
|
||||
outputJar.set(outputJarPath)
|
||||
decompileJar.set(decompileJarPath)
|
||||
}
|
||||
|
||||
return queue
|
||||
}
|
||||
|
||||
private abstract class LineMapJarAction : WorkAction<LineMapJarAction.Parameters> {
|
||||
interface Parameters : WorkParameters {
|
||||
val inputJar: RegularFileProperty
|
||||
val outputJar: RegularFileProperty
|
||||
val decompileJar: RegularFileProperty
|
||||
}
|
||||
|
||||
override fun execute() {
|
||||
val lineMap = readLineMap(parameters.decompileJar.path)
|
||||
parameters.outputJar.path.writeZip().use { out ->
|
||||
parameters.inputJar.path.openZip().use { jarFile ->
|
||||
JarProcessing.processJar(jarFile, out, LineMappingClassProcessor(lineMap))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class LineMappingClassProcessor(private val lineMap: Map<String, NavigableMap<Int, Int>>) : JarProcessing.ClassProcessor.VisitorBased {
|
||||
override fun processClass(node: ClassNode, parent: ClassVisitor, classNodeCache: ClassNodeCache): ClassVisitor? {
|
||||
val map = lineMap[node.name.substringBefore('$')]
|
||||
?: return null // No line maps for class?
|
||||
return LineMappingVisitor(parent, map)
|
||||
}
|
||||
|
||||
override fun shouldProcess(file: Path): Boolean {
|
||||
val name = file.toString()
|
||||
.substring(1) // remove leading /
|
||||
.substringBefore(".class")
|
||||
.substringBefore('$')
|
||||
return name in lineMap
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class LineMappingVisitor(
|
||||
parent: ClassVisitor?,
|
||||
private val lineMapping: NavigableMap<Int, Int>
|
||||
) : ClassVisitor(Opcodes.ASM9, parent) {
|
||||
override fun visitMethod(
|
||||
access: Int,
|
||||
name: String,
|
||||
descriptor: String,
|
||||
signature: String?,
|
||||
exceptions: Array<String>?
|
||||
): MethodVisitor =
|
||||
MethodLineFixer(super.visitMethod(access, name, descriptor, signature, exceptions), lineMapping)
|
||||
|
||||
private class MethodLineFixer(
|
||||
parent: MethodVisitor?,
|
||||
private val lineMapping: NavigableMap<Int, Int>
|
||||
) : MethodVisitor(Opcodes.ASM9, parent) {
|
||||
override fun visitLineNumber(line: Int, start: Label?) {
|
||||
var mapped = lineMapping[line]
|
||||
if (mapped == null) {
|
||||
val entry = lineMapping.ceilingEntry(line)
|
||||
if (entry != null) {
|
||||
mapped = entry.value
|
||||
}
|
||||
}
|
||||
super.visitLineNumber(mapped ?: line, start)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun readLineMap(decompileJar: Path): Map<String, NavigableMap<Int, Int>> {
|
||||
val classes: MutableMap<String, NavigableMap<Int, Int>> = HashMap()
|
||||
try {
|
||||
decompileJar.inputStream().use { fis ->
|
||||
ZipInputStream(fis).use { zip ->
|
||||
var entry: ZipEntry? = zip.nextEntry
|
||||
while (entry != null) {
|
||||
val extra: ByteArray? = entry.extra
|
||||
if (extra == null || !entry.name.endsWith(".java")) {
|
||||
entry = zip.nextEntry
|
||||
continue
|
||||
}
|
||||
val buf: ByteBuffer = ByteBuffer.wrap(extra)
|
||||
buf.order(ByteOrder.LITTLE_ENDIAN)
|
||||
while (buf.hasRemaining()) {
|
||||
val id: Short = buf.short
|
||||
val len: Short = buf.short
|
||||
if (id.toInt() == 0x4646) { // FF
|
||||
val cls: String = entry.name.substring(0, entry.name.length - 5)
|
||||
val ver: Byte = buf.get()
|
||||
if (ver != 1.toByte()) {
|
||||
throw PaperweightException("Wrong FF code line version for " + entry.name + " (got $ver, expected 1)")
|
||||
}
|
||||
val count = (len - 1) / 4
|
||||
val lines: NavigableMap<Int, Int> = TreeMap()
|
||||
for (x in 0 until count) {
|
||||
val oldLine: Int = buf.short.toInt()
|
||||
val newLine: Int = buf.short.toInt()
|
||||
lines[oldLine] = newLine
|
||||
}
|
||||
classes[cls] = lines
|
||||
} else {
|
||||
buf.position(buf.position() + len)
|
||||
}
|
||||
}
|
||||
|
||||
entry = zip.nextEntry
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (ex: Exception) {
|
||||
throw PaperweightException("Could not read line maps from decompiled jar: $decompileJar", ex)
|
||||
}
|
||||
return classes
|
||||
}
|
|
@ -1,109 +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.*
|
||||
|
||||
@CacheableTask
|
||||
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<String>
|
||||
|
||||
@get:Input
|
||||
abstract val toNamespace: Property<String>
|
||||
|
||||
@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()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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<RelocationInput>
|
||||
|
||||
@get:Input
|
||||
@get:Optional
|
||||
abstract val processOnly: ListProperty<String>
|
||||
|
||||
fun relocate(fromPackage: String, toPackage: String, op: Action<RelocationInput>) {
|
||||
relocations.add(
|
||||
objects.newInstance<RelocationInput>().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<RelocationWrapper>
|
||||
) : ClassVisitor(Opcodes.ASM9, parent) {
|
||||
override fun visitMethod(
|
||||
access: Int,
|
||||
name: String?,
|
||||
descriptor: String?,
|
||||
signature: String?,
|
||||
exceptions: Array<out String>?
|
||||
): 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<String>
|
||||
|
||||
@get:Input
|
||||
abstract val toPackage: Property<String>
|
||||
}
|
||||
}
|
|
@ -1,229 +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 java.nio.file.Path
|
||||
import kotlin.io.path.*
|
||||
import org.gradle.api.file.ConfigurableFileCollection
|
||||
import org.gradle.api.file.FileCollection
|
||||
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
|
||||
|
||||
@CacheableTask
|
||||
abstract class RemapJar : JavaLauncherTask() {
|
||||
|
||||
@get:Classpath
|
||||
abstract val inputJar: RegularFileProperty
|
||||
|
||||
@get:InputFile
|
||||
@get:PathSensitive(PathSensitivity.NONE)
|
||||
abstract val mappingsFile: RegularFileProperty
|
||||
|
||||
@get:Input
|
||||
abstract val fromNamespace: Property<String>
|
||||
|
||||
@get:Input
|
||||
abstract val toNamespace: Property<String>
|
||||
|
||||
@get:CompileClasspath
|
||||
abstract val remapClasspath: ConfigurableFileCollection
|
||||
|
||||
@get:Classpath
|
||||
abstract val remapper: ConfigurableFileCollection
|
||||
|
||||
@get:Input
|
||||
abstract val remapperArgs: ListProperty<String>
|
||||
|
||||
@get:OutputFile
|
||||
abstract val outputJar: RegularFileProperty
|
||||
|
||||
@get:Internal
|
||||
abstract val jvmArgs: ListProperty<String>
|
||||
|
||||
override fun init() {
|
||||
super.init()
|
||||
|
||||
outputJar.convention(defaultOutput())
|
||||
jvmArgs.convention(listOf("-Xmx1G"))
|
||||
remapperArgs.convention(TinyRemapper.createArgsList())
|
||||
}
|
||||
|
||||
@TaskAction
|
||||
fun run() {
|
||||
if (inputJar.path.absolute().normalize() == outputJar.path.absolute().normalize()) {
|
||||
throw PaperweightException(
|
||||
"Invalid configuration, inputJar and outputJar point to the same path: ${inputJar.path}\n" +
|
||||
"Consider removing customization of output locations, following the default Gradle conventions."
|
||||
)
|
||||
}
|
||||
|
||||
if (toNamespace.get() != fromNamespace.get()) {
|
||||
val logFile = layout.cache.resolve(paperTaskOutput("log"))
|
||||
TinyRemapper.run(
|
||||
argsList = remapperArgs.get(),
|
||||
logFile = logFile,
|
||||
inputJar = inputJar.path,
|
||||
mappingsFile = mappingsFile.path,
|
||||
fromNamespace = fromNamespace.get(),
|
||||
toNamespace = toNamespace.get(),
|
||||
remapClasspath = remapClasspath.files.map { it.toPath() },
|
||||
remapper = remapper,
|
||||
outputJar = outputJar.path,
|
||||
launcher = launcher.get(),
|
||||
workingDir = layout.cache,
|
||||
jvmArgs = jvmArgs.get()
|
||||
)
|
||||
} else {
|
||||
outputJar.path.deleteForcefully()
|
||||
outputJar.path.parent.createDirectories()
|
||||
inputJar.path.copyTo(outputJar.path)
|
||||
}
|
||||
|
||||
outputJar.path.openZip().use { fs ->
|
||||
fs.modifyManifest {
|
||||
mainAttributes.putValue(MAPPINGS_NAMESPACE_MANIFEST_KEY, toNamespace.get())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object TinyRemapper {
|
||||
private const val minecraftLvPattern = "\\$\\$\\d+"
|
||||
private const val fixPackageAccessArg = "--fixpackageaccess"
|
||||
private const val rebuildSourceFileNamesArg = "--rebuildsourcefilenames"
|
||||
private const val renameInvalidLocalsArg = "--renameinvalidlocals"
|
||||
private fun invalidLvNamePatternArg(pattern: String) = "--invalidlvnamepattern=$pattern"
|
||||
private fun threadsArg(num: Int) = "--threads=$num"
|
||||
|
||||
private val baseArgs: List<String> = listOf(
|
||||
"{input}",
|
||||
"{output}",
|
||||
"{mappings}",
|
||||
"{from}",
|
||||
"{to}",
|
||||
"{classpath}",
|
||||
)
|
||||
|
||||
val minecraftRemapArgs: List<String> = createArgsList(
|
||||
fixPackageAccess = true,
|
||||
renameInvalidLocals = true,
|
||||
invalidLvNamePattern = minecraftLvPattern,
|
||||
rebuildSourceFileNames = true,
|
||||
)
|
||||
|
||||
val pluginRemapArgs: List<String> = createArgsList()
|
||||
|
||||
fun createArgsList(
|
||||
fixPackageAccess: Boolean = false,
|
||||
renameInvalidLocals: Boolean = false,
|
||||
invalidLvNamePattern: String? = null,
|
||||
threads: Int = 1,
|
||||
rebuildSourceFileNames: Boolean = false,
|
||||
): List<String> {
|
||||
val args = baseArgs.toMutableList()
|
||||
|
||||
args += threadsArg(threads)
|
||||
|
||||
if (fixPackageAccess) {
|
||||
args += fixPackageAccessArg
|
||||
}
|
||||
if (renameInvalidLocals) {
|
||||
args += renameInvalidLocalsArg
|
||||
}
|
||||
invalidLvNamePattern?.let { pattern ->
|
||||
args += invalidLvNamePatternArg(pattern)
|
||||
}
|
||||
if (rebuildSourceFileNames) {
|
||||
args += rebuildSourceFileNamesArg
|
||||
}
|
||||
|
||||
return args
|
||||
}
|
||||
|
||||
private fun List<String>.expandArgs(
|
||||
input: String,
|
||||
output: String,
|
||||
mappings: String,
|
||||
fromNamespace: String,
|
||||
toNamespace: String,
|
||||
classpath: Array<String>,
|
||||
): List<String> {
|
||||
val args = mutableListOf<String>()
|
||||
|
||||
for (arg in this) {
|
||||
val mapped = when (arg) {
|
||||
"{input}" -> input
|
||||
"{output}" -> output
|
||||
"{mappings}" -> mappings
|
||||
"{from}" -> fromNamespace
|
||||
"{to}" -> toNamespace
|
||||
"{classpath}" -> classpath
|
||||
else -> arg
|
||||
}
|
||||
when (mapped) {
|
||||
is String -> args += mapped
|
||||
is Array<*> -> mapped.mapTo(args) { it as? String ?: throw PaperweightException("Expected String! Got: '$it'.") }
|
||||
else -> throw PaperweightException("Don't know what to do with '$mapped'!")
|
||||
}
|
||||
}
|
||||
|
||||
return args
|
||||
}
|
||||
|
||||
fun run(
|
||||
argsList: List<String>,
|
||||
logFile: Path,
|
||||
inputJar: Path,
|
||||
mappingsFile: Path,
|
||||
fromNamespace: String,
|
||||
toNamespace: String,
|
||||
remapClasspath: List<Path>,
|
||||
remapper: FileCollection,
|
||||
outputJar: Path,
|
||||
launcher: JavaLauncher,
|
||||
workingDir: Path,
|
||||
jvmArgs: List<String> = listOf("-Xmx1G"),
|
||||
) {
|
||||
ensureDeleted(logFile)
|
||||
ensureDeleted(outputJar)
|
||||
|
||||
val args = argsList.expandArgs(
|
||||
input = inputJar.absolutePathString(),
|
||||
output = outputJar.absolutePathString(),
|
||||
mappings = mappingsFile.absolutePathString(),
|
||||
fromNamespace = fromNamespace,
|
||||
toNamespace = toNamespace,
|
||||
classpath = remapClasspath.map { it.absolutePathString() }.toTypedArray(),
|
||||
)
|
||||
|
||||
ensureParentExists(logFile)
|
||||
ensureParentExists(outputJar)
|
||||
launcher.runJar(remapper, workingDir, logFile, jvmArgs = jvmArgs, args = args.toTypedArray())
|
||||
}
|
||||
}
|
|
@ -1,169 +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 java.nio.file.Path
|
||||
import java.util.jar.Attributes
|
||||
import java.util.jar.Manifest
|
||||
import kotlin.io.path.*
|
||||
import org.gradle.api.file.ConfigurableFileCollection
|
||||
import org.gradle.api.file.FileCollection
|
||||
import org.gradle.api.file.RegularFileProperty
|
||||
import org.gradle.api.provider.ListProperty
|
||||
import org.gradle.api.tasks.*
|
||||
import org.gradle.jvm.toolchain.JavaLauncher
|
||||
|
||||
val vineFlowerArgList: List<String> = listOf(
|
||||
"--synthetic-not-set=true",
|
||||
"--ternary-constant-simplification=true",
|
||||
"--include-runtime=current",
|
||||
"--decompile-complex-constant-dynamic=true",
|
||||
"--indent-string= ",
|
||||
"--decompile-inner=true", // is default
|
||||
"--remove-bridge=true", // is default
|
||||
"--decompile-generics=true", // is default
|
||||
"--ascii-strings=false", // is default
|
||||
"--remove-synthetic=true", // is default
|
||||
"--include-classpath=true",
|
||||
"--inline-simple-lambdas=true", // is default
|
||||
"--ignore-invalid-bytecode=false", // is default
|
||||
"--bytecode-source-mapping=true",
|
||||
"--dump-code-lines=true",
|
||||
"--override-annotation=false", // We add override annotations ourselves. Vineflower's impl doesn't work as well yet and conflicts
|
||||
"-cfg", // Pass the libraries as an argument file to avoid command line length limits
|
||||
"{libraries}",
|
||||
"{input}",
|
||||
"{output}"
|
||||
)
|
||||
|
||||
private fun List<String>.createDecompilerArgs(
|
||||
libraries: String,
|
||||
input: String,
|
||||
output: String,
|
||||
): List<String> = map {
|
||||
when (it) {
|
||||
"{libraries}" -> libraries
|
||||
"{input}" -> input
|
||||
"{output}" -> output
|
||||
else -> it
|
||||
}
|
||||
}
|
||||
|
||||
fun runDecompiler(
|
||||
argsList: List<String>,
|
||||
logFile: Path,
|
||||
workingDir: Path,
|
||||
executable: FileCollection,
|
||||
inputJar: Path,
|
||||
libraries: List<Path>,
|
||||
outputJar: Path,
|
||||
javaLauncher: JavaLauncher,
|
||||
jvmArgs: List<String> = listOf("-Xmx4G")
|
||||
) {
|
||||
val libs = ArrayList(libraries)
|
||||
libs.sort()
|
||||
val tempFile = createTempFile("paperweight", "txt")
|
||||
|
||||
try {
|
||||
val vineflower = isVineflower(executable)
|
||||
tempFile.bufferedWriter().use { writer ->
|
||||
for (lib in libs) {
|
||||
if (lib.isLibraryJar) {
|
||||
if (vineflower) {
|
||||
writer.appendLine("--add-external=${lib.absolutePathString()}")
|
||||
} else {
|
||||
writer.appendLine("-e=${lib.absolutePathString()}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val argList = argsList.createDecompilerArgs(
|
||||
tempFile.absolutePathString(),
|
||||
inputJar.absolutePathString(),
|
||||
outputJar.absolutePathString(),
|
||||
)
|
||||
|
||||
outputJar.deleteForcefully()
|
||||
logFile.deleteForcefully()
|
||||
outputJar.parent.createDirectories()
|
||||
|
||||
javaLauncher.runJar(executable, workingDir, logFile, jvmArgs = jvmArgs, args = argList.toTypedArray())
|
||||
} finally {
|
||||
tempFile.deleteForcefully()
|
||||
}
|
||||
}
|
||||
|
||||
private fun isVineflower(executable: FileCollection) = executable.files.any {
|
||||
it.toPath().openZip().use { fs ->
|
||||
val manifest = fs.getPath("META-INF/MANIFEST.MF").takeIf { f -> f.isRegularFile() }?.inputStream()?.buffered()?.use { reader ->
|
||||
Manifest(reader)
|
||||
}
|
||||
manifest != null &&
|
||||
manifest.mainAttributes.containsKey(Attributes.Name("Implementation-Name")) &&
|
||||
manifest.mainAttributes.getValue("Implementation-Name").equals("Vineflower", ignoreCase = true)
|
||||
}
|
||||
}
|
||||
|
||||
@CacheableTask
|
||||
abstract class RunVineFlower : JavaLauncherTask() {
|
||||
|
||||
@get:Classpath
|
||||
abstract val executable: ConfigurableFileCollection
|
||||
|
||||
@get:Classpath
|
||||
abstract val inputJar: RegularFileProperty
|
||||
|
||||
@get:CompileClasspath
|
||||
abstract val libraries: ConfigurableFileCollection
|
||||
|
||||
@get:OutputFile
|
||||
abstract val outputJar: RegularFileProperty
|
||||
|
||||
@get:Internal
|
||||
abstract val jvmargs: ListProperty<String>
|
||||
|
||||
override fun init() {
|
||||
super.init()
|
||||
|
||||
jvmargs.convention(listOf("-Xmx4G"))
|
||||
outputJar.convention(defaultOutput())
|
||||
}
|
||||
|
||||
@TaskAction
|
||||
fun run() {
|
||||
runDecompiler(
|
||||
vineFlowerArgList,
|
||||
layout.cache.resolve(paperTaskOutput("log")),
|
||||
layout.cache,
|
||||
executable,
|
||||
inputJar.path,
|
||||
libraries.files.map { it.toPath() },
|
||||
outputJar.path,
|
||||
launcher.get(),
|
||||
jvmargs.get()
|
||||
)
|
||||
}
|
||||
}
|
|
@ -1,170 +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 java.nio.file.FileSystem
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Path
|
||||
import javax.inject.Inject
|
||||
import kotlin.io.path.*
|
||||
import org.gradle.api.file.ConfigurableFileCollection
|
||||
import org.gradle.api.file.RegularFileProperty
|
||||
import org.gradle.api.logging.Logger
|
||||
import org.gradle.api.logging.Logging
|
||||
import org.gradle.api.provider.ListProperty
|
||||
import org.gradle.api.tasks.Classpath
|
||||
import org.gradle.api.tasks.Internal
|
||||
import org.gradle.api.tasks.OutputFile
|
||||
import org.gradle.api.tasks.TaskAction
|
||||
import org.gradle.workers.WorkAction
|
||||
import org.gradle.workers.WorkParameters
|
||||
import org.gradle.workers.WorkQueue
|
||||
import org.gradle.workers.WorkerExecutor
|
||||
import org.objectweb.asm.tree.ClassNode
|
||||
|
||||
abstract class ScanJar : JavaLauncherTask() {
|
||||
companion object {
|
||||
private val logger: Logger = Logging.getLogger(ScanJar::class.java)
|
||||
}
|
||||
|
||||
@get:Classpath
|
||||
abstract val jarToScan: RegularFileProperty
|
||||
|
||||
@get:Classpath
|
||||
abstract val classpath: ConfigurableFileCollection
|
||||
|
||||
@get:OutputFile
|
||||
abstract val log: RegularFileProperty
|
||||
|
||||
@get:Internal
|
||||
abstract val jvmArgs: ListProperty<String>
|
||||
|
||||
@get:Inject
|
||||
abstract val workerExecutor: WorkerExecutor
|
||||
|
||||
override fun init() {
|
||||
super.init()
|
||||
|
||||
jvmArgs.convention(listOf("-Xmx768m"))
|
||||
log.set(layout.cache.resolve(paperTaskOutput("txt")))
|
||||
}
|
||||
|
||||
@TaskAction
|
||||
fun run() {
|
||||
val launcher = launcher.get()
|
||||
val jvmArgs = jvmArgs.get()
|
||||
val queue = workerExecutor.processIsolation {
|
||||
forkOptions.jvmArgs(jvmArgs)
|
||||
forkOptions.executable(launcher.executablePath.path.absolutePathString())
|
||||
}
|
||||
this.queue(queue)
|
||||
}
|
||||
|
||||
abstract fun queue(queue: WorkQueue)
|
||||
|
||||
abstract class ScanJarAction<P : ScanJarAction.BaseParameters> : WorkAction<P>, AsmUtil {
|
||||
interface BaseParameters : WorkParameters {
|
||||
val jarToScan: RegularFileProperty
|
||||
val classpath: ConfigurableFileCollection
|
||||
val log: RegularFileProperty
|
||||
}
|
||||
|
||||
protected val log = mutableListOf<String>()
|
||||
|
||||
final override fun execute() {
|
||||
parameters.jarToScan.path.openZip().use { scan ->
|
||||
var fail: Exception? = null
|
||||
val classPathDirs = mutableListOf<Path>()
|
||||
val classPathJars = mutableListOf<FileSystem>()
|
||||
parameters.classpath.forEach {
|
||||
if (it.isDirectory) {
|
||||
classPathDirs.add(it.toPath())
|
||||
return@forEach
|
||||
}
|
||||
if (!it.isFile || !it.name.endsWith(".jar")) {
|
||||
return@forEach
|
||||
}
|
||||
try {
|
||||
classPathJars += it.toPath().openZip()
|
||||
} catch (ex: Exception) {
|
||||
logger.error("Failed to open zip $it", ex)
|
||||
if (fail == null) {
|
||||
fail = ex
|
||||
} else {
|
||||
fail!!.addSuppressed(ex)
|
||||
}
|
||||
}
|
||||
}
|
||||
try {
|
||||
if (fail != null) {
|
||||
throw PaperweightException("Failed to read classpath jars", fail)
|
||||
}
|
||||
val classNodeCache = ClassNodeCache.create(scan, classPathJars, classPathDirs)
|
||||
scan(scan, classNodeCache)
|
||||
} finally {
|
||||
var err: Exception? = null
|
||||
classPathJars.forEach {
|
||||
try {
|
||||
it.close()
|
||||
} catch (ex: Exception) {
|
||||
logger.error("Failed to close zip $it", ex)
|
||||
if (err == null) {
|
||||
err = ex
|
||||
} else {
|
||||
err!!.addSuppressed(ex)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (err != null) {
|
||||
throw PaperweightException("Failed to close classpath jars", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!Files.exists(parameters.log.path.parent)) {
|
||||
Files.createDirectories(parameters.log.path.parent)
|
||||
}
|
||||
parameters.log.path.writeLines(log)
|
||||
|
||||
if (log.isNotEmpty()) {
|
||||
throw PaperweightException("Bad code was found, see log file at ${parameters.log.path.toAbsolutePath()}")
|
||||
}
|
||||
}
|
||||
|
||||
private fun scan(scan: FileSystem, classNodeCache: ClassNodeCache) {
|
||||
scan.walk().use { stream ->
|
||||
stream.forEach { file ->
|
||||
if (!Files.isRegularFile(file) || !file.fileName.toString().endsWith(".class")) {
|
||||
return@forEach
|
||||
}
|
||||
val classNode = classNodeCache.findClass(file.toString()) ?: return@forEach
|
||||
this.handleClass(classNode, classNodeCache)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract fun handleClass(classNode: ClassNode, classNodeCache: ClassNodeCache)
|
||||
}
|
||||
}
|
|
@ -1,115 +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.logging.Logger
|
||||
import org.gradle.api.logging.Logging
|
||||
import org.gradle.api.provider.SetProperty
|
||||
import org.gradle.api.tasks.CacheableTask
|
||||
import org.gradle.api.tasks.Input
|
||||
import org.gradle.kotlin.dsl.*
|
||||
import org.gradle.workers.WorkQueue
|
||||
import org.objectweb.asm.Handle
|
||||
import org.objectweb.asm.Opcodes
|
||||
import org.objectweb.asm.tree.AbstractInsnNode
|
||||
import org.objectweb.asm.tree.ClassNode
|
||||
import org.objectweb.asm.tree.InvokeDynamicInsnNode
|
||||
import org.objectweb.asm.tree.MethodInsnNode
|
||||
import org.objectweb.asm.tree.MethodNode
|
||||
|
||||
@CacheableTask
|
||||
abstract class ScanJarForBadCalls : ScanJar() {
|
||||
companion object {
|
||||
private val logger: Logger = Logging.getLogger(ScanJarForBadCalls::class.java)
|
||||
}
|
||||
|
||||
@get:Input
|
||||
abstract val badAnnotations: SetProperty<String>
|
||||
|
||||
override fun queue(queue: WorkQueue) {
|
||||
queue.submit(ScanJarForBadCallsAction::class) {
|
||||
jarToScan.set(this@ScanJarForBadCalls.jarToScan)
|
||||
classpath.from(this@ScanJarForBadCalls.classpath)
|
||||
log.set(this@ScanJarForBadCalls.log)
|
||||
badAnnotations.set(this@ScanJarForBadCalls.badAnnotations)
|
||||
}
|
||||
}
|
||||
|
||||
abstract class ScanJarForBadCallsAction : ScanJarAction<ScanJarForBadCallsAction.Parameters>(), AsmUtil {
|
||||
interface Parameters : BaseParameters {
|
||||
val badAnnotations: SetProperty<String>
|
||||
}
|
||||
|
||||
override fun handleClass(classNode: ClassNode, classNodeCache: ClassNodeCache) {
|
||||
for (method in classNode.methods) {
|
||||
method.instructions.forEach { handleInstruction(classNode, method, it, classNodeCache) }
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleInstruction(classNode: ClassNode, method: MethodNode, absIsnNode: AbstractInsnNode, classNodeCache: ClassNodeCache) {
|
||||
when (absIsnNode) {
|
||||
is InvokeDynamicInsnNode -> handleInvokeDynamic(classNode, method, absIsnNode, classNodeCache)
|
||||
is MethodInsnNode -> handleMethodInvocation(classNode, method, absIsnNode, classNodeCache)
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleInvokeDynamic(
|
||||
classNode: ClassNode,
|
||||
method: MethodNode,
|
||||
invokeDynamicInsnNode: InvokeDynamicInsnNode,
|
||||
classNodeCache: ClassNodeCache
|
||||
) {
|
||||
if (invokeDynamicInsnNode.bsm.owner == "java/lang/invoke/LambdaMetafactory" && invokeDynamicInsnNode.bsmArgs.size > 1) {
|
||||
when (val methodHandle = invokeDynamicInsnNode.bsmArgs[1]) {
|
||||
is Handle -> checkMethod(classNode, method, methodHandle.owner, methodHandle.name, methodHandle.desc, classNodeCache)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleMethodInvocation(classNode: ClassNode, method: MethodNode, methodIsnNode: MethodInsnNode, classNodeCache: ClassNodeCache) {
|
||||
checkMethod(classNode, method, methodIsnNode.owner, methodIsnNode.name, methodIsnNode.desc, classNodeCache)
|
||||
}
|
||||
|
||||
private fun checkMethod(classNode: ClassNode, method: MethodNode, owner: String, name: String, desc: String, classNodeCache: ClassNodeCache) {
|
||||
val targetOwner = classNodeCache.findClass(owner) ?: return
|
||||
val target = targetOwner.methods.find {
|
||||
it.name == name && it.desc == desc
|
||||
} ?: return
|
||||
|
||||
val annotations = (target.visibleAnnotations ?: emptyList()) + (target.invisibleAnnotations ?: emptyList())
|
||||
annotations.find { it.desc in parameters.badAnnotations.get() } ?: return
|
||||
|
||||
val msg = warnMsg(classNode, method, targetOwner, target)
|
||||
log += msg
|
||||
logger.error(msg)
|
||||
}
|
||||
|
||||
private fun warnMsg(classNode: ClassNode, method: MethodNode, targetOwner: ClassNode, target: MethodNode): String {
|
||||
val methodDelimiter = if (Opcodes.ACC_STATIC in method.access) '.' else '#'
|
||||
val targetMethodDelimiter = if (Opcodes.ACC_STATIC in target.access) '.' else '#'
|
||||
return "Method ${classNode.name}$methodDelimiter${method.name}${method.desc} " +
|
||||
"includes reference to bad method ${targetOwner.name}$targetMethodDelimiter${target.name}${target.desc}"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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 org.gradle.api.logging.Logger
|
||||
import org.gradle.api.logging.Logging
|
||||
import org.gradle.api.provider.Property
|
||||
import org.gradle.api.tasks.CacheableTask
|
||||
import org.gradle.api.tasks.Input
|
||||
import org.gradle.kotlin.dsl.*
|
||||
import org.gradle.workers.WorkQueue
|
||||
import org.objectweb.asm.tree.ClassNode
|
||||
|
||||
@CacheableTask
|
||||
abstract class ScanJarForOldGeneratedCode : ScanJar() {
|
||||
companion object {
|
||||
private val logger: Logger = Logging.getLogger(ScanJarForOldGeneratedCode::class.java)
|
||||
}
|
||||
|
||||
@get:Input
|
||||
abstract val mcVersion: Property<String>
|
||||
|
||||
@get:Input
|
||||
abstract val annotation: Property<String>
|
||||
|
||||
override fun queue(queue: WorkQueue) {
|
||||
queue.submit(ScanJarForOldGeneratedCodeAction::class) {
|
||||
jarToScan.set(this@ScanJarForOldGeneratedCode.jarToScan)
|
||||
classpath.from(this@ScanJarForOldGeneratedCode.classpath)
|
||||
log.set(this@ScanJarForOldGeneratedCode.log)
|
||||
mcVersion.set(this@ScanJarForOldGeneratedCode.mcVersion)
|
||||
annotation.set(this@ScanJarForOldGeneratedCode.annotation)
|
||||
}
|
||||
}
|
||||
|
||||
abstract class ScanJarForOldGeneratedCodeAction : ScanJarAction<ScanJarForOldGeneratedCodeAction.Parameters>() {
|
||||
interface Parameters : BaseParameters {
|
||||
val mcVersion: Property<String>
|
||||
val annotation: Property<String>
|
||||
}
|
||||
|
||||
override fun handleClass(classNode: ClassNode, classNodeCache: ClassNodeCache) {
|
||||
val annotations = (classNode.visibleAnnotations ?: emptyList()) + (classNode.invisibleAnnotations ?: emptyList())
|
||||
|
||||
val generatedAnnotation = annotations
|
||||
.find { it.desc == parameters.annotation.get() }
|
||||
?.values
|
||||
?.chunked(2)
|
||||
?.find { it[0] == "value" } ?: return
|
||||
|
||||
val generatedVersion = generatedAnnotation[1].toString()
|
||||
val mcVersion = parameters.mcVersion.get()
|
||||
|
||||
if (generatedVersion != mcVersion) {
|
||||
val msg = errorMsg(classNode, generatedVersion, mcVersion)
|
||||
log += msg
|
||||
logger.error(msg)
|
||||
}
|
||||
}
|
||||
|
||||
private fun errorMsg(classNode: ClassNode, generatedVersion: String, mcVersion: String): String {
|
||||
return "Class ${classNode.name} is marked as being generated in version $generatedVersion when the set version is $mcVersion"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,417 +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.DownloadService
|
||||
import io.papermc.paperweight.util.*
|
||||
import io.papermc.paperweight.util.constants.*
|
||||
import io.papermc.paperweight.util.data.*
|
||||
import java.nio.file.Path
|
||||
import javax.inject.Inject
|
||||
import javax.xml.parsers.DocumentBuilderFactory
|
||||
import kotlin.io.path.*
|
||||
import org.gradle.api.DefaultTask
|
||||
import org.gradle.api.artifacts.component.ComponentIdentifier
|
||||
import org.gradle.api.artifacts.dsl.DependencyFactory
|
||||
import org.gradle.api.attributes.java.TargetJvmEnvironment
|
||||
import org.gradle.api.file.DirectoryProperty
|
||||
import org.gradle.api.file.RegularFileProperty
|
||||
import org.gradle.api.internal.project.ProjectInternal
|
||||
import org.gradle.api.internal.project.ProjectInternal.DetachedResolver
|
||||
import org.gradle.api.provider.ListProperty
|
||||
import org.gradle.api.provider.Property
|
||||
import org.gradle.api.provider.Provider
|
||||
import org.gradle.api.tasks.*
|
||||
import org.gradle.kotlin.dsl.*
|
||||
import org.gradle.work.DisableCachingByDefault
|
||||
import org.gradle.workers.WorkAction
|
||||
import org.gradle.workers.WorkParameters
|
||||
import org.gradle.workers.WorkQueue
|
||||
import org.gradle.workers.WorkerExecutor
|
||||
import org.w3c.dom.Document
|
||||
import org.w3c.dom.Element
|
||||
|
||||
// Not cached since these are Mojang's files
|
||||
abstract class DownloadTask : DefaultTask() {
|
||||
|
||||
@get:Input
|
||||
abstract val url: Property<String>
|
||||
|
||||
@get:OutputFile
|
||||
abstract val outputFile: RegularFileProperty
|
||||
|
||||
@get:Internal
|
||||
abstract val downloader: Property<DownloadService>
|
||||
|
||||
@get:Nested
|
||||
@get:Optional
|
||||
abstract val expectedHash: Property<Hash>
|
||||
|
||||
@TaskAction
|
||||
fun run() = downloader.get().download(url, outputFile, expectedHash.orNull)
|
||||
}
|
||||
|
||||
@CacheableTask
|
||||
abstract class DownloadMcLibraries : BaseTask() {
|
||||
|
||||
@get:InputFile
|
||||
@get:PathSensitive(PathSensitivity.NONE)
|
||||
abstract val mcLibrariesFile: RegularFileProperty
|
||||
|
||||
@get:Input
|
||||
abstract val repositories: ListProperty<String>
|
||||
|
||||
@get:OutputDirectory
|
||||
abstract val outputDir: DirectoryProperty
|
||||
|
||||
@get:Internal
|
||||
abstract val downloader: Property<DownloadService>
|
||||
|
||||
@get:Inject
|
||||
abstract val workerExecutor: WorkerExecutor
|
||||
|
||||
@get:Input
|
||||
abstract val sources: Property<Boolean>
|
||||
|
||||
override fun init() {
|
||||
super.init()
|
||||
sources.convention(false)
|
||||
}
|
||||
|
||||
@TaskAction
|
||||
fun run() {
|
||||
downloadMinecraftLibraries(
|
||||
downloader,
|
||||
workerExecutor,
|
||||
outputDir.path,
|
||||
repositories.get(),
|
||||
mcLibrariesFile.path.readLines(),
|
||||
sources.get()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun downloadMinecraftLibraries(
|
||||
download: Provider<DownloadService>,
|
||||
workerExecutor: WorkerExecutor,
|
||||
targetDir: Path,
|
||||
repositories: List<String>,
|
||||
mcLibraries: List<String>,
|
||||
sources: Boolean
|
||||
): WorkQueue {
|
||||
val excludes = listOf(targetDir.fileSystem.getPathMatcher("glob:*.etag"))
|
||||
targetDir.deleteRecursive(excludes)
|
||||
|
||||
val queue = workerExecutor.noIsolation()
|
||||
|
||||
for (lib in mcLibraries) {
|
||||
if (sources) {
|
||||
queue.submit(DownloadSourcesToDirAction::class) {
|
||||
repos.set(repositories)
|
||||
artifact.set(lib)
|
||||
target.set(targetDir)
|
||||
downloader.set(download)
|
||||
}
|
||||
} else {
|
||||
queue.submit(DownloadWorker::class) {
|
||||
repos.set(repositories)
|
||||
artifact.set(lib)
|
||||
target.set(targetDir)
|
||||
downloadToDir.set(true)
|
||||
downloader.set(download)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return queue
|
||||
}
|
||||
|
||||
@DisableCachingByDefault(because = "Gradle handles caching")
|
||||
abstract class DownloadSpigotDependencies : BaseTask() {
|
||||
|
||||
@get:InputFile
|
||||
@get:PathSensitive(PathSensitivity.NONE)
|
||||
abstract val apiPom: RegularFileProperty
|
||||
|
||||
@get:InputFile
|
||||
@get:PathSensitive(PathSensitivity.NONE)
|
||||
abstract val serverPom: RegularFileProperty
|
||||
|
||||
@get:InputFile
|
||||
@get:PathSensitive(PathSensitivity.NONE)
|
||||
abstract val mcLibrariesFile: RegularFileProperty
|
||||
|
||||
@get:OutputDirectory
|
||||
abstract val outputDir: DirectoryProperty
|
||||
|
||||
@get:OutputDirectory
|
||||
abstract val outputSourcesDir: DirectoryProperty
|
||||
|
||||
@get:Internal
|
||||
abstract val downloader: Property<DownloadService>
|
||||
|
||||
@get:Inject
|
||||
abstract val workerExecutor: WorkerExecutor
|
||||
|
||||
@get:Inject
|
||||
abstract val dependencyFactory: DependencyFactory
|
||||
|
||||
private val detachedResolver: DetachedResolver = (project as ProjectInternal).newDetachedResolver()
|
||||
|
||||
@TaskAction
|
||||
fun run() {
|
||||
val apiSetup = parsePom(apiPom.path)
|
||||
val serverSetup = parsePom(serverPom.path)
|
||||
val mcLibraries = mcLibrariesFile.path.readLines()
|
||||
|
||||
val out = outputDir.path
|
||||
out.deleteRecursive()
|
||||
|
||||
val outSources = outputSourcesDir.path
|
||||
outSources.deleteRecursive()
|
||||
|
||||
val spigotRepos = mutableSetOf<String>()
|
||||
spigotRepos += apiSetup.repos
|
||||
spigotRepos += serverSetup.repos
|
||||
|
||||
val artifacts = mutableSetOf<MavenArtifact>()
|
||||
artifacts += apiSetup.artifacts
|
||||
artifacts += serverSetup.artifacts
|
||||
|
||||
val resolver = detachedResolver
|
||||
for (repo in spigotRepos) {
|
||||
resolver.repositories.maven(repo)
|
||||
}
|
||||
val config = resolver.configurations.create("spigotDependencies") {
|
||||
attributes {
|
||||
attribute(TargetJvmEnvironment.TARGET_JVM_ENVIRONMENT_ATTRIBUTE, objects.named(TargetJvmEnvironment.STANDARD_JVM))
|
||||
}
|
||||
}
|
||||
for (artifact in artifacts) {
|
||||
val gav = artifact.gav.let {
|
||||
if (it == "com.google.guava:guava:32.1.2-jre") {
|
||||
// https://github.com/google/guava/issues/6657
|
||||
"com.google.guava:guava:32.1.3-jre"
|
||||
} else {
|
||||
it
|
||||
}
|
||||
}
|
||||
config.dependencies.add(
|
||||
dependencyFactory.create(gav).also {
|
||||
it.artifact {
|
||||
artifact.classifier?.let { s -> classifier = s }
|
||||
artifact.extension?.let { s -> extension = s }
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
// The source variants don't have transitives
|
||||
val flatComponents = mutableSetOf<ComponentIdentifier>()
|
||||
|
||||
for (artifact in config.incoming.artifacts.artifacts) {
|
||||
artifact.file.toPath().copyTo(outputDir.path.resolve(artifact.file.name).also { it.parent.createDirectories() }, true)
|
||||
flatComponents += artifact.id.componentIdentifier
|
||||
}
|
||||
|
||||
val sourcesConfig = resolver.configurations.create("spigotDependenciesSources") {
|
||||
attributes {
|
||||
// Mojang libs & Guava don't resolve metadata correctly, so we set the classifier below instead...
|
||||
|
||||
// attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage.JAVA_RUNTIME))
|
||||
// attribute(DocsType.DOCS_TYPE_ATTRIBUTE, objects.named(DocsType.SOURCES))
|
||||
// attribute(Bundling.BUNDLING_ATTRIBUTE, objects.named(Bundling.EXTERNAL))
|
||||
// attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category.DOCUMENTATION))
|
||||
|
||||
// Needed since we set the classifier instead of using above attributes
|
||||
attribute(TargetJvmEnvironment.TARGET_JVM_ENVIRONMENT_ATTRIBUTE, objects.named(TargetJvmEnvironment.STANDARD_JVM))
|
||||
}
|
||||
}
|
||||
for (component in flatComponents) {
|
||||
sourcesConfig.dependencies.add(
|
||||
dependencyFactory.create(component.displayName).also {
|
||||
it.artifact {
|
||||
classifier = "sources"
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
val sourcesView = sourcesConfig.incoming.artifactView {
|
||||
componentFilter {
|
||||
mcLibraries.none { l -> l == it.displayName } &&
|
||||
// This is only needed since we don't use variant-aware resolution properly
|
||||
it.displayName != "com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava"
|
||||
}
|
||||
}
|
||||
|
||||
for (artifact in sourcesView.artifacts.artifacts) {
|
||||
artifact.file.toPath().copyTo(outputSourcesDir.path.resolve(artifact.file.name).also { it.parent.createDirectories() }, true)
|
||||
}
|
||||
}
|
||||
|
||||
private fun parsePom(pomFile: Path): MavenSetup {
|
||||
val depList = arrayListOf<MavenArtifact>()
|
||||
// todo dum
|
||||
depList += MavenArtifact(
|
||||
"com.google.code.findbugs",
|
||||
"jsr305",
|
||||
"3.0.2"
|
||||
)
|
||||
depList += MavenArtifact(
|
||||
"org.apache.logging.log4j",
|
||||
"log4j-api",
|
||||
"2.17.0"
|
||||
)
|
||||
depList += MavenArtifact(
|
||||
"org.jetbrains",
|
||||
"annotations",
|
||||
"23.0.0"
|
||||
)
|
||||
val repoList = arrayListOf<String>()
|
||||
// Maven Central is implicit
|
||||
repoList += MAVEN_CENTRAL_URL
|
||||
|
||||
val builder = DocumentBuilderFactory.newInstance().newDocumentBuilder()
|
||||
val doc = pomFile.inputStream().buffered().use { stream ->
|
||||
stream.buffered().use { buffered ->
|
||||
builder.parse(buffered)
|
||||
}
|
||||
}
|
||||
|
||||
doc.documentElement.normalize()
|
||||
|
||||
depList += doc.extractDependencies()
|
||||
repoList += doc.extractRepos()
|
||||
|
||||
return MavenSetup(repos = repoList, artifacts = depList)
|
||||
}
|
||||
|
||||
private fun Document.extractDependencies(): List<MavenArtifact> {
|
||||
val depList = arrayListOf<MavenArtifact>()
|
||||
val list = getElementsByTagName("dependencies")
|
||||
val node = list.item(0) as Element // Only want the first dependencies element
|
||||
val depNode = node.getElementsByTagName("dependency")
|
||||
for (j in 0 until depNode.length) {
|
||||
val dependency = depNode.item(j) as? Element ?: continue
|
||||
val artifact = getDependency(dependency) ?: continue
|
||||
depList += artifact
|
||||
}
|
||||
return depList
|
||||
}
|
||||
|
||||
private fun Document.extractRepos(): List<String> {
|
||||
val repoList = arrayListOf<String>()
|
||||
val repos = getElementsByTagName("repositories")
|
||||
for (i in 0 until repos.length) {
|
||||
val node = repos.item(i) as? Element ?: continue
|
||||
val depNode = node.getElementsByTagName("repository")
|
||||
for (j in 0 until depNode.length) {
|
||||
val repo = depNode.item(j) as? Element ?: continue
|
||||
val repoUrl = repo.getElementsByTagName("url").item(0).textContent
|
||||
repoList += repoUrl
|
||||
}
|
||||
}
|
||||
return repoList
|
||||
}
|
||||
|
||||
private fun getDependency(node: Element): MavenArtifact? {
|
||||
val scopeNode = node.getElementsByTagName("scope")
|
||||
val scope = if (scopeNode.length == 0) {
|
||||
"compile"
|
||||
} else {
|
||||
scopeNode.item(0).textContent
|
||||
}
|
||||
|
||||
if (scope != "compile") {
|
||||
return null
|
||||
}
|
||||
|
||||
val group = node.getElementsByTagName("groupId").item(0).textContent
|
||||
val artifact = node.getElementsByTagName("artifactId").item(0).textContent
|
||||
val version = node.getElementsByTagName("version").item(0).textContent
|
||||
|
||||
if (version.contains("\${")) {
|
||||
// Don't handle complicated things
|
||||
// We don't need to (for now anyways)
|
||||
return null
|
||||
}
|
||||
|
||||
return MavenArtifact(
|
||||
group = group,
|
||||
artifact = artifact,
|
||||
version = version
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
data class MavenSetup(
|
||||
val repos: List<String>,
|
||||
val artifacts: List<MavenArtifact>
|
||||
)
|
||||
|
||||
interface DownloadParams : WorkParameters {
|
||||
val repos: ListProperty<String>
|
||||
val artifact: Property<String>
|
||||
val target: RegularFileProperty
|
||||
val downloadToDir: Property<Boolean>
|
||||
val downloader: Property<DownloadService>
|
||||
}
|
||||
|
||||
abstract class DownloadWorker : WorkAction<DownloadParams> {
|
||||
|
||||
override fun execute() {
|
||||
val target = parameters.target.path
|
||||
val artifact = MavenArtifact.parse(parameters.artifact.get())
|
||||
|
||||
if (parameters.downloadToDir.get()) {
|
||||
artifact.downloadToDir(parameters.downloader.get(), target, parameters.repos.get())
|
||||
} else {
|
||||
artifact.downloadToFile(parameters.downloader.get(), target, parameters.repos.get())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
abstract class DownloadSourcesToDirAction : WorkAction<DownloadSourcesToDirAction.Params> {
|
||||
|
||||
interface Params : WorkParameters {
|
||||
val repos: ListProperty<String>
|
||||
val artifact: Property<String>
|
||||
val target: RegularFileProperty
|
||||
val downloader: Property<DownloadService>
|
||||
}
|
||||
|
||||
override fun execute() {
|
||||
val sourceArtifact = MavenArtifact.parse(parameters.artifact.get())
|
||||
.copy(classifier = "sources")
|
||||
|
||||
try {
|
||||
sourceArtifact.downloadToDir(
|
||||
parameters.downloader.get(),
|
||||
parameters.target.path,
|
||||
parameters.repos.get()
|
||||
)
|
||||
} catch (ignored: Exception) {
|
||||
// Ignore failures because not every artifact we attempt to download actually has sources
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,61 +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 javax.inject.Inject
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.provider.Property
|
||||
import org.gradle.api.provider.Provider
|
||||
import org.gradle.api.tasks.Nested
|
||||
import org.gradle.jvm.toolchain.JavaLauncher
|
||||
import org.gradle.jvm.toolchain.JavaToolchainService
|
||||
|
||||
private fun JavaLauncherTaskBase.defaultJavaLauncher(project: Project): Provider<JavaLauncher> =
|
||||
javaToolchainService.defaultJavaLauncher(project)
|
||||
|
||||
interface JavaLauncherTaskBase {
|
||||
@get:Nested
|
||||
val launcher: Property<JavaLauncher>
|
||||
|
||||
@get:Inject
|
||||
val javaToolchainService: JavaToolchainService
|
||||
}
|
||||
|
||||
abstract class JavaLauncherTask : BaseTask(), JavaLauncherTaskBase {
|
||||
|
||||
override fun init() {
|
||||
super.init()
|
||||
|
||||
launcher.convention(defaultJavaLauncher(project))
|
||||
}
|
||||
}
|
||||
|
||||
abstract class JavaLauncherZippedTask : ZippedTask(), JavaLauncherTaskBase {
|
||||
|
||||
override fun init() {
|
||||
super.init()
|
||||
|
||||
launcher.convention(defaultJavaLauncher(project))
|
||||
}
|
||||
}
|
|
@ -1,93 +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.util
|
||||
|
||||
import java.nio.file.FileSystem
|
||||
import java.nio.file.Path
|
||||
import kotlin.io.path.*
|
||||
import org.objectweb.asm.ClassReader
|
||||
import org.objectweb.asm.Opcodes
|
||||
import org.objectweb.asm.tree.ClassNode
|
||||
|
||||
class ClassNodeCacheImpl(
|
||||
private val jarFile: FileSystem,
|
||||
private val fallbackJars: List<FileSystem?>,
|
||||
private val fallbackDirectories: List<Path>? = null
|
||||
) : ClassNodeCache {
|
||||
|
||||
private val classNodeMap = hashMapOf<String, ClassNode?>()
|
||||
|
||||
override fun findClass(name: String?): ClassNode? {
|
||||
if (name == null) {
|
||||
return null
|
||||
}
|
||||
return classNodeMap.computeIfAbsent(normalize(name)) { fileName ->
|
||||
val classData = findClassData(fileName) ?: return@computeIfAbsent null
|
||||
val classReader = ClassReader(classData)
|
||||
val node = ClassNode(Opcodes.ASM9)
|
||||
classReader.accept(node, 0)
|
||||
return@computeIfAbsent node
|
||||
}
|
||||
}
|
||||
|
||||
private fun findClassData(className: String): ByteArray? {
|
||||
jarFile.getPath(className).let { remappedClass ->
|
||||
if (remappedClass.exists()) {
|
||||
return remappedClass.readBytes()
|
||||
}
|
||||
}
|
||||
for (fallbackJar in fallbackJars) {
|
||||
fallbackJar?.getPath(className)?.let { libraryClass ->
|
||||
if (libraryClass.exists()) {
|
||||
return libraryClass.readBytes()
|
||||
}
|
||||
}
|
||||
}
|
||||
fallbackDirectories?.let { dirs ->
|
||||
for (path in dirs) {
|
||||
path.resolve(className).takeIf { it.exists() }
|
||||
?.let { libraryClass ->
|
||||
if (libraryClass.exists()) {
|
||||
return libraryClass.readBytes()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return ClassLoader.getSystemResourceAsStream(className)?.readBytes() // JDK class
|
||||
}
|
||||
|
||||
private fun normalize(name: String): String {
|
||||
val workingName = name.removeSuffix(".class")
|
||||
|
||||
var startIndex = 0
|
||||
var endIndex = workingName.length
|
||||
if (workingName.startsWith('L')) {
|
||||
startIndex = 1
|
||||
}
|
||||
if (workingName.endsWith(';')) {
|
||||
endIndex--
|
||||
}
|
||||
|
||||
return workingName.substring(startIndex, endIndex).replace('.', '/') + ".class"
|
||||
}
|
||||
}
|
|
@ -1,101 +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.util
|
||||
|
||||
import java.nio.file.FileSystem
|
||||
import java.nio.file.Path
|
||||
import kotlin.io.path.*
|
||||
import org.objectweb.asm.ClassVisitor
|
||||
import org.objectweb.asm.ClassWriter
|
||||
import org.objectweb.asm.tree.ClassNode
|
||||
|
||||
object JarProcessing {
|
||||
interface ClassProcessor {
|
||||
fun shouldProcess(file: Path): Boolean = true
|
||||
|
||||
interface NodeBased : ClassProcessor {
|
||||
fun processClass(node: ClassNode, classNodeCache: ClassNodeCache)
|
||||
}
|
||||
|
||||
interface VisitorBased : ClassProcessor {
|
||||
fun processClass(node: ClassNode, parent: ClassVisitor, classNodeCache: ClassNodeCache): ClassVisitor?
|
||||
}
|
||||
}
|
||||
|
||||
fun processJar(
|
||||
jarFile: FileSystem,
|
||||
output: FileSystem,
|
||||
processor: ClassProcessor
|
||||
) = processJar(jarFile, null, output, processor)
|
||||
|
||||
fun processJar(
|
||||
jarFile: FileSystem,
|
||||
fallbackJar: FileSystem?,
|
||||
output: FileSystem,
|
||||
processor: ClassProcessor
|
||||
) {
|
||||
val classNodeCache = ClassNodeCache.create(jarFile, fallbackJar)
|
||||
|
||||
jarFile.walk().use { stream ->
|
||||
stream.forEach { file ->
|
||||
processFile(file, output, classNodeCache, processor)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun processFile(file: Path, output: FileSystem, classNodeCache: ClassNodeCache, processor: ClassProcessor) {
|
||||
val outFile = output.getPath(file.absolutePathString())
|
||||
|
||||
if (file.isDirectory()) {
|
||||
outFile.createDirectories()
|
||||
return
|
||||
}
|
||||
|
||||
if (!file.name.endsWith(".class")) {
|
||||
file.copyTo(outFile)
|
||||
return
|
||||
}
|
||||
|
||||
if (processor.shouldProcess(file)) {
|
||||
processClass(file, outFile, classNodeCache, processor)
|
||||
} else {
|
||||
file.copyTo(outFile)
|
||||
}
|
||||
}
|
||||
|
||||
private fun processClass(file: Path, outFile: Path, classNodeCache: ClassNodeCache, processor: ClassProcessor) {
|
||||
val node = classNodeCache.findClass(file.toString()) ?: error("No ClassNode found for known entry: ${file.name}")
|
||||
|
||||
val writer = ClassWriter(0)
|
||||
val visitor = when (processor) {
|
||||
is ClassProcessor.VisitorBased -> processor.processClass(node, writer, classNodeCache) ?: writer
|
||||
is ClassProcessor.NodeBased -> {
|
||||
processor.processClass(node, classNodeCache)
|
||||
writer
|
||||
}
|
||||
else -> error("Unknown class processor type: ${processor::class.java.name}")
|
||||
}
|
||||
node.accept(visitor)
|
||||
outFile.writeBytes(writer.toByteArray())
|
||||
}
|
||||
}
|
|
@ -1,63 +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.util
|
||||
|
||||
import io.papermc.paperweight.util.constants.*
|
||||
import java.nio.file.Path
|
||||
import kotlin.io.path.*
|
||||
|
||||
fun makeMcDevSrc(
|
||||
cache: Path,
|
||||
decompileJar: Path,
|
||||
target: Path,
|
||||
paperProject: Path,
|
||||
paperSource: Path = paperProject.resolve("src/main/java"),
|
||||
dataSource: Path = paperProject.resolve("src/main/resources")
|
||||
) {
|
||||
val lockFile = cache.resolve(applyPatchesLock(paperProject))
|
||||
val alreadyHave = acquireProcessLockWaiting(lockFile)
|
||||
try {
|
||||
ensureDeleted(target)
|
||||
|
||||
decompileJar.openZip().use { fs ->
|
||||
val root = fs.getPath("/")
|
||||
fs.walk().use { stream ->
|
||||
stream.forEach { sourceFile ->
|
||||
if (sourceFile.isRegularFile()) {
|
||||
val sourceFilePath = sourceFile.relativeTo(root).invariantSeparatorsPathString
|
||||
|
||||
if (!paperSource.resolve(sourceFilePath).isRegularFile() && !dataSource.resolve(sourceFilePath).isRegularFile()) {
|
||||
val targetFile = target.resolve(sourceFilePath)
|
||||
targetFile.parent.createDirectories()
|
||||
sourceFile.copyTo(targetFile)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
if (!alreadyHave) {
|
||||
lockFile.deleteForcefully()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,267 +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.util
|
||||
|
||||
import io.papermc.paperweight.PaperweightException
|
||||
import java.nio.file.FileSystem
|
||||
import java.nio.file.Path
|
||||
import kotlin.io.path.*
|
||||
import org.gradle.api.logging.LogLevel
|
||||
import org.gradle.api.logging.Logger
|
||||
import org.gradle.api.logging.Logging
|
||||
|
||||
object McDev {
|
||||
private val logger: Logger = Logging.getLogger(McDev::class.java)
|
||||
|
||||
fun importMcDev(
|
||||
patches: Iterable<Path>,
|
||||
decompJar: Path,
|
||||
importsFile: Path?,
|
||||
targetDir: Path,
|
||||
dataTargetDir: Path? = null,
|
||||
librariesDirs: List<Path> = listOf(),
|
||||
printOutput: Boolean = true
|
||||
) {
|
||||
val (javaPatchLines, dataPatchLines) = readPatchLines(patches)
|
||||
|
||||
decompJar.openZip().use { zipFile ->
|
||||
val decompSourceFiles = mutableSetOf<String>()
|
||||
val decompDataFiles = mutableSetOf<String>()
|
||||
|
||||
zipFile.walk().use { stream ->
|
||||
for (zipEntry in stream) {
|
||||
// substring(1) trims the leading /
|
||||
val path = zipEntry.invariantSeparatorsPathString.substring(1)
|
||||
|
||||
if (path.endsWith(".java")) {
|
||||
decompSourceFiles += path
|
||||
}
|
||||
if (path.startsWith("data/")) {
|
||||
decompDataFiles += path
|
||||
}
|
||||
|
||||
// pull in all package-info classes
|
||||
if (zipEntry.toString().endsWith("package-info.java")) {
|
||||
val targetFile = targetDir.resolve(path)
|
||||
if (targetFile.exists()) {
|
||||
continue
|
||||
}
|
||||
if (!targetFile.parent.exists()) {
|
||||
targetFile.parent.createDirectories()
|
||||
}
|
||||
zipEntry.copyTo(targetFile)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val exactJavaImports = javaPatchLines.filter { decompSourceFiles.contains(it) }
|
||||
.map { targetDir.resolve(it) }
|
||||
val exactDataImports = if (dataTargetDir != null) {
|
||||
dataPatchLines.map { "data/minecraft/$it" }.filter { decompDataFiles.contains(it) }
|
||||
.map { dataTargetDir.resolve(it) }
|
||||
} else {
|
||||
listOf()
|
||||
}
|
||||
|
||||
val (additionalSrcImports, additionalDataImports) = readAdditionalImports(importsFile)
|
||||
|
||||
val srcMatcherImports = additionalSrcImports.distinct()
|
||||
.map { zipFile.getPathMatcher("glob:/$it.java") }
|
||||
val dataMatcherImports = additionalDataImports.distinct()
|
||||
.map { zipFile.getPathMatcher("glob:/data/minecraft/$it") }
|
||||
|
||||
val (srcImportMcDev, dataImportMcDev) = zipFile.walk().use { stream ->
|
||||
val src = hashSetOf<Path>()
|
||||
val data = hashSetOf<Path>()
|
||||
stream.forEach { file ->
|
||||
if (srcMatcherImports.any { it.matches(file) }) {
|
||||
src.add(targetDir.resolve(file.invariantSeparatorsPathString.substring(1)))
|
||||
} else if (dataTargetDir != null && dataMatcherImports.any { it.matches(file) }) {
|
||||
data.add(dataTargetDir.resolve(file.invariantSeparatorsPathString.substring(1)))
|
||||
}
|
||||
}
|
||||
Pair((src + exactJavaImports).filterNot { it.exists() }, (data + exactDataImports).filterNot { it.exists() })
|
||||
}
|
||||
|
||||
logger.log(if (printOutput) LogLevel.LIFECYCLE else LogLevel.DEBUG, "Importing {} classes from vanilla...", srcImportMcDev.size)
|
||||
|
||||
importFiles(srcImportMcDev, targetDir, zipFile, printOutput)
|
||||
|
||||
if (dataTargetDir != null) {
|
||||
logger.log(
|
||||
if (printOutput) LogLevel.LIFECYCLE else LogLevel.DEBUG,
|
||||
"Importing {} data files from vanilla...",
|
||||
dataImportMcDev.size
|
||||
)
|
||||
|
||||
importFiles(dataImportMcDev, dataTargetDir, zipFile, printOutput, true)
|
||||
}
|
||||
}
|
||||
|
||||
if (librariesDirs.isEmpty()) {
|
||||
return
|
||||
}
|
||||
val libFiles = librariesDirs.flatMap { it.listDirectoryEntries("*-sources.jar") }
|
||||
if (libFiles.isEmpty()) {
|
||||
throw PaperweightException("No library files found")
|
||||
}
|
||||
|
||||
// Import library classes
|
||||
val imports = findLibraries(importsFile, libFiles, javaPatchLines)
|
||||
logger.log(if (printOutput) LogLevel.LIFECYCLE else LogLevel.DEBUG, "Importing {} classes from library sources...", imports.size)
|
||||
|
||||
for ((libraryFileName, importFilePath) in imports) {
|
||||
val libFile = libFiles.firstOrNull { it.name == libraryFileName }
|
||||
?: throw PaperweightException("Failed to find library: $libraryFileName for class $importFilePath")
|
||||
|
||||
val outputFile = targetDir.resolve(importFilePath)
|
||||
if (outputFile.exists()) {
|
||||
continue
|
||||
}
|
||||
outputFile.parent.createDirectories()
|
||||
|
||||
libFile.openZip().use { zipFile ->
|
||||
val libEntry = zipFile.getPath(importFilePath)
|
||||
libEntry.copyTo(outputFile)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun importFiles(files: List<Path>, targetDir: Path, zipFile: FileSystem, printOutput: Boolean, checkFinalNewline: Boolean = false) {
|
||||
for (file in files) {
|
||||
if (!file.parent.exists()) {
|
||||
file.parent.createDirectories()
|
||||
}
|
||||
val vanillaFile = file.relativeTo(targetDir).toString()
|
||||
|
||||
val zipPath = zipFile.getPath(vanillaFile)
|
||||
if (zipPath.notExists()) {
|
||||
logger.log(if (printOutput) LogLevel.WARN else LogLevel.DEBUG, "Skipped importing '{}': File not found", file.toString())
|
||||
continue
|
||||
}
|
||||
zipPath.copyTo(file)
|
||||
if (checkFinalNewline) {
|
||||
var content = file.readText(Charsets.UTF_8)
|
||||
if (!content.endsWith("\n")) {
|
||||
content += "\n"
|
||||
file.writeText(content, Charsets.UTF_8)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun readPatchLines(patches: Iterable<Path>): Pair<Set<String>, Set<String>> {
|
||||
val srcResult = hashSetOf<String>()
|
||||
val dataResult = hashSetOf<String>()
|
||||
|
||||
val javaPrefix = "+++ b/src/main/java/"
|
||||
val dataPrefix = "+++ b/src/main/resources/data/minecraft/"
|
||||
|
||||
for (patch in patches) {
|
||||
patch.useLines { lines ->
|
||||
val matches = lines.partition {
|
||||
it.startsWith(javaPrefix)
|
||||
}
|
||||
matches.first
|
||||
.mapTo(srcResult) { it.substring(javaPrefix.length, it.length) }
|
||||
matches.second
|
||||
.filter { it.startsWith(dataPrefix) }
|
||||
.mapTo(dataResult) { it.substring(dataPrefix.length, it.length) }
|
||||
}
|
||||
}
|
||||
|
||||
return Pair(srcResult, dataResult)
|
||||
}
|
||||
|
||||
private fun readAdditionalImports(
|
||||
additionalClasses: Path?
|
||||
): Pair<Set<String>, Set<String>> {
|
||||
val srcResult = hashSetOf<String>()
|
||||
val dataResult = hashSetOf<String>()
|
||||
|
||||
val suffix = ".java"
|
||||
|
||||
additionalClasses?.useLines { lines ->
|
||||
lines.filterNot { it.startsWith("#") }
|
||||
.forEach {
|
||||
val parts = it.split(" ")
|
||||
if (parts[0] == "minecraft") {
|
||||
srcResult += parts[1].removeSuffix(suffix).replace('.', '/')
|
||||
} else if (parts[0] == "mc_data") {
|
||||
dataResult += parts[1]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Pair(srcResult, dataResult)
|
||||
}
|
||||
|
||||
private fun findLibraries(libraryImports: Path?, libFiles: List<Path>, patchLines: Set<String>): Set<LibraryImport> {
|
||||
val result = hashSetOf<LibraryImport>()
|
||||
|
||||
// Imports from library-imports.txt
|
||||
libraryImports?.useLines { lines ->
|
||||
lines.filterNot { it.startsWith('#') }
|
||||
.map { it.split(' ') }
|
||||
.filter { it.size == 2 }
|
||||
.filter { it[0] != "minecraft" && it[0] != "mc_data" }
|
||||
.mapTo(result) { parts ->
|
||||
val libFileName = libFiles.firstOrNull { it.name.startsWith(parts[0]) }?.name
|
||||
?: throw PaperweightException("Failed to read library line '${parts[0]} ${parts[1]}', no library file was found.")
|
||||
LibraryImport(libFileName, parts[1].removeSuffix(".java").replace('.', '/') + ".java")
|
||||
}
|
||||
}
|
||||
|
||||
// Scan patches for necessary imports
|
||||
result += findNeededLibraryImports(patchLines, libFiles)
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
private fun findNeededLibraryImports(patchLines: Set<String>, libFiles: List<Path>): Set<LibraryImport> {
|
||||
val knownImportMap = findPossibleLibraryImports(libFiles)
|
||||
.associateBy { it.importFilePath }
|
||||
val prefix = "+++ b/src/main/java/"
|
||||
return patchLines.map { it.substringAfter(prefix) }
|
||||
.mapNotNull { knownImportMap[it] }
|
||||
.toSet()
|
||||
}
|
||||
|
||||
private fun findPossibleLibraryImports(libFiles: List<Path>): Collection<LibraryImport> {
|
||||
val found = hashSetOf<LibraryImport>()
|
||||
val suffix = ".java"
|
||||
libFiles.map { libFile ->
|
||||
libFile.openZip().use { zipFile ->
|
||||
zipFile.walk()
|
||||
.filter { it.isRegularFile() && it.name.endsWith(suffix) }
|
||||
.map { sourceFile ->
|
||||
LibraryImport(libFile.name, sourceFile.toString().substring(1))
|
||||
}
|
||||
.forEach(found::add)
|
||||
}
|
||||
}
|
||||
return found
|
||||
}
|
||||
|
||||
private data class LibraryImport(val libraryFileName: String, val importFilePath: String)
|
||||
}
|
|
@ -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.util
|
||||
|
||||
import org.objectweb.asm.Opcodes
|
||||
import org.objectweb.asm.Type
|
||||
import org.objectweb.asm.tree.AnnotationNode
|
||||
import org.objectweb.asm.tree.ClassNode
|
||||
import org.objectweb.asm.tree.MethodNode
|
||||
|
||||
/*
|
||||
* This was adapted from code originally written by Pokechu22 in MCInjector
|
||||
* Link: https://github.com/ModCoderPack/MCInjector/pull/3
|
||||
*/
|
||||
class ParameterAnnotationFixer(private val node: ClassNode) : AsmUtil {
|
||||
|
||||
fun visitNode() {
|
||||
val expected = expectedSyntheticParams() ?: return
|
||||
|
||||
for (method in node.methods) {
|
||||
if (method.name == "<init>") {
|
||||
processConstructor(method, expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun expectedSyntheticParams(): List<Type>? {
|
||||
if (Opcodes.ACC_ENUM in node.access) {
|
||||
return listOf(Type.getObjectType("java/lang/String"), Type.INT_TYPE)
|
||||
}
|
||||
|
||||
val innerNode = node.innerClasses.firstOrNull { it.name == node.name } ?: return null
|
||||
if (innerNode.innerName == null || (Opcodes.ACC_STATIC or Opcodes.ACC_INTERFACE) in innerNode.access) {
|
||||
return null
|
||||
}
|
||||
if (innerNode.outerName == null) {
|
||||
// method local class/other complex case
|
||||
return null
|
||||
}
|
||||
|
||||
return listOf(Type.getObjectType(innerNode.outerName))
|
||||
}
|
||||
|
||||
private fun processConstructor(method: MethodNode, synthParams: List<Type>) {
|
||||
val params = Type.getArgumentTypes(method.desc).asList()
|
||||
|
||||
if (!params.beginsWith(synthParams)) {
|
||||
return
|
||||
}
|
||||
|
||||
method.visibleParameterAnnotations = process(params.size, synthParams.size, method.visibleParameterAnnotations)
|
||||
method.invisibleParameterAnnotations =
|
||||
process(params.size, synthParams.size, method.invisibleParameterAnnotations)
|
||||
|
||||
method.visibleParameterAnnotations?.let {
|
||||
method.visibleAnnotableParameterCount = it.size
|
||||
}
|
||||
method.invisibleParameterAnnotations?.let {
|
||||
method.invisibleAnnotableParameterCount = it.size
|
||||
}
|
||||
}
|
||||
|
||||
private fun process(
|
||||
paramCount: Int,
|
||||
synthCount: Int,
|
||||
annotations: Array<List<AnnotationNode>>?
|
||||
): Array<List<AnnotationNode>>? {
|
||||
if (annotations == null) {
|
||||
return null
|
||||
}
|
||||
if (paramCount == annotations.size) {
|
||||
return annotations.copyOfRange(synthCount, paramCount)
|
||||
}
|
||||
return annotations
|
||||
}
|
||||
|
||||
private fun <T> List<T>.beginsWith(other: List<T>): Boolean {
|
||||
if (this.size < other.size) {
|
||||
return false
|
||||
}
|
||||
for (i in other.indices) {
|
||||
if (this[i] != other[i]) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
|
@ -1,124 +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.util.constants
|
||||
|
||||
import io.papermc.paperweight.util.*
|
||||
import java.nio.file.Path
|
||||
import kotlin.io.path.*
|
||||
import org.gradle.api.Task
|
||||
|
||||
const val PAPERWEIGHT_EXTENSION = "paperweight"
|
||||
const val PAPERWEIGHT_DEBUG = "paperweight.debug"
|
||||
const val PAPERWEIGHT_VERBOSE_APPLY_PATCHES = "paperweight.verboseApplyPatches"
|
||||
|
||||
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 PAPER_MAVEN_REPO_URL = "https://repo.papermc.io/repository/maven-public/"
|
||||
|
||||
const val MAVEN_CENTRAL_URL = "https://repo.maven.apache.org/maven2/"
|
||||
|
||||
const val PARAM_MAPPINGS_CONFIG = "paramMappings"
|
||||
const val REMAPPER_CONFIG = "remapper"
|
||||
const val PLUGIN_REMAPPER_CONFIG = "pluginRemapper"
|
||||
const val DECOMPILER_CONFIG = "decompiler"
|
||||
const val PAPERCLIP_CONFIG = "paperclip"
|
||||
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"
|
||||
const val CONSUMABLE_RUNTIME_CLASSPATH = "consumableRuntimeClasspath"
|
||||
const val SERVER_RUNTIME_CLASSPATH = "serverRuntimeClasspath"
|
||||
|
||||
const val PARAM_MAPPINGS_REPO_NAME = "paperweightParamMappingsRepository"
|
||||
const val DECOMPILER_REPO_NAME = "paperweightDecompilerRepository"
|
||||
const val REMAPPER_REPO_NAME = "paperweightRemapperRepository"
|
||||
|
||||
const val CACHE_PATH = "caches"
|
||||
private const val PAPER_PATH = "paperweight"
|
||||
|
||||
const val LOCK_DIR = "$PAPER_PATH/lock"
|
||||
const val USERDEV_SETUP_LOCK = "$LOCK_DIR/userdev/setup.lock"
|
||||
const val APPLY_PATCHES_LOCK_DIR = "$LOCK_DIR/apply-patches"
|
||||
|
||||
fun applyPatchesLock(targetDir: Path): String = APPLY_PATCHES_LOCK_DIR + '/' +
|
||||
targetDir.absolutePathString().hash(HashingAlgorithm.SHA256).asHexString() + ".lock"
|
||||
|
||||
const val UPSTREAMS = "$PAPER_PATH/upstreams"
|
||||
const val UPSTREAM_WORK_DIR_PROPERTY = "paperweightUpstreamWorkDir"
|
||||
const val PAPERWEIGHT_PREPARE_DOWNSTREAM = "prepareForDownstream"
|
||||
const val PAPERWEIGHT_DOWNSTREAM_FILE_PROPERTY = "paperweightDownstreamDataFile"
|
||||
|
||||
private const val JARS_PATH = "$PAPER_PATH/jars"
|
||||
const val MINECRAFT_JARS_PATH = "$JARS_PATH/minecraft"
|
||||
const val MINECRAFT_SOURCES_PATH = "$JARS_PATH/minecraft-sources"
|
||||
|
||||
const val SPIGOT_JARS_PATH = "$JARS_PATH/spigot"
|
||||
const val SPIGOT_SOURCES_JARS_PATH = "$JARS_PATH/spigot-sources"
|
||||
|
||||
private const val MAPPINGS_DIR = "$PAPER_PATH/mappings"
|
||||
const val SERVER_MAPPINGS = "$MAPPINGS_DIR/server_mappings.txt"
|
||||
const val MOJANG_YARN_MAPPINGS = "$MAPPINGS_DIR/official-mojang+yarn.tiny"
|
||||
|
||||
const val SPIGOT_MOJANG_YARN_MAPPINGS = "$MAPPINGS_DIR/spigot-mojang+yarn.tiny"
|
||||
const val OBF_SPIGOT_MAPPINGS = "$MAPPINGS_DIR/official-spigot.tiny"
|
||||
const val SPIGOT_MEMBER_MAPPINGS = "$MAPPINGS_DIR/spigot-members.csrg"
|
||||
const val CLEANED_SPIGOT_MOJANG_YARN_MAPPINGS = "$MAPPINGS_DIR/spigot-mojang+yarn-cleaned.tiny"
|
||||
const val PATCHED_SPIGOT_MOJANG_YARN_MAPPINGS = "$MAPPINGS_DIR/spigot-mojang+yarn-patched.tiny"
|
||||
const val PATCHED_SPIGOT_MOJANG_YARN_SOURCE_MAPPINGS = "$MAPPINGS_DIR/spigot-mojang+yarn-patched-source.tiny"
|
||||
const val REOBF_MOJANG_SPIGOT_MAPPINGS = "$MAPPINGS_DIR/mojang+yarn-spigot-reobf.tiny"
|
||||
const val PATCHED_REOBF_MOJANG_SPIGOT_MAPPINGS = "$MAPPINGS_DIR/mojang+yarn-spigot-reobf-patched.tiny"
|
||||
const val RELOCATED_PATCHED_REOBF_MOJANG_SPIGOT_MAPPINGS = "$MAPPINGS_DIR/mojang+yarn-spigot-reobf-patched-relocated.tiny"
|
||||
|
||||
const val OBF_NAMESPACE = "official"
|
||||
const val SPIGOT_NAMESPACE = "spigot"
|
||||
const val DEOBF_NAMESPACE = "mojang+yarn"
|
||||
const val MAPPINGS_NAMESPACE_MANIFEST_KEY = "paperweight-mappings-namespace"
|
||||
|
||||
private const val DATA_PATH = "$PAPER_PATH/data"
|
||||
const val MC_MANIFEST = "$DATA_PATH/McManifest.json"
|
||||
const val VERSION_JSON = "$DATA_PATH/McVersion.json"
|
||||
|
||||
private const val BUNDLER_PATH = "$DATA_PATH/bundler"
|
||||
const val SERVER_VERSION_JSON = "$BUNDLER_PATH/version.json"
|
||||
const val SERVER_LIBRARIES_TXT = "$BUNDLER_PATH/ServerLibraries.txt"
|
||||
const val SERVER_LIBRARIES_LIST = "$BUNDLER_PATH/libraries.list"
|
||||
const val SERVER_VERSIONS_LIST = "$BUNDLER_PATH/versions.list"
|
||||
|
||||
private const val SETUP_CACHE = "$PAPER_PATH/setupCache"
|
||||
private const val TASK_CACHE = "$PAPER_PATH/taskCache"
|
||||
|
||||
const val FINAL_REMAPPED_JAR = "$TASK_CACHE/minecraft.jar"
|
||||
const val FINAL_FILTERED_REMAPPED_JAR = "$TASK_CACHE/filteredMinecraft.jar"
|
||||
const val FINAL_DECOMPILE_JAR = "$TASK_CACHE/decompileJar.jar"
|
||||
|
||||
const val MC_DEV_SOURCES_DIR = "$PAPER_PATH/mc-dev-sources"
|
||||
|
||||
const val IVY_REPOSITORY = "$PAPER_PATH/ivyRepository"
|
||||
|
||||
const val DOWNLOAD_SERVICE_NAME = "paperweightDownloadService"
|
||||
|
||||
fun paperSetupOutput(name: String, ext: String) = "$SETUP_CACHE/$name.$ext"
|
||||
fun Task.paperTaskOutput(ext: String) = paperTaskOutput(name, ext)
|
||||
fun paperTaskOutput(name: String, ext: String) = "$TASK_CACHE/$name.$ext"
|
|
@ -1,59 +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.util.data
|
||||
|
||||
import java.nio.file.Path
|
||||
import kotlin.io.path.*
|
||||
|
||||
data class FileEntry<T>(
|
||||
val hash: String,
|
||||
val id: T,
|
||||
val path: String
|
||||
) {
|
||||
override fun toString(): String {
|
||||
return "$hash\t$id\t$path"
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val VERSION_JSON = "version.json"
|
||||
const val VERSIONS_DIR = "META-INF/versions"
|
||||
const val LIBRARIES_DIR = "META-INF/libraries"
|
||||
const val VERSIONS_LIST = "META-INF/versions.list"
|
||||
const val LIBRARIES_LIST = "META-INF/libraries.list"
|
||||
|
||||
fun parse(file: Path): List<FileEntry<String>> {
|
||||
return parse(file) { it }
|
||||
}
|
||||
|
||||
fun <T> parse(file: Path, transform: (String) -> T): List<FileEntry<T>> {
|
||||
return file.readLines().mapNotNull { line ->
|
||||
if (line.isBlank() || line.startsWith("#")) {
|
||||
return@mapNotNull null
|
||||
}
|
||||
|
||||
val (hash, id, path) = line.split("\t")
|
||||
FileEntry(hash, transform(id), path)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,60 +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.util.data
|
||||
|
||||
import io.papermc.paperweight.util.*
|
||||
|
||||
data class MinecraftManifest(
|
||||
val latest: Map<String, *>,
|
||||
val versions: List<ManifestVersion>
|
||||
)
|
||||
|
||||
data class ManifestVersion(
|
||||
val id: String,
|
||||
val type: String,
|
||||
val time: String,
|
||||
val releaseTime: String,
|
||||
val url: String,
|
||||
val sha1: String,
|
||||
) {
|
||||
fun hash(): Hash = Hash(sha1, HashingAlgorithm.SHA1)
|
||||
}
|
||||
|
||||
data class MinecraftVersionManifest(
|
||||
val downloads: Map<String, Download>,
|
||||
) {
|
||||
data class Download(
|
||||
val sha1: String,
|
||||
val url: String,
|
||||
) {
|
||||
fun hash(): Hash = Hash(sha1, HashingAlgorithm.SHA1)
|
||||
}
|
||||
|
||||
fun download(name: String): Download {
|
||||
return downloads[name] ?: error("No such download '$name' in version manifest")
|
||||
}
|
||||
|
||||
fun serverDownload(): Download = download("server")
|
||||
|
||||
fun serverMappingsDownload(): Download = download("server_mappings")
|
||||
}
|
|
@ -1,62 +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.util.data
|
||||
|
||||
import org.gradle.api.artifacts.component.ComponentArtifactIdentifier
|
||||
import org.gradle.api.artifacts.component.ModuleComponentIdentifier
|
||||
import org.gradle.internal.component.external.model.DefaultModuleComponentArtifactIdentifier
|
||||
|
||||
data class ModuleId(val group: String, val name: String, val version: String, val classifier: String? = null) : Comparable<ModuleId> {
|
||||
fun toPath(): String {
|
||||
val fileName = listOfNotNull(name, version, classifier).joinToString("-") + ".jar"
|
||||
return "${group.replace('.', '/')}/$name/$version/$fileName"
|
||||
}
|
||||
|
||||
override fun compareTo(other: ModuleId): Int {
|
||||
return comparator.compare(this, other)
|
||||
}
|
||||
|
||||
override fun toString(): String = listOfNotNull(group, name, version, classifier).joinToString(":")
|
||||
|
||||
companion object {
|
||||
private val comparator = compareBy<ModuleId>({ it.group }, { it.name }, { it.version }, { it.classifier })
|
||||
|
||||
fun parse(text: String): ModuleId {
|
||||
val split = text.split(":")
|
||||
val (group, name, version) = split
|
||||
return ModuleId(group, name, version, split.getOrNull(3))
|
||||
}
|
||||
|
||||
fun fromIdentifier(id: ComponentArtifactIdentifier): ModuleId {
|
||||
if (id is DefaultModuleComponentArtifactIdentifier) {
|
||||
val idx = id.componentIdentifier
|
||||
return ModuleId(idx.group, idx.module, idx.version, id.name.classifier)
|
||||
}
|
||||
val compId = id.componentIdentifier
|
||||
if (compId is ModuleComponentIdentifier) {
|
||||
return ModuleId(compId.group, compId.module, compId.version)
|
||||
}
|
||||
error("Could not create ModuleId from ComponentArtifactIdentifier $id")
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,63 +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.util.data
|
||||
|
||||
import io.papermc.paperweight.PaperweightException
|
||||
import io.papermc.paperweight.util.*
|
||||
import java.nio.file.Path
|
||||
import kotlin.io.path.*
|
||||
|
||||
data class UpstreamData(
|
||||
val vanillaJar: Path,
|
||||
val remappedJar: Path,
|
||||
val decompiledJar: Path,
|
||||
val mcVersion: String,
|
||||
val libDir: Path,
|
||||
val libSourceDir: Path,
|
||||
val libFile: Path,
|
||||
val spigotLibSourcesDir: Path,
|
||||
val mappings: Path,
|
||||
val notchToSpigotMappings: Path,
|
||||
val sourceMappings: Path,
|
||||
val reobfPackagesToFix: List<String>,
|
||||
val reobfMappingsPatch: Path,
|
||||
val vanillaIncludes: List<String>,
|
||||
val paramMappings: MavenDep,
|
||||
val accessTransform: Path,
|
||||
val spigotRecompiledClasses: Path,
|
||||
val bundlerVersionJson: Path,
|
||||
val serverLibrariesTxt: Path,
|
||||
val serverLibrariesList: Path
|
||||
)
|
||||
|
||||
fun readUpstreamData(inputFile: Any): UpstreamData = inputFile.convertToPath().let { file ->
|
||||
try {
|
||||
if (file.isRegularFile()) {
|
||||
gson.fromJson(file)
|
||||
} else {
|
||||
throw PaperweightException("Upstream data file does not exist.")
|
||||
}
|
||||
} catch (ex: Exception) {
|
||||
throw PaperweightException("Failed to read upstream data.", ex)
|
||||
}
|
||||
}
|
|
@ -1,59 +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.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<String>)
|
||||
|
||||
fun determineMavenDep(url: Provider<String>, configuration: Provider<Configuration>): MavenDep {
|
||||
return MavenDep(url.get(), determineArtifactCoordinates(configuration.get()))
|
||||
}
|
||||
|
||||
fun determineArtifactCoordinates(configuration: Configuration): List<String> {
|
||||
return configuration.dependencies.filterIsInstance<ModuleDependency>().map { dep ->
|
||||
sequenceOf(
|
||||
"group" to dep.group,
|
||||
"name" to dep.name,
|
||||
"version" to dep.version,
|
||||
"classifier" to (dep.artifacts.singleOrNull()?.classifier ?: "")
|
||||
).filter {
|
||||
if (it.second == null) error("No ${it.first}: $dep")
|
||||
it.second?.isNotEmpty() ?: false
|
||||
}.map {
|
||||
it.second
|
||||
}.joinToString(":")
|
||||
}
|
||||
}
|
||||
|
||||
fun RepositoryContentDescriptor.includeFromDependencyNotation(dependencyNotation: String) {
|
||||
val split = dependencyNotation.split(':')
|
||||
when {
|
||||
split.size == 1 -> includeGroup(split[0])
|
||||
split.size == 2 -> includeModule(split[0], split[1])
|
||||
split.size >= 3 -> includeVersion(split[0], split[1], split[2])
|
||||
}
|
||||
}
|
|
@ -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.util
|
||||
|
||||
import java.time.Duration
|
||||
import java.time.temporal.ChronoUnit
|
||||
|
||||
/**
|
||||
* Map of accepted abbreviation [Char]s to [ChronoUnit].
|
||||
*/
|
||||
private val units = mapOf(
|
||||
'd' to ChronoUnit.DAYS,
|
||||
'h' to ChronoUnit.HOURS,
|
||||
'm' to ChronoUnit.MINUTES,
|
||||
's' to ChronoUnit.SECONDS
|
||||
)
|
||||
|
||||
/**
|
||||
* Parses a [Duration] from [input].
|
||||
*
|
||||
* Accepted format is a number followed by a unit abbreviation.
|
||||
* See [units] for possible units.
|
||||
* Example input strings: `["1d", "12h", "1m", "30s"]`
|
||||
*
|
||||
* @param input formatted input string
|
||||
* @throws InvalidDurationException when [input] is improperly formatted
|
||||
*/
|
||||
@Throws(InvalidDurationException::class)
|
||||
fun parseDuration(input: String): Duration {
|
||||
if (input.isBlank()) {
|
||||
throw InvalidDurationException.noInput(input)
|
||||
}
|
||||
if (input.length < 2) {
|
||||
throw InvalidDurationException.invalidInput(input)
|
||||
}
|
||||
val unitAbbreviation = input.last()
|
||||
|
||||
val unit = units[unitAbbreviation] ?: throw InvalidDurationException.invalidInput(input)
|
||||
|
||||
val length = try {
|
||||
input.substring(0, input.length - 1).toLong()
|
||||
} catch (ex: NumberFormatException) {
|
||||
throw InvalidDurationException.invalidInput(input, ex)
|
||||
}
|
||||
|
||||
return Duration.of(length, unit)
|
||||
}
|
||||
|
||||
private class InvalidDurationException private constructor(
|
||||
message: String,
|
||||
cause: Throwable? = null
|
||||
) : IllegalArgumentException(message, cause) {
|
||||
companion object {
|
||||
private val infoMessage = """
|
||||
Accepted format is a number followed by a unit abbreviation.
|
||||
Possible units: $units
|
||||
Example input strings: ["1d", "12h", "1m", "30s"]
|
||||
""".trimIndent()
|
||||
|
||||
fun noInput(input: String): InvalidDurationException =
|
||||
InvalidDurationException("Cannot parse a Duration from a blank input string '$input'.\n$infoMessage")
|
||||
|
||||
fun invalidInput(input: String, cause: Throwable? = null) =
|
||||
InvalidDurationException("Cannot parse a Duration from input '$input'.\n$infoMessage", cause)
|
||||
}
|
||||
}
|
|
@ -1,259 +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.util
|
||||
|
||||
import io.papermc.paperweight.PaperweightException
|
||||
import java.io.InputStream
|
||||
import java.net.URI
|
||||
import java.nio.file.FileSystem
|
||||
import java.nio.file.FileSystems
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.PathMatcher
|
||||
import java.nio.file.attribute.DosFileAttributeView
|
||||
import java.util.Arrays
|
||||
import java.util.stream.Collectors
|
||||
import java.util.stream.Stream
|
||||
import java.util.stream.StreamSupport
|
||||
import kotlin.io.path.*
|
||||
import kotlin.streams.asSequence
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.file.DirectoryProperty
|
||||
import org.gradle.api.file.FileSystemLocation
|
||||
import org.gradle.api.file.FileSystemLocationProperty
|
||||
import org.gradle.api.file.RegularFileProperty
|
||||
import org.gradle.api.logging.Logging
|
||||
import org.gradle.api.provider.Provider
|
||||
|
||||
// utils for dealing with java.nio.file.Path and java.io.File
|
||||
|
||||
val FileSystemLocation.path: Path
|
||||
get() = asFile.toPath()
|
||||
val Provider<out FileSystemLocation>.path: Path
|
||||
get() = get().path
|
||||
val Provider<out FileSystemLocation>.pathOrNull: Path?
|
||||
get() = orNull?.path
|
||||
|
||||
fun FileSystemLocationProperty<*>.set(path: Path?) = set(path?.toFile())
|
||||
fun <P : FileSystemLocationProperty<*>> P.pathProvider(path: Provider<Path>) = apply { fileProvider(path.map { it.toFile() }) }
|
||||
|
||||
fun DirectoryProperty.convention(project: Project, path: Provider<Path>) = convention(project.layout.dir(path.map { it.toFile() }))
|
||||
fun RegularFileProperty.convention(project: Project, path: Provider<Path>) = convention(project.layout.file(path.map { it.toFile() }))
|
||||
fun DirectoryProperty.convention(project: Project, path: Path) = convention(project.layout.dir(project.provider { path.toFile() }))
|
||||
|
||||
val Path.isLibraryJar: Boolean
|
||||
get() = name.endsWith(".jar") && !name.endsWith("-sources.jar")
|
||||
|
||||
fun Path.deleteForcefully() {
|
||||
fixWindowsPermissionsForDeletion()
|
||||
deleteIfExists()
|
||||
}
|
||||
|
||||
fun Path.deleteRecursive(excludes: Iterable<PathMatcher> = emptyList()) {
|
||||
if (!exists()) {
|
||||
return
|
||||
}
|
||||
if (!isDirectory()) {
|
||||
if (excludes.any { it.matches(this) }) {
|
||||
return
|
||||
}
|
||||
fixWindowsPermissionsForDeletion()
|
||||
deleteIfExists()
|
||||
return
|
||||
}
|
||||
|
||||
val fileList = Files.walk(this).use { stream ->
|
||||
stream.asSequence().filterNot { file -> excludes.any { it.matches(file) } }.toList()
|
||||
}
|
||||
|
||||
fileList.forEach { f -> f.fixWindowsPermissionsForDeletion() }
|
||||
fileList.asReversed().forEach { f ->
|
||||
// Don't try to delete directories where the excludes glob has caused files to not get deleted inside it
|
||||
if (f.isRegularFile()) {
|
||||
f.deleteIfExists()
|
||||
} else if (f.isDirectory() && f.listDirectoryEntries().isEmpty()) {
|
||||
f.deleteIfExists()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val isWindows = System.getProperty("os.name").contains("windows", ignoreCase = true)
|
||||
|
||||
private fun Path.fixWindowsPermissionsForDeletion() {
|
||||
if (!isWindows || notExists()) {
|
||||
return
|
||||
}
|
||||
|
||||
runCatching {
|
||||
val dosAttr = fileAttributesView<DosFileAttributeView>()
|
||||
dosAttr.setHidden(false)
|
||||
dosAttr.setReadOnly(false)
|
||||
}
|
||||
}
|
||||
|
||||
fun Path.copyRecursivelyTo(target: Path) {
|
||||
target.createDirectories()
|
||||
if (!exists()) {
|
||||
return
|
||||
}
|
||||
Files.walk(this).use { stream ->
|
||||
for (f in stream) {
|
||||
val targetPath = target.resolve(f.relativeTo(this).invariantSeparatorsPathString)
|
||||
if (f.isDirectory()) {
|
||||
targetPath.createDirectories()
|
||||
} else {
|
||||
f.copyTo(targetPath)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun Path.filesMatchingRecursive(glob: String = "*"): List<Path> {
|
||||
val matcher = fileSystem.getPathMatcher("glob:$glob")
|
||||
return Files.walk(this).use { stream ->
|
||||
stream.filter {
|
||||
it.isRegularFile() && matcher.matches(it.fileName)
|
||||
}.collect(Collectors.toList())
|
||||
}
|
||||
}
|
||||
|
||||
private fun Path.jarUri(): URI {
|
||||
return URI.create("jar:${toUri()}")
|
||||
}
|
||||
|
||||
fun Path.openZip(): FileSystem {
|
||||
return FileSystems.newFileSystem(jarUri(), emptyMap<String, Any>())
|
||||
}
|
||||
|
||||
fun Path.writeZip(): FileSystem {
|
||||
return FileSystems.newFileSystem(jarUri(), mapOf("create" to "true"))
|
||||
}
|
||||
|
||||
fun FileSystem.walk(): Stream<Path> {
|
||||
return StreamSupport.stream(rootDirectories.spliterator(), false)
|
||||
.flatMap { Files.walk(it) }
|
||||
}
|
||||
|
||||
fun ProcessBuilder.directory(path: Path?): ProcessBuilder = directory(path?.toFile())
|
||||
|
||||
fun Path.hashFile(algorithm: HashingAlgorithm): ByteArray = inputStream().use { input -> input.hash(algorithm) }
|
||||
|
||||
fun Path.sha256asHex(): String = hashFile(HashingAlgorithm.SHA256).asHexString()
|
||||
|
||||
fun Path.contentEquals(two: InputStream, bufferSizeBytes: Int = 8192): Boolean {
|
||||
inputStream().use { one ->
|
||||
val bufOne = ByteArray(bufferSizeBytes)
|
||||
val bufTwo = ByteArray(bufferSizeBytes)
|
||||
|
||||
while (true) {
|
||||
val readOne = one.read(bufOne)
|
||||
val readTwo = two.read(bufTwo)
|
||||
|
||||
if (readOne != readTwo) {
|
||||
// length differs
|
||||
return false
|
||||
}
|
||||
|
||||
if (readOne == -1) {
|
||||
// end of content
|
||||
break
|
||||
}
|
||||
|
||||
if (!Arrays.equals(bufOne, 0, readOne, bufTwo, 0, readOne)) {
|
||||
// content differs
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
fun Path.contentEquals(file: Path, bufferSizeBytes: Int = 8192): Boolean = file.inputStream().use { two ->
|
||||
contentEquals(two, bufferSizeBytes)
|
||||
}
|
||||
|
||||
fun Path.withDifferentExtension(ext: String): Path = resolveSibling("$nameWithoutExtension.$ext")
|
||||
|
||||
// Returns true if our process already owns the lock
|
||||
fun acquireProcessLockWaiting(
|
||||
lockFile: Path,
|
||||
timeoutMs: Long = 1000L * 60 * 60 // one hour
|
||||
): Boolean {
|
||||
val logger = Logging.getLogger("paperweight lock file")
|
||||
val currentPid = ProcessHandle.current().pid()
|
||||
|
||||
if (lockFile.exists()) {
|
||||
val lockingProcessId = lockFile.readText().toLong()
|
||||
if (lockingProcessId == currentPid) {
|
||||
return true
|
||||
}
|
||||
|
||||
logger.lifecycle("Lock file '$lockFile' is currently held by pid '$lockingProcessId'.")
|
||||
if (ProcessHandle.of(lockingProcessId).isEmpty) {
|
||||
logger.lifecycle("Locking process does not exist, assuming abrupt termination and deleting lock file.")
|
||||
lockFile.deleteIfExists()
|
||||
} else {
|
||||
logger.lifecycle("Waiting for lock to be released...")
|
||||
var sleptMs: Long = 0
|
||||
while (lockFile.exists()) {
|
||||
Thread.sleep(100)
|
||||
sleptMs += 100
|
||||
if (sleptMs >= 1000 * 60 && sleptMs % (1000 * 60) == 0L) {
|
||||
logger.lifecycle(
|
||||
"Have been waiting on lock file '$lockFile' held by pid '$lockingProcessId' for ${sleptMs / 1000 / 60} minute(s).\n" +
|
||||
"If this persists for an unreasonable length of time, kill this process, run './gradlew --stop' and then try again.\n" +
|
||||
"If the problem persists, the lock file may need to be deleted manually."
|
||||
)
|
||||
}
|
||||
if (sleptMs >= timeoutMs) {
|
||||
throw PaperweightException("Have been waiting on lock file '$lockFile' for $sleptMs ms. Giving up as timeout is $timeoutMs ms.")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!lockFile.parent.exists()) {
|
||||
lockFile.parent.createDirectories()
|
||||
}
|
||||
lockFile.writeText(currentPid.toString())
|
||||
return false
|
||||
}
|
||||
|
||||
fun relativeCopy(baseDir: Path, file: Path, outputDir: Path) {
|
||||
relativeCopyOrMove(baseDir, file, outputDir, false)
|
||||
}
|
||||
|
||||
fun relativeMove(baseDir: Path, file: Path, outputDir: Path) {
|
||||
relativeCopyOrMove(baseDir, file, outputDir, true)
|
||||
}
|
||||
|
||||
private fun relativeCopyOrMove(baseDir: Path, file: Path, outputDir: Path, move: Boolean) {
|
||||
val destination = outputDir.resolve(file.relativeTo(baseDir).invariantSeparatorsPathString)
|
||||
destination.parent.createDirectories()
|
||||
if (move) {
|
||||
file.moveTo(destination, overwrite = true)
|
||||
} else {
|
||||
file.copyTo(destination, overwrite = true)
|
||||
}
|
||||
}
|
|
@ -1,50 +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.util
|
||||
|
||||
import java.nio.file.Path
|
||||
import kotlin.io.path.*
|
||||
|
||||
fun filterJar(
|
||||
inputJar: Path,
|
||||
outputJar: Path,
|
||||
includes: List<String>,
|
||||
predicate: (Path) -> Boolean = { false }
|
||||
) {
|
||||
ensureDeleted(outputJar)
|
||||
|
||||
outputJar.writeZip().use { outputFs ->
|
||||
inputJar.openZip().use { inputFs ->
|
||||
val matchers = includes.map { inputFs.getPathMatcher("glob:$it") }
|
||||
|
||||
inputFs.walk().use { stream ->
|
||||
stream.filter { p -> predicate(p) || matchers.any { matcher -> matcher.matches(p) } }
|
||||
.forEach { p ->
|
||||
val targetFile = outputFs.getPath(p.toAbsolutePath().invariantSeparatorsPathString.substring(1))
|
||||
targetFile.parent?.createDirectories()
|
||||
p.copyTo(targetFile)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,161 +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.util
|
||||
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.nio.file.Path
|
||||
import javax.xml.XMLConstants
|
||||
import javax.xml.stream.XMLOutputFactory
|
||||
import kotlin.io.path.*
|
||||
import org.gradle.api.artifacts.dsl.RepositoryHandler
|
||||
import org.gradle.api.artifacts.repositories.IvyArtifactRepository
|
||||
import org.gradle.kotlin.dsl.*
|
||||
|
||||
fun RepositoryHandler.setupIvyRepository(
|
||||
url: Any,
|
||||
configuration: IvyArtifactRepository.() -> Unit
|
||||
) = ivy(url) {
|
||||
patternLayout {
|
||||
artifact(IvyArtifactRepository.MAVEN_ARTIFACT_PATTERN)
|
||||
ivy(IvyArtifactRepository.MAVEN_IVY_PATTERN)
|
||||
setM2compatible(true)
|
||||
}
|
||||
metadataSources(IvyArtifactRepository.MetadataSources::ivyDescriptor)
|
||||
isAllowInsecureProtocol = true
|
||||
resolve.isDynamicMode = false
|
||||
configuration(this)
|
||||
}
|
||||
|
||||
fun installToIvyRepo(
|
||||
repo: Path,
|
||||
artifactCoordinates: String,
|
||||
dependencies: List<String>,
|
||||
binaryJar: Path,
|
||||
sourcesJar: Path?,
|
||||
): Boolean {
|
||||
val (module, versionDir) = parseModuleLocation(artifactCoordinates, repo)
|
||||
val (_, name, version) = module
|
||||
|
||||
versionDir.createDirectories()
|
||||
|
||||
val sourcesDestination = versionDir.resolve("$name-$version-sources.jar")
|
||||
val jarDestination = versionDir.resolve("$name-$version.jar")
|
||||
|
||||
val ivy = versionDir.resolve("ivy-$version.xml")
|
||||
val xml = writeIvyModule(module, dependencies.map { parseModule(it) })
|
||||
|
||||
val upToDate = upToDate(sourcesDestination, jarDestination, ivy, sourcesJar, binaryJar, xml)
|
||||
if (upToDate) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (sourcesJar == null) {
|
||||
sourcesDestination.deleteIfExists()
|
||||
} else {
|
||||
sourcesJar.copyTo(sourcesDestination, overwrite = true)
|
||||
}
|
||||
binaryJar.copyTo(jarDestination, overwrite = true)
|
||||
ivy.writeText(xml, Charsets.UTF_8)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
private fun upToDate(
|
||||
sourcesDest: Path,
|
||||
binDest: Path,
|
||||
ivyXml: Path,
|
||||
sourcesIn: Path?,
|
||||
binaryIn: Path,
|
||||
xmlIn: String
|
||||
): Boolean {
|
||||
val bin = binDest.isRegularFile() && binDest.contentEquals(binaryIn)
|
||||
val xml = ivyXml.isRegularFile() && ivyXml.contentEquals(xmlIn.byteInputStream())
|
||||
val sources = if (sourcesIn == null) {
|
||||
sourcesDest.notExists()
|
||||
} else {
|
||||
sourcesDest.isRegularFile() && sourcesDest.contentEquals(sourcesIn)
|
||||
}
|
||||
return bin && xml && sources
|
||||
}
|
||||
|
||||
private val OUTPUT_FACTORY = XMLOutputFactory.newInstance()
|
||||
private const val XSI = XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI
|
||||
private const val IVY = "http://ant.apache.org/ivy/schemas/ivy.xsd"
|
||||
|
||||
private fun writeIvyModule(
|
||||
module: Module,
|
||||
dependencies: List<Module>
|
||||
): String = ByteArrayOutputStream().use { outputStream ->
|
||||
val writer = OUTPUT_FACTORY.createXMLStreamWriter(outputStream, Charsets.UTF_8.name())
|
||||
|
||||
writer.writeStartDocument("UTF-8", "1.0")
|
||||
writer.writeStartElement("ivy-module")
|
||||
writer.writeNamespace("xsi", XSI)
|
||||
writer.writeAttribute(XSI, "noNamespaceSchemaLocation", IVY)
|
||||
writer.writeAttribute("version", "2.0")
|
||||
|
||||
writer.writeEmptyElement("info")
|
||||
writer.writeAttribute("organisation", module.group)
|
||||
writer.writeAttribute("module", module.name)
|
||||
writer.writeAttribute("revision", module.version)
|
||||
writer.writeAttribute("status", "release")
|
||||
|
||||
writer.writeStartElement("dependencies")
|
||||
for (dep in dependencies) {
|
||||
writer.writeEmptyElement("dependency")
|
||||
writer.writeAttribute("org", dep.group)
|
||||
writer.writeAttribute("name", dep.name)
|
||||
writer.writeAttribute("rev", dep.version)
|
||||
}
|
||||
writer.writeEndElement()
|
||||
|
||||
writer.writeEndElement()
|
||||
writer.writeEndDocument()
|
||||
|
||||
String(outputStream.toByteArray(), Charsets.UTF_8)
|
||||
}
|
||||
|
||||
private fun parseModule(coordinatesString: String): Module {
|
||||
val parts = coordinatesString.split(":")
|
||||
val group = parts[0]
|
||||
val name = parts[1]
|
||||
val version = parts[2]
|
||||
return Module(group, name, version)
|
||||
}
|
||||
|
||||
private fun parseModuleLocation(coordinatesString: String, root: Path): ModuleLocation {
|
||||
val (group, name, version) = parseModule(coordinatesString)
|
||||
val versionDir = root / group.replace(".", "/") / name / version
|
||||
return ModuleLocation(Module(group, name, version), versionDir)
|
||||
}
|
||||
|
||||
private data class Module(
|
||||
val group: String,
|
||||
val name: String,
|
||||
val version: String,
|
||||
)
|
||||
|
||||
private data class ModuleLocation(
|
||||
val module: Module,
|
||||
val versionDir: Path,
|
||||
)
|
|
@ -1,230 +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.util
|
||||
|
||||
import io.papermc.paperweight.extension.PaperweightServerExtension
|
||||
import io.papermc.paperweight.tasks.*
|
||||
import io.papermc.paperweight.util.constants.*
|
||||
import java.nio.file.Path
|
||||
import kotlin.io.path.*
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.artifacts.Configuration
|
||||
import org.gradle.api.artifacts.repositories.MavenArtifactRepository
|
||||
import org.gradle.api.attributes.Usage
|
||||
import org.gradle.api.plugins.JavaPlugin
|
||||
import org.gradle.api.plugins.JavaPluginExtension
|
||||
import org.gradle.api.provider.Provider
|
||||
import org.gradle.api.tasks.SourceSet
|
||||
import org.gradle.api.tasks.TaskProvider
|
||||
import org.gradle.api.tasks.bundling.Jar
|
||||
import org.gradle.api.tasks.bundling.Zip
|
||||
import org.gradle.kotlin.dsl.*
|
||||
import org.gradle.plugins.ide.idea.model.IdeaModel
|
||||
|
||||
fun Project.setupServerProject(
|
||||
parent: Project,
|
||||
remappedJar: Provider<*>,
|
||||
remappedJarSources: Any,
|
||||
mcDevSourceDir: Path,
|
||||
libsFile: Any,
|
||||
packagesToFix: Provider<List<String>?>,
|
||||
relocatedReobfMappings: TaskProvider<GenerateRelocatedReobfMappings>,
|
||||
serverJar: TaskProvider<Zip>
|
||||
): ServerTasks? {
|
||||
if (!projectDir.exists()) {
|
||||
return null
|
||||
}
|
||||
|
||||
plugins.apply("java")
|
||||
|
||||
val serverExt = extensions.create<PaperweightServerExtension>(PAPERWEIGHT_EXTENSION, objects)
|
||||
relocatedReobfMappings {
|
||||
craftBukkitPackageVersion.set(serverExt.craftBukkitPackageVersion)
|
||||
}
|
||||
|
||||
exportRuntimeClasspathTo(parent)
|
||||
|
||||
@Suppress("UNUSED_VARIABLE", "KotlinRedundantDiagnosticSuppress")
|
||||
val filterPatchedFiles by tasks.registering<FilterPatchedFiles> {
|
||||
inputSrcDir.set(file("src/main/java"))
|
||||
inputResourcesDir.set(file("src/main/resources"))
|
||||
vanillaJar.set(
|
||||
// unlink dependency on upstream clone task for patcher (hack); it's implicitly handled when we get upstream data
|
||||
parent.layout.file(parent.files(remappedJar).elements.map { it.single().asFile })
|
||||
)
|
||||
outputJar.set(parent.layout.cache.resolve(FINAL_FILTERED_REMAPPED_JAR))
|
||||
}
|
||||
|
||||
val vanillaServer: Configuration by configurations.creating {
|
||||
withDependencies {
|
||||
dependencies {
|
||||
// update mc-dev sources on dependency resolution
|
||||
makeMcDevSrc(
|
||||
parent.layout.cache,
|
||||
remappedJarSources.convertToPath(),
|
||||
mcDevSourceDir,
|
||||
layout.projectDirectory.path
|
||||
)
|
||||
|
||||
add(create(parent.files(filterPatchedFiles.flatMap { it.outputJar })))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
configurations.named(JavaPlugin.IMPLEMENTATION_CONFIGURATION_NAME) {
|
||||
extendsFrom(vanillaServer)
|
||||
withDependencies {
|
||||
dependencies {
|
||||
val libs = libsFile.convertToPathOrNull()
|
||||
if (libs != null && libs.exists()) {
|
||||
libs.forEachLine { line ->
|
||||
add(create(line))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
addMcDevSourcesRoot(mcDevSourceDir)
|
||||
|
||||
return createBuildTasks(parent, serverExt, serverJar, vanillaServer, packagesToFix, relocatedReobfMappings)
|
||||
}
|
||||
|
||||
private fun Project.exportRuntimeClasspathTo(parent: Project) {
|
||||
configurations.create(CONSUMABLE_RUNTIME_CLASSPATH) {
|
||||
isCanBeConsumed = true
|
||||
isCanBeResolved = false
|
||||
attributes.attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage.JAVA_RUNTIME))
|
||||
extendsFrom(configurations.getByName(JavaPlugin.RUNTIME_CLASSPATH_CONFIGURATION_NAME))
|
||||
}
|
||||
parent.configurations.create(SERVER_RUNTIME_CLASSPATH) {
|
||||
isCanBeConsumed = false
|
||||
isCanBeResolved = true
|
||||
attributes.attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage.JAVA_RUNTIME))
|
||||
}
|
||||
parent.dependencies {
|
||||
add(SERVER_RUNTIME_CLASSPATH, parent.dependencies.project(path, configuration = CONSUMABLE_RUNTIME_CLASSPATH))
|
||||
}
|
||||
afterEvaluate {
|
||||
val old = parent.repositories.toList()
|
||||
parent.repositories.clear()
|
||||
repositories.filterIsInstance<MavenArtifactRepository>().forEach {
|
||||
parent.repositories.maven(it.url) {
|
||||
name = "serverRuntimeClasspath repo ${it.url}"
|
||||
content { onlyForConfigurations(SERVER_RUNTIME_CLASSPATH) }
|
||||
}
|
||||
}
|
||||
parent.repositories.addAll(old)
|
||||
}
|
||||
}
|
||||
|
||||
private fun Project.createBuildTasks(
|
||||
parent: Project,
|
||||
serverExt: PaperweightServerExtension,
|
||||
serverJar: TaskProvider<Zip>,
|
||||
vanillaServer: Configuration,
|
||||
packagesToFix: Provider<List<String>?>,
|
||||
relocatedReobfMappings: TaskProvider<GenerateRelocatedReobfMappings>
|
||||
): ServerTasks {
|
||||
serverJar {
|
||||
destinationDirectory.set(layout.buildDirectory.dir("libs"))
|
||||
archiveExtension.set("jar")
|
||||
archiveClassifier.set("mojang-mapped")
|
||||
from(zipTree(tasks.named<Jar>("jar").flatMap { it.archiveFile }.map { it.asFile }))
|
||||
from(vanillaServer.elements.map { it.map { f -> zipTree(f.asFile) } }) {
|
||||
exclude("META-INF/MANIFEST.MF")
|
||||
}
|
||||
isReproducibleFileOrder = true
|
||||
isPreserveFileTimestamps = false
|
||||
}
|
||||
|
||||
val fixJarForReobf by tasks.registering<FixJarForReobf> {
|
||||
inputJar.set(serverJar.flatMap { it.archiveFile })
|
||||
packagesToProcess.set(packagesToFix)
|
||||
}
|
||||
|
||||
val includeMappings by tasks.registering<IncludeMappings> {
|
||||
inputJar.set(fixJarForReobf.flatMap { it.outputJar })
|
||||
mappings.set(relocatedReobfMappings.flatMap { it.outputMappings })
|
||||
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<RelocateClassNameConstants> {
|
||||
inputJar.set(includeMappings.flatMap { it.outputJar })
|
||||
}
|
||||
afterEvaluate {
|
||||
relocateConstants {
|
||||
relocate("org.bukkit.craftbukkit", "org.bukkit.craftbukkit.${serverExt.craftBukkitPackageVersion.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<RemapJar> {
|
||||
group = "paperweight"
|
||||
description = "Re-obfuscate the built jar to obf mappings"
|
||||
|
||||
inputJar.set(relocateConstants.flatMap { it.outputJar })
|
||||
|
||||
mappingsFile.set(relocatedReobfMappings.flatMap { it.outputMappings })
|
||||
|
||||
fromNamespace.set(DEOBF_NAMESPACE)
|
||||
toNamespace.set(SPIGOT_NAMESPACE)
|
||||
|
||||
remapper.from(parent.configurations.named(REMAPPER_CONFIG))
|
||||
remapperArgs.set(TinyRemapper.minecraftRemapArgs)
|
||||
|
||||
outputJar.set(layout.buildDirectory.map { it.dir("libs").file("${project.name}-${project.version}-reobf.jar") })
|
||||
}
|
||||
|
||||
return ServerTasks(includeMappings, reobfJar)
|
||||
}
|
||||
|
||||
data class ServerTasks(
|
||||
val includeMappings: TaskProvider<IncludeMappings>,
|
||||
val reobfJar: TaskProvider<RemapJar>,
|
||||
)
|
||||
|
||||
private fun Project.addMcDevSourcesRoot(mcDevSourceDir: Path) {
|
||||
plugins.apply("idea")
|
||||
|
||||
val dir = mcDevSourceDir.toFile()
|
||||
|
||||
extensions.getByType<JavaPluginExtension>().sourceSets.named(SourceSet.MAIN_SOURCE_SET_NAME) {
|
||||
java {
|
||||
srcDirs(dir)
|
||||
val pathString = dir.invariantSeparatorsPath
|
||||
exclude {
|
||||
it.file.absoluteFile.invariantSeparatorsPath.contains(pathString)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extensions.configure<IdeaModel> {
|
||||
module {
|
||||
generatedSourceDirs.add(dir)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,42 +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.util
|
||||
|
||||
data class Relocation(
|
||||
val owningLibraryCoordinates: String?,
|
||||
val fromPackage: String,
|
||||
val toPackage: String,
|
||||
val excludes: List<String>
|
||||
) {
|
||||
fun exclude(exclude: String) {
|
||||
(excludes as MutableList) += exclude
|
||||
}
|
||||
}
|
||||
|
||||
data class RelocationWrapper(
|
||||
val relocation: Relocation,
|
||||
val fromSlash: String = relocation.fromPackage.replace('.', '/'),
|
||||
val fromDot: String = relocation.fromPackage.replace('/', '.'),
|
||||
val toSlash: String = relocation.toPackage.replace('.', '/'),
|
||||
val toDot: String = relocation.toPackage.replace('/', '.')
|
||||
)
|
|
@ -1,418 +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.util
|
||||
|
||||
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.PaperweightException
|
||||
import io.papermc.paperweight.tasks.*
|
||||
import io.papermc.paperweight.util.constants.*
|
||||
import java.io.File
|
||||
import java.io.InputStream
|
||||
import java.io.OutputStream
|
||||
import java.lang.management.ManagementFactory
|
||||
import java.lang.reflect.Type
|
||||
import java.net.URI
|
||||
import java.net.URL
|
||||
import java.nio.file.FileSystem
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.Paths
|
||||
import java.security.MessageDigest
|
||||
import java.util.Collections
|
||||
import java.util.IdentityHashMap
|
||||
import java.util.Locale
|
||||
import java.util.Optional
|
||||
import java.util.concurrent.CompletableFuture
|
||||
import java.util.concurrent.ThreadLocalRandom
|
||||
import java.util.concurrent.atomic.AtomicLong
|
||||
import java.util.jar.Attributes
|
||||
import java.util.jar.Manifest
|
||||
import kotlin.io.path.*
|
||||
import org.cadixdev.lorenz.merge.MergeResult
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.Task
|
||||
import org.gradle.api.file.FileCollection
|
||||
import org.gradle.api.file.FileSystemLocation
|
||||
import org.gradle.api.file.ProjectLayout
|
||||
import org.gradle.api.file.RegularFile
|
||||
import org.gradle.api.file.RegularFileProperty
|
||||
import org.gradle.api.invocation.Gradle
|
||||
import org.gradle.api.logging.LogLevel
|
||||
import org.gradle.api.logging.Logger
|
||||
import org.gradle.api.plugins.JavaPluginExtension
|
||||
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.api.tasks.TaskProvider
|
||||
import org.gradle.jvm.toolchain.JavaLanguageVersion
|
||||
import org.gradle.jvm.toolchain.JavaLauncher
|
||||
import org.gradle.jvm.toolchain.JavaToolchainService
|
||||
import org.gradle.kotlin.dsl.*
|
||||
|
||||
val gson: Gson = GsonBuilder().disableHtmlEscaping().setPrettyPrinting().registerTypeHierarchyAdapter(Path::class.java, PathJsonConverter()).create()
|
||||
|
||||
class PathJsonConverter : JsonDeserializer<Path?>, JsonSerializer<Path?> {
|
||||
override fun deserialize(json: JsonElement?, typeOfT: Type?, context: JsonDeserializationContext?): Path? {
|
||||
return if (json != null) Paths.get(json.asString) else null
|
||||
}
|
||||
|
||||
override fun serialize(src: Path?, typeOfSrc: Type?, context: JsonSerializationContext?): JsonElement {
|
||||
return JsonPrimitive(src.toString())
|
||||
}
|
||||
}
|
||||
|
||||
inline fun <reified T> Gson.fromJson(any: Any): T = when (any) {
|
||||
is String -> fromJson(any)
|
||||
else -> any.convertToPath().bufferedReader(Charsets.UTF_8).use { fromJson(it) }
|
||||
}
|
||||
|
||||
val ProjectLayout.cache: Path
|
||||
get() = projectDirectory.dir(".gradle/$CACHE_PATH").path
|
||||
|
||||
fun ProjectLayout.cacheDir(path: String) = projectDirectory.dir(".gradle/$CACHE_PATH").dir(path)
|
||||
|
||||
fun ProjectLayout.maybeInitSubmodules(offline: Boolean, logger: Logger) {
|
||||
if (offline) {
|
||||
Git.checkForGit()
|
||||
logger.lifecycle("Offline mode enabled, not initializing submodules. This may cause problems if submodules are not already initialized.")
|
||||
} else {
|
||||
initSubmodules()
|
||||
}
|
||||
}
|
||||
|
||||
fun ProjectLayout.initSubmodules() {
|
||||
Git.checkForGit()
|
||||
Git(projectDirectory.path)("submodule", "update", "--init").executeOut()
|
||||
}
|
||||
|
||||
fun Project.offlineMode(): Boolean = gradle.startParameter.isOffline
|
||||
|
||||
fun <T : FileSystemLocation> Provider<out T>.fileExists(project: Project): Provider<out T?> {
|
||||
return flatMap { project.provider { it.takeIf { f -> f.path.exists() } } }
|
||||
}
|
||||
|
||||
inline fun <reified T : Task> TaskContainer.providerFor(name: String): TaskProvider<T> {
|
||||
return if (names.contains(name)) {
|
||||
named<T>(name)
|
||||
} else {
|
||||
register<T>(name)
|
||||
}
|
||||
}
|
||||
|
||||
inline fun <reified T : Task> TaskContainer.configureTask(name: String, noinline configure: T.() -> Unit): TaskProvider<T> {
|
||||
return if (names.contains(name)) {
|
||||
named(name, configure)
|
||||
} else {
|
||||
register(name, configure)
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val Project.download: Provider<DownloadService>
|
||||
get() = gradle.sharedServices.registrations.getByName(DOWNLOAD_SERVICE_NAME).service as Provider<DownloadService>
|
||||
|
||||
fun commentRegex(): Regex {
|
||||
return Regex("\\s*#.*")
|
||||
}
|
||||
|
||||
val ProviderFactory.isBaseExecution: Provider<Boolean>
|
||||
get() = gradleProperty(PAPERWEIGHT_DOWNSTREAM_FILE_PROPERTY)
|
||||
.orElse(provider { "false" })
|
||||
.map { it == "false" }
|
||||
|
||||
val Project.isBaseExecution: Boolean
|
||||
get() = providers.isBaseExecution.get()
|
||||
|
||||
fun ProviderFactory.verboseApplyPatches(): Provider<Boolean> =
|
||||
gradleProperty(PAPERWEIGHT_VERBOSE_APPLY_PATCHES)
|
||||
.map { it.toBoolean() }
|
||||
.orElse(false)
|
||||
|
||||
val redirectThreadCount: AtomicLong = AtomicLong(0)
|
||||
|
||||
fun redirect(input: InputStream, out: OutputStream): CompletableFuture<Unit> {
|
||||
val future = CompletableFuture<Unit>()
|
||||
val thread = Thread {
|
||||
try {
|
||||
input.copyTo(out)
|
||||
future.complete(Unit)
|
||||
} catch (e: Throwable) {
|
||||
future.completeExceptionally(PaperweightException("Failed to copy $input to $out", e))
|
||||
}
|
||||
}
|
||||
thread.name = "paperweight stream redirect thread #${redirectThreadCount.getAndIncrement()}"
|
||||
thread.isDaemon = true
|
||||
thread.start()
|
||||
return future
|
||||
}
|
||||
|
||||
object UselessOutputStream : OutputStream() {
|
||||
override fun write(b: Int) {
|
||||
}
|
||||
}
|
||||
|
||||
class DelegatingOutputStream(vararg delegates: OutputStream) : OutputStream() {
|
||||
|
||||
private val delegates: MutableSet<OutputStream> = Collections.newSetFromMap(IdentityHashMap())
|
||||
|
||||
init {
|
||||
this.delegates.addAll(delegates)
|
||||
}
|
||||
|
||||
override fun write(b: Int) {
|
||||
for (delegate in delegates) {
|
||||
delegate.write(b)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun Any.convertToPath(): Path {
|
||||
return when (this) {
|
||||
is Path -> this
|
||||
is File -> this.toPath()
|
||||
is FileSystemLocation -> this.path
|
||||
is Provider<*> -> this.get().convertToPath()
|
||||
else -> throw PaperweightException("Unknown type representing a file: ${this.javaClass.name}")
|
||||
}
|
||||
}
|
||||
|
||||
fun Any.convertToFileProvider(layout: ProjectLayout, providers: ProviderFactory): Provider<RegularFile> {
|
||||
return when (this) {
|
||||
is Path -> layout.file(providers.provider { toFile() })
|
||||
is File -> layout.file(providers.provider { this })
|
||||
is FileSystemLocation -> layout.file(providers.provider { asFile })
|
||||
is Provider<*> -> flatMap { it.convertToFileProvider(layout, providers) }
|
||||
else -> throw PaperweightException("Unknown type representing a file: ${this.javaClass.name}")
|
||||
}
|
||||
}
|
||||
|
||||
fun Any?.convertToPathOrNull(): Path? {
|
||||
if (this == null) {
|
||||
return null
|
||||
}
|
||||
return this.convertToPath()
|
||||
}
|
||||
|
||||
fun Any.convertToUrl(): URL {
|
||||
return when (this) {
|
||||
is URL -> this
|
||||
is URI -> this.toURL()
|
||||
is String -> URI.create(this).toURL()
|
||||
is Provider<*> -> this.get().convertToUrl()
|
||||
else -> throw PaperweightException("Unknown URL type: ${this.javaClass.name}")
|
||||
}
|
||||
}
|
||||
|
||||
fun String.capitalized(): String {
|
||||
return replaceFirstChar(Char::uppercase)
|
||||
}
|
||||
|
||||
fun ensureParentExists(vararg files: Any) {
|
||||
for (file in files) {
|
||||
val parent = file.convertToPath().parent
|
||||
try {
|
||||
parent.createDirectories()
|
||||
} catch (e: Exception) {
|
||||
throw PaperweightException("Failed to create directory $parent", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun ensureDeleted(vararg files: Any) {
|
||||
for (file in files) {
|
||||
val f = file.convertToPath()
|
||||
try {
|
||||
f.deleteRecursive()
|
||||
} catch (e: Exception) {
|
||||
throw PaperweightException("Failed to delete file or directory $f", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun BaseTask.defaultOutput(name: String, ext: String): RegularFileProperty {
|
||||
return objects.fileProperty().convention {
|
||||
layout.cache.resolve(paperTaskOutput(name, ext)).toFile()
|
||||
}
|
||||
}
|
||||
|
||||
fun BaseTask.defaultOutput(ext: String): RegularFileProperty {
|
||||
return defaultOutput(name, ext)
|
||||
}
|
||||
|
||||
fun BaseTask.defaultOutput(): RegularFileProperty {
|
||||
return defaultOutput("jar")
|
||||
}
|
||||
|
||||
val <T> Optional<T>.orNull: T?
|
||||
get() = orElse(null)
|
||||
|
||||
inline fun <reified T : Any> Project.contents(contentFile: RegularFileProperty, crossinline convert: (String) -> T): Provider<T> {
|
||||
return providers.fileContents(contentFile)
|
||||
.asText
|
||||
.map { convert(it) }
|
||||
}
|
||||
|
||||
fun findOutputDir(baseFile: Path): Path {
|
||||
var dir: Path
|
||||
do {
|
||||
dir = baseFile.resolveSibling("${baseFile.name}-" + ThreadLocalRandom.current().nextInt())
|
||||
} while (dir.exists())
|
||||
return dir
|
||||
}
|
||||
|
||||
private val emptyMergeResult = MergeResult(null)
|
||||
fun <T> emptyMergeResult(): MergeResult<T?> {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
return emptyMergeResult as MergeResult<T?>
|
||||
}
|
||||
|
||||
inline fun <reified T : Task> TaskContainer.registering(noinline configuration: T.() -> Unit) = registering(T::class, configuration)
|
||||
inline fun <reified T : Task> TaskContainer.registering() = registering(T::class)
|
||||
|
||||
enum class HashingAlgorithm(val algorithmName: String) {
|
||||
SHA256("SHA-256"),
|
||||
SHA1("SHA-1");
|
||||
|
||||
private val threadLocalMessageDigest = ThreadLocal.withInitial { createDigest() }
|
||||
|
||||
fun createDigest(): MessageDigest = MessageDigest.getInstance(algorithmName)
|
||||
|
||||
val threadLocalDigest: MessageDigest
|
||||
get() = threadLocalMessageDigest.get()
|
||||
}
|
||||
|
||||
class Hash(
|
||||
@get:Input
|
||||
val value: String,
|
||||
@get:Input
|
||||
val algorithm: HashingAlgorithm
|
||||
) {
|
||||
@get:Internal
|
||||
val valueLower: String by lazy { value.lowercase(Locale.ENGLISH) }
|
||||
}
|
||||
|
||||
fun String.hash(algorithm: HashingAlgorithm): ByteArray = algorithm.threadLocalDigest.let {
|
||||
it.update(toByteArray())
|
||||
it.digest()
|
||||
}
|
||||
|
||||
fun InputStream.hash(algorithm: HashingAlgorithm, bufferSize: Int = 8192): ByteArray {
|
||||
val digest = algorithm.threadLocalDigest
|
||||
val buffer = ByteArray(bufferSize)
|
||||
while (true) {
|
||||
val count = read(buffer)
|
||||
if (count == -1) {
|
||||
break
|
||||
}
|
||||
digest.update(buffer, 0, count)
|
||||
}
|
||||
return digest.digest()
|
||||
}
|
||||
|
||||
private val hexChars = "0123456789abcdef".toCharArray()
|
||||
|
||||
fun ByteArray.asHexString(): String {
|
||||
val chars = CharArray(2 * size)
|
||||
forEachIndexed { i, byte ->
|
||||
val unsigned = byte.toInt() and 0xFF
|
||||
chars[2 * i] = hexChars[unsigned / 16]
|
||||
chars[2 * i + 1] = hexChars[unsigned % 16]
|
||||
}
|
||||
return String(chars)
|
||||
}
|
||||
|
||||
fun JavaToolchainService.defaultJavaLauncher(project: Project): Provider<JavaLauncher> {
|
||||
return launcherFor(project.extensions.getByType<JavaPluginExtension>().toolchain).orElse(
|
||||
launcherFor {
|
||||
// If the java plugin isn't applied, or no toolchain value was set
|
||||
languageVersion.set(JavaLanguageVersion.of(21))
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
fun <P : Property<*>> P.withDisallowChanges(): P = apply { disallowChanges() }
|
||||
fun <P : Property<*>> P.withDisallowUnsafeRead(): P = apply { disallowUnsafeRead() }
|
||||
|
||||
fun FileCollection.toJarClassProviderRoots(): List<ClassProviderRoot> = files.asSequence()
|
||||
.map { f -> f.toPath() }
|
||||
.filter { p -> p.isLibraryJar }
|
||||
.map { p -> ClassProviderRoot.fromJar(p) }
|
||||
.toList()
|
||||
|
||||
private fun javaVersion(): Int {
|
||||
val version = System.getProperty("java.specification.version")
|
||||
val parts = version.split("\\.".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
|
||||
val errorMsg = "Could not determine version of the current JVM"
|
||||
check(parts.isNotEmpty()) { errorMsg }
|
||||
return if (parts[0] == "1") {
|
||||
check(parts.size >= 2) { errorMsg }
|
||||
parts[1].toInt()
|
||||
} else {
|
||||
parts[0].toInt()
|
||||
}
|
||||
}
|
||||
|
||||
fun checkJavaVersion() {
|
||||
val minimumJava = 11
|
||||
val ver = javaVersion()
|
||||
if (ver < minimumJava) {
|
||||
var msg = "paperweight requires Gradle to be run with a Java $minimumJava runtime or newer."
|
||||
val runtimeMX = ManagementFactory.getRuntimeMXBean()
|
||||
if (runtimeMX != null) {
|
||||
msg += " Current runtime: Java ${runtimeMX.specVersion} (${runtimeMX.vmName} ${runtimeMX.vmVersion})"
|
||||
}
|
||||
throw PaperweightException(msg)
|
||||
}
|
||||
}
|
||||
|
||||
inline fun <reified P> printId(pluginId: String, gradle: Gradle) {
|
||||
if (gradle.startParameter.logLevel == LogLevel.QUIET) {
|
||||
return
|
||||
}
|
||||
println("$pluginId v${P::class.java.`package`.implementationVersion} (running on '${System.getProperty("os.name")}')")
|
||||
}
|
||||
|
||||
fun FileSystem.modifyManifest(create: Boolean = true, op: Manifest.() -> Unit) {
|
||||
modifyManifest(getPath("META-INF/MANIFEST.MF"), create, op)
|
||||
}
|
||||
|
||||
fun modifyManifest(path: Path, create: Boolean = true, op: Manifest.() -> Unit) {
|
||||
val exists = path.exists()
|
||||
if (exists || create) {
|
||||
val mf = if (exists) {
|
||||
path.inputStream().buffered().use { Manifest(it) }
|
||||
} else {
|
||||
path.parent.createDirectories()
|
||||
val manifest = Manifest()
|
||||
manifest.mainAttributes[Attributes.Name.MANIFEST_VERSION] = "1.0"
|
||||
manifest
|
||||
}
|
||||
op(mf)
|
||||
path.outputStream().buffered().use { mf.write(it) }
|
||||
}
|
||||
}
|
|
@ -1,65 +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.util
|
||||
|
||||
import java.nio.file.Path
|
||||
import kotlin.io.path.*
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertFalse
|
||||
import kotlin.test.assertTrue
|
||||
import org.junit.jupiter.api.io.TempDir
|
||||
|
||||
class FileUtilsTest {
|
||||
@Test
|
||||
fun testContentEquals(@TempDir tempDir: Path) {
|
||||
val someBytes = classBytes<FileUtilsTest>()
|
||||
|
||||
// write same data to two files
|
||||
val one = tempDir.resolve("${FileUtilsTest::class.simpleName}.class")
|
||||
one.writeBytes(someBytes)
|
||||
val two = tempDir.resolve("${FileUtilsTest::class.simpleName}.class_1")
|
||||
two.writeBytes(someBytes)
|
||||
|
||||
// assert the content equals what we just wrote
|
||||
assertTrue(one.contentEquals(someBytes.inputStream()), "File content doesn't equal what was written to the file")
|
||||
|
||||
// assert the files have matching content
|
||||
assertTrue(one.contentEquals(two), "These files have the same content")
|
||||
assertTrue(two.contentEquals(one), "These files have the same content")
|
||||
|
||||
val someDifferentBytes = classBytes<Map<*, *>>()
|
||||
|
||||
// write some different data to a third file
|
||||
val three = tempDir.resolve("Map.class")
|
||||
three.writeBytes(someDifferentBytes)
|
||||
|
||||
// assert it's content is different from the previously written files
|
||||
assertFalse(one.contentEquals(three), "These files are different")
|
||||
}
|
||||
|
||||
private inline fun <reified C> classBytes(): ByteArray {
|
||||
val resourceName = C::class.java.name.replace(".", "/") + ".class"
|
||||
return this::class.java.classLoader.getResource(resourceName)
|
||||
?.openStream()?.readAllBytes() ?: error("Couldn't get resource $resourceName")
|
||||
}
|
||||
}
|
|
@ -1,16 +0,0 @@
|
|||
plugins {
|
||||
`config-kotlin`
|
||||
`config-publish`
|
||||
}
|
||||
|
||||
dependencies {
|
||||
shade(projects.nuggetLib)
|
||||
implementation(libs.bundles.kotson)
|
||||
}
|
||||
|
||||
gradlePlugin {
|
||||
plugins.all {
|
||||
description = "Gradle plugin for developing Paper derivatives"
|
||||
implementationClass = "io.papermc.paperweight.patcher.PaperweightPatcher"
|
||||
}
|
||||
}
|
|
@ -1,314 +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.patcher
|
||||
|
||||
import io.papermc.paperweight.DownloadService
|
||||
import io.papermc.paperweight.patcher.tasks.*
|
||||
import io.papermc.paperweight.patcher.upstream.PatchTaskConfig
|
||||
import io.papermc.paperweight.patcher.upstream.PatcherUpstream
|
||||
import io.papermc.paperweight.patcher.upstream.RepoPatcherUpstream
|
||||
import io.papermc.paperweight.taskcontainers.BundlerJarTasks
|
||||
import io.papermc.paperweight.taskcontainers.DevBundleTasks
|
||||
import io.papermc.paperweight.tasks.*
|
||||
import io.papermc.paperweight.util.*
|
||||
import io.papermc.paperweight.util.constants.*
|
||||
import io.papermc.paperweight.util.data.*
|
||||
import java.io.File
|
||||
import java.util.concurrent.atomic.AtomicReference
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.Task
|
||||
import org.gradle.api.provider.Provider
|
||||
import org.gradle.api.tasks.Delete
|
||||
import org.gradle.api.tasks.TaskProvider
|
||||
import org.gradle.api.tasks.bundling.Zip
|
||||
import org.gradle.kotlin.dsl.*
|
||||
import org.gradle.kotlin.dsl.registering
|
||||
|
||||
class PaperweightPatcher : Plugin<Project> {
|
||||
|
||||
override fun apply(target: Project) {
|
||||
checkJavaVersion()
|
||||
Git.checkForGit()
|
||||
printId<PaperweightPatcher>("paperweight-patcher", target.gradle)
|
||||
|
||||
val patcher = target.extensions.create(PAPERWEIGHT_EXTENSION, PaperweightPatcherExtension::class, target)
|
||||
|
||||
target.gradle.sharedServices.registerIfAbsent(DOWNLOAD_SERVICE_NAME, DownloadService::class) {}
|
||||
|
||||
target.tasks.register<Delete>("cleanCache") {
|
||||
group = "paperweight"
|
||||
description = "Delete the project setup cache and task outputs."
|
||||
delete(target.layout.cache)
|
||||
}
|
||||
|
||||
target.configurations.create(DECOMPILER_CONFIG)
|
||||
target.configurations.create(REMAPPER_CONFIG)
|
||||
target.configurations.create(PAPERCLIP_CONFIG)
|
||||
|
||||
val workDirProp = target.providers.gradleProperty(UPSTREAM_WORK_DIR_PROPERTY)
|
||||
val dataFileProp = target.providers.gradleProperty(PAPERWEIGHT_DOWNSTREAM_FILE_PROPERTY)
|
||||
|
||||
val applyPatches by target.tasks.registering { group = "paperweight" }
|
||||
val rebuildPatches by target.tasks.registering { group = "paperweight" }
|
||||
val generateReobfMappings by target.tasks.registering(GenerateReobfMappings::class)
|
||||
|
||||
val mergeReobfMappingsPatches by target.tasks.registering<PatchMappings> {
|
||||
patch.set(patcher.reobfMappingsPatch.fileExists(target))
|
||||
outputMappings.convention(defaultOutput("tiny"))
|
||||
|
||||
fromNamespace.set(DEOBF_NAMESPACE)
|
||||
toNamespace.set(SPIGOT_NAMESPACE)
|
||||
}
|
||||
|
||||
val patchReobfMappings by target.tasks.registering<PatchMappings> {
|
||||
inputMappings.set(generateReobfMappings.flatMap { it.reobfMappings })
|
||||
patch.set(mergeReobfMappingsPatches.flatMap { it.outputMappings })
|
||||
outputMappings.set(target.layout.cache.resolve(PATCHED_REOBF_MOJANG_SPIGOT_MAPPINGS))
|
||||
|
||||
fromNamespace.set(DEOBF_NAMESPACE)
|
||||
toNamespace.set(SPIGOT_NAMESPACE)
|
||||
}
|
||||
|
||||
val generateRelocatedReobfMappings by target.tasks.registering<GenerateRelocatedReobfMappings> {
|
||||
inputMappings.set(patchReobfMappings.flatMap { it.outputMappings })
|
||||
outputMappings.set(target.layout.cache.resolve(RELOCATED_PATCHED_REOBF_MOJANG_SPIGOT_MAPPINGS))
|
||||
}
|
||||
|
||||
val prepareForDownstream = target.tasks.register<PaperweightPatcherPrepareForDownstream>(PAPERWEIGHT_PREPARE_DOWNSTREAM) {
|
||||
dataFile.fileProvider(dataFileProp.map { File(it) })
|
||||
reobfMappingsPatch.set(mergeReobfMappingsPatches.flatMap { it.outputMappings })
|
||||
}
|
||||
|
||||
val upstreamDataTaskRef = AtomicReference<TaskProvider<PaperweightPatcherUpstreamData>>(null)
|
||||
|
||||
patcher.upstreams.all {
|
||||
val taskPair = target.createUpstreamTask(this, patcher, workDirProp, upstreamDataTaskRef)
|
||||
|
||||
patchTasks.all {
|
||||
val createdPatchTask = target.createPatchTask(this, patcher, taskPair, applyPatches)
|
||||
prepareForDownstream {
|
||||
dependsOn(createdPatchTask)
|
||||
}
|
||||
target.rebuildPatchTask(this, rebuildPatches)
|
||||
}
|
||||
}
|
||||
|
||||
val devBundleTasks = DevBundleTasks(target)
|
||||
|
||||
val bundlerJarTasks = BundlerJarTasks(
|
||||
target,
|
||||
patcher.bundlerJarName,
|
||||
patcher.mainClass
|
||||
)
|
||||
|
||||
target.afterEvaluate {
|
||||
target.repositories {
|
||||
maven(patcher.remapRepo) {
|
||||
name = REMAPPER_REPO_NAME
|
||||
content { onlyForConfigurations(REMAPPER_CONFIG) }
|
||||
}
|
||||
maven(patcher.decompileRepo) {
|
||||
name = DECOMPILER_REPO_NAME
|
||||
content { onlyForConfigurations(DECOMPILER_CONFIG) }
|
||||
}
|
||||
}
|
||||
|
||||
val upstreamDataTask = upstreamDataTaskRef.get() ?: return@afterEvaluate
|
||||
val upstreamData = upstreamDataTask.map { readUpstreamData(it.dataFile) }
|
||||
|
||||
mergeReobfMappingsPatches {
|
||||
inputMappings.pathProvider(upstreamData.map { it.reobfMappingsPatch })
|
||||
}
|
||||
val mergedReobfPackagesToFix = upstreamData.zip(patcher.reobfPackagesToFix) { data, pkgs ->
|
||||
data.reobfPackagesToFix + pkgs
|
||||
}
|
||||
|
||||
prepareForDownstream {
|
||||
upstreamDataFile.set(upstreamDataTask.flatMap { it.dataFile })
|
||||
reobfPackagesToFix.set(mergedReobfPackagesToFix)
|
||||
}
|
||||
|
||||
for (upstream in patcher.upstreams) {
|
||||
for (patchTask in upstream.patchTasks) {
|
||||
patchTask.patchTask {
|
||||
sourceMcDevJar.convention(target, upstreamData.map { it.decompiledJar })
|
||||
mcLibrariesDir.convention(target, upstreamData.map { it.libSourceDir })
|
||||
spigotLibrariesSourceDir.convention(target, upstreamData.map { it.spigotLibSourcesDir })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val serverProj = patcher.serverProject.orNull ?: return@afterEvaluate
|
||||
val serverJar = serverProj.tasks.register("serverJar", Zip::class)
|
||||
|
||||
generateReobfMappings {
|
||||
inputMappings.pathProvider(upstreamData.map { it.mappings })
|
||||
notchToSpigotMappings.pathProvider(upstreamData.map { it.notchToSpigotMappings })
|
||||
sourceMappings.pathProvider(upstreamData.map { it.sourceMappings })
|
||||
inputJar.set(serverJar.flatMap { it.archiveFile })
|
||||
spigotRecompiledClasses.pathProvider(upstreamData.map { it.spigotRecompiledClasses })
|
||||
|
||||
reobfMappings.set(target.layout.cache.resolve(REOBF_MOJANG_SPIGOT_MAPPINGS))
|
||||
}
|
||||
generateRelocatedReobfMappings {
|
||||
inputJar.set(serverJar.flatMap { it.archiveFile })
|
||||
}
|
||||
|
||||
val (includeMappings, reobfJar) = serverProj.setupServerProject(
|
||||
target,
|
||||
upstreamData.map { it.remappedJar },
|
||||
upstreamData.map { it.decompiledJar },
|
||||
patcher.mcDevSourceDir.path,
|
||||
upstreamData.map { it.libFile },
|
||||
mergedReobfPackagesToFix,
|
||||
generateRelocatedReobfMappings,
|
||||
serverJar
|
||||
) ?: return@afterEvaluate
|
||||
|
||||
devBundleTasks.configure(
|
||||
patcher.serverProject.get(),
|
||||
patcher.bundlerJarName.get(),
|
||||
patcher.mainClass,
|
||||
upstreamData.map { it.mcVersion },
|
||||
upstreamData.map { it.decompiledJar },
|
||||
upstreamData.map { it.serverLibrariesTxt },
|
||||
upstreamData.map { it.serverLibrariesList },
|
||||
upstreamData.map { it.vanillaJar },
|
||||
upstreamData.map { it.accessTransform },
|
||||
upstreamData.map { it.bundlerVersionJson }.convertToFileProvider(layout, providers)
|
||||
) {
|
||||
vanillaJarIncludes.set(upstreamData.map { it.vanillaIncludes })
|
||||
reobfMappingsFile.set(generateRelocatedReobfMappings.flatMap { it.outputMappings })
|
||||
|
||||
paramMappingsCoordinates.set(upstreamData.map { it.paramMappings.coordinates.single() })
|
||||
paramMappingsUrl.set(upstreamData.map { it.paramMappings.url })
|
||||
}
|
||||
devBundleTasks.configureAfterEvaluate()
|
||||
|
||||
bundlerJarTasks.configureBundlerTasks(
|
||||
upstreamData.map { it.bundlerVersionJson }.convertToFileProvider(target.layout, target.providers),
|
||||
upstreamData.map { it.serverLibrariesList }.convertToFileProvider(target.layout, target.providers),
|
||||
upstreamData.map { it.vanillaJar }.convertToFileProvider(target.layout, target.providers),
|
||||
includeMappings.flatMap { it.outputJar },
|
||||
reobfJar,
|
||||
upstreamData.map { it.mcVersion }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun Project.createUpstreamTask(
|
||||
upstream: PatcherUpstream,
|
||||
ext: PaperweightPatcherExtension,
|
||||
workDirProp: Provider<String>,
|
||||
upstreamDataTaskRef: AtomicReference<TaskProvider<PaperweightPatcherUpstreamData>>
|
||||
): Pair<TaskProvider<CheckoutRepo>?, TaskProvider<PaperweightPatcherUpstreamData>> {
|
||||
val workDirFromProp = layout.dir(workDirProp.map { File(it) }).orElse(ext.upstreamsDir)
|
||||
|
||||
val upstreamData = tasks.configureTask<PaperweightPatcherUpstreamData>(upstream.upstreamDataTaskName) {
|
||||
workDir.convention(workDirFromProp)
|
||||
dataFile.convention(workDirFromProp.map { it.file("upstreamData${upstream.name.capitalized()}.json") })
|
||||
}
|
||||
|
||||
val cloneTask = (upstream as? RepoPatcherUpstream)?.let { repo ->
|
||||
val cloneTask = tasks.configureTask<CheckoutRepo>(repo.cloneTaskName) {
|
||||
repoName.convention(repo.name)
|
||||
url.convention(repo.url)
|
||||
ref.convention(repo.ref)
|
||||
|
||||
workDir.convention(workDirFromProp)
|
||||
}
|
||||
|
||||
upstreamData {
|
||||
dependsOn(cloneTask)
|
||||
projectDir.convention(cloneTask.flatMap { it.outputDir })
|
||||
}
|
||||
|
||||
return@let cloneTask
|
||||
}
|
||||
|
||||
if (upstream.useForUpstreamData.getOrElse(false)) {
|
||||
upstreamDataTaskRef.set(upstreamData)
|
||||
} else {
|
||||
upstreamDataTaskRef.compareAndSet(null, upstreamData)
|
||||
}
|
||||
|
||||
return cloneTask to upstreamData
|
||||
}
|
||||
|
||||
private fun Project.createPatchTask(
|
||||
config: PatchTaskConfig,
|
||||
ext: PaperweightPatcherExtension,
|
||||
upstreamTaskPair: Pair<TaskProvider<CheckoutRepo>?, TaskProvider<PaperweightPatcherUpstreamData>>,
|
||||
applyPatches: TaskProvider<Task>
|
||||
): TaskProvider<PatcherApplyGitPatches> {
|
||||
val project = this
|
||||
val patchTask = tasks.configureTask<PatcherApplyGitPatches>(config.patchTaskName) {
|
||||
group = "paperweight"
|
||||
|
||||
if (isBaseExecution) {
|
||||
doNotTrackState("$name should always run when requested as part of the base execution.")
|
||||
}
|
||||
printOutput.set(isBaseExecution)
|
||||
|
||||
val (cloneTask, upstreamDataTask) = upstreamTaskPair
|
||||
dependsOn(upstreamDataTask)
|
||||
|
||||
if (cloneTask != null) {
|
||||
upstreamDir.convention(cloneTask.flatMap { it.outputDir.dir(config.upstreamDirPath) })
|
||||
} else {
|
||||
upstreamDir.convention(config.upstreamDir)
|
||||
}
|
||||
|
||||
patchDir.convention(config.patchDir.fileExists(project))
|
||||
outputDir.convention(config.outputDir)
|
||||
mcDevSources.set(ext.mcDevSourceDir)
|
||||
|
||||
bareDirectory.convention(config.isBareDirectory)
|
||||
importMcDev.convention(config.importMcDev)
|
||||
devImports.convention(ext.devImports.fileExists(project))
|
||||
}
|
||||
|
||||
applyPatches {
|
||||
dependsOn(patchTask)
|
||||
}
|
||||
|
||||
return patchTask
|
||||
}
|
||||
|
||||
private fun Project.rebuildPatchTask(config: PatchTaskConfig, rebuildPatches: TaskProvider<Task>): TaskProvider<RebuildGitPatches> {
|
||||
val rebuildTask = tasks.configureTask<RebuildGitPatches>(config.rebuildTaskName) {
|
||||
group = "paperweight"
|
||||
|
||||
patchDir.convention(config.patchDir)
|
||||
inputDir.convention(config.outputDir)
|
||||
baseRef.convention("base")
|
||||
}
|
||||
|
||||
rebuildPatches {
|
||||
dependsOn(rebuildTask)
|
||||
}
|
||||
|
||||
return rebuildTask
|
||||
}
|
||||
}
|
|
@ -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.patcher.tasks
|
||||
|
||||
import io.papermc.paperweight.tasks.*
|
||||
import io.papermc.paperweight.util.*
|
||||
import io.papermc.paperweight.util.data.readUpstreamData
|
||||
import kotlin.io.path.*
|
||||
import org.gradle.api.file.RegularFileProperty
|
||||
import org.gradle.api.provider.ListProperty
|
||||
import org.gradle.api.tasks.Input
|
||||
import org.gradle.api.tasks.InputFile
|
||||
import org.gradle.api.tasks.OutputFile
|
||||
import org.gradle.api.tasks.TaskAction
|
||||
import org.gradle.api.tasks.UntrackedTask
|
||||
|
||||
@UntrackedTask(because = "PaperweightPatcherPrepareForDownstream should always run when requested")
|
||||
abstract class PaperweightPatcherPrepareForDownstream : BaseTask() {
|
||||
|
||||
@get:InputFile
|
||||
abstract val upstreamDataFile: RegularFileProperty
|
||||
|
||||
@get:Input
|
||||
abstract val reobfPackagesToFix: ListProperty<String>
|
||||
|
||||
@get:InputFile
|
||||
abstract val reobfMappingsPatch: RegularFileProperty
|
||||
|
||||
@get:OutputFile
|
||||
abstract val dataFile: RegularFileProperty
|
||||
|
||||
@TaskAction
|
||||
fun run() {
|
||||
val upstreamData = readUpstreamData(upstreamDataFile)
|
||||
|
||||
val ourData = upstreamData.copy(
|
||||
reobfPackagesToFix = reobfPackagesToFix.get(),
|
||||
reobfMappingsPatch = reobfMappingsPatch.path,
|
||||
)
|
||||
|
||||
dataFile.path.parent.createDirectories()
|
||||
dataFile.path.bufferedWriter().use { writer ->
|
||||
gson.toJson(ourData, writer)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,168 +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.patcher.tasks
|
||||
|
||||
import 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.*
|
||||
import org.gradle.api.file.DirectoryProperty
|
||||
import org.gradle.api.file.RegularFileProperty
|
||||
import org.gradle.api.provider.Property
|
||||
import org.gradle.api.provider.ProviderFactory
|
||||
import org.gradle.api.tasks.Input
|
||||
import org.gradle.api.tasks.InputDirectory
|
||||
import org.gradle.api.tasks.InputFile
|
||||
import org.gradle.api.tasks.Optional
|
||||
import org.gradle.api.tasks.OutputDirectory
|
||||
import org.gradle.api.tasks.TaskAction
|
||||
|
||||
abstract class PatcherApplyGitPatches : ControllableOutputTask() {
|
||||
|
||||
@get:InputDirectory
|
||||
abstract val upstreamDir: DirectoryProperty
|
||||
|
||||
@get:Input
|
||||
abstract val upstreamBranch: Property<String>
|
||||
|
||||
@get:Optional
|
||||
@get:InputDirectory
|
||||
abstract val patchDir: DirectoryProperty
|
||||
|
||||
@get:Input
|
||||
abstract val bareDirectory: Property<Boolean>
|
||||
|
||||
@get:Input
|
||||
abstract val importMcDev: Property<Boolean>
|
||||
|
||||
@get:Optional
|
||||
@get:InputFile
|
||||
abstract val sourceMcDevJar: RegularFileProperty
|
||||
|
||||
@get:Optional
|
||||
@get:InputFile
|
||||
abstract val devImports: RegularFileProperty
|
||||
|
||||
@get:Optional
|
||||
@get:InputDirectory
|
||||
abstract val mcLibrariesDir: DirectoryProperty
|
||||
|
||||
@get:Optional
|
||||
@get:InputDirectory
|
||||
abstract val spigotLibrariesSourceDir: DirectoryProperty
|
||||
|
||||
@get:Input
|
||||
abstract val ignoreGitIgnore: Property<Boolean>
|
||||
|
||||
@get:OutputDirectory
|
||||
abstract val outputDir: DirectoryProperty
|
||||
|
||||
@get:Inject
|
||||
abstract val providers: ProviderFactory
|
||||
|
||||
@get:OutputDirectory
|
||||
abstract val mcDevSources: DirectoryProperty
|
||||
|
||||
@get:Input
|
||||
abstract val verbose: Property<Boolean>
|
||||
|
||||
override fun init() {
|
||||
upstreamBranch.convention("master")
|
||||
importMcDev.convention(false)
|
||||
printOutput.convention(true).finalizeValueOnRead()
|
||||
ignoreGitIgnore.convention(Git.ignoreProperty(providers)).finalizeValueOnRead()
|
||||
verbose.convention(providers.verboseApplyPatches())
|
||||
}
|
||||
|
||||
@TaskAction
|
||||
fun runLocking() {
|
||||
val lockFile = layout.cache.resolve(applyPatchesLock(outputDir.path))
|
||||
acquireProcessLockWaiting(lockFile)
|
||||
try {
|
||||
run()
|
||||
} finally {
|
||||
lockFile.deleteForcefully()
|
||||
}
|
||||
}
|
||||
|
||||
private fun run() {
|
||||
Git.checkForGit()
|
||||
|
||||
val output = outputDir.path
|
||||
recreateCloneDirectory(output)
|
||||
|
||||
val target = output.name
|
||||
|
||||
if (printOutput.get()) {
|
||||
logger.lifecycle("Creating $target from patch source...")
|
||||
}
|
||||
|
||||
if (bareDirectory.get()) {
|
||||
val up = upstreamDir.path
|
||||
up.resolve(".git").deleteRecursive()
|
||||
Git(up).let { upstreamGit ->
|
||||
upstreamGit("init", "--quiet").executeSilently(silenceErr = true)
|
||||
upstreamGit("checkout", "-b", upstreamBranch.get()).executeSilently(silenceErr = true)
|
||||
upstreamGit.disableAutoGpgSigningInRepo()
|
||||
upstreamGit("add", ".").executeSilently(silenceErr = true)
|
||||
upstreamGit("commit", "-m", "Initial Source", "--author=Initial <auto@mated.null>").executeSilently(silenceErr = true)
|
||||
}
|
||||
}
|
||||
|
||||
val git = Git(output)
|
||||
checkoutRepoFromUpstream(git, upstreamDir.path, upstreamBranch.get())
|
||||
|
||||
git.disableAutoGpgSigningInRepo()
|
||||
|
||||
val srcDir = output.resolve("src/main/java")
|
||||
val dataDir = output.resolve("src/main/resources")
|
||||
|
||||
val patches = patchDir.pathOrNull?.listDirectoryEntries("*.patch") ?: listOf()
|
||||
val librarySources = ArrayList<Path>()
|
||||
spigotLibrariesSourceDir.pathOrNull?.let { librarySources.add(it) }
|
||||
mcLibrariesDir.pathOrNull?.let { librarySources.add(it) }
|
||||
|
||||
if (sourceMcDevJar.isPresent && importMcDev.get()) {
|
||||
McDev.importMcDev(
|
||||
patches = patches,
|
||||
decompJar = sourceMcDevJar.path,
|
||||
importsFile = devImports.pathOrNull,
|
||||
targetDir = srcDir,
|
||||
dataTargetDir = dataDir,
|
||||
librariesDirs = librarySources,
|
||||
printOutput = printOutput.get()
|
||||
)
|
||||
}
|
||||
|
||||
git(*Git.add(ignoreGitIgnore, ".")).executeSilently()
|
||||
git("commit", "--allow-empty", "-m", "Initial", "--author=Initial Source <auto@mated.null>").executeSilently()
|
||||
git("tag", "-d", "base").runSilently(silenceErr = true)
|
||||
git("tag", "base").executeSilently()
|
||||
|
||||
applyGitPatches(git, target, output, patchDir.pathOrNull, printOutput.get(), verbose.get())
|
||||
|
||||
makeMcDevSrc(layout.cache, sourceMcDevJar.path, mcDevSources.path, outputDir.path, srcDir, dataDir)
|
||||
}
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
plugins {
|
||||
`config-kotlin`
|
||||
`config-publish`
|
||||
}
|
||||
|
||||
dependencies {
|
||||
shade(projects.nuggetLib)
|
||||
implementation(libs.bundles.kotson)
|
||||
implementation(variantOf(libs.diffpatch) { classifier("all") }) {
|
||||
isTransitive = false
|
||||
}
|
||||
}
|
||||
|
||||
gradlePlugin {
|
||||
plugins.all {
|
||||
description = "Gradle plugin for developing Paper plugins using server internals"
|
||||
implementationClass = "io.papermc.paperweight.userdev.PaperweightUser"
|
||||
}
|
||||
}
|
|
@ -1,333 +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.userdev
|
||||
|
||||
import io.papermc.paperweight.DownloadService
|
||||
import io.papermc.paperweight.PaperweightException
|
||||
import io.papermc.paperweight.tasks.*
|
||||
import io.papermc.paperweight.userdev.attribute.Obfuscation
|
||||
import io.papermc.paperweight.userdev.internal.JunitExclusionRule
|
||||
import io.papermc.paperweight.userdev.internal.setup.SetupHandler
|
||||
import io.papermc.paperweight.userdev.internal.setup.UserdevSetup
|
||||
import io.papermc.paperweight.userdev.internal.setup.util.*
|
||||
import io.papermc.paperweight.util.*
|
||||
import io.papermc.paperweight.util.constants.*
|
||||
import java.nio.file.Path
|
||||
import javax.inject.Inject
|
||||
import org.gradle.api.Action
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.artifacts.ModuleDependency
|
||||
import org.gradle.api.artifacts.ProjectDependency
|
||||
import org.gradle.api.artifacts.component.ModuleComponentIdentifier
|
||||
import org.gradle.api.artifacts.result.ResolvedDependencyResult
|
||||
import org.gradle.api.attributes.Bundling
|
||||
import org.gradle.api.attributes.Category
|
||||
import org.gradle.api.attributes.LibraryElements
|
||||
import org.gradle.api.attributes.Usage
|
||||
import org.gradle.api.plugins.JavaPlugin
|
||||
import org.gradle.api.provider.Provider
|
||||
import org.gradle.api.tasks.Delete
|
||||
import org.gradle.api.tasks.bundling.Jar
|
||||
import org.gradle.jvm.toolchain.JavaToolchainService
|
||||
import org.gradle.kotlin.dsl.*
|
||||
import org.gradle.util.internal.NameMatcher
|
||||
import org.gradle.workers.WorkerExecutor
|
||||
|
||||
abstract class PaperweightUser : Plugin<Project> {
|
||||
|
||||
@get:Inject
|
||||
abstract val workerExecutor: WorkerExecutor
|
||||
|
||||
@get:Inject
|
||||
abstract val javaToolchainService: JavaToolchainService
|
||||
|
||||
override fun apply(target: Project) {
|
||||
checkJavaVersion()
|
||||
|
||||
val sharedCacheRoot = target.gradle.gradleUserHomeDir.toPath().resolve("caches/paperweight-userdev")
|
||||
|
||||
target.gradle.sharedServices.registerIfAbsent(DOWNLOAD_SERVICE_NAME, DownloadService::class) {}
|
||||
|
||||
val cleanAll = target.tasks.register<Delete>("cleanAllPaperweightUserdevCaches") {
|
||||
group = "paperweight"
|
||||
description = "Delete the project-local & all shared paperweight-userdev setup caches."
|
||||
delete(target.layout.cache)
|
||||
delete(sharedCacheRoot)
|
||||
}
|
||||
val cleanCache by target.tasks.registering<Delete> {
|
||||
group = "paperweight"
|
||||
description = "Delete the project-local paperweight-userdev setup cache."
|
||||
delete(target.layout.cache)
|
||||
}
|
||||
|
||||
target.configurations.register(DEV_BUNDLE_CONFIG)
|
||||
|
||||
// must not be initialized until afterEvaluate, as it resolves the dev bundle
|
||||
val userdevSetup by lazy { createSetup(target, sharedCacheRoot.resolve(paperweightHash)) }
|
||||
|
||||
val userdev = target.extensions.create(
|
||||
PAPERWEIGHT_EXTENSION,
|
||||
PaperweightUserExtension::class,
|
||||
target,
|
||||
workerExecutor,
|
||||
javaToolchainService,
|
||||
target.provider { userdevSetup },
|
||||
target.objects
|
||||
)
|
||||
|
||||
target.dependencies.extensions.create(
|
||||
PAPERWEIGHT_EXTENSION,
|
||||
PaperweightUserDependenciesExtension::class,
|
||||
target.dependencies
|
||||
)
|
||||
|
||||
createConfigurations(target, target.provider { userdevSetup })
|
||||
|
||||
val reobfJar by target.tasks.registering<RemapJar> {
|
||||
group = "paperweight"
|
||||
description = "Remap the compiled plugin jar to Spigot's obfuscated runtime names."
|
||||
|
||||
mappingsFile.pathProvider(target.provider { userdevSetup.reobfMappings })
|
||||
remapClasspath.from(target.provider { userdevSetup.serverJar })
|
||||
|
||||
fromNamespace.set(DEOBF_NAMESPACE)
|
||||
toNamespace.set(SPIGOT_NAMESPACE)
|
||||
|
||||
remapper.from(project.configurations.named(PLUGIN_REMAPPER_CONFIG))
|
||||
remapperArgs.set(target.provider { userdevSetup.pluginRemapArgs })
|
||||
}
|
||||
|
||||
target.configurations.register(REOBF_CONFIG) {
|
||||
isCanBeConsumed = true
|
||||
isCanBeResolved = false
|
||||
attributes {
|
||||
attribute(Usage.USAGE_ATTRIBUTE, target.objects.named(Usage.JAVA_RUNTIME))
|
||||
attribute(Category.CATEGORY_ATTRIBUTE, target.objects.named(Category.LIBRARY))
|
||||
attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, target.objects.named(LibraryElements.JAR))
|
||||
attribute(Bundling.BUNDLING_ATTRIBUTE, target.objects.named(Bundling.EXTERNAL))
|
||||
attribute(Obfuscation.OBFUSCATION_ATTRIBUTE, target.objects.named(Obfuscation.OBFUSCATED))
|
||||
}
|
||||
outgoing.artifact(reobfJar)
|
||||
}
|
||||
|
||||
target.afterEvaluate {
|
||||
// Manually check if cleanCache is a target, and skip setup.
|
||||
// Gradle moved NameMatcher to internal packages in 7.1, so this solution isn't ideal,
|
||||
// but it does work and allows using the cleanCache task without setting up the workspace first
|
||||
val cleaningCache = gradle.startParameter.taskRequests
|
||||
.any { req ->
|
||||
req.args.any { arg ->
|
||||
NameMatcher().find(arg, tasks.names) in setOf(cleanCache.name, cleanAll.name)
|
||||
}
|
||||
}
|
||||
if (cleaningCache) {
|
||||
return@afterEvaluate
|
||||
}
|
||||
|
||||
userdev.reobfArtifactConfiguration.get()
|
||||
.configure(this, reobfJar)
|
||||
|
||||
decorateJarManifests()
|
||||
|
||||
if (userdev.injectPaperRepository.get()) {
|
||||
repositories.maven(PAPER_MAVEN_REPO_URL) {
|
||||
content { onlyForConfigurations(DEV_BUNDLE_CONFIG) }
|
||||
}
|
||||
}
|
||||
|
||||
if (userdev.applyJunitExclusionRule.get()) {
|
||||
applyJunitExclusionRule()
|
||||
}
|
||||
|
||||
// Print a friendly error message if the dev bundle is missing before we call anything else that will try and resolve it
|
||||
checkForDevBundle()
|
||||
|
||||
configureRepositories(userdevSetup)
|
||||
|
||||
cleanSharedCaches(this, sharedCacheRoot)
|
||||
}
|
||||
}
|
||||
|
||||
private fun Project.decorateJarManifests() {
|
||||
val op = Action<Jar> {
|
||||
manifest {
|
||||
attributes(MAPPINGS_NAMESPACE_MANIFEST_KEY to DEOBF_NAMESPACE)
|
||||
}
|
||||
}
|
||||
tasks.named("jar", Jar::class, op)
|
||||
if ("shadowJar" in tasks.names) {
|
||||
tasks.named("shadowJar", Jar::class, op)
|
||||
}
|
||||
}
|
||||
|
||||
private fun Project.applyJunitExclusionRule() = dependencies {
|
||||
components {
|
||||
withModule<JunitExclusionRule>(JunitExclusionRule.TARGET)
|
||||
}
|
||||
}
|
||||
|
||||
private fun Project.configureRepositories(userdevSetup: UserdevSetup) = repositories {
|
||||
maven(userdevSetup.paramMappings.url) {
|
||||
name = PARAM_MAPPINGS_REPO_NAME
|
||||
content { onlyForConfigurations(PARAM_MAPPINGS_CONFIG) }
|
||||
}
|
||||
maven(userdevSetup.remapper.url) {
|
||||
name = REMAPPER_REPO_NAME
|
||||
content { onlyForConfigurations(REMAPPER_CONFIG) }
|
||||
}
|
||||
maven(userdevSetup.decompiler.url) {
|
||||
name = DECOMPILER_REPO_NAME
|
||||
content { onlyForConfigurations(DECOMPILER_CONFIG) }
|
||||
}
|
||||
for (repo in userdevSetup.libraryRepositories) {
|
||||
maven(repo)
|
||||
}
|
||||
|
||||
userdevSetup.addIvyRepository(project)
|
||||
}
|
||||
|
||||
private fun Project.checkForDevBundle() {
|
||||
val hasDevBundle = runCatching {
|
||||
!configurations.getByName(DEV_BUNDLE_CONFIG).isEmpty
|
||||
}
|
||||
if (hasDevBundle.isFailure || !hasDevBundle.getOrThrow()) {
|
||||
val message = "paperweight requires a development bundle to be added to the 'paperweightDevelopmentBundle' configuration, as" +
|
||||
" well as a repository to resolve it from in order to function. Use the paperweightDevBundle extension function to do this easily."
|
||||
throw PaperweightException(
|
||||
message,
|
||||
hasDevBundle.exceptionOrNull()?.let { PaperweightException("Failed to resolve dev bundle", it) }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun createConfigurations(
|
||||
target: Project,
|
||||
userdevSetup: Provider<UserdevSetup>
|
||||
) {
|
||||
target.configurations.register(DECOMPILER_CONFIG) {
|
||||
defaultDependencies {
|
||||
for (dep in userdevSetup.get().decompiler.coordinates) {
|
||||
add(target.dependencies.create(dep))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
target.configurations.register(PARAM_MAPPINGS_CONFIG) {
|
||||
defaultDependencies {
|
||||
for (dep in userdevSetup.get().paramMappings.coordinates) {
|
||||
add(target.dependencies.create(dep))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun makeRemapperConfig(name: String) {
|
||||
target.configurations.register(name) {
|
||||
defaultDependencies {
|
||||
for (dep in userdevSetup.get().remapper.coordinates) {
|
||||
// we use a fat jar for tiny-remapper, so we don't need its transitive deps
|
||||
val fatTiny = dep.contains(":tiny-remapper:") && dep.endsWith(":fat")
|
||||
add(
|
||||
target.dependencies.create(dep) {
|
||||
if (fatTiny) {
|
||||
isTransitive = false
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
makeRemapperConfig(REMAPPER_CONFIG)
|
||||
makeRemapperConfig(PLUGIN_REMAPPER_CONFIG)
|
||||
|
||||
val mojangMappedServerConfig = target.configurations.register(MOJANG_MAPPED_SERVER_CONFIG) {
|
||||
defaultDependencies {
|
||||
val ctx = createContext(target)
|
||||
userdevSetup.get().let { setup ->
|
||||
setup.createOrUpdateIvyRepository(ctx)
|
||||
setup.populateCompileConfiguration(ctx, this)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
target.plugins.withType<JavaPlugin>().configureEach {
|
||||
listOf(
|
||||
JavaPlugin.COMPILE_ONLY_CONFIGURATION_NAME,
|
||||
JavaPlugin.TEST_IMPLEMENTATION_CONFIGURATION_NAME
|
||||
).map(target.configurations::named).forEach { config ->
|
||||
config {
|
||||
extendsFrom(mojangMappedServerConfig.get())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
target.configurations.register(MOJANG_MAPPED_SERVER_RUNTIME_CONFIG) {
|
||||
defaultDependencies {
|
||||
val ctx = createContext(target)
|
||||
userdevSetup.get().let { setup ->
|
||||
setup.createOrUpdateIvyRepository(ctx)
|
||||
setup.populateRuntimeConfiguration(ctx, this)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun createContext(project: Project): SetupHandler.Context =
|
||||
SetupHandler.Context(project, workerExecutor, javaToolchainService)
|
||||
|
||||
private fun createSetup(target: Project, sharedCacheRoot: Path): UserdevSetup {
|
||||
val bundleConfig = target.configurations.named(DEV_BUNDLE_CONFIG)
|
||||
val devBundleZip = bundleConfig.map { it.singleFile }.convertToPath()
|
||||
val bundleHash = devBundleZip.sha256asHex()
|
||||
val cacheDir = if (!target.sharedCaches) {
|
||||
target.layout.cache
|
||||
} else {
|
||||
when (bundleConfig.get().dependencies.single()) {
|
||||
is ProjectDependency -> target.layout.cache
|
||||
|
||||
is ModuleDependency -> {
|
||||
val resolved =
|
||||
bundleConfig.get().incoming.resolutionResult.rootComponent.get().dependencies.single() as ResolvedDependencyResult
|
||||
val resolvedId = resolved.selected.id as ModuleComponentIdentifier
|
||||
sharedCacheRoot.resolve("module/${resolvedId.group}/${resolvedId.module}/${resolvedId.version}")
|
||||
}
|
||||
|
||||
else -> sharedCacheRoot.resolve("non-module/$bundleHash")
|
||||
}
|
||||
}
|
||||
|
||||
val serviceName = "paperweight-userdev:setupService:$bundleHash"
|
||||
return target.gradle.sharedServices
|
||||
.registerIfAbsent(serviceName, UserdevSetup::class) {
|
||||
parameters {
|
||||
cache.set(cacheDir)
|
||||
bundleZip.set(devBundleZip)
|
||||
bundleZipHash.set(bundleHash)
|
||||
downloadService.set(target.download)
|
||||
genSources.set(target.genSources)
|
||||
}
|
||||
}
|
||||
.get()
|
||||
}
|
||||
}
|
|
@ -1,263 +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.userdev
|
||||
|
||||
import io.papermc.paperweight.util.constants.*
|
||||
import org.gradle.api.Action
|
||||
import org.gradle.api.artifacts.ExternalModuleDependency
|
||||
import org.gradle.api.artifacts.MinimalExternalModuleDependency
|
||||
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
|
||||
) {
|
||||
/**
|
||||
* Adds a dependency on Paper'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 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
|
||||
*/
|
||||
@JvmOverloads
|
||||
fun paperDevBundle(
|
||||
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<ExternalModuleDependency> = nullAction()
|
||||
): ExternalModuleDependency {
|
||||
val dep = dependencies.create(group, artifactId, version, configuration, classifier, ext)
|
||||
configurationAction(dep)
|
||||
dependencies.add(devBundleConfigurationName, dep)
|
||||
return dep
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a dependency on Folia'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 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
|
||||
*/
|
||||
@JvmOverloads
|
||||
fun foliaDevBundle(
|
||||
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<ExternalModuleDependency> = nullAction()
|
||||
): ExternalModuleDependency {
|
||||
val dep = dependencies.create(group, artifactId, version, configuration, classifier, ext)
|
||||
configurationAction(dep)
|
||||
dependencies.add(devBundleConfigurationName, dep)
|
||||
return dep
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a dependency to the dev bundle [org.gradle.api.artifacts.Configuration].
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
@JvmOverloads
|
||||
fun devBundle(
|
||||
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<ExternalModuleDependency> = nullAction()
|
||||
): ExternalModuleDependency {
|
||||
val dep = dependencies.create(group, artifactId, version, configuration, classifier, ext)
|
||||
configurationAction(dep)
|
||||
dependencies.add(devBundleConfigurationName, dep)
|
||||
return dep
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a dependency to the [DEV_BUNDLE_CONFIG] configuration.
|
||||
*
|
||||
* Intended for use with Gradle version catalogs.
|
||||
*
|
||||
* @param bundle dev bundle dependency provider
|
||||
* @param configurationAction action configuring the dependency
|
||||
*/
|
||||
@JvmOverloads
|
||||
fun devBundle(
|
||||
bundle: Provider<MinimalExternalModuleDependency>,
|
||||
configurationAction: Action<ExternalModuleDependency> = nullAction()
|
||||
) {
|
||||
dependencies.addProvider(DEV_BUNDLE_CONFIG, bundle, configurationAction)
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a dependency on the Paper 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 paperDevBundle(
|
||||
version: Provider<String>,
|
||||
configurationAction: Action<ExternalModuleDependency> = nullAction()
|
||||
) {
|
||||
dependencies.addProvider(DEV_BUNDLE_CONFIG, version.map { "io.papermc.paper:dev-bundle:$it" }, configurationAction)
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a dependency on the Folia 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 foliaDevBundle(
|
||||
version: Provider<String>,
|
||||
configurationAction: Action<ExternalModuleDependency> = nullAction()
|
||||
) {
|
||||
dependencies.addProvider(DEV_BUNDLE_CONFIG, version.map { "dev.folia:dev-bundle:$it" }, configurationAction)
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a Folia dev bundle dependency without adding it to any configurations.
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
@JvmOverloads
|
||||
fun foliaDevBundleDependency(
|
||||
version: String? = null,
|
||||
group: String = "dev.folia",
|
||||
artifactId: String = "dev-bundle",
|
||||
configuration: String? = null,
|
||||
classifier: String? = null,
|
||||
ext: String? = null,
|
||||
configurationAction: Action<ExternalModuleDependency> = nullAction()
|
||||
): ExternalModuleDependency {
|
||||
val dep = dependencies.create(group, artifactId, version, configuration, classifier, ext)
|
||||
configurationAction(dep)
|
||||
return dep
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a Paper dev bundle dependency without adding it to any configurations.
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
@JvmOverloads
|
||||
fun paperDevBundleDependency(
|
||||
version: String? = null,
|
||||
group: String = "io.papermc.paper",
|
||||
artifactId: String = "dev-bundle",
|
||||
configuration: String? = null,
|
||||
classifier: String? = null,
|
||||
ext: String? = null,
|
||||
configurationAction: Action<ExternalModuleDependency> = nullAction()
|
||||
): ExternalModuleDependency {
|
||||
val dep = dependencies.create(group, artifactId, version, configuration, classifier, ext)
|
||||
configurationAction(dep)
|
||||
return dep
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a dev bundle dependency without adding it to any configurations.
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
@JvmOverloads
|
||||
fun devBundleDependency(
|
||||
group: String,
|
||||
version: String? = null,
|
||||
artifactId: String = "dev-bundle",
|
||||
configuration: String? = null,
|
||||
classifier: String? = null,
|
||||
ext: String? = null,
|
||||
configurationAction: Action<ExternalModuleDependency> = nullAction()
|
||||
): ExternalModuleDependency {
|
||||
val dep = dependencies.create(group, artifactId, version, configuration, classifier, ext)
|
||||
configurationAction(dep)
|
||||
return dep
|
||||
}
|
||||
|
||||
@Suppress("unchecked_cast")
|
||||
private fun <T> nullAction(): Action<T> {
|
||||
return NullAction as Action<T>
|
||||
}
|
||||
|
||||
private object NullAction : Action<Any> {
|
||||
override fun execute(t: Any) {}
|
||||
}
|
||||
}
|
|
@ -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.userdev
|
||||
|
||||
import io.papermc.paperweight.userdev.internal.setup.SetupHandler
|
||||
import io.papermc.paperweight.util.*
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.file.RegularFile
|
||||
import org.gradle.api.model.ObjectFactory
|
||||
import org.gradle.api.provider.Property
|
||||
import org.gradle.api.provider.Provider
|
||||
import org.gradle.jvm.toolchain.JavaToolchainService
|
||||
import org.gradle.kotlin.dsl.*
|
||||
import org.gradle.workers.WorkerExecutor
|
||||
|
||||
/**
|
||||
* Extension exposing configuration and other APIs for paperweight userdev.
|
||||
*/
|
||||
abstract class PaperweightUserExtension(
|
||||
project: Project,
|
||||
workerExecutor: WorkerExecutor,
|
||||
javaToolchainService: JavaToolchainService,
|
||||
setup: Provider<SetupHandler>,
|
||||
objects: ObjectFactory
|
||||
) {
|
||||
/**
|
||||
* Whether to inject the Paper maven repository for use by the dev bundle configuration.
|
||||
*
|
||||
* True by default to allow easily resolving Paper development bundles.
|
||||
*/
|
||||
val injectPaperRepository: Property<Boolean> = objects.property<Boolean>().convention(true)
|
||||
|
||||
/**
|
||||
* Whether to patch dependencies to exclude `junit:junit` from the transitive dependencies of `com.googlecode.json-simple:json-simple`.
|
||||
*
|
||||
* True by default to avoid `junit:junit` appearing on the `compileClasspath` with older versions of Paper.
|
||||
*/
|
||||
val applyJunitExclusionRule: Property<Boolean> = objects.property<Boolean>().convention(true)
|
||||
|
||||
/**
|
||||
* The [ReobfArtifactConfiguration] is responsible for setting the input and output jars for `reobfJar`,
|
||||
* as well as changing the classifiers of other jars (i.e. `jar` or `shadowJar`).
|
||||
*/
|
||||
val reobfArtifactConfiguration: Property<ReobfArtifactConfiguration> = objects.property<ReobfArtifactConfiguration>()
|
||||
.convention(ReobfArtifactConfiguration.REOBF_PRODUCTION)
|
||||
|
||||
/**
|
||||
* Provides a runnable Mojang mapped server jar, extracted from the current dev bundle.
|
||||
*/
|
||||
@Deprecated(
|
||||
message = "As of 1.18, the dev bundle no longer contains a runnable server jar. Use the mojangMappedServerRuntime configuration instead.",
|
||||
replaceWith = ReplaceWith("project.configurations.mojangMappedServerRuntime"),
|
||||
level = DeprecationLevel.WARNING
|
||||
)
|
||||
val mojangMappedServerJar: Provider<RegularFile> = objects.fileProperty().pathProvider(
|
||||
setup.map { it.serverJar(SetupHandler.Context(project, workerExecutor, javaToolchainService)) }
|
||||
).withDisallowChanges().withDisallowUnsafeRead()
|
||||
|
||||
/**
|
||||
* Provides the Minecraft version of the current dev bundle.
|
||||
*/
|
||||
val minecraftVersion: Provider<String> = objects.property<String>().value(
|
||||
setup.map { it.minecraftVersion }
|
||||
).withDisallowChanges()
|
||||
}
|
|
@ -1,91 +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.userdev
|
||||
|
||||
import io.papermc.paperweight.tasks.*
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.UnknownTaskException
|
||||
import org.gradle.api.plugins.BasePluginExtension
|
||||
import org.gradle.api.plugins.JavaPlugin
|
||||
import org.gradle.api.provider.Provider
|
||||
import org.gradle.api.tasks.TaskProvider
|
||||
import org.gradle.api.tasks.bundling.AbstractArchiveTask
|
||||
import org.gradle.kotlin.dsl.*
|
||||
|
||||
/**
|
||||
* Configures input/output for `reobfJar` and potentially changes classifiers of other jars.
|
||||
*/
|
||||
fun interface ReobfArtifactConfiguration {
|
||||
fun configure(project: Project, reobfJar: TaskProvider<RemapJar>)
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* Used when the reobfJar artifact is the main production output.
|
||||
*
|
||||
* Sets the `jar` classifier to `dev`, and the `shadowJar` classifier to `dev-all` if it exists.
|
||||
* The `reobfJar` will have no classifier. [BasePluginExtension.getArchivesName] is used to name
|
||||
* the `reobfJar`, falling back to the project name if it is not configured.
|
||||
*/
|
||||
@JvmStatic
|
||||
val REOBF_PRODUCTION: ReobfArtifactConfiguration = ReobfArtifactConfiguration { project, reobfJar ->
|
||||
val jar = project.tasks.named<AbstractArchiveTask>(JavaPlugin.JAR_TASK_NAME) {
|
||||
archiveClassifier.set("dev")
|
||||
}
|
||||
|
||||
val devJarTask = try {
|
||||
project.tasks.named<AbstractArchiveTask>("shadowJar") {
|
||||
archiveClassifier.set("dev-all")
|
||||
}
|
||||
} catch (ex: UnknownTaskException) {
|
||||
jar
|
||||
}
|
||||
|
||||
reobfJar {
|
||||
inputJar.set(devJarTask.flatMap { it.archiveFile })
|
||||
outputJar.convention(archivesName(project).flatMap { layout.buildDirectory.file("libs/$it-${project.version}.jar") })
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Used when the Mojang-mapped artifact (`jar`/`shadowJar`) is the main production output.
|
||||
*
|
||||
* Does not modify `jar` or `shadowJar` classifier, [BasePluginExtension.getArchivesName] is used to name
|
||||
* the `reobfJar`, falling back to the project name if it is not configured.
|
||||
*/
|
||||
@JvmStatic
|
||||
val MOJANG_PRODUCTION: ReobfArtifactConfiguration = ReobfArtifactConfiguration { project, reobfJar ->
|
||||
val devJarTask = try {
|
||||
project.tasks.named<AbstractArchiveTask>("shadowJar")
|
||||
} catch (ex: UnknownTaskException) {
|
||||
project.tasks.named<AbstractArchiveTask>(JavaPlugin.JAR_TASK_NAME)
|
||||
}
|
||||
reobfJar {
|
||||
inputJar.set(devJarTask.flatMap { it.archiveFile })
|
||||
outputJar.convention(archivesName(project).flatMap { layout.buildDirectory.file("libs/$it-${project.version}-reobf.jar") })
|
||||
}
|
||||
}
|
||||
|
||||
fun archivesName(project: Project): Provider<String> =
|
||||
project.extensions.findByType(BasePluginExtension::class)?.archivesName ?: project.provider { project.name }
|
||||
}
|
||||
}
|
|
@ -1,50 +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.userdev.attribute
|
||||
|
||||
import org.gradle.api.Named
|
||||
import org.gradle.api.attributes.Attribute
|
||||
|
||||
/**
|
||||
* Attribute representing the state of obfuscation.
|
||||
*/
|
||||
interface Obfuscation : Named {
|
||||
companion object {
|
||||
val OBFUSCATION_ATTRIBUTE = Attribute.of(
|
||||
"io.papermc.paperweight.obfuscation",
|
||||
Obfuscation::class.java
|
||||
)
|
||||
|
||||
// Note that we don't reference this in the project, but you can use it as a value to pick the
|
||||
// runtimeElements configuration instead of reobf.
|
||||
/**
|
||||
* No obfuscation, i.e. using human-readable names.
|
||||
*/
|
||||
const val NONE = "none"
|
||||
|
||||
/**
|
||||
* Obfuscated, i.e. using runtime names.
|
||||
*/
|
||||
const val OBFUSCATED = "obfuscated"
|
||||
}
|
||||
}
|
|
@ -1,83 +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
|
||||
*/
|
||||
|
||||
import io.papermc.paperweight.util.constants.*
|
||||
import org.gradle.api.artifacts.ExternalModuleDependency
|
||||
import org.gradle.kotlin.dsl.*
|
||||
|
||||
/**
|
||||
* Adds a dependency on Paper'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 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
|
||||
*/
|
||||
@Deprecated(
|
||||
message = "Replaced by extension methods",
|
||||
replaceWith = ReplaceWith(
|
||||
"paperweight.paperDevBundle"
|
||||
)
|
||||
)
|
||||
fun DependencyHandlerScope.paperDevBundle(
|
||||
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: ExternalModuleDependency.() -> Unit = {}
|
||||
): ExternalModuleDependency = devBundleConfigurationName(group, artifactId, version, configuration, classifier, ext, configurationAction)
|
||||
|
||||
/**
|
||||
* Adds a dependency to the dev bundle [org.gradle.api.artifacts.Configuration].
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
@Deprecated(
|
||||
message = "Replaced by extension methods",
|
||||
replaceWith = ReplaceWith(
|
||||
"paperweight.devBundle"
|
||||
)
|
||||
)
|
||||
fun DependencyHandlerScope.paperweightDevBundle(
|
||||
group: String,
|
||||
version: String? = null,
|
||||
artifactId: String = "dev-bundle",
|
||||
configuration: String? = null,
|
||||
classifier: String? = null,
|
||||
ext: String? = null,
|
||||
devBundleConfigurationName: String = DEV_BUNDLE_CONFIG,
|
||||
configurationAction: ExternalModuleDependency.() -> Unit = {}
|
||||
): ExternalModuleDependency = devBundleConfigurationName(group, artifactId, version, configuration, classifier, ext, configurationAction)
|
|
@ -1,47 +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.userdev.internal
|
||||
|
||||
import org.gradle.api.artifacts.CacheableRule
|
||||
import org.gradle.api.artifacts.ComponentMetadataContext
|
||||
import org.gradle.api.artifacts.ComponentMetadataRule
|
||||
|
||||
/**
|
||||
* Excludes `junit:junit` from the transitive dependencies of all variants.
|
||||
*/
|
||||
@CacheableRule
|
||||
abstract class JunitExclusionRule : ComponentMetadataRule {
|
||||
companion object {
|
||||
const val TARGET = "com.googlecode.json-simple:json-simple"
|
||||
}
|
||||
|
||||
override fun execute(ctx: ComponentMetadataContext) {
|
||||
ctx.details.allVariants {
|
||||
withDependencies {
|
||||
removeIf {
|
||||
it.group == "junit" && it.name == "junit"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,91 +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.userdev.internal.setup
|
||||
|
||||
import io.papermc.paperweight.PaperweightException
|
||||
import io.papermc.paperweight.tasks.*
|
||||
import io.papermc.paperweight.userdev.internal.setup.v2.DevBundleV2
|
||||
import io.papermc.paperweight.util.*
|
||||
import java.nio.file.Path
|
||||
import kotlin.io.path.*
|
||||
|
||||
private val supported = mapOf(
|
||||
2 to DevBundleV2.Config::class, // 1.17.1
|
||||
3 to GenerateDevBundle.DevBundleConfig::class, // up to 1.20.4
|
||||
4 to GenerateDevBundle.DevBundleConfig::class, // 1.20.5, early 1.20.6
|
||||
GenerateDevBundle.currentDataVersion to GenerateDevBundle.DevBundleConfig::class, // 1.20.6+ (nullable mojangApiCoordinates)
|
||||
)
|
||||
|
||||
data class ExtractedBundle<C>(
|
||||
val changed: Boolean,
|
||||
val config: C,
|
||||
val dataVersion: Int,
|
||||
val dir: Path,
|
||||
) {
|
||||
constructor(bundleChanged: Boolean, pair: Pair<C, Int>, dir: Path) :
|
||||
this(bundleChanged, pair.first, pair.second, dir)
|
||||
}
|
||||
|
||||
fun extractDevBundle(
|
||||
destinationDirectory: Path,
|
||||
devBundle: Path,
|
||||
newDevBundleHash: String
|
||||
): ExtractedBundle<Any> {
|
||||
val hashFile = destinationDirectory.resolve("current.sha256")
|
||||
|
||||
if (destinationDirectory.exists()) {
|
||||
val currentDevBundleHash = if (hashFile.isRegularFile()) hashFile.readText(Charsets.UTF_8) else ""
|
||||
|
||||
if (currentDevBundleHash.isNotBlank() && newDevBundleHash == currentDevBundleHash) {
|
||||
return ExtractedBundle(false, readDevBundle(destinationDirectory), destinationDirectory)
|
||||
}
|
||||
destinationDirectory.deleteRecursive()
|
||||
}
|
||||
destinationDirectory.createDirectories()
|
||||
|
||||
hashFile.writeText(newDevBundleHash, Charsets.UTF_8)
|
||||
devBundle.openZip().use { fs ->
|
||||
fs.getPath("/").copyRecursivelyTo(destinationDirectory)
|
||||
}
|
||||
|
||||
return ExtractedBundle(true, readDevBundle(destinationDirectory), destinationDirectory)
|
||||
}
|
||||
|
||||
private fun readDevBundle(
|
||||
extractedDevBundlePath: Path
|
||||
): Pair<Any, Int> {
|
||||
val dataVersion = extractedDevBundlePath.resolve("data-version.txt").readText().trim().toInt()
|
||||
if (dataVersion !in supported) {
|
||||
throw PaperweightException(
|
||||
"The paperweight development bundle you are attempting to use is of data version '$dataVersion', but" +
|
||||
" the currently running version of paperweight only supports data versions '$supported'."
|
||||
)
|
||||
}
|
||||
|
||||
val configClass = supported[dataVersion] ?: throw PaperweightException("Could not find config class for version $dataVersion?")
|
||||
val configFile = extractedDevBundlePath.resolve("config.json")
|
||||
val config: Any = configFile.bufferedReader(Charsets.UTF_8).use { reader ->
|
||||
gson.fromJson(reader, configClass.java)
|
||||
}
|
||||
return config to dataVersion
|
||||
}
|
|
@ -1,91 +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.userdev.internal.setup
|
||||
|
||||
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.v2.SetupHandlerImplV2
|
||||
import io.papermc.paperweight.util.*
|
||||
import java.nio.file.Path
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.artifacts.DependencySet
|
||||
import org.gradle.api.artifacts.repositories.IvyArtifactRepository
|
||||
import org.gradle.jvm.toolchain.JavaLauncher
|
||||
import org.gradle.jvm.toolchain.JavaToolchainService
|
||||
import org.gradle.workers.WorkerExecutor
|
||||
|
||||
interface SetupHandler {
|
||||
fun createOrUpdateIvyRepository(context: Context)
|
||||
|
||||
fun configureIvyRepo(repo: IvyArtifactRepository)
|
||||
|
||||
fun populateCompileConfiguration(context: Context, dependencySet: DependencySet)
|
||||
|
||||
fun populateRuntimeConfiguration(context: Context, dependencySet: DependencySet)
|
||||
|
||||
fun serverJar(context: Context): Path
|
||||
|
||||
val serverJar: Path
|
||||
|
||||
val reobfMappings: Path
|
||||
|
||||
val minecraftVersion: String
|
||||
|
||||
val pluginRemapArgs: List<String>
|
||||
|
||||
val paramMappings: MavenDep
|
||||
|
||||
val decompiler: MavenDep
|
||||
|
||||
val remapper: MavenDep
|
||||
|
||||
val libraryRepositories: List<String>
|
||||
|
||||
data class Context(
|
||||
val project: Project,
|
||||
val workerExecutor: WorkerExecutor,
|
||||
val javaToolchainService: JavaToolchainService
|
||||
) {
|
||||
val defaultJavaLauncher: JavaLauncher
|
||||
get() = javaToolchainService.defaultJavaLauncher(project).get()
|
||||
}
|
||||
|
||||
companion object {
|
||||
@Suppress("unchecked_cast")
|
||||
fun create(
|
||||
parameters: UserdevSetup.Parameters,
|
||||
extractedBundle: ExtractedBundle<Any>
|
||||
): SetupHandler = when (extractedBundle.config) {
|
||||
is GenerateDevBundle.DevBundleConfig -> SetupHandlerImpl(
|
||||
parameters,
|
||||
extractedBundle as ExtractedBundle<GenerateDevBundle.DevBundleConfig>,
|
||||
)
|
||||
is DevBundleV2.Config -> SetupHandlerImplV2(
|
||||
parameters,
|
||||
extractedBundle as ExtractedBundle<DevBundleV2.Config>
|
||||
)
|
||||
else -> throw PaperweightException("Unknown dev bundle config type: ${extractedBundle.config::class.java.typeName}")
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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.userdev.internal.setup
|
||||
|
||||
import io.papermc.paperweight.tasks.*
|
||||
import io.papermc.paperweight.userdev.internal.setup.step.*
|
||||
import io.papermc.paperweight.userdev.internal.setup.util.*
|
||||
import io.papermc.paperweight.util.*
|
||||
import io.papermc.paperweight.util.constants.*
|
||||
import java.nio.file.Path
|
||||
import org.gradle.api.artifacts.DependencySet
|
||||
import org.gradle.api.artifacts.ExternalModuleDependency
|
||||
import org.gradle.api.artifacts.repositories.IvyArtifactRepository
|
||||
|
||||
class SetupHandlerImpl(
|
||||
private val parameters: UserdevSetup.Parameters,
|
||||
private val bundle: ExtractedBundle<GenerateDevBundle.DevBundleConfig>,
|
||||
private val cache: Path = parameters.cache.path,
|
||||
) : SetupHandler {
|
||||
private val vanillaSteps by lazy {
|
||||
VanillaSteps(
|
||||
bundle.config.minecraftVersion,
|
||||
cache,
|
||||
parameters.downloadService.get(),
|
||||
bundle.changed,
|
||||
)
|
||||
}
|
||||
private val vanillaServerJar: Path = cache.resolve(paperSetupOutput("vanillaServerJar", "jar"))
|
||||
private val minecraftLibraryJars = cache.resolve(MINECRAFT_JARS_PATH)
|
||||
private val filteredVanillaServerJar: Path = cache.resolve(paperSetupOutput("filterJar", "jar"))
|
||||
private val mojangPlusYarnMappings: Path = cache.resolve(MOJANG_YARN_MAPPINGS)
|
||||
private val mappedMinecraftServerJar: Path = cache.resolve(paperSetupOutput("mappedMinecraftServerJar", "jar"))
|
||||
private val fixedMinecraftServerJar: Path = cache.resolve(paperSetupOutput("fixedMinecraftServerJar", "jar"))
|
||||
private val accessTransformedServerJar: Path = cache.resolve(paperSetupOutput("accessTransformedServerJar", "jar"))
|
||||
private val decompiledMinecraftServerJar: Path = cache.resolve(paperSetupOutput("decompileMinecraftServerJar", "jar"))
|
||||
private val patchedSourcesJar: Path = cache.resolve(paperSetupOutput("patchedSourcesJar", "jar"))
|
||||
private val mojangMappedPaperJar: Path = cache.resolve(paperSetupOutput("applyMojangMappedPaperclipPatch", "jar"))
|
||||
|
||||
private fun minecraftLibraryJars(): List<Path> = minecraftLibraryJars.filesMatchingRecursive("*.jar")
|
||||
|
||||
private fun generateSources(context: SetupHandler.Context) {
|
||||
vanillaSteps.downloadVanillaServerJar()
|
||||
|
||||
val extractStep = createExtractFromBundlerStep()
|
||||
|
||||
val filterVanillaJarStep = FilterVanillaJar(vanillaServerJar, bundle.config.buildData.vanillaJarIncludes, filteredVanillaServerJar)
|
||||
|
||||
val genMappingsStep = GenerateMappingsStep.create(
|
||||
context,
|
||||
vanillaSteps,
|
||||
filteredVanillaServerJar,
|
||||
::minecraftLibraryJars,
|
||||
mojangPlusYarnMappings,
|
||||
)
|
||||
|
||||
val remapMinecraftStep = RemapMinecraft.create(
|
||||
context,
|
||||
bundle.config.buildData.minecraftRemapArgs,
|
||||
filteredVanillaServerJar,
|
||||
::minecraftLibraryJars,
|
||||
mojangPlusYarnMappings,
|
||||
mappedMinecraftServerJar,
|
||||
cache,
|
||||
)
|
||||
|
||||
val fixStep = FixMinecraftJar(mappedMinecraftServerJar, fixedMinecraftServerJar, vanillaServerJar)
|
||||
|
||||
val atStep = AccessTransformMinecraft(
|
||||
bundle.dir.resolve(bundle.config.buildData.accessTransformFile),
|
||||
fixedMinecraftServerJar,
|
||||
accessTransformedServerJar,
|
||||
)
|
||||
|
||||
val decomp = DecompileMinecraft.create(
|
||||
context,
|
||||
accessTransformedServerJar,
|
||||
decompiledMinecraftServerJar,
|
||||
cache,
|
||||
::minecraftLibraryJars,
|
||||
bundle.config.decompile.args,
|
||||
)
|
||||
|
||||
val applyDevBundlePatchesStep = ApplyDevBundlePatches(
|
||||
decompiledMinecraftServerJar,
|
||||
bundle.dir.resolve(bundle.config.patchDir),
|
||||
patchedSourcesJar
|
||||
)
|
||||
|
||||
StepExecutor.executeSteps(
|
||||
bundle.changed,
|
||||
context,
|
||||
extractStep,
|
||||
filterVanillaJarStep,
|
||||
genMappingsStep,
|
||||
remapMinecraftStep,
|
||||
fixStep,
|
||||
atStep,
|
||||
decomp,
|
||||
applyDevBundlePatchesStep,
|
||||
)
|
||||
}
|
||||
|
||||
// This can be called when a user queries the server jar provider in
|
||||
// PaperweightUserExtension, possibly by a task running in a separate
|
||||
// thread to dependency resolution.
|
||||
@Synchronized
|
||||
private fun applyMojangMappedPaperclipPatch(context: SetupHandler.Context) {
|
||||
if (setupCompleted) {
|
||||
return
|
||||
}
|
||||
|
||||
lockSetup(cache, true) {
|
||||
StepExecutor.executeStep(
|
||||
context,
|
||||
RunPaperclip(
|
||||
bundle.dir.resolve(bundle.config.buildData.mojangMappedPaperclipFile),
|
||||
mojangMappedPaperJar,
|
||||
vanillaSteps.mojangJar,
|
||||
minecraftVersion,
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private var setupCompleted = false
|
||||
|
||||
@Synchronized
|
||||
override fun createOrUpdateIvyRepository(context: SetupHandler.Context) {
|
||||
if (setupCompleted) {
|
||||
return
|
||||
}
|
||||
|
||||
lockSetup(cache) {
|
||||
createOrUpdateIvyRepositoryDirect(context)
|
||||
}
|
||||
}
|
||||
|
||||
private fun createOrUpdateIvyRepositoryDirect(context: SetupHandler.Context) {
|
||||
val source = if (parameters.genSources.get()) {
|
||||
generateSources(context)
|
||||
patchedSourcesJar
|
||||
} else {
|
||||
vanillaSteps.downloadVanillaServerJar()
|
||||
StepExecutor.executeStep(context, createExtractFromBundlerStep())
|
||||
null
|
||||
}
|
||||
|
||||
applyMojangMappedPaperclipPatch(context)
|
||||
|
||||
val deps = mutableListOf<String>()
|
||||
deps.addAll(bundle.config.buildData.compileDependencies)
|
||||
deps.add(bundle.config.apiCoordinates)
|
||||
bundle.config.mojangApiCoordinates?.let { deps.add(it) }
|
||||
installPaperServer(
|
||||
cache,
|
||||
bundle.config.mappedServerCoordinates,
|
||||
deps,
|
||||
mojangMappedPaperJar,
|
||||
source,
|
||||
minecraftVersion,
|
||||
)
|
||||
|
||||
setupCompleted = true
|
||||
}
|
||||
|
||||
override fun configureIvyRepo(repo: IvyArtifactRepository) {
|
||||
repo.content {
|
||||
includeFromDependencyNotation(bundle.config.mappedServerCoordinates)
|
||||
}
|
||||
}
|
||||
|
||||
override fun populateCompileConfiguration(context: SetupHandler.Context, dependencySet: DependencySet) {
|
||||
dependencySet.add(context.project.dependencies.create(bundle.config.mappedServerCoordinates))
|
||||
}
|
||||
|
||||
override fun populateRuntimeConfiguration(context: SetupHandler.Context, dependencySet: DependencySet) {
|
||||
listOfNotNull(
|
||||
bundle.config.mappedServerCoordinates,
|
||||
bundle.config.apiCoordinates,
|
||||
bundle.config.mojangApiCoordinates
|
||||
).forEach { coordinate ->
|
||||
val dep = context.project.dependencies.create(coordinate).also {
|
||||
(it as ExternalModuleDependency).isTransitive = false
|
||||
}
|
||||
dependencySet.add(dep)
|
||||
}
|
||||
|
||||
for (coordinates in bundle.config.buildData.runtimeDependencies) {
|
||||
val dep = context.project.dependencies.create(coordinates).also {
|
||||
(it as ExternalModuleDependency).isTransitive = false
|
||||
}
|
||||
dependencySet.add(dep)
|
||||
}
|
||||
}
|
||||
|
||||
override fun serverJar(context: SetupHandler.Context): Path {
|
||||
applyMojangMappedPaperclipPatch(context)
|
||||
return mojangMappedPaperJar
|
||||
}
|
||||
|
||||
override val serverJar: Path
|
||||
get() = mojangMappedPaperJar
|
||||
|
||||
override val reobfMappings: Path
|
||||
get() = bundle.dir.resolve(bundle.config.buildData.reobfMappingsFile)
|
||||
|
||||
override val minecraftVersion: String
|
||||
get() = bundle.config.minecraftVersion
|
||||
|
||||
override val pluginRemapArgs: List<String>
|
||||
get() = bundle.config.buildData.pluginRemapArgs
|
||||
|
||||
override val paramMappings: MavenDep
|
||||
get() = bundle.config.buildData.paramMappings
|
||||
|
||||
override val decompiler: MavenDep
|
||||
get() = bundle.config.decompile.dep
|
||||
|
||||
override val remapper: MavenDep
|
||||
get() = bundle.config.remapper
|
||||
|
||||
override val libraryRepositories: List<String>
|
||||
get() = bundle.config.buildData.libraryRepositories
|
||||
|
||||
private fun createExtractFromBundlerStep(): ExtractFromBundlerStep = ExtractFromBundlerStep(
|
||||
cache,
|
||||
vanillaSteps,
|
||||
vanillaServerJar,
|
||||
minecraftLibraryJars,
|
||||
::minecraftLibraryJars
|
||||
)
|
||||
|
||||
private class ExtractFromBundlerStep(
|
||||
cache: Path,
|
||||
private val vanillaSteps: VanillaSteps,
|
||||
private val vanillaServerJar: Path,
|
||||
private val minecraftLibraryJars: Path,
|
||||
private val listMinecraftLibraryJars: () -> List<Path>,
|
||||
) : SetupStep {
|
||||
override val name: String = "extract libraries and server from downloaded jar"
|
||||
|
||||
override val hashFile: Path = cache.resolve(paperSetupOutput("extractFromServerBundler", "hashes"))
|
||||
|
||||
override fun run(context: SetupHandler.Context) {
|
||||
ServerBundler.extractFromBundler(
|
||||
vanillaSteps.mojangJar,
|
||||
vanillaServerJar,
|
||||
minecraftLibraryJars,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
)
|
||||
}
|
||||
|
||||
override fun touchHashFunctionBuilder(builder: HashFunctionBuilder) {
|
||||
builder.include(vanillaSteps.mojangJar, vanillaServerJar)
|
||||
builder.include(listMinecraftLibraryJars())
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,124 +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.userdev.internal.setup
|
||||
|
||||
import io.papermc.paperweight.DownloadService
|
||||
import io.papermc.paperweight.userdev.internal.setup.util.*
|
||||
import io.papermc.paperweight.util.*
|
||||
import io.papermc.paperweight.util.constants.*
|
||||
import java.nio.file.Path
|
||||
import kotlin.io.path.*
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.artifacts.DependencySet
|
||||
import org.gradle.api.artifacts.repositories.IvyArtifactRepository
|
||||
import org.gradle.api.file.RegularFileProperty
|
||||
import org.gradle.api.logging.Logger
|
||||
import org.gradle.api.logging.Logging
|
||||
import org.gradle.api.provider.Property
|
||||
import org.gradle.api.services.BuildService
|
||||
import org.gradle.api.services.BuildServiceParameters
|
||||
import org.gradle.kotlin.dsl.*
|
||||
|
||||
abstract class UserdevSetup : BuildService<UserdevSetup.Parameters>, SetupHandler {
|
||||
|
||||
companion object {
|
||||
val LOGGER: Logger = Logging.getLogger(UserdevSetup::class.java)
|
||||
}
|
||||
|
||||
interface Parameters : BuildServiceParameters {
|
||||
val bundleZip: RegularFileProperty
|
||||
val bundleZipHash: Property<String>
|
||||
val cache: RegularFileProperty
|
||||
val downloadService: Property<DownloadService>
|
||||
val genSources: Property<Boolean>
|
||||
}
|
||||
|
||||
private val extractDevBundle: ExtractedBundle<Any> = lockSetup(parameters.cache.path) {
|
||||
val extract = extractDevBundle(
|
||||
parameters.cache.path.resolve(paperSetupOutput("extractDevBundle", "dir")),
|
||||
parameters.bundleZip.path,
|
||||
parameters.bundleZipHash.get()
|
||||
)
|
||||
lastUsedFile(parameters.cache.path).writeText(System.currentTimeMillis().toString())
|
||||
extract
|
||||
}
|
||||
|
||||
private val setup = createSetup()
|
||||
|
||||
private fun createSetup(): SetupHandler =
|
||||
SetupHandler.create(parameters, extractDevBundle)
|
||||
|
||||
fun addIvyRepository(project: Project) {
|
||||
project.repositories {
|
||||
setupIvyRepository(parameters.cache.path.resolve(IVY_REPOSITORY)) {
|
||||
configureIvyRepo(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// begin delegate to setup
|
||||
override fun createOrUpdateIvyRepository(context: SetupHandler.Context) {
|
||||
setup.createOrUpdateIvyRepository(context)
|
||||
}
|
||||
|
||||
override fun configureIvyRepo(repo: IvyArtifactRepository) {
|
||||
setup.configureIvyRepo(repo)
|
||||
}
|
||||
|
||||
override fun populateCompileConfiguration(context: SetupHandler.Context, dependencySet: DependencySet) {
|
||||
setup.populateCompileConfiguration(context, dependencySet)
|
||||
}
|
||||
|
||||
override fun populateRuntimeConfiguration(context: SetupHandler.Context, dependencySet: DependencySet) {
|
||||
setup.populateRuntimeConfiguration(context, dependencySet)
|
||||
}
|
||||
|
||||
override fun serverJar(context: SetupHandler.Context): Path {
|
||||
return setup.serverJar(context)
|
||||
}
|
||||
|
||||
override val serverJar: Path
|
||||
get() = setup.serverJar
|
||||
|
||||
override val reobfMappings: Path
|
||||
get() = setup.reobfMappings
|
||||
|
||||
override val minecraftVersion: String
|
||||
get() = setup.minecraftVersion
|
||||
|
||||
override val pluginRemapArgs: List<String>
|
||||
get() = setup.pluginRemapArgs
|
||||
|
||||
override val paramMappings: MavenDep
|
||||
get() = setup.paramMappings
|
||||
|
||||
override val decompiler: MavenDep
|
||||
get() = setup.decompiler
|
||||
|
||||
override val remapper: MavenDep
|
||||
get() = setup.remapper
|
||||
|
||||
override val libraryRepositories: List<String>
|
||||
get() = setup.libraryRepositories
|
||||
// end delegate to setup
|
||||
}
|
|
@ -1,48 +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.userdev.internal.setup.step
|
||||
|
||||
import io.papermc.paperweight.tasks.*
|
||||
import io.papermc.paperweight.userdev.internal.setup.SetupHandler
|
||||
import io.papermc.paperweight.userdev.internal.setup.util.siblingHashesFile
|
||||
import java.nio.file.Path
|
||||
|
||||
class AccessTransformMinecraft(
|
||||
@Input private val at: Path,
|
||||
@Input private val inputJar: Path,
|
||||
@Output private val outputJar: Path,
|
||||
) : SetupStep {
|
||||
override val name: String = "access transform minecraft server jar"
|
||||
|
||||
override val hashFile: Path = outputJar.siblingHashesFile()
|
||||
|
||||
override fun run(context: SetupHandler.Context) {
|
||||
applyAccessTransform(
|
||||
inputJarPath = inputJar,
|
||||
outputJarPath = outputJar,
|
||||
atFilePath = at,
|
||||
workerExecutor = context.workerExecutor,
|
||||
launcher = context.defaultJavaLauncher
|
||||
).await()
|
||||
}
|
||||
}
|
|
@ -1,99 +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.userdev.internal.setup.step
|
||||
|
||||
import codechicken.diffpatch.cli.PatchOperation
|
||||
import codechicken.diffpatch.util.LogLevel
|
||||
import codechicken.diffpatch.util.archiver.ArchiveFormat
|
||||
import io.papermc.paperweight.PaperweightException
|
||||
import io.papermc.paperweight.userdev.internal.setup.SetupHandler
|
||||
import io.papermc.paperweight.userdev.internal.setup.util.HashFunctionBuilder
|
||||
import io.papermc.paperweight.userdev.internal.setup.util.hashDirectory
|
||||
import io.papermc.paperweight.userdev.internal.setup.util.siblingHashesFile
|
||||
import io.papermc.paperweight.userdev.internal.setup.util.siblingLogFile
|
||||
import io.papermc.paperweight.util.*
|
||||
import java.io.PrintStream
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Path
|
||||
import kotlin.io.path.*
|
||||
import kotlin.streams.asSequence
|
||||
|
||||
class ApplyDevBundlePatches(
|
||||
@Input private val decompiledJar: Path,
|
||||
private val devBundlePatches: Path,
|
||||
@Output private val outputJar: Path,
|
||||
) : SetupStep {
|
||||
override val name: String = "apply patches to decompiled jar"
|
||||
|
||||
override val hashFile: Path = outputJar.siblingHashesFile()
|
||||
|
||||
override fun run(context: SetupHandler.Context) {
|
||||
val tempPatchDir = findOutputDir(outputJar)
|
||||
val outputDir = findOutputDir(outputJar)
|
||||
val log = outputJar.siblingLogFile()
|
||||
|
||||
try {
|
||||
val (patches, newFiles) = Files.walk(devBundlePatches).use { stream ->
|
||||
stream.asSequence()
|
||||
.filter { it.isRegularFile() }
|
||||
.partition { it.name.endsWith(".patch") }
|
||||
}
|
||||
for (patch in patches) {
|
||||
relativeCopy(devBundlePatches, patch, tempPatchDir)
|
||||
}
|
||||
|
||||
ensureDeleted(log)
|
||||
PrintStream(log.toFile(), Charsets.UTF_8).use { logOut ->
|
||||
val op = PatchOperation.builder()
|
||||
.logTo(logOut)
|
||||
.level(LogLevel.ALL)
|
||||
.summary(true)
|
||||
.basePath(decompiledJar, ArchiveFormat.ZIP)
|
||||
.patchesPath(tempPatchDir)
|
||||
.outputPath(outputDir)
|
||||
.build()
|
||||
try {
|
||||
op.operate().throwOnError()
|
||||
} catch (ex: Exception) {
|
||||
throw PaperweightException(
|
||||
"Failed to apply dev bundle patches. See the log file at '${log.toFile()}' for more details.",
|
||||
ex
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
for (file in newFiles) {
|
||||
relativeCopy(devBundlePatches, file, outputDir)
|
||||
}
|
||||
|
||||
ensureDeleted(outputJar)
|
||||
zip(outputDir, outputJar)
|
||||
} finally {
|
||||
ensureDeleted(outputDir, tempPatchDir)
|
||||
}
|
||||
}
|
||||
|
||||
override fun touchHashFunctionBuilder(builder: HashFunctionBuilder) {
|
||||
builder.include(hashDirectory(devBundlePatches))
|
||||
}
|
||||
}
|
|
@ -1,78 +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.userdev.internal.setup.step
|
||||
|
||||
import io.papermc.paperweight.tasks.runDecompiler
|
||||
import io.papermc.paperweight.userdev.internal.setup.SetupHandler
|
||||
import io.papermc.paperweight.userdev.internal.setup.util.HashFunctionBuilder
|
||||
import io.papermc.paperweight.userdev.internal.setup.util.siblingHashesFile
|
||||
import io.papermc.paperweight.userdev.internal.setup.util.siblingLogFile
|
||||
import io.papermc.paperweight.util.constants.DECOMPILER_CONFIG
|
||||
import java.nio.file.Path
|
||||
import org.gradle.api.artifacts.Configuration
|
||||
|
||||
class DecompileMinecraft(
|
||||
@Input private val inputJar: Path,
|
||||
@Output private val outputJar: Path,
|
||||
private val cache: Path,
|
||||
private val minecraftLibraryJars: () -> List<Path>,
|
||||
@Input private val decompileArgs: List<String>,
|
||||
private val decompiler: Configuration,
|
||||
) : SetupStep {
|
||||
override val name: String = "decompile transformed minecraft server jar"
|
||||
|
||||
override val hashFile: Path = outputJar.siblingHashesFile()
|
||||
|
||||
override fun run(context: SetupHandler.Context) {
|
||||
runDecompiler(
|
||||
argsList = decompileArgs,
|
||||
logFile = outputJar.siblingLogFile(),
|
||||
workingDir = cache,
|
||||
executable = decompiler,
|
||||
inputJar = inputJar,
|
||||
libraries = minecraftLibraryJars(),
|
||||
outputJar = outputJar,
|
||||
javaLauncher = context.defaultJavaLauncher
|
||||
)
|
||||
}
|
||||
|
||||
override fun touchHashFunctionBuilder(builder: HashFunctionBuilder) {
|
||||
builder.include(minecraftLibraryJars())
|
||||
builder.include(decompiler.map { it.toPath() })
|
||||
builder.includePaperweightHash = false
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun create(
|
||||
context: SetupHandler.Context,
|
||||
inputJar: Path,
|
||||
outputJar: Path,
|
||||
cache: Path,
|
||||
minecraftLibraryJars: () -> List<Path>,
|
||||
decompileArgs: List<String>,
|
||||
): DecompileMinecraft {
|
||||
val decompiler = context.project.configurations.getByName(DECOMPILER_CONFIG).also { it.resolve() } // resolve decompiler
|
||||
return DecompileMinecraft(inputJar, outputJar, cache, minecraftLibraryJars, decompileArgs, decompiler)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,120 +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.userdev.internal.setup.step
|
||||
|
||||
import io.papermc.paperweight.userdev.internal.setup.SetupHandler
|
||||
import io.papermc.paperweight.userdev.internal.setup.util.HashFunctionBuilder
|
||||
import io.papermc.paperweight.userdev.internal.setup.util.siblingHashesFile
|
||||
import io.papermc.paperweight.util.*
|
||||
import java.nio.file.Path
|
||||
import kotlin.io.path.isRegularFile
|
||||
import kotlin.io.path.name
|
||||
import kotlin.io.path.pathString
|
||||
|
||||
class FilterPaperShadowJar(
|
||||
@Input private val sourcesJar: Path,
|
||||
@Input private val inputJar: Path,
|
||||
@Output private val outputJar: Path,
|
||||
private val relocations: List<Relocation>,
|
||||
) : SetupStep {
|
||||
override val name: String = "filter mojang mapped paper jar"
|
||||
|
||||
override val hashFile: Path = outputJar.siblingHashesFile()
|
||||
|
||||
override fun run(context: SetupHandler.Context) {
|
||||
filterPaperJar(sourcesJar, inputJar, outputJar, relocations)
|
||||
}
|
||||
|
||||
override fun touchHashFunctionBuilder(builder: HashFunctionBuilder) {
|
||||
builder.include(gson.toJson(relocations))
|
||||
}
|
||||
|
||||
private fun filterPaperJar(
|
||||
sourcesJar: Path,
|
||||
inputJar: Path,
|
||||
outputJar: Path,
|
||||
relocations: List<Relocation>
|
||||
) {
|
||||
val includes = arrayListOf<String>()
|
||||
// Include relocated packages
|
||||
for (relocation in relocations.map { RelocationWrapper(it) }) {
|
||||
includes += '/' + relocation.toSlash + "/**"
|
||||
for (exclude in relocation.relocation.excludes) {
|
||||
includes += '/' + exclude.replace('.', '/')
|
||||
}
|
||||
}
|
||||
|
||||
val includedFiles = collectIncludes(sourcesJar, inputJar)
|
||||
filterJar(
|
||||
inputJar,
|
||||
outputJar,
|
||||
includes
|
||||
) { path ->
|
||||
val str = path.pathString
|
||||
if (str.contains('$')) {
|
||||
includedFiles.contains(str.split("$")[0] + ".class")
|
||||
} else {
|
||||
includedFiles.contains(str)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun collectIncludes(
|
||||
sourcesJar: Path,
|
||||
inputJar: Path
|
||||
): Set<String> {
|
||||
val extraIncludes = hashSetOf<String>()
|
||||
|
||||
// Include all files we have sources for
|
||||
iterateJar(sourcesJar) { entry ->
|
||||
if (entry.isRegularFile()) {
|
||||
val string = entry.pathString
|
||||
extraIncludes += if (string.endsWith(".java")) {
|
||||
string.substringBeforeLast(".java") + ".class"
|
||||
} else {
|
||||
string
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Include non-class resource files from server jar
|
||||
iterateJar(inputJar) { entry ->
|
||||
if (entry.isRegularFile() && !entry.name.endsWith(".class")) {
|
||||
extraIncludes += entry.pathString
|
||||
}
|
||||
}
|
||||
|
||||
return extraIncludes
|
||||
}
|
||||
|
||||
private fun iterateJar(
|
||||
jar: Path,
|
||||
visitor: (Path) -> Unit
|
||||
) = jar.openZip().use { fs ->
|
||||
fs.walk().use { stream ->
|
||||
stream.forEach { path ->
|
||||
visitor(path)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,42 +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.userdev.internal.setup.step
|
||||
|
||||
import io.papermc.paperweight.userdev.internal.setup.SetupHandler
|
||||
import io.papermc.paperweight.userdev.internal.setup.util.siblingHashesFile
|
||||
import io.papermc.paperweight.util.*
|
||||
import java.nio.file.Path
|
||||
|
||||
class FilterVanillaJar(
|
||||
@Input private val vanillaJar: Path,
|
||||
@Input private val includes: List<String>,
|
||||
@Output private val outputJar: Path,
|
||||
) : SetupStep {
|
||||
override val name: String = "filter vanilla server jar"
|
||||
|
||||
override val hashFile: Path = outputJar.siblingHashesFile()
|
||||
|
||||
override fun run(context: SetupHandler.Context) {
|
||||
filterJar(vanillaJar, outputJar, includes)
|
||||
}
|
||||
}
|
|
@ -1,50 +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.userdev.internal.setup.step
|
||||
|
||||
import io.papermc.paperweight.tasks.*
|
||||
import io.papermc.paperweight.userdev.internal.setup.SetupHandler
|
||||
import io.papermc.paperweight.userdev.internal.setup.util.siblingHashesFile
|
||||
import java.nio.file.Path
|
||||
|
||||
class FixMinecraftJar(
|
||||
@Input private val inputJar: Path,
|
||||
@Output private val outputJar: Path,
|
||||
@Input private val vanillaServerJar: Path,
|
||||
private val useLegacyParameterAnnotationFixer: Boolean = false,
|
||||
) : SetupStep {
|
||||
override val name: String = "fix minecraft server jar"
|
||||
|
||||
override val hashFile: Path = outputJar.siblingHashesFile()
|
||||
|
||||
override fun run(context: SetupHandler.Context) {
|
||||
fixJar(
|
||||
workerExecutor = context.workerExecutor,
|
||||
launcher = context.defaultJavaLauncher,
|
||||
vanillaJarPath = vanillaServerJar,
|
||||
inputJarPath = inputJar,
|
||||
outputJarPath = outputJar,
|
||||
useLegacyParameterAnnotationFixer = useLegacyParameterAnnotationFixer
|
||||
).await()
|
||||
}
|
||||
}
|
|
@ -1,77 +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.userdev.internal.setup.step
|
||||
|
||||
import io.papermc.paperweight.tasks.*
|
||||
import io.papermc.paperweight.userdev.internal.setup.SetupHandler
|
||||
import io.papermc.paperweight.userdev.internal.setup.util.HashFunctionBuilder
|
||||
import io.papermc.paperweight.userdev.internal.setup.util.siblingHashesFile
|
||||
import io.papermc.paperweight.util.*
|
||||
import io.papermc.paperweight.util.constants.*
|
||||
import java.nio.file.Path
|
||||
|
||||
class GenerateMappingsStep(
|
||||
private val vanillaSteps: VanillaSteps,
|
||||
@Input private val filteredVanillaJar: Path,
|
||||
@Input private val paramMappings: Path,
|
||||
private val minecraftLibraryJars: () -> List<Path>,
|
||||
@Output private val outputMappings: Path,
|
||||
) : SetupStep {
|
||||
override val name: String = "generate mappings"
|
||||
|
||||
override val hashFile: Path = outputMappings.siblingHashesFile()
|
||||
|
||||
override fun run(context: SetupHandler.Context) {
|
||||
generateMappings(
|
||||
vanillaJarPath = filteredVanillaJar,
|
||||
libraryPaths = minecraftLibraryJars(),
|
||||
vanillaMappingsPath = vanillaSteps.serverMappings,
|
||||
paramMappingsPath = paramMappings,
|
||||
outputMappingsPath = outputMappings,
|
||||
workerExecutor = context.workerExecutor,
|
||||
launcher = context.defaultJavaLauncher
|
||||
).await()
|
||||
}
|
||||
|
||||
override fun touchHashFunctionBuilder(builder: HashFunctionBuilder) {
|
||||
builder.include(minecraftLibraryJars())
|
||||
builder.include(vanillaSteps.serverMappings)
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun create(
|
||||
context: SetupHandler.Context,
|
||||
vanillaSteps: VanillaSteps,
|
||||
filteredVanillaJar: Path,
|
||||
minecraftLibraryJars: () -> List<Path>,
|
||||
outputMappings: Path,
|
||||
): GenerateMappingsStep {
|
||||
vanillaSteps.downloadServerMappings()
|
||||
|
||||
// resolve param mappings
|
||||
val paramMappings = context.project.configurations.named(PARAM_MAPPINGS_CONFIG).map { it.singleFile }.convertToPath()
|
||||
|
||||
return GenerateMappingsStep(vanillaSteps, filteredVanillaJar, paramMappings, minecraftLibraryJars, outputMappings)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,49 +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.userdev.internal.setup.step
|
||||
|
||||
import io.papermc.paperweight.userdev.internal.setup.UserdevSetup
|
||||
import io.papermc.paperweight.util.constants.IVY_REPOSITORY
|
||||
import io.papermc.paperweight.util.installToIvyRepo
|
||||
import java.nio.file.Path
|
||||
|
||||
fun installPaperServer(
|
||||
cache: Path,
|
||||
mappedServerCoordinates: String,
|
||||
dependencies: List<String>,
|
||||
serverJar: Path,
|
||||
serverSourcesJar: Path?,
|
||||
mcVersion: String,
|
||||
) {
|
||||
val didInstall = installToIvyRepo(
|
||||
cache.resolve(IVY_REPOSITORY),
|
||||
mappedServerCoordinates,
|
||||
dependencies,
|
||||
serverJar,
|
||||
serverSourcesJar,
|
||||
)
|
||||
if (didInstall) {
|
||||
UserdevSetup.LOGGER.lifecycle(":installed server artifacts to cache")
|
||||
UserdevSetup.LOGGER.lifecycle(":done setting up paperweight userdev workspace for minecraft {}", mcVersion)
|
||||
}
|
||||
}
|
|
@ -1,91 +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.userdev.internal.setup.step
|
||||
|
||||
import io.papermc.paperweight.tasks.*
|
||||
import io.papermc.paperweight.userdev.internal.setup.SetupHandler
|
||||
import io.papermc.paperweight.userdev.internal.setup.util.HashFunctionBuilder
|
||||
import io.papermc.paperweight.userdev.internal.setup.util.siblingHashesFile
|
||||
import io.papermc.paperweight.userdev.internal.setup.util.siblingLogFile
|
||||
import io.papermc.paperweight.util.constants.*
|
||||
import java.nio.file.Path
|
||||
import org.gradle.api.artifacts.Configuration
|
||||
|
||||
class RemapMinecraft(
|
||||
@Input private val minecraftRemapArgs: List<String>,
|
||||
@Input private val filteredVanillaJar: Path,
|
||||
private val minecraftLibraryJars: () -> List<Path>,
|
||||
@Input private val mappings: Path,
|
||||
private val remapper: Configuration,
|
||||
@Output private val outputJar: Path,
|
||||
private val cache: Path,
|
||||
) : SetupStep {
|
||||
override val name: String = "remap minecraft server jar"
|
||||
|
||||
override val hashFile: Path = outputJar.siblingHashesFile()
|
||||
|
||||
override fun run(context: SetupHandler.Context) {
|
||||
TinyRemapper.run(
|
||||
argsList = minecraftRemapArgs,
|
||||
logFile = outputJar.siblingLogFile(),
|
||||
inputJar = filteredVanillaJar,
|
||||
mappingsFile = mappings,
|
||||
fromNamespace = OBF_NAMESPACE,
|
||||
toNamespace = DEOBF_NAMESPACE,
|
||||
remapClasspath = minecraftLibraryJars(),
|
||||
remapper = remapper,
|
||||
outputJar = outputJar,
|
||||
launcher = context.defaultJavaLauncher,
|
||||
workingDir = cache
|
||||
)
|
||||
}
|
||||
|
||||
override fun touchHashFunctionBuilder(builder: HashFunctionBuilder) {
|
||||
builder.includePaperweightHash = false
|
||||
builder.include(minecraftLibraryJars())
|
||||
builder.include(remapper.map { it.toPath() })
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun create(
|
||||
context: SetupHandler.Context,
|
||||
minecraftRemapArgs: List<String>,
|
||||
filteredVanillaJar: Path,
|
||||
minecraftLibraryJars: () -> List<Path>,
|
||||
mappings: Path,
|
||||
outputJar: Path,
|
||||
cache: Path,
|
||||
): RemapMinecraft {
|
||||
val remapper = context.project.configurations.getByName(REMAPPER_CONFIG).also { it.resolve() } // resolve remapper
|
||||
return RemapMinecraft(
|
||||
minecraftRemapArgs,
|
||||
filteredVanillaJar,
|
||||
minecraftLibraryJars,
|
||||
mappings,
|
||||
remapper,
|
||||
outputJar,
|
||||
cache,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,112 +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.userdev.internal.setup
|
||||
|
||||
import com.google.gson.JsonObject
|
||||
import io.papermc.paperweight.userdev.internal.setup.step.Input
|
||||
import io.papermc.paperweight.userdev.internal.setup.step.Output
|
||||
import io.papermc.paperweight.userdev.internal.setup.step.SetupStep
|
||||
import io.papermc.paperweight.userdev.internal.setup.util.HashFunctionBuilder
|
||||
import io.papermc.paperweight.userdev.internal.setup.util.siblingHashesFile
|
||||
import io.papermc.paperweight.userdev.internal.setup.util.siblingLogFile
|
||||
import io.papermc.paperweight.util.*
|
||||
import io.papermc.paperweight.util.data.*
|
||||
import java.nio.file.Path
|
||||
import kotlin.io.path.*
|
||||
|
||||
class RunPaperclip(
|
||||
@Input private val paperclip: Path,
|
||||
@Output private val outputJar: Path,
|
||||
@Input private val mojangJar: Path,
|
||||
@Input private val minecraftVersion: String,
|
||||
private val bundler: Boolean = true,
|
||||
) : SetupStep {
|
||||
override val name: String = "apply mojang mapped paperclip patch"
|
||||
|
||||
override val hashFile: Path = outputJar.siblingHashesFile()
|
||||
|
||||
override fun run(context: SetupHandler.Context) {
|
||||
patchPaperclip(context, paperclip, outputJar, mojangJar, minecraftVersion, bundler)
|
||||
}
|
||||
|
||||
override fun touchHashFunctionBuilder(builder: HashFunctionBuilder) {
|
||||
builder.includePaperweightHash = false
|
||||
}
|
||||
|
||||
private fun patchPaperclip(
|
||||
context: SetupHandler.Context,
|
||||
paperclip: Path,
|
||||
outputJar: Path,
|
||||
mojangJar: Path,
|
||||
minecraftVersion: String,
|
||||
bundler: Boolean,
|
||||
) {
|
||||
val logFile = outputJar.siblingLogFile()
|
||||
|
||||
val work = createTempDirectory()
|
||||
ensureDeleted(logFile)
|
||||
|
||||
// Copy in mojang jar, so we don't download it twice
|
||||
val cache = work.resolve("cache")
|
||||
cache.createDirectories()
|
||||
mojangJar.copyTo(cache.resolve("mojang_$minecraftVersion.jar"))
|
||||
|
||||
context.defaultJavaLauncher.runJar(
|
||||
classpath = context.project.files(paperclip),
|
||||
workingDir = work,
|
||||
logFile = logFile,
|
||||
jvmArgs = listOf("-Dpaperclip.patchonly=true"),
|
||||
args = arrayOf()
|
||||
)
|
||||
|
||||
if (bundler) {
|
||||
handleBundler(paperclip, work, outputJar)
|
||||
} else {
|
||||
handleOldPaperclip(work, outputJar)
|
||||
}
|
||||
|
||||
ensureDeleted(work)
|
||||
}
|
||||
|
||||
private fun handleBundler(paperclip: Path, work: Path, outputJar: Path) {
|
||||
paperclip.openZip().use { fs ->
|
||||
val root = fs.rootDirectories.single()
|
||||
|
||||
val serverVersionJson = root.resolve(FileEntry.VERSION_JSON)
|
||||
val versionId = gson.fromJson<JsonObject>(serverVersionJson)["id"].asString
|
||||
val versions = root.resolve(FileEntry.VERSIONS_LIST).readLines()
|
||||
.map { it.split('\t') }
|
||||
.associate { it[1] to it[2] }
|
||||
|
||||
val serverJarPath = work.resolve("versions/${versions[versionId]}")
|
||||
outputJar.parent.createDirectories()
|
||||
serverJarPath.copyTo(outputJar, overwrite = true)
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleOldPaperclip(work: Path, outputJar: Path) {
|
||||
val patched = work.resolve("cache").listDirectoryEntries()
|
||||
.find { it.name.startsWith("patched") } ?: error("Can't find patched jar!")
|
||||
patched.copyTo(outputJar, overwrite = true)
|
||||
}
|
||||
}
|
|
@ -1,78 +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.userdev.internal.setup.step
|
||||
|
||||
import io.papermc.paperweight.DownloadService
|
||||
import io.papermc.paperweight.PaperweightException
|
||||
import io.papermc.paperweight.userdev.internal.setup.util.*
|
||||
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.kotlin.dsl.*
|
||||
|
||||
class VanillaSteps(
|
||||
private val minecraftVersion: String,
|
||||
private val cache: Path,
|
||||
private val downloadService: DownloadService,
|
||||
private val bundleChanged: Boolean,
|
||||
) {
|
||||
private val versionManifest: MinecraftVersionManifest by lazy { setupMinecraftVersionManifest() }
|
||||
val mojangJar: Path = cache.resolve(paperSetupOutput("downloadServerJar", "jar"))
|
||||
val serverMappings: Path = cache.resolve(SERVER_MAPPINGS)
|
||||
|
||||
fun downloadVanillaServerJar(): DownloadResult<Unit> = downloadService.download(
|
||||
"vanilla minecraft server jar",
|
||||
versionManifest.serverDownload().url,
|
||||
mojangJar,
|
||||
expectedHash = versionManifest.serverDownload().hash()
|
||||
)
|
||||
|
||||
fun downloadServerMappings(): DownloadResult<Unit> = downloadService.download(
|
||||
"mojang server mappings",
|
||||
versionManifest.serverMappingsDownload().url,
|
||||
serverMappings,
|
||||
expectedHash = versionManifest.serverMappingsDownload().hash()
|
||||
)
|
||||
|
||||
private fun downloadMinecraftManifest(force: Boolean): DownloadResult<MinecraftManifest> =
|
||||
downloadService.download("minecraft manifest", MC_MANIFEST_URL, cache.resolve(MC_MANIFEST), force)
|
||||
.mapData { gson.fromJson(it.path) }
|
||||
|
||||
private fun setupMinecraftVersionManifest(): MinecraftVersionManifest {
|
||||
var minecraftManifest = downloadMinecraftManifest(bundleChanged)
|
||||
if (!minecraftManifest.didDownload && minecraftManifest.data.versions.none { it.id == minecraftVersion }) {
|
||||
minecraftManifest = downloadMinecraftManifest(true)
|
||||
}
|
||||
|
||||
val ver = minecraftManifest.data.versions.firstOrNull { it.id == minecraftVersion }
|
||||
?: throw PaperweightException("Could not find Minecraft version '$minecraftVersion' in the downloaded manifest.")
|
||||
val minecraftVersionManifestJson = downloadService.download(
|
||||
"minecraft version manifest",
|
||||
ver.url,
|
||||
cache.resolve(VERSION_JSON),
|
||||
expectedHash = ver.hash()
|
||||
)
|
||||
return gson.fromJson(minecraftVersionManifestJson.path)
|
||||
}
|
||||
}
|
|
@ -1,137 +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.userdev.internal.setup.step
|
||||
|
||||
import io.papermc.paperweight.PaperweightException
|
||||
import io.papermc.paperweight.userdev.internal.setup.SetupHandler
|
||||
import io.papermc.paperweight.userdev.internal.setup.UserdevSetup
|
||||
import io.papermc.paperweight.userdev.internal.setup.util.HashFunction
|
||||
import io.papermc.paperweight.userdev.internal.setup.util.HashFunctionBuilder
|
||||
import io.papermc.paperweight.userdev.internal.setup.util.buildHashFunction
|
||||
import java.nio.file.Path
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import kotlin.io.path.*
|
||||
import kotlin.reflect.KClass
|
||||
import kotlin.reflect.KProperty1
|
||||
import kotlin.reflect.full.declaredMemberProperties
|
||||
import kotlin.reflect.jvm.isAccessible
|
||||
import kotlin.system.measureTimeMillis
|
||||
|
||||
@Target(AnnotationTarget.PROPERTY)
|
||||
@Retention(AnnotationRetention.RUNTIME)
|
||||
annotation class Input
|
||||
|
||||
@Target(AnnotationTarget.PROPERTY)
|
||||
@Retention(AnnotationRetention.RUNTIME)
|
||||
annotation class Output
|
||||
|
||||
interface SetupStep {
|
||||
val name: String
|
||||
|
||||
val hashFile: Path
|
||||
|
||||
fun run(context: SetupHandler.Context)
|
||||
|
||||
fun touchHashFunctionBuilder(builder: HashFunctionBuilder) {}
|
||||
}
|
||||
|
||||
object StepExecutor {
|
||||
private data class InputOutputData(
|
||||
val inputs: List<KProperty1<Any, *>>,
|
||||
val outputs: List<KProperty1<Any, *>>,
|
||||
)
|
||||
|
||||
private val inputOutputDataCache: MutableMap<KClass<out SetupStep>, InputOutputData> =
|
||||
ConcurrentHashMap<KClass<out SetupStep>, InputOutputData>()
|
||||
|
||||
fun executeSteps(expectingChange: Boolean, context: SetupHandler.Context, vararg steps: SetupStep) {
|
||||
// if we aren't expecting change, assume the last step is the output that matters
|
||||
// and only verify its inputs/outputs - if it fails then we need to go back through
|
||||
// and check each step anyway
|
||||
if (!expectingChange) {
|
||||
val lastStep = steps.last()
|
||||
if (makeHashFunction(lastStep).upToDate(lastStep.hashFile)) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
for (step in steps) {
|
||||
executeStep(context, step)
|
||||
}
|
||||
} catch (ex: Exception) {
|
||||
for (step in steps) {
|
||||
step.hashFile.deleteIfExists()
|
||||
}
|
||||
throw PaperweightException("Failed to execute steps, invalidated relevant caches. Run with --stacktrace for more details.", ex)
|
||||
}
|
||||
}
|
||||
|
||||
fun executeStep(context: SetupHandler.Context, step: SetupStep) {
|
||||
val hashFunction = makeHashFunction(step)
|
||||
|
||||
if (hashFunction.upToDate(step.hashFile)) {
|
||||
return
|
||||
}
|
||||
|
||||
UserdevSetup.LOGGER.lifecycle(":executing '{}'", step.name)
|
||||
val elapsed = measureTimeMillis {
|
||||
step.run(context)
|
||||
}
|
||||
UserdevSetup.LOGGER.info("done executing '{}', took {}s", step.name, elapsed / 1000.00)
|
||||
|
||||
hashFunction.writeHash(step.hashFile)
|
||||
}
|
||||
|
||||
private fun makeHashFunction(step: SetupStep): HashFunction {
|
||||
val data = step.inputOutputData
|
||||
val inputs = data.inputs.mapNotNull { it.get(step) }
|
||||
val outputs = data.outputs.mapNotNull { it.get(step) }
|
||||
|
||||
return buildHashFunction(inputs, outputs) {
|
||||
step.touchHashFunctionBuilder(this)
|
||||
}
|
||||
}
|
||||
|
||||
private val SetupStep.inputOutputData: InputOutputData
|
||||
get() = inputOutputDataCache.computeIfAbsent(this::class) {
|
||||
InputOutputData(
|
||||
it.collectAnnotatedDeclaredProperties<Input>(),
|
||||
it.collectAnnotatedDeclaredProperties<Output>(),
|
||||
)
|
||||
}
|
||||
|
||||
private inline fun <reified A : Annotation> KClass<*>.collectAnnotatedDeclaredProperties() =
|
||||
collectDeclaredProperties {
|
||||
it.annotations.any { a -> a is A }
|
||||
}
|
||||
|
||||
@Suppress("unchecked_cast")
|
||||
private fun KClass<*>.collectDeclaredProperties(
|
||||
filter: (KProperty1<*, *>) -> Boolean
|
||||
): List<KProperty1<Any, *>> =
|
||||
declaredMemberProperties.filter(filter).map {
|
||||
it.isAccessible = true
|
||||
it as KProperty1<Any, *>
|
||||
}
|
||||
}
|
|
@ -1,236 +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.userdev.internal.setup.util
|
||||
|
||||
import io.papermc.paperweight.DownloadService
|
||||
import io.papermc.paperweight.userdev.PaperweightUser
|
||||
import io.papermc.paperweight.userdev.internal.setup.UserdevSetup
|
||||
import io.papermc.paperweight.util.*
|
||||
import io.papermc.paperweight.util.constants.*
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.Paths
|
||||
import java.time.Duration
|
||||
import java.util.stream.Collectors
|
||||
import kotlin.io.path.*
|
||||
import kotlin.streams.asSequence
|
||||
import kotlin.system.measureTimeMillis
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.provider.Provider
|
||||
|
||||
val paperweightHash: String by lazy { hashPaperweightJar() }
|
||||
|
||||
fun Path.siblingLogFile(): Path = withDifferentExtension("log")
|
||||
|
||||
fun Path.siblingHashesFile(): Path = withDifferentExtension("hashes")
|
||||
|
||||
fun Path.siblingLogAndHashesFiles() = Pair(siblingLogFile(), siblingHashesFile())
|
||||
|
||||
fun hash(vararg things: Any): String = hash(things.toList())
|
||||
|
||||
fun hash(things: List<Any>): String {
|
||||
val strings = arrayListOf<String>()
|
||||
val paths = arrayListOf<Path>()
|
||||
|
||||
for (thing in things) {
|
||||
when (thing) {
|
||||
is String -> strings.add(thing)
|
||||
is Path -> paths.add(thing)
|
||||
is Iterable<*> -> strings.add(hash(thing.filterNotNull()))
|
||||
else -> error("Unknown type: ${thing.javaClass.name}")
|
||||
}
|
||||
}
|
||||
|
||||
return hashFiles(paths) + if (strings.isEmpty()) {
|
||||
""
|
||||
} else {
|
||||
"\n" + strings.sorted().joinToString("\n") { it.hash(HashingAlgorithm.SHA256).asHexString() }
|
||||
}
|
||||
}
|
||||
|
||||
fun hashFiles(files: List<Path>): String = files.asSequence()
|
||||
.filter { it.isRegularFile() }
|
||||
.sortedBy { it.pathString }
|
||||
.joinToString("\n") {
|
||||
"${it.fileName.pathString}:${it.sha256asHex()}"
|
||||
}
|
||||
|
||||
fun hashDirectory(dir: Path): String = Files.walk(dir).use { stream -> hashFiles(stream.filter { it.isRegularFile() }.collect(Collectors.toList())) }
|
||||
|
||||
fun DownloadService.download(
|
||||
downloadName: String,
|
||||
remote: String,
|
||||
destination: Path,
|
||||
forceDownload: Boolean = false,
|
||||
expectedHash: Hash? = null
|
||||
): DownloadResult<Unit> {
|
||||
val hashFile = destination.siblingHashesFile()
|
||||
|
||||
val toHash = mutableListOf<Any>(remote, destination)
|
||||
expectedHash?.let { toHash += it.valueLower }
|
||||
val upToDate = !forceDownload &&
|
||||
hashFile.isRegularFile() &&
|
||||
hashFile.readText() == hash(toHash)
|
||||
if (upToDate) {
|
||||
return DownloadResult(destination, false, Unit)
|
||||
}
|
||||
|
||||
UserdevSetup.LOGGER.lifecycle(":executing 'download {}'", downloadName)
|
||||
val elapsed = measureTimeMillis {
|
||||
destination.parent.createDirectories()
|
||||
destination.deleteIfExists()
|
||||
download(remote, destination, expectedHash)
|
||||
}
|
||||
UserdevSetup.LOGGER.info("done executing 'download {}', took {}s", downloadName, elapsed / 1000.00)
|
||||
hashFile.writeText(hash(toHash))
|
||||
|
||||
return DownloadResult(destination, true, Unit)
|
||||
}
|
||||
|
||||
fun buildHashFunction(vararg things: Any, op: HashFunctionBuilder.() -> Unit = {}): HashFunction = HashFunction {
|
||||
val builder = HashFunctionBuilder.create()
|
||||
builder.op()
|
||||
if (builder.includePaperweightHash) {
|
||||
builder.include(paperweightHash)
|
||||
}
|
||||
builder.include(*things)
|
||||
|
||||
hash(builder)
|
||||
}
|
||||
|
||||
data class DownloadResult<D>(val path: Path, val didDownload: Boolean, val data: D) {
|
||||
fun <N> mapData(mapper: (DownloadResult<D>) -> N): DownloadResult<N> = DownloadResult(path, didDownload, mapper(this))
|
||||
}
|
||||
|
||||
interface HashFunctionBuilder : MutableList<Any> {
|
||||
var includePaperweightHash: Boolean
|
||||
|
||||
fun include(vararg things: Any): Boolean = addAll(things.toList())
|
||||
|
||||
fun include(thing: Any): Boolean = add(thing)
|
||||
|
||||
companion object {
|
||||
fun create(): HashFunctionBuilder = HashFunctionBuilderImpl()
|
||||
}
|
||||
|
||||
private class HashFunctionBuilderImpl(
|
||||
override var includePaperweightHash: Boolean = true,
|
||||
) : HashFunctionBuilder, MutableList<Any> by ArrayList()
|
||||
}
|
||||
|
||||
fun interface HashFunction : () -> String {
|
||||
fun writeHash(hashFile: Path) {
|
||||
hashFile.parent.createDirectories()
|
||||
hashFile.writeText(this())
|
||||
}
|
||||
|
||||
fun upToDate(hashFile: Path): Boolean {
|
||||
return hashFile.isRegularFile() && hashFile.readText() == this()
|
||||
}
|
||||
}
|
||||
|
||||
private fun hashPaperweightJar(): String {
|
||||
val userdevShadowJar = Paths.get(PaperweightUser::class.java.protectionDomain.codeSource.location.toURI())
|
||||
return userdevShadowJar.sha256asHex()
|
||||
}
|
||||
|
||||
fun <R> lockSetup(cache: Path, canBeNested: Boolean = false, action: () -> R): R {
|
||||
val lockFile = cache.resolve(USERDEV_SETUP_LOCK)
|
||||
val alreadyHad = acquireProcessLockWaiting(lockFile)
|
||||
try {
|
||||
return action()
|
||||
} finally {
|
||||
if (!canBeNested || !alreadyHad) {
|
||||
lockFile.deleteForcefully()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// set by most CI
|
||||
val Project.ci: Provider<Boolean>
|
||||
get() = providers.environmentVariable("CI")
|
||||
.map { it.toBoolean() }
|
||||
.orElse(false)
|
||||
|
||||
private fun stableProp(name: String) = "paperweight.$name"
|
||||
|
||||
private fun experimentalProp(name: String) = "paperweight.experimental.$name"
|
||||
|
||||
val Project.genSources: Boolean
|
||||
get() {
|
||||
val ci = ci.get()
|
||||
val prop = providers.gradleProperty(experimentalProp("genSources")).orNull?.toBoolean()
|
||||
return prop ?: !ci
|
||||
}
|
||||
|
||||
val Project.sharedCaches: Boolean
|
||||
get() = providers.gradleProperty(stableProp("sharedCaches"))
|
||||
.map { it.toBoolean() }
|
||||
.orElse(true)
|
||||
.get()
|
||||
|
||||
private fun deleteUnusedAfter(target: Project): Long = target.providers.gradleProperty(stableProp("sharedCaches.deleteUnusedAfter"))
|
||||
.map { value -> parseDuration(value) }
|
||||
.orElse(Duration.ofDays(7))
|
||||
.map { duration -> duration.toMillis() }
|
||||
.get()
|
||||
|
||||
fun lastUsedFile(cacheDir: Path): Path = cacheDir.resolve(paperSetupOutput("last-used", "txt"))
|
||||
|
||||
fun cleanSharedCaches(target: Project, root: Path) {
|
||||
if (!root.exists()) {
|
||||
return
|
||||
}
|
||||
val toDelete = Files.walk(root).use { stream ->
|
||||
stream.asSequence()
|
||||
.filter { it.name == "last-used.txt" }
|
||||
.mapNotNull {
|
||||
val pwDir = it.parent.parent // paperweight dir
|
||||
val cacheDir = pwDir.parent // cache dir
|
||||
val lock = cacheDir.resolve(USERDEV_SETUP_LOCK)
|
||||
if (lock.exists() && ProcessHandle.of(lock.readText().toLong()).isPresent) {
|
||||
return@mapNotNull null
|
||||
}
|
||||
val lastUsed = it.readText().toLong()
|
||||
val since = System.currentTimeMillis() - lastUsed
|
||||
val cutoff = deleteUnusedAfter(target)
|
||||
if (since > cutoff) pwDir else null
|
||||
}
|
||||
.toList()
|
||||
}
|
||||
for (path in toDelete) {
|
||||
path.deleteRecursive()
|
||||
|
||||
// clean up empty parent directories
|
||||
var parent: Path = path.parent
|
||||
while (true) {
|
||||
val entries = parent.listDirectoryEntries()
|
||||
if (entries.isEmpty()) {
|
||||
parent.deleteIfExists()
|
||||
} else {
|
||||
break
|
||||
}
|
||||
parent = parent.parent
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,52 +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.userdev.internal.setup.v2
|
||||
|
||||
import io.papermc.paperweight.util.*
|
||||
|
||||
object DevBundleV2 {
|
||||
data class Config(
|
||||
val minecraftVersion: String,
|
||||
val mappedServerCoordinates: String,
|
||||
val apiCoordinates: String,
|
||||
val mojangApiCoordinates: String,
|
||||
val buildData: BuildData,
|
||||
val decompile: Runner,
|
||||
val remap: Runner,
|
||||
val patchDir: String
|
||||
)
|
||||
|
||||
data class BuildData(
|
||||
val paramMappings: MavenDep,
|
||||
val reobfMappingsFile: String,
|
||||
val accessTransformFile: String,
|
||||
val mojangMappedPaperclipFile: String,
|
||||
val vanillaJarIncludes: List<String>,
|
||||
val vanillaServerLibraries: List<String>,
|
||||
val libraryDependencies: Set<String>,
|
||||
val libraryRepositories: List<String>,
|
||||
val relocations: List<Relocation>
|
||||
)
|
||||
|
||||
data class Runner(val dep: MavenDep, val args: List<String>)
|
||||
}
|
|
@ -1,263 +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.userdev.internal.setup.v2
|
||||
|
||||
import io.papermc.paperweight.tasks.*
|
||||
import io.papermc.paperweight.userdev.internal.setup.ExtractedBundle
|
||||
import io.papermc.paperweight.userdev.internal.setup.RunPaperclip
|
||||
import io.papermc.paperweight.userdev.internal.setup.SetupHandler
|
||||
import io.papermc.paperweight.userdev.internal.setup.UserdevSetup
|
||||
import io.papermc.paperweight.userdev.internal.setup.step.*
|
||||
import io.papermc.paperweight.userdev.internal.setup.util.*
|
||||
import io.papermc.paperweight.util.*
|
||||
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.artifacts.repositories.IvyArtifactRepository
|
||||
|
||||
class SetupHandlerImplV2(
|
||||
private val parameters: UserdevSetup.Parameters,
|
||||
private val bundle: ExtractedBundle<DevBundleV2.Config>,
|
||||
private val cache: Path = parameters.cache.path,
|
||||
) : SetupHandler {
|
||||
private val vanillaSteps by lazy {
|
||||
VanillaSteps(
|
||||
bundle.config.minecraftVersion,
|
||||
cache,
|
||||
parameters.downloadService.get(),
|
||||
bundle.changed,
|
||||
)
|
||||
}
|
||||
private val filteredVanillaServerJar: Path = cache.resolve(paperSetupOutput("filterJar", "jar"))
|
||||
private val minecraftLibraryJars = cache.resolve(MINECRAFT_JARS_PATH)
|
||||
private val mojangPlusYarnMappings: Path = cache.resolve(MOJANG_YARN_MAPPINGS)
|
||||
private val mappedMinecraftServerJar: Path = cache.resolve(paperSetupOutput("mappedMinecraftServerJar", "jar"))
|
||||
private val fixedMinecraftServerJar: Path = cache.resolve(paperSetupOutput("fixedMinecraftServerJar", "jar"))
|
||||
private val accessTransformedServerJar: Path = cache.resolve(paperSetupOutput("accessTransformedServerJar", "jar"))
|
||||
private val decompiledMinecraftServerJar: Path = cache.resolve(paperSetupOutput("decompileMinecraftServerJar", "jar"))
|
||||
private val patchedSourcesJar: Path = cache.resolve(paperSetupOutput("patchedSourcesJar", "jar"))
|
||||
private val mojangMappedPaperJar: Path = cache.resolve(paperSetupOutput("applyMojangMappedPaperclipPatch", "jar"))
|
||||
|
||||
private fun minecraftLibraryJars(): List<Path> = minecraftLibraryJars.listDirectoryEntries("*.jar")
|
||||
|
||||
private fun generateSources(context: SetupHandler.Context) {
|
||||
vanillaSteps.downloadVanillaServerJar()
|
||||
|
||||
val downloadMcLibs = object : SetupStep {
|
||||
override val name: String = "download minecraft libraries"
|
||||
|
||||
override val hashFile: Path = cache.resolve(paperSetupOutput("minecraftLibraries", "hashes"))
|
||||
|
||||
override fun run(context: SetupHandler.Context) {
|
||||
downloadMinecraftLibraries(
|
||||
download = parameters.downloadService,
|
||||
workerExecutor = context.workerExecutor,
|
||||
targetDir = minecraftLibraryJars,
|
||||
repositories = listOf(MC_LIBRARY_URL, MAVEN_CENTRAL_URL),
|
||||
mcLibraries = bundle.config.buildData.vanillaServerLibraries,
|
||||
sources = false
|
||||
).await()
|
||||
}
|
||||
|
||||
override fun touchHashFunctionBuilder(builder: HashFunctionBuilder) {
|
||||
builder.include(MC_LIBRARY_URL)
|
||||
builder.include(bundle.config.buildData.vanillaServerLibraries)
|
||||
builder.include(minecraftLibraryJars())
|
||||
builder.includePaperweightHash = false
|
||||
}
|
||||
}
|
||||
|
||||
val filterVanillaJarStep = FilterVanillaJar(vanillaSteps.mojangJar, bundle.config.buildData.vanillaJarIncludes, filteredVanillaServerJar)
|
||||
|
||||
val genMappingsStep = GenerateMappingsStep.create(
|
||||
context,
|
||||
vanillaSteps,
|
||||
filteredVanillaServerJar,
|
||||
::minecraftLibraryJars,
|
||||
mojangPlusYarnMappings,
|
||||
)
|
||||
|
||||
val remapMinecraftStep = RemapMinecraft.create(
|
||||
context,
|
||||
bundle.config.remap.args,
|
||||
filteredVanillaServerJar,
|
||||
::minecraftLibraryJars,
|
||||
mojangPlusYarnMappings,
|
||||
mappedMinecraftServerJar,
|
||||
cache,
|
||||
)
|
||||
|
||||
val fixStep = FixMinecraftJar(
|
||||
mappedMinecraftServerJar,
|
||||
fixedMinecraftServerJar,
|
||||
vanillaSteps.mojangJar,
|
||||
true,
|
||||
)
|
||||
|
||||
val atStep = AccessTransformMinecraft(
|
||||
bundle.dir.resolve(bundle.config.buildData.accessTransformFile),
|
||||
fixedMinecraftServerJar,
|
||||
accessTransformedServerJar,
|
||||
)
|
||||
|
||||
val decomp = DecompileMinecraft.create(
|
||||
context,
|
||||
accessTransformedServerJar,
|
||||
decompiledMinecraftServerJar,
|
||||
cache,
|
||||
::minecraftLibraryJars,
|
||||
bundle.config.decompile.args,
|
||||
)
|
||||
|
||||
val applyDevBundlePatchesStep = ApplyDevBundlePatches(
|
||||
decompiledMinecraftServerJar,
|
||||
bundle.dir.resolve(bundle.config.patchDir),
|
||||
patchedSourcesJar
|
||||
)
|
||||
|
||||
StepExecutor.executeSteps(
|
||||
bundle.changed,
|
||||
context,
|
||||
downloadMcLibs,
|
||||
filterVanillaJarStep,
|
||||
genMappingsStep,
|
||||
remapMinecraftStep,
|
||||
fixStep,
|
||||
atStep,
|
||||
decomp,
|
||||
applyDevBundlePatchesStep,
|
||||
)
|
||||
|
||||
applyMojangMappedPaperclipPatch(context)
|
||||
|
||||
StepExecutor.executeStep(
|
||||
context,
|
||||
FilterPaperShadowJar(
|
||||
patchedSourcesJar,
|
||||
mojangMappedPaperJar,
|
||||
filteredMojangMappedPaperJar,
|
||||
bundle.config.buildData.relocations,
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
// This can be called when a user queries the server jar provider in
|
||||
// PaperweightUserExtension, possibly by a task running in a separate
|
||||
// thread to dependency resolution.
|
||||
@Synchronized
|
||||
private fun applyMojangMappedPaperclipPatch(context: SetupHandler.Context) {
|
||||
if (setupCompleted) {
|
||||
return
|
||||
}
|
||||
|
||||
lockSetup(cache, true) {
|
||||
StepExecutor.executeStep(
|
||||
context,
|
||||
RunPaperclip(
|
||||
bundle.dir.resolve(bundle.config.buildData.mojangMappedPaperclipFile),
|
||||
mojangMappedPaperJar,
|
||||
vanillaSteps.mojangJar,
|
||||
minecraftVersion,
|
||||
false,
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private val filteredMojangMappedPaperJar: Path = cache.resolve(paperSetupOutput("filteredMojangMappedPaperJar", "jar"))
|
||||
|
||||
private var setupCompleted = false
|
||||
|
||||
@Synchronized
|
||||
override fun createOrUpdateIvyRepository(context: SetupHandler.Context) {
|
||||
if (setupCompleted) {
|
||||
return
|
||||
}
|
||||
|
||||
lockSetup(cache) {
|
||||
createOrUpdateIvyRepositoryDirect(context)
|
||||
}
|
||||
}
|
||||
|
||||
private fun createOrUpdateIvyRepositoryDirect(context: SetupHandler.Context) {
|
||||
generateSources(context)
|
||||
|
||||
val deps = bundle.config.buildData.libraryDependencies.toList() +
|
||||
bundle.config.apiCoordinates +
|
||||
bundle.config.mojangApiCoordinates
|
||||
installPaperServer(
|
||||
cache,
|
||||
bundle.config.mappedServerCoordinates,
|
||||
deps,
|
||||
filteredMojangMappedPaperJar,
|
||||
patchedSourcesJar,
|
||||
minecraftVersion,
|
||||
)
|
||||
|
||||
setupCompleted = true
|
||||
}
|
||||
|
||||
override fun configureIvyRepo(repo: IvyArtifactRepository) {
|
||||
repo.content {
|
||||
includeFromDependencyNotation(bundle.config.mappedServerCoordinates)
|
||||
}
|
||||
}
|
||||
|
||||
override fun populateCompileConfiguration(context: SetupHandler.Context, dependencySet: DependencySet) {
|
||||
dependencySet.add(context.project.dependencies.create(bundle.config.mappedServerCoordinates))
|
||||
}
|
||||
|
||||
override fun populateRuntimeConfiguration(context: SetupHandler.Context, dependencySet: DependencySet) {
|
||||
dependencySet.add(context.project.dependencies.create(context.project.files(serverJar(context))))
|
||||
}
|
||||
|
||||
override fun serverJar(context: SetupHandler.Context): Path {
|
||||
applyMojangMappedPaperclipPatch(context)
|
||||
return mojangMappedPaperJar
|
||||
}
|
||||
|
||||
override val serverJar: Path
|
||||
get() = mojangMappedPaperJar
|
||||
|
||||
override val reobfMappings: Path
|
||||
get() = bundle.dir.resolve(bundle.config.buildData.reobfMappingsFile)
|
||||
|
||||
override val minecraftVersion: String
|
||||
get() = bundle.config.minecraftVersion
|
||||
|
||||
override val pluginRemapArgs: List<String>
|
||||
get() = TinyRemapper.pluginRemapArgs // plugin remap args were not included in v2 bundles, if these change check this
|
||||
|
||||
override val paramMappings: MavenDep
|
||||
get() = bundle.config.buildData.paramMappings
|
||||
|
||||
override val decompiler: MavenDep
|
||||
get() = bundle.config.decompile.dep
|
||||
|
||||
override val remapper: MavenDep
|
||||
get() = bundle.config.remap.dep
|
||||
|
||||
override val libraryRepositories: List<String>
|
||||
get() = bundle.config.buildData.libraryRepositories
|
||||
}
|
10
paperweight-core/build.gradle.kts
Normal file
10
paperweight-core/build.gradle.kts
Normal file
|
@ -0,0 +1,10 @@
|
|||
plugins {
|
||||
`config-kotlin`
|
||||
`config-publish`
|
||||
}
|
||||
|
||||
dependencies {
|
||||
shade(projects.paperweightLib)
|
||||
|
||||
implementation(libs.kotson)
|
||||
}
|
160
paperweight-core/src/main/kotlin/PaperweightCore.kt
Normal file
160
paperweight-core/src/main/kotlin/PaperweightCore.kt
Normal file
|
@ -0,0 +1,160 @@
|
|||
/*
|
||||
* paperweight is a Gradle plugin for the PaperMC project.
|
||||
*
|
||||
* Copyright (c) 2021 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.core
|
||||
|
||||
import io.papermc.paperweight.DownloadService
|
||||
import io.papermc.paperweight.core.extension.PaperweightCoreExtension
|
||||
import io.papermc.paperweight.core.taskcontainers.AllTasks
|
||||
import io.papermc.paperweight.core.tasks.PaperweightCoreUpstreamData
|
||||
import io.papermc.paperweight.tasks.GeneratePaperclipPatch
|
||||
import io.papermc.paperweight.tasks.patchremap.RemapPatches
|
||||
import io.papermc.paperweight.util.Constants
|
||||
import io.papermc.paperweight.util.cache
|
||||
import io.papermc.paperweight.util.initSubmodules
|
||||
import io.papermc.paperweight.util.registering
|
||||
import io.papermc.paperweight.util.setupServerProject
|
||||
import java.io.File
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.tasks.Delete
|
||||
import org.gradle.api.tasks.TaskProvider
|
||||
import org.gradle.api.tasks.bundling.Jar
|
||||
import org.gradle.kotlin.dsl.*
|
||||
|
||||
class PaperweightCore : Plugin<Project> {
|
||||
override fun apply(target: Project) {
|
||||
target.extensions.create(Constants.EXTENSION, PaperweightCoreExtension::class)
|
||||
|
||||
target.gradle.sharedServices.registerIfAbsent("download", DownloadService::class) {}
|
||||
|
||||
target.tasks.register<Delete>("cleanCache") {
|
||||
group = "paper"
|
||||
description = "Delete the project setup cache and task outputs."
|
||||
delete(target.layout.cache)
|
||||
}
|
||||
|
||||
// Make sure the submodules are initialized, since there are files there
|
||||
// which are required for configuration
|
||||
target.layout.initSubmodules()
|
||||
|
||||
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)
|
||||
|
||||
val tasks = AllTasks(target)
|
||||
target.createPatchRemapTask(tasks)
|
||||
|
||||
target.tasks.register<PaperweightCoreUpstreamData>(Constants.PAPERWEIGHT_PREPARE_DOWNSTREAM) {
|
||||
dependsOn(tasks.applyPatches)
|
||||
vanillaJar.set(tasks.downloadServerJar.flatMap { it.outputJar })
|
||||
remappedJar.set(tasks.copyResources.flatMap { it.outputJar })
|
||||
mcVersion.set(target.ext.minecraftVersion)
|
||||
mcLibrariesFile.set(tasks.inspectVanillaJar.flatMap { it.serverLibraries })
|
||||
mcLibrariesDir.set(tasks.downloadMcLibraries.flatMap { it.sourcesOutputDir })
|
||||
reobfMappings.set(tasks.generateReobfMappings.flatMap { it.reobfMappings })
|
||||
|
||||
dataFile.set(target.layout.file(providers.gradleProperty(Constants.PAPERWEIGHT_PREPARE_DOWNSTREAM).map { File(it) }))
|
||||
}
|
||||
|
||||
target.afterEvaluate {
|
||||
// Setup the server jar
|
||||
val cache = target.layout.cache
|
||||
|
||||
val serverProj = target.ext.serverProject.forUseAtConfigurationTime().orNull ?: return@afterEvaluate
|
||||
val reobfJar = serverProj.setupServerProject(
|
||||
target,
|
||||
cache.resolve(Constants.FINAL_REMAPPED_JAR),
|
||||
cache.resolve(Constants.SERVER_LIBRARIES),
|
||||
) {
|
||||
mappingsFile.set(tasks.generateReobfMappings.flatMap { it.reobfMappings })
|
||||
} ?: return@afterEvaluate
|
||||
|
||||
val generatePaperclipPatch by target.tasks.registering<GeneratePaperclipPatch> {
|
||||
originalJar.set(tasks.downloadServerJar.flatMap { it.outputJar })
|
||||
patchedJar.set(reobfJar.flatMap { it.outputJar })
|
||||
mcVersion.set(target.ext.minecraftVersion)
|
||||
}
|
||||
|
||||
@Suppress("UNUSED_VARIABLE")
|
||||
val paperclipJar by target.tasks.registering<Jar> {
|
||||
with(target.tasks.named("jar", Jar::class).get())
|
||||
|
||||
val paperclipConfig = target.configurations.named(Constants.PAPERCLIP_CONFIG)
|
||||
dependsOn(paperclipConfig, generatePaperclipPatch)
|
||||
|
||||
val paperclipZip = target.zipTree(paperclipConfig.map { it.singleFile })
|
||||
from(paperclipZip) {
|
||||
exclude("META-INF/MANIFEST.MF")
|
||||
}
|
||||
from(target.zipTree(generatePaperclipPatch.flatMap { it.outputZip }.get()))
|
||||
|
||||
manifest.from(paperclipZip.matching { include("META-INF/MANIFEST.MF") }.singleFile)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun Project.createPatchRemapTask(allTasks: AllTasks) {
|
||||
val extension: PaperweightCoreExtension = ext
|
||||
|
||||
/*
|
||||
* To ease the waiting time for debugging this task, all of the task dependencies have been removed (notice all
|
||||
* of those .get() calls). This means when you make changes to paperweight Gradle won't know that this task
|
||||
* technically depends on the output of all of those other tasks.
|
||||
*
|
||||
* In order to run all of the other necessary tasks before running this task in order to setup the inputs, run:
|
||||
*
|
||||
* ./gradlew patchPaper applyVanillaSrgAt
|
||||
*
|
||||
* Then you should be able to run `./gradlew remapPatches` without having to worry about all of the other tasks
|
||||
* running whenever you make changes to paperweight.
|
||||
*/
|
||||
|
||||
@Suppress("UNUSED_VARIABLE")
|
||||
val remapPatches: TaskProvider<RemapPatches> by tasks.registering<RemapPatches> {
|
||||
group = "paper"
|
||||
description = "EXPERIMENTAL & BROKEN: Attempt to remap Paper's patches from Spigot mappings to SRG."
|
||||
|
||||
inputPatchDir.set(extension.paper.unmappedSpigotServerPatchDir)
|
||||
apiPatchDir.set(extension.paper.spigotApiPatchDir)
|
||||
|
||||
mappingsFile.set(allTasks.patchMappings.flatMap { it.outputMappings }.get())
|
||||
ats.set(allTasks.remapSpigotSources.flatMap { it.generatedAt }.get())
|
||||
|
||||
// Pull in as many jars as possible to reduce the possibility of type bindings not resolving
|
||||
classpathJars.from(allTasks.applyMergedAt.flatMap { it.outputJar }.get()) // final remapped jar
|
||||
classpathJars.from(allTasks.remapSpigotSources.flatMap { it.vanillaRemappedSpigotJar }.get()) // Spigot remapped jar
|
||||
classpathJars.from(allTasks.downloadServerJar.flatMap { it.outputJar }.get()) // pure vanilla jar
|
||||
|
||||
spigotApiDir.set(allTasks.patchSpigotApi.flatMap { it.outputDir }.get())
|
||||
spigotServerDir.set(allTasks.patchSpigotServer.flatMap { it.outputDir }.get())
|
||||
spigotDecompJar.set(allTasks.spigotDecompileJar.flatMap { it.outputJar }.get())
|
||||
|
||||
// library class imports
|
||||
mcLibrarySourcesDir.set(allTasks.downloadMcLibraries.flatMap { it.sourcesOutputDir }.get())
|
||||
libraryImports.set(extension.paper.libraryClassImports)
|
||||
|
||||
outputPatchDir.set(extension.paper.remappedSpigotServerPatchDir)
|
||||
}
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue