Compare commits
No commits in common. "main" and "minishit" have entirely different histories.
191 changed files with 4453 additions and 17440 deletions
|
@ -1,85 +0,0 @@
|
|||
# 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
|
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 }}
|
30
.github/workflows/deploy.yml
vendored
30
.github/workflows/deploy.yml
vendored
|
@ -1,30 +0,0 @@
|
|||
name: Deploy
|
||||
on:
|
||||
push:
|
||||
tags: [ 'v*' ]
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
name: Deploy
|
||||
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: Deploy release
|
||||
run: ./gradlew publishPlugins --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 }}
|
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
|
4
.gitignore
vendored
4
.gitignore
vendored
|
@ -37,8 +37,8 @@ ehthumbs_vista.db
|
|||
*.lnk
|
||||
|
||||
# Gradle
|
||||
**/.gradle/
|
||||
**/build/
|
||||
.gradle
|
||||
/build/
|
||||
.gradletasknamecache
|
||||
|
||||
# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored)
|
||||
|
|
|
@ -1,9 +1,74 @@
|
|||
tasks.register("printVersion") {
|
||||
doFirst {
|
||||
println(version)
|
||||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||
|
||||
plugins {
|
||||
idea
|
||||
eclipse
|
||||
maven
|
||||
`kotlin-dsl`
|
||||
id("net.minecrell.licenser") version "0.4.1"
|
||||
id("com.github.johnrengelman.shadow") version "6.0.0"
|
||||
}
|
||||
|
||||
group = "io.papermc.paperweight"
|
||||
version = "1.0.0-SNAPSHOT"
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
maven("https://oss.sonatype.org/content/repositories/snapshots/")
|
||||
maven("https://files.minecraftforge.net/maven/")
|
||||
maven("https://maven.fabricmc.net/")
|
||||
mavenLocal()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation("org.apache.httpcomponents:httpclient:4.5.13")
|
||||
|
||||
// Utils
|
||||
implementation("com.opencsv:opencsv:5.3")
|
||||
implementation("com.github.salomonbrys.kotson:kotson:2.5.0")
|
||||
|
||||
// ASM for inspection
|
||||
val asmVersion = "9.0"
|
||||
implementation("org.ow2.asm:asm:$asmVersion")
|
||||
implementation("org.ow2.asm:asm-tree:$asmVersion")
|
||||
|
||||
// Cadix
|
||||
val lorenzVersion = "0.5.6"
|
||||
implementation("org.cadixdev:lorenz:$lorenzVersion")
|
||||
implementation("org.cadixdev:lorenz-asm:$lorenzVersion")
|
||||
implementation("org.cadixdev:lorenz-io-proguard:$lorenzVersion")
|
||||
implementation("org.cadixdev:atlas:0.2.0")
|
||||
implementation("org.cadixdev:at:0.1.0-rc1")
|
||||
implementation("org.cadixdev:mercury:0.1.0-rc2-PW-SNAPSHOT")
|
||||
|
||||
implementation("net.fabricmc:lorenz-tiny:3.0.0")
|
||||
}
|
||||
|
||||
tasks.withType<KotlinCompile> {
|
||||
kotlinOptions.jvmTarget = "1.8"
|
||||
kotlinOptions.freeCompilerArgs = listOf("-Xjvm-default=enable")
|
||||
}
|
||||
|
||||
tasks.jar {
|
||||
archiveBaseName.set("io.papermc.paperweight.gradle.plugin")
|
||||
}
|
||||
|
||||
idea {
|
||||
module {
|
||||
isDownloadSources = true
|
||||
isDownloadJavadoc = true
|
||||
}
|
||||
}
|
||||
|
||||
tasks.wrapper {
|
||||
distributionType = Wrapper.DistributionType.ALL
|
||||
eclipse {
|
||||
classpath {
|
||||
isDownloadSources = true
|
||||
isDownloadJavadoc = true
|
||||
}
|
||||
}
|
||||
|
||||
license {
|
||||
header = file("license/copyright.txt")
|
||||
|
||||
include("**/*.kt")
|
||||
}
|
||||
|
|
|
@ -1,22 +0,0 @@
|
|||
plugins {
|
||||
`kotlin-dsl`
|
||||
`kotlin-dsl-precompiled-script-plugins`
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
gradlePluginPortal()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(libs.gradle.licenser)
|
||||
implementation(libs.gradle.spotless)
|
||||
implementation(libs.gradle.shadow)
|
||||
implementation(libs.gradle.kotlin.dsl)
|
||||
implementation(libs.gradle.plugin.kotlin.withVersion(embeddedKotlinVersion))
|
||||
implementation(libs.gradle.plugin.publish)
|
||||
}
|
||||
|
||||
fun Provider<MinimalExternalModuleDependency>.withVersion(version: String): Provider<String> {
|
||||
return map { "${it.module.group}:${it.module.name}:$version" }
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
rootProject.name = "buildSrc"
|
||||
|
||||
dependencyResolutionManagement {
|
||||
versionCatalogs {
|
||||
create("libs") {
|
||||
from(files("../gradle/libs.versions.toml"))
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,117 +0,0 @@
|
|||
import com.diffplug.gradle.spotless.SpotlessExtension
|
||||
import net.kyori.indra.licenser.spotless.IndraSpotlessLicenserExtension
|
||||
|
||||
plugins {
|
||||
idea
|
||||
id("org.gradle.kotlin.kotlin-dsl")
|
||||
}
|
||||
|
||||
java {
|
||||
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/") {
|
||||
mavenContent {
|
||||
includeModule("org.cadixdev", "mercury")
|
||||
}
|
||||
}
|
||||
maven("https://repo.papermc.io/repository/maven-public/") {
|
||||
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")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
configurations.all {
|
||||
if (name == "compileOnly") {
|
||||
return@all
|
||||
}
|
||||
dependencies.remove(project.dependencies.gradleApi())
|
||||
dependencies.removeIf { it.group == "org.jetbrains.kotlin" }
|
||||
}
|
||||
|
||||
tasks.jar {
|
||||
manifest {
|
||||
attributes(
|
||||
"Implementation-Version" to project.version
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// The following is to work around https://github.com/diffplug/spotless/issues/1599
|
||||
// Ensure the ktlint step is before the license header step
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
tasks.register("format") {
|
||||
group = "formatting"
|
||||
description = "Formats source code according to project style"
|
||||
dependsOn(tasks.named("spotlessApply"))
|
||||
}
|
||||
|
||||
idea {
|
||||
module {
|
||||
isDownloadSources = true
|
||||
}
|
||||
}
|
|
@ -1,157 +0,0 @@
|
|||
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
|
||||
|
||||
plugins {
|
||||
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
|
||||
configurations.implementation {
|
||||
extendsFrom(shade)
|
||||
}
|
||||
|
||||
fun ShadowJar.configureStandard() {
|
||||
configurations = listOf(shade)
|
||||
|
||||
dependencies {
|
||||
exclude(dependency("org.jetbrains.kotlin:.*:.*"))
|
||||
}
|
||||
|
||||
exclude("META-INF/*.SF", "META-INF/*.DSA", "META-INF/*.RSA", "OSGI-INF/**", "*.profile", "module-info.class", "ant_tasks/**")
|
||||
|
||||
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 shadowJar by tasks.existing(ShadowJar::class) {
|
||||
archiveClassifier.set(null as String?)
|
||||
configureStandard()
|
||||
|
||||
inputs.property("noRelocate", noRelocate)
|
||||
if (noRelocate) {
|
||||
return@existing
|
||||
}
|
||||
|
||||
val prefix = "obsidian.libs"
|
||||
listOf(
|
||||
"com.github.salomonbrys.kotson",
|
||||
"com.google.errorprone.annotations",
|
||||
"com.google.gson",
|
||||
"dev.denwav.hypo",
|
||||
"io.sigpipe.jbsdiff",
|
||||
"me.jamiemansfield",
|
||||
"net.fabricmc",
|
||||
"org.apache.commons",
|
||||
"org.apache.felix",
|
||||
"org.apache.http",
|
||||
"org.cadixdev",
|
||||
"org.eclipse",
|
||||
"org.jgrapht",
|
||||
"org.jheaps",
|
||||
"org.objectweb.asm",
|
||||
"org.osgi",
|
||||
"org.tukaani.xz",
|
||||
"org.slf4j",
|
||||
"codechicken.diffpatch",
|
||||
"codechicken.repack"
|
||||
).forEach { pack ->
|
||||
relocate(pack, "$prefix.$pack")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
val MAVEN_PASSWORD = "AriasCreationsMavenPassword"
|
||||
val MAVEN_USER = "AriasCreationsMavenUser"
|
||||
|
||||
|
||||
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 {
|
||||
withType(MavenPublication::class).configureEach {
|
||||
pom {
|
||||
pomConfig()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun MavenPom.pomConfig() {
|
||||
val repoPath = "ObsidianMC/nugget"
|
||||
val repoUrl = "https://git.zontreck.com/$repoPath"
|
||||
|
||||
name.set("nugget")
|
||||
description.set("Gradle plugin for the ObsidianMC project")
|
||||
url.set(repoUrl)
|
||||
inceptionYear.set("2020")
|
||||
|
||||
licenses {
|
||||
license {
|
||||
name.set("LGPLv2.1")
|
||||
url.set("$repoUrl/blob/master/license/LGPLv2.1.txt")
|
||||
distribution.set("repo")
|
||||
}
|
||||
}
|
||||
|
||||
issueManagement {
|
||||
system.set("GitHub")
|
||||
url.set("$repoUrl/issues")
|
||||
}
|
||||
|
||||
developers {
|
||||
developer {
|
||||
id.set("DenWav")
|
||||
name.set("Kyle Wood")
|
||||
email.set("kyle@denwav.dev")
|
||||
url.set("https://github.com/DenWav")
|
||||
}
|
||||
}
|
||||
|
||||
scm {
|
||||
url.set(repoUrl)
|
||||
connection.set("scm:git:$repoUrl.git")
|
||||
developerConnection.set("scm:git:git@github.com:$repoPath.git")
|
||||
}
|
||||
}
|
|
@ -1,5 +0,0 @@
|
|||
group = com.zontreck.nugget
|
||||
version = 1.7.4-SNAPSHOT
|
||||
|
||||
org.gradle.caching = true
|
||||
org.gradle.parallel = true
|
|
@ -1,46 +0,0 @@
|
|||
[versions]
|
||||
asm = "9.7"
|
||||
lorenz = "0.5.8"
|
||||
hypo = "1.2.4"
|
||||
|
||||
[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"
|
||||
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-at = "org.cadixdev:at:0.1.0-rc1"
|
||||
cadix-mercury = "org.cadixdev:mercury:0.1.2-paperweight-SNAPSHOT"
|
||||
|
||||
hypo-model = { module = "dev.denwav.hypo:hypo-model", version.ref = "hypo" }
|
||||
hypo-core = { module = "dev.denwav.hypo:hypo-core", version.ref = "hypo" }
|
||||
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"
|
||||
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"
|
||||
|
||||
[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-6.7-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,12 +1,12 @@
|
|||
paperweight is a Gradle plugin for the PaperMC project.
|
||||
paperweight is a Gradle plugin for the PaperMC project. It uses
|
||||
some code and systems originally from ForgeGradle.
|
||||
|
||||
Copyright (c) 2023 Kyle Wood (DenWav)
|
||||
Contributors
|
||||
Copyright (C) 2020 Kyle Wood
|
||||
|
||||
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.
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
|
|
|
@ -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,64 +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 com.github.salomonbrys.kotson.fromJson
|
||||
import io.papermc.paperweight.core.ext
|
||||
import io.papermc.paperweight.core.extension.PaperweightCoreExtension
|
||||
import io.papermc.paperweight.tasks.*
|
||||
import io.papermc.paperweight.util.*
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.provider.Provider
|
||||
import org.gradle.api.tasks.TaskContainer
|
||||
import org.gradle.kotlin.dsl.*
|
||||
|
||||
@Suppress("MemberVisibilityCanBePrivate")
|
||||
open class GeneralTasks(
|
||||
project: Project,
|
||||
tasks: TaskContainer = project.tasks,
|
||||
extension: PaperweightCoreExtension = project.ext,
|
||||
) : InitialTasks(project) {
|
||||
|
||||
// Configuration won't necessarily always run, so do it as the first task when it's needed as well
|
||||
val initSubmodules by tasks.registering<InitSubmodules> {
|
||||
offlineMode.set(project.offlineMode())
|
||||
}
|
||||
|
||||
val buildDataInfo: Provider<BuildDataInfo> = project.contents(extension.craftBukkit.buildDataInfo) {
|
||||
gson.fromJson(it)
|
||||
}
|
||||
|
||||
val filterVanillaJar by tasks.registering<FilterJar> {
|
||||
inputJar.set(extractFromBundler.flatMap { it.serverJar })
|
||||
includes.set(extension.vanillaJarIncludes)
|
||||
}
|
||||
|
||||
val collectAtsFromPatches by tasks.registering<CollectATsFromPatches> {
|
||||
patchDir.set(extension.paper.spigotServerPatchDir)
|
||||
}
|
||||
|
||||
val mergePaperAts by tasks.registering<MergeAccessTransforms> {
|
||||
firstFile.set(extension.paper.additionalAts.fileExists(project))
|
||||
secondFile.set(collectAtsFromPatches.flatMap { it.outputFile })
|
||||
}
|
||||
}
|
|
@ -1,98 +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 io.papermc.paperweight.util.data.*
|
||||
import java.nio.file.Path
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.provider.Provider
|
||||
import org.gradle.api.tasks.TaskContainer
|
||||
import org.gradle.kotlin.dsl.*
|
||||
|
||||
@Suppress("MemberVisibilityCanBePrivate")
|
||||
open class InitialTasks(
|
||||
project: Project,
|
||||
tasks: TaskContainer = project.tasks,
|
||||
cache: Path = project.layout.cache,
|
||||
extension: PaperweightCoreExtension = project.ext,
|
||||
downloadService: Provider<DownloadService> = project.download
|
||||
) {
|
||||
|
||||
val downloadMcManifest by tasks.registering<DownloadTask> {
|
||||
url.set(MC_MANIFEST_URL)
|
||||
outputFile.set(cache.resolve(MC_MANIFEST))
|
||||
|
||||
doNotTrackState("The Minecraft manifest is a changing resource")
|
||||
|
||||
downloader.set(downloadService)
|
||||
}
|
||||
private val mcManifest = downloadMcManifest.flatMap { it.outputFile }.map { gson.fromJson<MinecraftManifest>(it) }
|
||||
|
||||
val downloadMcVersionManifest by tasks.registering<DownloadTask> {
|
||||
url.set(
|
||||
mcManifest.zip(extension.minecraftVersion) { manifest, version ->
|
||||
manifest.versions.first { it.id == version }.url
|
||||
}
|
||||
)
|
||||
expectedHash.set(
|
||||
mcManifest.zip(extension.minecraftVersion) { manifest, version ->
|
||||
manifest.versions.first { it.id == version }.hash()
|
||||
}
|
||||
)
|
||||
outputFile.set(cache.resolve(VERSION_JSON))
|
||||
|
||||
downloader.set(downloadService)
|
||||
}
|
||||
private val versionManifest = downloadMcVersionManifest.flatMap { it.outputFile }.map { gson.fromJson<MinecraftVersionManifest>(it) }
|
||||
|
||||
val downloadMappings by tasks.registering<DownloadTask> {
|
||||
url.set(versionManifest.map { version -> version.serverMappingsDownload().url })
|
||||
expectedHash.set(versionManifest.map { version -> version.serverMappingsDownload().hash() })
|
||||
outputFile.set(cache.resolve(SERVER_MAPPINGS))
|
||||
|
||||
downloader.set(downloadService)
|
||||
}
|
||||
|
||||
val downloadServerJar by tasks.registering<DownloadServerJar> {
|
||||
downloadUrl.set(versionManifest.map { version -> version.serverDownload().url })
|
||||
expectedHash.set(versionManifest.map { version -> version.serverDownload().hash() })
|
||||
|
||||
downloader.set(downloadService)
|
||||
}
|
||||
|
||||
val extractFromBundler by tasks.registering<ExtractFromBundler> {
|
||||
bundlerJar.set(downloadServerJar.flatMap { it.outputJar })
|
||||
|
||||
versionJson.set(cache.resolve(SERVER_VERSION_JSON))
|
||||
serverLibrariesTxt.set(cache.resolve(SERVER_LIBRARIES_TXT))
|
||||
serverLibrariesList.set(cache.resolve(SERVER_LIBRARIES_LIST))
|
||||
serverVersionsList.set(cache.resolve(SERVER_VERSIONS_LIST))
|
||||
serverLibraryJars.set(cache.resolve(MINECRAFT_JARS_PATH))
|
||||
}
|
||||
}
|
|
@ -1,205 +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 SpigotTasks(
|
||||
project: Project,
|
||||
tasks: TaskContainer = project.tasks,
|
||||
cache: Path = project.layout.cache,
|
||||
extension: PaperweightCoreExtension = project.ext,
|
||||
downloadService: Provider<DownloadService> = project.download,
|
||||
) : VanillaTasks(project) {
|
||||
|
||||
val addAdditionalSpigotMappings by tasks.registering<AddAdditionalSpigotMappings> {
|
||||
dependsOn(initSubmodules)
|
||||
classSrg.set(extension.craftBukkit.mappingsDir.file(buildDataInfo.map { it.classMappings }))
|
||||
additionalClassEntriesSrg.set(extension.paper.additionalSpigotClassMappings.fileExists(project))
|
||||
}
|
||||
|
||||
val generateSpigotMappings by tasks.registering<GenerateSpigotMappings> {
|
||||
classMappings.set(addAdditionalSpigotMappings.flatMap { it.outputClassSrg })
|
||||
|
||||
sourceMappings.set(generateMappings.flatMap { it.outputMappings })
|
||||
|
||||
outputMappings.set(cache.resolve(SPIGOT_MOJANG_YARN_MAPPINGS))
|
||||
notchToSpigotMappings.set(cache.resolve(OBF_SPIGOT_MAPPINGS))
|
||||
spigotMemberMappings.set(cache.resolve(SPIGOT_MEMBER_MAPPINGS))
|
||||
}
|
||||
|
||||
val spigotRemapJar by tasks.registering<SpigotRemapJar> {
|
||||
inputJar.set(filterVanillaJar.flatMap { it.outputJar })
|
||||
classMappings.set(addAdditionalSpigotMappings.flatMap { it.outputClassSrg })
|
||||
accessTransformers.set(extension.craftBukkit.mappingsDir.file(buildDataInfo.map { it.accessTransforms }))
|
||||
|
||||
memberMappings.set(generateSpigotMappings.flatMap { it.spigotMemberMappings })
|
||||
|
||||
mcVersion.set(extension.minecraftVersion)
|
||||
|
||||
workDirName.set(extension.craftBukkit.buildDataInfo.asFile.map { it.parentFile.parentFile.name })
|
||||
|
||||
specialSourceJar.set(extension.craftBukkit.specialSourceJar)
|
||||
specialSource2Jar.set(extension.craftBukkit.specialSource2Jar)
|
||||
|
||||
classMapCommand.set(buildDataInfo.map { it.classMapCommand })
|
||||
memberMapCommand.set(buildDataInfo.map { it.memberMapCommand })
|
||||
finalMapCommand.set(buildDataInfo.map { it.finalMapCommand })
|
||||
}
|
||||
|
||||
val cleanupMappings by tasks.registering<CleanupMappings> {
|
||||
sourceJar.set(spigotRemapJar.flatMap { it.outputJar })
|
||||
libraries.from(extractFromBundler.map { it.serverLibraryJars.asFileTree })
|
||||
inputMappings.set(generateSpigotMappings.flatMap { it.outputMappings })
|
||||
|
||||
outputMappings.set(cache.resolve(CLEANED_SPIGOT_MOJANG_YARN_MAPPINGS))
|
||||
}
|
||||
|
||||
val patchMappings by tasks.registering<PatchMappings> {
|
||||
inputMappings.set(cleanupMappings.flatMap { it.outputMappings })
|
||||
patch.set(extension.paper.mappingsPatch.fileExists(project))
|
||||
|
||||
fromNamespace.set(SPIGOT_NAMESPACE)
|
||||
toNamespace.set(DEOBF_NAMESPACE)
|
||||
|
||||
outputMappings.set(cache.resolve(PATCHED_SPIGOT_MOJANG_YARN_MAPPINGS))
|
||||
}
|
||||
|
||||
val cleanupSourceMappings by tasks.registering<CleanupSourceMappings> {
|
||||
sourceJar.set(spigotRemapJar.flatMap { it.outputJar })
|
||||
libraries.from(extractFromBundler.map { it.serverLibraryJars.asFileTree })
|
||||
inputMappings.set(patchMappings.flatMap { it.outputMappings })
|
||||
|
||||
outputMappings.set(cache.resolve(PATCHED_SPIGOT_MOJANG_YARN_SOURCE_MAPPINGS))
|
||||
}
|
||||
|
||||
val filterSpigotExcludes by tasks.registering<FilterSpigotExcludes> {
|
||||
inputZip.set(spigotRemapJar.flatMap { it.outputJar })
|
||||
excludesFile.set(extension.craftBukkit.excludesFile)
|
||||
}
|
||||
|
||||
val spigotDecompileJar by tasks.registering<SpigotDecompileJar> {
|
||||
inputJar.set(filterSpigotExcludes.flatMap { it.outputZip })
|
||||
fernFlowerJar.set(extension.craftBukkit.fernFlowerJar)
|
||||
decompileCommand.set(buildDataInfo.map { it.decompileCommand })
|
||||
}
|
||||
|
||||
val patchCraftBukkitPatches by tasks.registering<ApplyRawDiffPatches> {
|
||||
dependsOn(initSubmodules)
|
||||
inputDir.set(extension.craftBukkit.patchDir)
|
||||
patchDir.set(extension.paper.craftBukkitPatchPatchesDir.fileExists(project))
|
||||
}
|
||||
|
||||
val patchCraftBukkit by tasks.registering<ApplyCraftBukkitPatches> {
|
||||
sourceJar.set(spigotDecompileJar.flatMap { it.outputJar })
|
||||
cleanDirPath.set("net/minecraft")
|
||||
branch.set("patched")
|
||||
patchZip.set(patchCraftBukkitPatches.flatMap { it.outputZip })
|
||||
craftBukkitDir.set(extension.craftBukkit.craftBukkitDir)
|
||||
}
|
||||
|
||||
val patchSpigotApiPatches by tasks.registering<ApplyRawDiffPatches> {
|
||||
dependsOn(initSubmodules)
|
||||
inputDir.set(extension.spigot.bukkitPatchDir)
|
||||
patchDir.set(extension.paper.spigotApiPatchPatchesDir.fileExists(project))
|
||||
}
|
||||
|
||||
val patchSpigotServerPatches by tasks.registering<ApplyRawDiffPatches> {
|
||||
dependsOn(initSubmodules)
|
||||
inputDir.set(extension.spigot.craftBukkitPatchDir)
|
||||
patchDir.set(extension.paper.spigotServerPatchPatchesDir.fileExists(project))
|
||||
}
|
||||
|
||||
val patchSpigotApi by tasks.registering<ApplyGitPatches> {
|
||||
branch.set("HEAD")
|
||||
upstreamBranch.set("upstream")
|
||||
upstream.set(extension.craftBukkit.bukkitDir)
|
||||
patchZip.set(patchSpigotApiPatches.flatMap { it.outputZip })
|
||||
|
||||
outputDir.set(extension.spigot.spigotApiDir)
|
||||
}
|
||||
|
||||
val patchSpigotServer by tasks.registering<ApplyGitPatches> {
|
||||
branch.set("HEAD")
|
||||
upstreamBranch.set("upstream")
|
||||
upstream.set(patchCraftBukkit.flatMap { it.outputDir })
|
||||
patchZip.set(patchSpigotServerPatches.flatMap { it.outputZip })
|
||||
|
||||
outputDir.set(extension.spigot.spigotServerDir)
|
||||
}
|
||||
|
||||
val patchSpigot by tasks.registering<Task> {
|
||||
dependsOn(patchSpigotApi, patchSpigotServer)
|
||||
}
|
||||
|
||||
val downloadSpigotDependencies by tasks.registering<DownloadSpigotDependencies> {
|
||||
dependsOn(patchSpigot)
|
||||
apiPom.set(patchSpigotApi.flatMap { it.outputDir.file("pom.xml") })
|
||||
serverPom.set(patchSpigotServer.flatMap { it.outputDir.file("pom.xml") })
|
||||
mcLibrariesFile.set(extractFromBundler.flatMap { it.serverLibrariesTxt })
|
||||
outputDir.set(cache.resolve(SPIGOT_JARS_PATH))
|
||||
outputSourcesDir.set(cache.resolve(SPIGOT_SOURCES_JARS_PATH))
|
||||
|
||||
downloader.set(downloadService)
|
||||
}
|
||||
|
||||
val remapSpigotAt by tasks.registering<RemapSpigotAt> {
|
||||
inputJar.set(spigotRemapJar.flatMap { it.outputJar })
|
||||
mapping.set(patchMappings.flatMap { it.outputMappings })
|
||||
spigotAt.set(extension.craftBukkit.atFile)
|
||||
}
|
||||
|
||||
@Suppress("DuplicatedCode")
|
||||
val remapSpigotSources by tasks.registering<RemapSources> {
|
||||
spigotServerDir.set(patchSpigotServer.flatMap { it.outputDir })
|
||||
spigotApiDir.set(patchSpigotApi.flatMap { it.outputDir })
|
||||
mappings.set(cleanupSourceMappings.flatMap { it.outputMappings })
|
||||
vanillaJar.set(extractFromBundler.flatMap { it.serverJar })
|
||||
mojangMappedVanillaJar.set(fixJar.flatMap { it.outputJar })
|
||||
vanillaRemappedSpigotJar.set(filterSpigotExcludes.flatMap { it.outputZip })
|
||||
spigotDeps.from(downloadSpigotDependencies.map { it.outputDir.asFileTree })
|
||||
additionalAts.set(mergePaperAts.flatMap { it.outputFile })
|
||||
}
|
||||
|
||||
val remapGeneratedAt by tasks.registering<RemapAccessTransform> {
|
||||
inputFile.set(remapSpigotSources.flatMap { it.generatedAt })
|
||||
mappings.set(patchMappings.flatMap { it.outputMappings })
|
||||
}
|
||||
|
||||
val mergeGeneratedAts by tasks.registering<MergeAccessTransforms> {
|
||||
firstFile.set(remapGeneratedAt.flatMap { it.outputFile })
|
||||
secondFile.set(remapSpigotAt.flatMap { it.outputFile })
|
||||
}
|
||||
}
|
|
@ -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.core.taskcontainers
|
||||
|
||||
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.tasks.TaskContainer
|
||||
import org.gradle.kotlin.dsl.*
|
||||
|
||||
@Suppress("MemberVisibilityCanBePrivate")
|
||||
open class VanillaTasks(
|
||||
project: Project,
|
||||
tasks: TaskContainer = project.tasks,
|
||||
cache: Path = project.layout.cache,
|
||||
) : GeneralTasks(project) {
|
||||
|
||||
val generateMappings by tasks.registering<GenerateMappings> {
|
||||
vanillaJar.set(filterVanillaJar.flatMap { it.outputJar })
|
||||
libraries.from(extractFromBundler.map { it.serverLibraryJars.asFileTree })
|
||||
|
||||
vanillaMappings.set(downloadMappings.flatMap { it.outputFile })
|
||||
paramMappings.fileProvider(project.configurations.named(PARAM_MAPPINGS_CONFIG).map { it.singleFile })
|
||||
|
||||
outputMappings.set(cache.resolve(MOJANG_YARN_MAPPINGS))
|
||||
}
|
||||
|
||||
val remapJar by tasks.registering<RemapJar> {
|
||||
inputJar.set(filterVanillaJar.flatMap { it.outputJar })
|
||||
mappingsFile.set(generateMappings.flatMap { it.outputMappings })
|
||||
fromNamespace.set(OBF_NAMESPACE)
|
||||
toNamespace.set(DEOBF_NAMESPACE)
|
||||
remapper.from(project.configurations.named(REMAPPER_CONFIG))
|
||||
remapperArgs.set(TinyRemapper.minecraftRemapArgs)
|
||||
}
|
||||
|
||||
val fixJar by tasks.registering<FixJarTask> {
|
||||
inputJar.set(remapJar.flatMap { it.outputJar })
|
||||
vanillaJar.set(extractFromBundler.flatMap { it.serverJar })
|
||||
}
|
||||
}
|
|
@ -1,146 +0,0 @@
|
|||
/*
|
||||
* paperweight is a Gradle plugin for the PaperMC project.
|
||||
*
|
||||
* Copyright (c) 2023 Kyle Wood (DenWav)
|
||||
* Contributors
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation;
|
||||
* version 2.1 only, no later versions.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
|
||||
* USA
|
||||
*/
|
||||
|
||||
package io.papermc.paperweight.core.tasks
|
||||
|
||||
import io.papermc.paperweight.util.*
|
||||
import io.papermc.paperweight.util.data.UpstreamData
|
||||
import javax.inject.Inject
|
||||
import kotlin.io.path.*
|
||||
import org.gradle.api.DefaultTask
|
||||
import org.gradle.api.artifacts.Configuration
|
||||
import org.gradle.api.file.DirectoryProperty
|
||||
import org.gradle.api.file.RegularFileProperty
|
||||
import org.gradle.api.provider.ListProperty
|
||||
import org.gradle.api.provider.Property
|
||||
import org.gradle.api.provider.ProviderFactory
|
||||
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.OutputFile
|
||||
import org.gradle.api.tasks.TaskAction
|
||||
|
||||
abstract class PaperweightCorePrepareForDownstream : DefaultTask() {
|
||||
|
||||
@get:InputFile
|
||||
abstract val vanillaJar: RegularFileProperty
|
||||
|
||||
@get:InputFile
|
||||
abstract val remappedJar: RegularFileProperty
|
||||
|
||||
@get:InputFile
|
||||
abstract val decompiledJar: RegularFileProperty
|
||||
|
||||
@get:Input
|
||||
abstract val mcVersion: Property<String>
|
||||
|
||||
@get:InputDirectory
|
||||
abstract val mcLibrariesDir: DirectoryProperty
|
||||
|
||||
@get:InputDirectory
|
||||
abstract val mcLibrariesSourcesDir: DirectoryProperty
|
||||
|
||||
@get:InputDirectory
|
||||
abstract val spigotLibrariesSourcesDir: DirectoryProperty
|
||||
|
||||
@get:Input
|
||||
abstract val vanillaJarIncludes: ListProperty<String>
|
||||
|
||||
@get:InputFile
|
||||
abstract val mcLibrariesFile: RegularFileProperty
|
||||
|
||||
@get:InputFile
|
||||
abstract val mappings: RegularFileProperty
|
||||
|
||||
@get:InputFile
|
||||
abstract val notchToSpigotMappings: RegularFileProperty
|
||||
|
||||
@get:InputFile
|
||||
abstract val sourceMappings: RegularFileProperty
|
||||
|
||||
@get:Input
|
||||
abstract val reobfPackagesToFix: ListProperty<String>
|
||||
|
||||
@get:InputFile
|
||||
abstract val reobfMappingsPatch: RegularFileProperty
|
||||
|
||||
@get:OutputFile
|
||||
abstract val dataFile: RegularFileProperty
|
||||
|
||||
@get:Inject
|
||||
abstract val providers: ProviderFactory
|
||||
|
||||
@get:Input
|
||||
abstract val paramMappingsUrl: Property<String>
|
||||
|
||||
@get:Classpath
|
||||
abstract val paramMappingsConfig: Property<Configuration>
|
||||
|
||||
@get:InputFile
|
||||
abstract val atFile: RegularFileProperty
|
||||
|
||||
@get:InputFile
|
||||
abstract val spigotRecompiledClasses: RegularFileProperty
|
||||
|
||||
@get:InputFile
|
||||
abstract val bundlerVersionJson: RegularFileProperty
|
||||
|
||||
@get:InputFile
|
||||
abstract val serverLibrariesTxt: RegularFileProperty
|
||||
|
||||
@get:InputFile
|
||||
abstract val serverLibrariesList: RegularFileProperty
|
||||
|
||||
@TaskAction
|
||||
fun run() {
|
||||
val dataFilePath = dataFile.path
|
||||
|
||||
dataFilePath.parent.createDirectories()
|
||||
|
||||
val data = UpstreamData(
|
||||
vanillaJar.path,
|
||||
remappedJar.path,
|
||||
decompiledJar.path,
|
||||
mcVersion.get(),
|
||||
mcLibrariesDir.path,
|
||||
mcLibrariesSourcesDir.path,
|
||||
mcLibrariesFile.path,
|
||||
spigotLibrariesSourcesDir.path,
|
||||
mappings.path,
|
||||
notchToSpigotMappings.path,
|
||||
sourceMappings.path,
|
||||
reobfPackagesToFix.get(),
|
||||
reobfMappingsPatch.path,
|
||||
vanillaJarIncludes.get(),
|
||||
determineMavenDep(paramMappingsUrl, paramMappingsConfig),
|
||||
atFile.path,
|
||||
spigotRecompiledClasses.path,
|
||||
bundlerVersionJson.path,
|
||||
serverLibrariesTxt.path,
|
||||
serverLibrariesList.path
|
||||
)
|
||||
dataFilePath.bufferedWriter(Charsets.UTF_8).use { writer ->
|
||||
gson.toJson(data, writer)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,30 +0,0 @@
|
|||
/*
|
||||
* paperweight is a Gradle plugin for the PaperMC project.
|
||||
*
|
||||
* Copyright (c) 2023 Kyle Wood (DenWav)
|
||||
* Contributors
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation;
|
||||
* version 2.1 only, no later versions.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
|
||||
* USA
|
||||
*/
|
||||
|
||||
package io.papermc.paperweight.core
|
||||
|
||||
import io.papermc.paperweight.core.extension.PaperweightCoreExtension
|
||||
import io.papermc.paperweight.util.constants.*
|
||||
import org.gradle.api.Project
|
||||
|
||||
val Project.ext: PaperweightCoreExtension
|
||||
get() = extensions.getByName(PAPERWEIGHT_EXTENSION) as PaperweightCoreExtension
|
|
@ -1,27 +0,0 @@
|
|||
plugins {
|
||||
`config-kotlin`
|
||||
}
|
||||
|
||||
repositories {
|
||||
gradlePluginPortal()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(libs.httpclient)
|
||||
implementation(libs.bundles.kotson)
|
||||
|
||||
// ASM for inspection
|
||||
implementation(libs.bundles.asm)
|
||||
|
||||
implementation(libs.bundles.hypo)
|
||||
implementation(libs.slf4j.jdk14) // slf4j impl for hypo
|
||||
implementation(libs.bundles.cadix)
|
||||
|
||||
implementation(libs.lorenzTiny)
|
||||
|
||||
implementation(libs.jbsdiff)
|
||||
|
||||
implementation(variantOf(libs.diffpatch) { classifier("all") }) {
|
||||
isTransitive = false
|
||||
}
|
||||
}
|
|
@ -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,237 +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.cadixdev.at.AccessChange
|
||||
import org.cadixdev.at.AccessTransform
|
||||
import org.cadixdev.at.AccessTransformSet
|
||||
import org.cadixdev.at.ModifierChange
|
||||
import org.cadixdev.at.io.AccessTransformFormats
|
||||
import org.cadixdev.atlas.Atlas
|
||||
import org.cadixdev.atlas.AtlasTransformerContext
|
||||
import org.cadixdev.bombe.analysis.InheritanceProvider
|
||||
import org.cadixdev.bombe.asm.analysis.ClassProviderInheritanceProvider
|
||||
import org.cadixdev.bombe.asm.jar.ClassProvider
|
||||
import org.cadixdev.bombe.jar.JarClassEntry
|
||||
import org.cadixdev.bombe.jar.JarEntryTransformer
|
||||
import org.cadixdev.bombe.type.signature.MethodSignature
|
||||
import org.gradle.api.file.RegularFileProperty
|
||||
import org.gradle.api.provider.ListProperty
|
||||
import org.gradle.api.tasks.*
|
||||
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.ClassReader
|
||||
import org.objectweb.asm.ClassVisitor
|
||||
import org.objectweb.asm.ClassWriter
|
||||
import org.objectweb.asm.FieldVisitor
|
||||
import org.objectweb.asm.MethodVisitor
|
||||
import org.objectweb.asm.Opcodes
|
||||
|
||||
fun applyAccessTransform(
|
||||
inputJarPath: Path,
|
||||
outputJarPath: Path,
|
||||
atFilePath: Path,
|
||||
jvmArgs: List<String> = listOf("-Xmx1G"),
|
||||
workerExecutor: WorkerExecutor,
|
||||
launcher: JavaLauncher
|
||||
): WorkQueue {
|
||||
ensureParentExists(outputJarPath)
|
||||
ensureDeleted(outputJarPath)
|
||||
|
||||
val queue = workerExecutor.processIsolation {
|
||||
forkOptions.jvmArgs(jvmArgs)
|
||||
forkOptions.executable(launcher.executablePath.path.absolutePathString())
|
||||
}
|
||||
|
||||
queue.submit(ApplyAccessTransform.AtlasAction::class) {
|
||||
inputJar.set(inputJarPath)
|
||||
atFile.set(atFilePath)
|
||||
outputJar.set(outputJarPath)
|
||||
}
|
||||
|
||||
return queue
|
||||
}
|
||||
|
||||
@CacheableTask
|
||||
abstract class ApplyAccessTransform : JavaLauncherTask() {
|
||||
|
||||
@get:Classpath
|
||||
abstract val inputJar: RegularFileProperty
|
||||
|
||||
@get:InputFile
|
||||
@get:PathSensitive(PathSensitivity.NONE)
|
||||
abstract val atFile: 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("-Xmx1G"))
|
||||
outputJar.convention(defaultOutput())
|
||||
}
|
||||
|
||||
@TaskAction
|
||||
fun run() {
|
||||
applyAccessTransform(
|
||||
inputJarPath = inputJar.path,
|
||||
outputJarPath = outputJar.path,
|
||||
atFilePath = atFile.path,
|
||||
jvmArgs = jvmargs.get(),
|
||||
workerExecutor = workerExecutor,
|
||||
launcher = launcher.get()
|
||||
)
|
||||
}
|
||||
|
||||
abstract class AtlasAction : WorkAction<AtlasParameters> {
|
||||
override fun execute() {
|
||||
val at = AccessTransformFormats.FML.read(parameters.atFile.path)
|
||||
|
||||
Atlas().apply {
|
||||
install {
|
||||
// Replace the inheritance provider to set ASM9 opcodes
|
||||
val inheritanceProvider = AtlasTransformerContext::class.java.getDeclaredField("inheritanceProvider")
|
||||
inheritanceProvider.isAccessible = true
|
||||
val classProvider = ClassProviderInheritanceProvider::class.java.getDeclaredField("provider")
|
||||
classProvider.isAccessible = true
|
||||
inheritanceProvider.set(
|
||||
it,
|
||||
ClassProviderInheritanceProvider(
|
||||
Opcodes.ASM9,
|
||||
classProvider.get(inheritanceProvider.get(it)) as ClassProvider
|
||||
)
|
||||
)
|
||||
// End replace inheritance provider
|
||||
|
||||
AtJarEntryTransformer(it, at)
|
||||
}
|
||||
run(parameters.inputJar.path, parameters.outputJar.path)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface AtlasParameters : WorkParameters {
|
||||
val inputJar: RegularFileProperty
|
||||
val atFile: RegularFileProperty
|
||||
val outputJar: RegularFileProperty
|
||||
}
|
||||
}
|
||||
|
||||
class AtJarEntryTransformer(
|
||||
private val context: AtlasTransformerContext,
|
||||
private val at: AccessTransformSet
|
||||
) : JarEntryTransformer {
|
||||
override fun transform(entry: JarClassEntry): JarClassEntry {
|
||||
val reader = ClassReader(entry.contents)
|
||||
val writer = ClassWriter(reader, 0)
|
||||
reader.accept(AccessTransformerVisitor(at, context.inheritanceProvider(), writer), 0)
|
||||
return JarClassEntry(entry.name, entry.time, writer.toByteArray())
|
||||
}
|
||||
}
|
||||
|
||||
class AccessTransformerVisitor(
|
||||
private val at: AccessTransformSet,
|
||||
private val inheritanceProvider: InheritanceProvider,
|
||||
writer: ClassWriter
|
||||
) : ClassVisitor(Opcodes.ASM9, writer) {
|
||||
|
||||
private lateinit var classTransform: AccessTransformSet.Class
|
||||
|
||||
override fun visit(
|
||||
version: Int,
|
||||
access: Int,
|
||||
name: String,
|
||||
signature: String?,
|
||||
superName: String?,
|
||||
interfaces: Array<out String>?
|
||||
) {
|
||||
classTransform = completedClassAt(name)
|
||||
super.visit(version, classTransform.get().apply(access), name, signature, superName, interfaces)
|
||||
}
|
||||
|
||||
override fun visitField(
|
||||
access: Int,
|
||||
name: String,
|
||||
descriptor: String,
|
||||
signature: String?,
|
||||
value: Any?
|
||||
): FieldVisitor {
|
||||
return super.visitField(classTransform.getField(name).apply(access), name, descriptor, signature, value)
|
||||
}
|
||||
|
||||
override fun visitMethod(
|
||||
access: Int,
|
||||
name: String,
|
||||
descriptor: String,
|
||||
signature: String?,
|
||||
exceptions: Array<out String>?
|
||||
): MethodVisitor {
|
||||
val newAccess = classTransform.getMethod(MethodSignature.of(name, descriptor)).apply(access)
|
||||
return super.visitMethod(newAccess, name, descriptor, signature, exceptions)
|
||||
}
|
||||
|
||||
override fun visitInnerClass(name: String, outerName: String?, innerName: String?, access: Int) {
|
||||
super.visitInnerClass(name, outerName, innerName, completedClassAt(name).get().apply(access))
|
||||
}
|
||||
|
||||
private fun completedClassAt(className: String): AccessTransformSet.Class = synchronized(at) {
|
||||
at.getOrCreateClass(className).apply { complete(inheritanceProvider) }
|
||||
}
|
||||
}
|
||||
|
||||
fun AccessTransform?.apply(currentModifier: Int): Int {
|
||||
if (this == null) {
|
||||
return currentModifier
|
||||
}
|
||||
var value = currentModifier
|
||||
if (this.access != AccessChange.NONE) {
|
||||
value = value and AsmUtil.RESET_ACCESS
|
||||
value = value or this.access.modifier
|
||||
}
|
||||
when (this.final) {
|
||||
ModifierChange.REMOVE -> {
|
||||
value = value and Opcodes.ACC_FINAL.inv()
|
||||
}
|
||||
ModifierChange.ADD -> {
|
||||
value = value or Opcodes.ACC_FINAL
|
||||
}
|
||||
else -> {
|
||||
}
|
||||
}
|
||||
return value
|
||||
}
|
|
@ -1,157 +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.file.Files
|
||||
import java.nio.file.Path
|
||||
import java.util.Date
|
||||
import javax.inject.Inject
|
||||
import kotlin.io.path.*
|
||||
import kotlin.streams.asSequence
|
||||
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 ApplyCraftBukkitPatches : ControllableOutputTask() {
|
||||
|
||||
@get:InputFile
|
||||
abstract val sourceJar: RegularFileProperty
|
||||
|
||||
@get:Input
|
||||
abstract val cleanDirPath: Property<String>
|
||||
|
||||
@get:Optional
|
||||
@get:InputDirectory
|
||||
abstract val patchDir: DirectoryProperty
|
||||
|
||||
@get:Optional
|
||||
@get:InputFile
|
||||
abstract val patchZip: RegularFileProperty
|
||||
|
||||
@get:Input
|
||||
abstract val branch: Property<String>
|
||||
|
||||
@get:Input
|
||||
abstract val ignoreGitIgnore: Property<Boolean>
|
||||
|
||||
@get:InputDirectory
|
||||
abstract val craftBukkitDir: DirectoryProperty
|
||||
|
||||
@get:OutputDirectory
|
||||
abstract val outputDir: DirectoryProperty
|
||||
|
||||
@get:Inject
|
||||
abstract val providers: ProviderFactory
|
||||
|
||||
override fun init() {
|
||||
printOutput.convention(false)
|
||||
ignoreGitIgnore.convention(Git.ignoreProperty(providers)).finalizeValueOnRead()
|
||||
outputDir.convention(project, defaultOutput("repo").path)
|
||||
}
|
||||
|
||||
@TaskAction
|
||||
fun run() {
|
||||
Git.checkForGit()
|
||||
|
||||
outputDir.path.deleteRecursive()
|
||||
outputDir.path.parent.let {
|
||||
it.createDirectories()
|
||||
val git = Git(it)
|
||||
git("clone", "--no-hardlinks", craftBukkitDir.path.absolutePathString(), outputDir.path.absolutePathString()).setupOut().execute()
|
||||
}
|
||||
|
||||
val git = Git(outputDir.path)
|
||||
|
||||
val basePatchDirFile = outputDir.path.resolve("src/main/java")
|
||||
basePatchDirFile.resolve(cleanDirPath.get()).deleteRecursive()
|
||||
|
||||
val patchSource = patchDir.pathOrNull ?: patchZip.path // used for error messages
|
||||
val rootPatchDir = patchDir.pathOrNull ?: patchZip.path.let { unzip(it, findOutputDir(it)) }
|
||||
|
||||
try {
|
||||
if (!rootPatchDir.isDirectory()) {
|
||||
throw PaperweightException("Patch directory does not exist $patchSource")
|
||||
}
|
||||
|
||||
val patchList = Files.walk(rootPatchDir).use { it.asSequence().filter { file -> file.isRegularFile() }.toSet() }
|
||||
if (patchList.isEmpty()) {
|
||||
throw PaperweightException("No patch files found in $patchSource")
|
||||
}
|
||||
|
||||
// Copy in patch targets
|
||||
sourceJar.path.openZip().use { fs ->
|
||||
for (file in patchList) {
|
||||
val javaName = javaFileName(rootPatchDir, file)
|
||||
val out = basePatchDirFile.resolve(javaName)
|
||||
val sourcePath = fs.getPath(javaName)
|
||||
|
||||
out.parent.createDirectories()
|
||||
sourcePath.copyTo(out)
|
||||
}
|
||||
}
|
||||
|
||||
git(*Git.add(ignoreGitIgnore, "src")).setupOut().execute()
|
||||
git("commit", "-m", "Vanilla $ ${Date()}", "--author=Vanilla <auto@mated.null>").setupOut().execute()
|
||||
|
||||
// Apply patches
|
||||
for (file in patchList) {
|
||||
val javaName = javaFileName(rootPatchDir, file)
|
||||
|
||||
if (printOutput.get()) {
|
||||
println("Patching ${javaName.removeSuffix(".java")}")
|
||||
}
|
||||
|
||||
val dirPrefix = basePatchDirFile.relativeTo(outputDir.path).invariantSeparatorsPathString
|
||||
git("apply", "--ignore-whitespace", "--directory=$dirPrefix", file.absolutePathString()).setupOut().execute()
|
||||
}
|
||||
|
||||
git(*Git.add(ignoreGitIgnore, "src")).setupOut().execute()
|
||||
git("commit", "-m", "CraftBukkit $ ${Date()}", "--author=CraftBukkit <auto@mated.null>").setupOut().execute()
|
||||
} finally {
|
||||
if (rootPatchDir != patchDir.pathOrNull) {
|
||||
rootPatchDir.deleteRecursive()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun javaFileName(rootDir: Path, file: Path): String {
|
||||
return file.relativeTo(rootDir).toString().replaceAfterLast('.', "java")
|
||||
}
|
||||
|
||||
private fun Command.setupOut() = apply {
|
||||
if (printOutput.get()) {
|
||||
setup(System.out, System.err)
|
||||
} else {
|
||||
setup(UselessOutputStream, UselessOutputStream)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,220 +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.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.ListProperty
|
||||
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 ApplyGitPatches : ControllableOutputTask() {
|
||||
|
||||
@get:Input
|
||||
abstract val branch: Property<String>
|
||||
|
||||
@get:Input
|
||||
abstract val upstreamBranch: Property<String>
|
||||
|
||||
@get:InputDirectory
|
||||
abstract val upstream: DirectoryProperty
|
||||
|
||||
@get:Optional
|
||||
@get:InputDirectory
|
||||
abstract val patchDir: DirectoryProperty
|
||||
|
||||
@get:Optional
|
||||
@get:InputFile
|
||||
abstract val patchZip: RegularFileProperty
|
||||
|
||||
@get:Optional
|
||||
@get:Input
|
||||
abstract val unneededFiles: ListProperty<String>
|
||||
|
||||
@get:Input
|
||||
abstract val ignoreGitIgnore: Property<Boolean>
|
||||
|
||||
@get:OutputDirectory
|
||||
abstract val outputDir: DirectoryProperty
|
||||
|
||||
@get:Inject
|
||||
abstract val providers: ProviderFactory
|
||||
|
||||
@get:Input
|
||||
abstract val verbose: Property<Boolean>
|
||||
|
||||
override fun init() {
|
||||
printOutput.convention(false).finalizeValueOnRead()
|
||||
ignoreGitIgnore.convention(Git.ignoreProperty(providers)).finalizeValueOnRead()
|
||||
verbose.convention(providers.verboseApplyPatches())
|
||||
}
|
||||
|
||||
@TaskAction
|
||||
fun run() {
|
||||
Git.checkForGit()
|
||||
|
||||
Git(upstream.path).let { git ->
|
||||
git("fetch").setupOut().run()
|
||||
git("branch", "-f", upstreamBranch.get(), branch.get()).runSilently(silenceErr = true)
|
||||
}
|
||||
|
||||
val outputPath = outputDir.path
|
||||
recreateCloneDirectory(outputPath)
|
||||
|
||||
val target = outputPath.name
|
||||
|
||||
if (printOutput.get()) {
|
||||
logger.lifecycle("Resetting $target to ${upstream.path.name}...")
|
||||
}
|
||||
|
||||
val rootPatchDir = patchDir.pathOrNull ?: patchZip.path.let { unzip(it, findOutputDir(it)) }
|
||||
|
||||
try {
|
||||
Git(outputPath).let { git ->
|
||||
checkoutRepoFromUpstream(git, upstream.path, upstreamBranch.get())
|
||||
|
||||
if (unneededFiles.isPresent && unneededFiles.get().size > 0) {
|
||||
unneededFiles.get().forEach { path -> outputDir.path.resolve(path).deleteRecursive() }
|
||||
git(*Git.add(ignoreGitIgnore, ".")).executeSilently()
|
||||
git("commit", "-m", "Initial", "--author=Initial Source <auto@mated.null>").executeSilently()
|
||||
}
|
||||
|
||||
git("tag", "-d", "base").runSilently(silenceErr = true)
|
||||
git("tag", "base").executeSilently(silenceErr = true)
|
||||
|
||||
applyGitPatches(git, target, outputDir.path, rootPatchDir, printOutput.get(), verbose.get())
|
||||
}
|
||||
} finally {
|
||||
if (rootPatchDir != patchDir.pathOrNull) {
|
||||
rootPatchDir.deleteRecursive()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun ControllableOutputTask.applyGitPatches(
|
||||
git: Git,
|
||||
target: String,
|
||||
outputDir: Path,
|
||||
patchDir: Path?,
|
||||
printOutput: Boolean,
|
||||
verbose: Boolean,
|
||||
) {
|
||||
if (printOutput) {
|
||||
logger.lifecycle("Applying patches to $target...")
|
||||
}
|
||||
|
||||
val statusFile = outputDir.resolve(".git/patch-apply-failed")
|
||||
statusFile.deleteForcefully()
|
||||
|
||||
git("am", "--abort").runSilently(silenceErr = true)
|
||||
|
||||
val patches = patchDir?.useDirectoryEntries("*.patch") { it.toMutableList() } ?: mutableListOf()
|
||||
if (patches.isEmpty()) {
|
||||
if (printOutput) {
|
||||
logger.lifecycle("No patches found")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// This prevents the `git am` command line from getting too big with too many patches
|
||||
// mostly an issue with Windows
|
||||
layout.cache.createDirectories()
|
||||
val tempDir = createTempDirectory(layout.cache, "paperweight")
|
||||
try {
|
||||
val mailDir = tempDir.resolve("new")
|
||||
mailDir.createDirectories()
|
||||
|
||||
for (patch in patches) {
|
||||
patch.copyTo(mailDir.resolve(patch.fileName))
|
||||
}
|
||||
|
||||
val gitOut = printOutput && verbose
|
||||
val result = git("am", "--3way", "--ignore-whitespace", tempDir.absolutePathString()).captureOut(gitOut)
|
||||
if (result.exit != 0) {
|
||||
statusFile.writeText("1")
|
||||
|
||||
if (!gitOut) {
|
||||
// Log the output anyway on failure
|
||||
logger.lifecycle(result.out)
|
||||
}
|
||||
logger.error("*** Please review above details and finish the apply then")
|
||||
logger.error("*** save the changes with `./gradlew rebuildPatches`")
|
||||
|
||||
throw PaperweightException("Failed to apply patches")
|
||||
} else {
|
||||
statusFile.deleteForcefully()
|
||||
if (printOutput) {
|
||||
logger.lifecycle("${patches.size} patches applied cleanly to $target")
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
tempDir.deleteRecursive()
|
||||
}
|
||||
}
|
||||
|
||||
fun Git.disableAutoGpgSigningInRepo() {
|
||||
invoke("config", "commit.gpgSign", "false").executeSilently(silenceErr = true)
|
||||
invoke("config", "tag.gpgSign", "false").executeSilently(silenceErr = true)
|
||||
}
|
||||
|
||||
fun checkoutRepoFromUpstream(git: Git, upstream: Path, upstreamBranch: String) {
|
||||
git("init", "--quiet").executeSilently(silenceErr = true)
|
||||
git.disableAutoGpgSigningInRepo()
|
||||
git("remote", "remove", "upstream").runSilently(silenceErr = true)
|
||||
git("remote", "add", "upstream", upstream.toUri().toString()).executeSilently(silenceErr = true)
|
||||
git("fetch", "upstream", "--prune").executeSilently(silenceErr = true)
|
||||
if (git("checkout", "master").runSilently(silenceErr = true) != 0) {
|
||||
git("checkout", "-b", "master").runSilently(silenceErr = true)
|
||||
}
|
||||
git("reset", "--hard", "upstream/$upstreamBranch").executeSilently(silenceErr = true)
|
||||
git("gc").runSilently(silenceErr = true)
|
||||
}
|
||||
|
||||
fun recreateCloneDirectory(target: Path) {
|
||||
if (target.exists()) {
|
||||
if (target.resolve(".git").isDirectory()) {
|
||||
val git = Git(target)
|
||||
git("clean", "-fxd").runSilently(silenceErr = true)
|
||||
git("reset", "--hard", "HEAD").runSilently(silenceErr = true)
|
||||
} else {
|
||||
for (entry in target.listDirectoryEntries()) {
|
||||
entry.deleteRecursive()
|
||||
}
|
||||
target.createDirectories()
|
||||
}
|
||||
} else {
|
||||
target.createDirectories()
|
||||
}
|
||||
}
|
|
@ -1,179 +0,0 @@
|
|||
/*
|
||||
* paperweight is a Gradle plugin for the PaperMC project.
|
||||
*
|
||||
* Copyright (c) 2023 Kyle Wood (DenWav)
|
||||
* Contributors
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation;
|
||||
* version 2.1 only, no later versions.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
|
||||
* USA
|
||||
*/
|
||||
|
||||
package io.papermc.paperweight.tasks
|
||||
|
||||
import com.github.salomonbrys.kotson.fromJson
|
||||
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.ListProperty
|
||||
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 ApplyPaperPatches : ControllableOutputTask() {
|
||||
|
||||
@get:InputDirectory
|
||||
abstract val patchDir: DirectoryProperty
|
||||
|
||||
@get:InputFile
|
||||
abstract val remappedSource: RegularFileProperty
|
||||
|
||||
@get:InputFile
|
||||
abstract val remappedTests: RegularFileProperty
|
||||
|
||||
@get:InputFile
|
||||
abstract val caseOnlyClassNameChanges: RegularFileProperty
|
||||
|
||||
@get:InputDirectory
|
||||
abstract val upstreamDir: DirectoryProperty
|
||||
|
||||
@get:Input
|
||||
abstract val upstreamBranch: Property<String>
|
||||
|
||||
@get:InputFile
|
||||
abstract val sourceMcDevJar: RegularFileProperty
|
||||
|
||||
@get:InputDirectory
|
||||
abstract val mcLibrariesDir: DirectoryProperty
|
||||
|
||||
@get:InputDirectory
|
||||
abstract val spigotLibrariesDir: DirectoryProperty
|
||||
|
||||
@get:Optional
|
||||
@get:InputFile
|
||||
abstract val devImports: RegularFileProperty
|
||||
|
||||
@get:Optional
|
||||
@get:Input
|
||||
abstract val unneededFiles: ListProperty<String>
|
||||
|
||||
@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")
|
||||
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 outputFile = outputDir.path
|
||||
recreateCloneDirectory(outputFile)
|
||||
|
||||
val target = outputFile.name
|
||||
|
||||
if (printOutput.get()) {
|
||||
logger.lifecycle("Creating $target from remapped source...")
|
||||
}
|
||||
|
||||
Git(outputFile).let { git ->
|
||||
checkoutRepoFromUpstream(git, upstreamDir.path, upstreamBranch.get())
|
||||
|
||||
val sourceDir = createDir(outputDir.path.resolve("src/main/java"))
|
||||
val mcDataDir = outputDir.path.resolve("src/main/resources")
|
||||
val testDir = createDir(outputDir.path.resolve("src/test/java"))
|
||||
|
||||
fs.copy {
|
||||
from(archives.zipTree(remappedSource.path))
|
||||
into(sourceDir)
|
||||
}
|
||||
fs.copy {
|
||||
from(archives.zipTree(remappedTests.path))
|
||||
into(testDir)
|
||||
}
|
||||
|
||||
val patches = patchDir.path.listDirectoryEntries("*.patch")
|
||||
McDev.importMcDev(
|
||||
patches = patches,
|
||||
decompJar = sourceMcDevJar.path,
|
||||
importsFile = devImports.pathOrNull,
|
||||
targetDir = sourceDir,
|
||||
dataTargetDir = mcDataDir,
|
||||
librariesDirs = listOf(spigotLibrariesDir.path, mcLibrariesDir.path),
|
||||
printOutput = printOutput.get()
|
||||
)
|
||||
|
||||
val caseOnlyChanges = caseOnlyClassNameChanges.path.bufferedReader(Charsets.UTF_8).use { reader ->
|
||||
gson.fromJson<List<ClassNameChange>>(reader)
|
||||
}
|
||||
|
||||
for (caseOnlyChange in caseOnlyChanges) {
|
||||
val obfFile = sourceDir.resolve(caseOnlyChange.obfName + ".java").relativeTo(outputFile)
|
||||
val deobfFile = sourceDir.resolve(caseOnlyChange.deobfName + ".java").relativeTo(outputFile)
|
||||
git("mv", "-f", obfFile.toString(), deobfFile.toString()).runSilently(silenceErr = true)
|
||||
}
|
||||
|
||||
unneededFiles.orNull?.forEach { path -> outputFile.resolve(path).deleteRecursive() }
|
||||
|
||||
git(*Git.add(ignoreGitIgnore, ".")).executeSilently()
|
||||
git("commit", "-m", "Initial", "--author=Initial Source <auto@mated.null>").executeSilently()
|
||||
git("tag", "-d", "base").runSilently(silenceErr = true)
|
||||
git("tag", "base").executeSilently()
|
||||
|
||||
applyGitPatches(git, target, outputFile, patchDir.path, printOutput.get(), verbose.get())
|
||||
|
||||
makeMcDevSrc(layout.cache, sourceMcDevJar.path, mcDevSources.path, outputDir.path, sourceDir, mcDataDir)
|
||||
}
|
||||
}
|
||||
|
||||
private fun createDir(dir: Path): Path {
|
||||
dir.deleteRecursive()
|
||||
dir.createDirectories()
|
||||
return dir
|
||||
}
|
||||
}
|
|
@ -1,69 +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.tasks.CacheableTask
|
||||
import org.gradle.api.tasks.InputDirectory
|
||||
import org.gradle.api.tasks.Optional
|
||||
import org.gradle.api.tasks.PathSensitive
|
||||
import org.gradle.api.tasks.PathSensitivity
|
||||
|
||||
@CacheableTask
|
||||
abstract class ApplyRawDiffPatches : ZippedTask() {
|
||||
|
||||
@get:InputDirectory
|
||||
@get:PathSensitive(PathSensitivity.RELATIVE)
|
||||
abstract val inputDir: DirectoryProperty
|
||||
|
||||
@get:Optional
|
||||
@get:InputDirectory
|
||||
@get:PathSensitive(PathSensitivity.RELATIVE)
|
||||
abstract val patchDir: DirectoryProperty
|
||||
|
||||
override fun init() {
|
||||
super.init()
|
||||
outputZip.convention(defaultOutput("zip"))
|
||||
}
|
||||
|
||||
override fun run(rootDir: Path) {
|
||||
Git.checkForGit()
|
||||
|
||||
val input = inputDir.path
|
||||
input.copyRecursivelyTo(rootDir)
|
||||
|
||||
val patches = patchDir.pathOrNull ?: return
|
||||
val patchSet = patches.useDirectoryEntries("*.patch") { it.toMutableList() }
|
||||
|
||||
patchSet.sort()
|
||||
|
||||
val git = Git(rootDir)
|
||||
|
||||
for (patch in patchSet) {
|
||||
git("apply", patch.absolutePathString()).executeOut()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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,336 +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.ChangeRegistry
|
||||
import dev.denwav.hypo.mappings.ClassMappingsChange
|
||||
import dev.denwav.hypo.mappings.LorenzUtil
|
||||
import dev.denwav.hypo.mappings.MappingsCompletionManager
|
||||
import dev.denwav.hypo.mappings.MergeResult
|
||||
import dev.denwav.hypo.mappings.MergeableMappingsChange
|
||||
import dev.denwav.hypo.mappings.changes.AbstractMappingsChange
|
||||
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.model.ClassProviderRoot
|
||||
import dev.denwav.hypo.model.data.ClassData
|
||||
import dev.denwav.hypo.model.data.types.PrimitiveType
|
||||
import io.papermc.paperweight.util.*
|
||||
import io.papermc.paperweight.util.constants.*
|
||||
import java.util.Collections
|
||||
import javax.inject.Inject
|
||||
import kotlin.io.path.*
|
||||
import org.cadixdev.lorenz.MappingSet
|
||||
import org.cadixdev.lorenz.model.ClassMapping
|
||||
import org.cadixdev.lorenz.model.TopLevelClassMapping
|
||||
import org.gradle.api.file.ConfigurableFileCollection
|
||||
import org.gradle.api.file.RegularFileProperty
|
||||
import org.gradle.api.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 CleanupSourceMappings : 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:OutputFile
|
||||
abstract val caseOnlyNameChanges: RegularFileProperty
|
||||
|
||||
@get:Internal
|
||||
abstract val jvmargs: ListProperty<String>
|
||||
|
||||
@get:Inject
|
||||
abstract val workerExecutor: WorkerExecutor
|
||||
|
||||
override fun init() {
|
||||
super.init()
|
||||
|
||||
jvmargs.convention(listOf("-Xmx1G"))
|
||||
caseOnlyNameChanges.convention(defaultOutput("caseOnlyClassNameChanges", "json"))
|
||||
}
|
||||
|
||||
@TaskAction
|
||||
fun run() {
|
||||
val queue = workerExecutor.processIsolation {
|
||||
forkOptions.jvmArgs(jvmargs.get())
|
||||
forkOptions.executable(launcher.get().executablePath.path.absolutePathString())
|
||||
}
|
||||
|
||||
queue.submit(CleanupSourceMappingsAction::class) {
|
||||
inputMappings.set(this@CleanupSourceMappings.inputMappings.path)
|
||||
libraries.from(this@CleanupSourceMappings.libraries.files)
|
||||
sourceJar.set(this@CleanupSourceMappings.sourceJar.path)
|
||||
|
||||
outputMappings.set(this@CleanupSourceMappings.outputMappings.path)
|
||||
caseOnlyNameChanges.set(this@CleanupSourceMappings.caseOnlyNameChanges.path)
|
||||
}
|
||||
}
|
||||
|
||||
object ParamIndexesForSource : ChangeContributor {
|
||||
|
||||
override fun contribute(currentClass: ClassData?, classMapping: ClassMapping<*, *>?, context: HypoContext, registry: ChangeRegistry) {
|
||||
if (currentClass == null || classMapping == null) {
|
||||
return
|
||||
}
|
||||
|
||||
for (methodMapping in classMapping.methodMappings) {
|
||||
val method = LorenzUtil.findMethod(currentClass, methodMapping) ?: continue
|
||||
|
||||
var methodRef: MemberReference? = null
|
||||
|
||||
var lvtIndex = if (method.isStatic) 0 else 1
|
||||
if (method.isConstructor && currentClass.outerClass() != null && !currentClass.isStaticInnerClass) {
|
||||
lvtIndex += 1
|
||||
}
|
||||
for ((sourceIndex, param) in method.params().withIndex()) {
|
||||
if (methodMapping.hasParameterMapping(lvtIndex)) {
|
||||
if (methodRef == null) {
|
||||
methodRef = MemberReference.of(methodMapping)
|
||||
}
|
||||
registry.submitChange(ParamIndexChange(methodRef, lvtIndex, sourceIndex))
|
||||
}
|
||||
lvtIndex++
|
||||
if (param === PrimitiveType.LONG || param === PrimitiveType.DOUBLE) {
|
||||
lvtIndex++
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun name(): String = "ParamIndexesForSource"
|
||||
|
||||
class ParamIndexChange(
|
||||
target: MemberReference,
|
||||
fromIndex: Int,
|
||||
toIndex: Int
|
||||
) : AbstractMappingsChange(target), MergeableMappingsChange<ParamIndexChange> {
|
||||
|
||||
private val indexMap: MutableMap<Int, Int> = HashMap()
|
||||
|
||||
init {
|
||||
indexMap[fromIndex] = toIndex
|
||||
}
|
||||
|
||||
override fun applyChange(input: MappingSet, ref: MemberReference) {
|
||||
val classMapping = input.getOrCreateClassMapping(ref.className())
|
||||
val methodMapping = classMapping.getOrCreateMethodMapping(ref.name(), ref.desc())
|
||||
|
||||
val paramsMap = LorenzUtil.getParamsMap(methodMapping)
|
||||
val params = paramsMap.values.toList()
|
||||
paramsMap.clear()
|
||||
|
||||
for (param in params) {
|
||||
methodMapping.createParameterMapping(indexMap[param.index] ?: param.index, param.deobfuscatedName)
|
||||
}
|
||||
}
|
||||
|
||||
override fun mergeWith(that: ParamIndexChange): MergeResult<ParamIndexChange> {
|
||||
for (fromIndex in this.indexMap.keys) {
|
||||
if (that.indexMap.containsKey(fromIndex)) {
|
||||
return MergeResult.failure("Cannot merge 2 param mappings changes with matching fromIndexes")
|
||||
}
|
||||
}
|
||||
for (toIndex in this.indexMap.values) {
|
||||
if (that.indexMap.containsValue(toIndex)) {
|
||||
return MergeResult.failure("Cannot merge 2 param mappings changes with matching toIndex")
|
||||
}
|
||||
}
|
||||
|
||||
this.indexMap += that.indexMap
|
||||
return MergeResult.success(this)
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "Move param mappings for ${target()} for index pairs [${indexMap.entries.joinToString(", ") { "${it.key}:${it.value}" }}]"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object RemoveLambdaMappings : ChangeContributor {
|
||||
|
||||
override fun contribute(currentClass: ClassData?, classMapping: ClassMapping<*, *>?, context: HypoContext, registry: ChangeRegistry) {
|
||||
if (currentClass == null || classMapping == null) {
|
||||
return
|
||||
}
|
||||
|
||||
for (methodMapping in classMapping.methodMappings) {
|
||||
if (methodMapping.deobfuscatedName.startsWith("lambda$")) {
|
||||
registry.submitChange(RemoveMappingChange.of(MemberReference.of(methodMapping)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun name(): String = "RemoveLambdaMappings"
|
||||
}
|
||||
|
||||
class FindCaseOnlyClassNameChanges(private val changes: MutableList<ClassNameChange>) : ChangeContributor {
|
||||
override fun contribute(currentClass: ClassData?, classMapping: ClassMapping<*, *>?, context: HypoContext, registry: ChangeRegistry) {
|
||||
if (classMapping !is TopLevelClassMapping) {
|
||||
return
|
||||
}
|
||||
val obfName = classMapping.obfuscatedName
|
||||
val deobfName = classMapping.deobfuscatedName
|
||||
|
||||
if (obfName != deobfName && obfName.equals(deobfName, ignoreCase = true)) {
|
||||
changes += ClassNameChange(obfName, deobfName)
|
||||
}
|
||||
}
|
||||
|
||||
override fun name(): String = "FindCaseOnlyClassNameChanges"
|
||||
}
|
||||
|
||||
class ChangeObfClassName(
|
||||
private val targetClass: String,
|
||||
private val newFullObfuscatedName: String
|
||||
) : ClassMappingsChange {
|
||||
override fun targetClass(): String = targetClass
|
||||
|
||||
override fun applyChange(input: MappingSet) {
|
||||
val classMapping = LorenzUtil.getClassMapping(input, targetClass) ?: return
|
||||
LorenzUtil.removeClassMapping(classMapping)
|
||||
|
||||
val newMap = input.getOrCreateClassMapping(newFullObfuscatedName)
|
||||
copyMapping(classMapping, newMap)
|
||||
}
|
||||
|
||||
private fun copyMapping(from: ClassMapping<*, *>, to: ClassMapping<*, *>) {
|
||||
to.deobfuscatedName = from.deobfuscatedName
|
||||
|
||||
for (methodMapping in from.methodMappings) {
|
||||
methodMapping.copy(to)
|
||||
}
|
||||
for (fieldMapping in from.fieldMappings) {
|
||||
fieldMapping.copy(to)
|
||||
}
|
||||
for (innerClassMapping in from.innerClassMappings) {
|
||||
innerClassMapping.copy(to)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val TEMP_SUFFIX = "paperweight-remove-anon-renames-temp-suffix"
|
||||
}
|
||||
|
||||
object RemoveAnonymousClassRenames : ChangeContributor {
|
||||
override fun contribute(currentClass: ClassData?, classMapping: ClassMapping<*, *>?, context: HypoContext, registry: ChangeRegistry) {
|
||||
if (classMapping == null) return
|
||||
|
||||
val obf = classMapping.obfuscatedName.toIntOrNull()
|
||||
val deobf = classMapping.deobfuscatedName.toIntOrNull()
|
||||
|
||||
if (obf != null && deobf != null && obf != deobf) {
|
||||
val newName = classMapping.fullObfuscatedName.substringBeforeLast('$') + '$' + classMapping.deobfuscatedName + TEMP_SUFFIX
|
||||
registry.submitChange(ChangeObfClassName(classMapping.fullObfuscatedName, newName))
|
||||
}
|
||||
}
|
||||
|
||||
override fun name(): String = "RemoveAnonymousClassRenames"
|
||||
}
|
||||
|
||||
object CleanupAfterRemoveAnonymousClassRenames : ChangeContributor {
|
||||
override fun contribute(currentClass: ClassData?, classMapping: ClassMapping<*, *>?, context: HypoContext, registry: ChangeRegistry) {
|
||||
if (classMapping == null) return
|
||||
|
||||
if (classMapping.fullObfuscatedName.endsWith(TEMP_SUFFIX)) {
|
||||
val newName = classMapping.fullObfuscatedName.substringBefore(TEMP_SUFFIX)
|
||||
registry.submitChange(ChangeObfClassName(classMapping.fullObfuscatedName, newName))
|
||||
}
|
||||
}
|
||||
|
||||
override fun name(): String = "CleanupAfterRemoveAnonymousClassRenames"
|
||||
}
|
||||
|
||||
abstract class CleanupSourceMappingsAction : WorkAction<CleanupSourceMappingsAction.Parameters> {
|
||||
|
||||
interface Parameters : WorkParameters {
|
||||
val inputMappings: RegularFileProperty
|
||||
val libraries: ConfigurableFileCollection
|
||||
val sourceJar: RegularFileProperty
|
||||
|
||||
val outputMappings: RegularFileProperty
|
||||
val caseOnlyNameChanges: RegularFileProperty
|
||||
}
|
||||
|
||||
override fun execute() {
|
||||
val mappings = MappingFormats.TINY.read(
|
||||
parameters.inputMappings.path,
|
||||
SPIGOT_NAMESPACE,
|
||||
DEOBF_NAMESPACE
|
||||
)
|
||||
|
||||
val caseOnlyChanges = Collections.synchronizedList(mutableListOf<ClassNameChange>())
|
||||
|
||||
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(RemoveLambdaMappings)
|
||||
.addLink(ParamIndexesForSource)
|
||||
.addLink(FindCaseOnlyClassNameChanges(caseOnlyChanges))
|
||||
.addLink(RemoveAnonymousClassRenames)
|
||||
.addLink(CleanupAfterRemoveAnonymousClassRenames)
|
||||
.applyChain(mappings, MappingsCompletionManager.create(hypoContext))
|
||||
}
|
||||
|
||||
MappingFormats.TINY.write(
|
||||
cleanedMappings,
|
||||
parameters.outputMappings.path,
|
||||
SPIGOT_NAMESPACE,
|
||||
DEOBF_NAMESPACE
|
||||
)
|
||||
|
||||
parameters.caseOnlyNameChanges.path.bufferedWriter(Charsets.UTF_8).use { writer ->
|
||||
gson.toJson(caseOnlyChanges, writer)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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,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
|
||||
*/
|
||||
|
||||
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.ListProperty
|
||||
import org.gradle.api.tasks.CacheableTask
|
||||
import org.gradle.api.tasks.Classpath
|
||||
import org.gradle.api.tasks.Input
|
||||
import org.gradle.api.tasks.OutputFile
|
||||
import org.gradle.api.tasks.TaskAction
|
||||
|
||||
@CacheableTask
|
||||
abstract class CopyResources : BaseTask() {
|
||||
|
||||
@get:Classpath
|
||||
abstract val inputJar: RegularFileProperty
|
||||
|
||||
@get:Classpath
|
||||
abstract val vanillaJar: RegularFileProperty
|
||||
|
||||
@get:Input
|
||||
abstract val includes: ListProperty<String>
|
||||
|
||||
@get:OutputFile
|
||||
abstract val outputJar: RegularFileProperty
|
||||
|
||||
override fun init() {
|
||||
outputJar.convention(defaultOutput())
|
||||
includes.convention(
|
||||
listOf(
|
||||
"/data/**",
|
||||
"/assets/**",
|
||||
"version.json",
|
||||
"yggdrasil_session_pubkey.der",
|
||||
"pack.mcmeta",
|
||||
"flightrecorder-config.jfc",
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@TaskAction
|
||||
fun run() {
|
||||
val out = outputJar.path
|
||||
val target = out.resolveSibling("${out.name}.dir")
|
||||
target.createDirectories()
|
||||
|
||||
fs.copy {
|
||||
from(archives.zipTree(vanillaJar)) {
|
||||
for (inc in this@CopyResources.includes.get()) {
|
||||
include(inc)
|
||||
}
|
||||
}
|
||||
from(archives.zipTree(inputJar))
|
||||
into(target)
|
||||
}
|
||||
|
||||
zip(target, outputJar)
|
||||
target.deleteRecursive()
|
||||
}
|
||||
}
|
|
@ -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,53 +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 org.gradle.api.file.RegularFileProperty
|
||||
import org.gradle.api.provider.Property
|
||||
import org.gradle.api.tasks.*
|
||||
|
||||
// Not cached since this is Mojang's server jar
|
||||
abstract class DownloadServerJar : BaseTask() {
|
||||
|
||||
@get:Input
|
||||
abstract val downloadUrl: Property<String>
|
||||
|
||||
@get:OutputFile
|
||||
abstract val outputJar: RegularFileProperty
|
||||
|
||||
@get:Internal
|
||||
abstract val downloader: Property<DownloadService>
|
||||
|
||||
@get:Nested
|
||||
@get:Optional
|
||||
abstract val expectedHash: Property<Hash>
|
||||
|
||||
override fun init() {
|
||||
outputJar.convention(defaultOutput())
|
||||
}
|
||||
|
||||
@TaskAction
|
||||
fun run() = downloader.get().download(downloadUrl, outputJar, expectedHash.orNull)
|
||||
}
|
|
@ -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,329 +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 java.nio.file.Path
|
||||
import javax.inject.Inject
|
||||
import kotlin.io.path.*
|
||||
import org.cadixdev.lorenz.MappingSet
|
||||
import org.cadixdev.lorenz.merge.FieldMergeStrategy
|
||||
import org.cadixdev.lorenz.merge.MappingSetMerger
|
||||
import org.cadixdev.lorenz.merge.MappingSetMergerHandler
|
||||
import org.cadixdev.lorenz.merge.MergeConfig
|
||||
import org.cadixdev.lorenz.merge.MergeContext
|
||||
import org.cadixdev.lorenz.merge.MergeResult
|
||||
import org.cadixdev.lorenz.model.ClassMapping
|
||||
import org.cadixdev.lorenz.model.FieldMapping
|
||||
import org.cadixdev.lorenz.model.InnerClassMapping
|
||||
import org.cadixdev.lorenz.model.MethodMapping
|
||||
import org.cadixdev.lorenz.model.MethodParameterMapping
|
||||
import org.cadixdev.lorenz.model.TopLevelClassMapping
|
||||
import org.gradle.api.file.ConfigurableFileCollection
|
||||
import org.gradle.api.file.RegularFileProperty
|
||||
import org.gradle.api.provider.ListProperty
|
||||
import org.gradle.api.tasks.*
|
||||
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
|
||||
|
||||
fun generateMappings(
|
||||
vanillaJarPath: Path,
|
||||
libraryPaths: List<Path>,
|
||||
vanillaMappingsPath: Path,
|
||||
paramMappingsPath: Path,
|
||||
outputMappingsPath: Path,
|
||||
workerExecutor: WorkerExecutor,
|
||||
launcher: JavaLauncher,
|
||||
jvmArgs: List<String> = listOf("-Xmx1G")
|
||||
): WorkQueue {
|
||||
val queue = workerExecutor.processIsolation {
|
||||
forkOptions.jvmArgs(jvmArgs)
|
||||
forkOptions.executable(launcher.executablePath.path.absolutePathString())
|
||||
}
|
||||
|
||||
queue.submit(GenerateMappings.GenerateMappingsAction::class) {
|
||||
vanillaJar.set(vanillaJarPath)
|
||||
libraries.from(libraryPaths)
|
||||
vanillaMappings.set(vanillaMappingsPath)
|
||||
paramMappings.set(paramMappingsPath)
|
||||
outputMappings.set(outputMappingsPath)
|
||||
}
|
||||
|
||||
return queue
|
||||
}
|
||||
|
||||
@CacheableTask
|
||||
abstract class GenerateMappings : JavaLauncherTask() {
|
||||
|
||||
@get:Classpath
|
||||
abstract val vanillaJar: RegularFileProperty
|
||||
|
||||
@get:Classpath
|
||||
abstract val libraries: ConfigurableFileCollection
|
||||
|
||||
@get:InputFile
|
||||
@get:PathSensitive(PathSensitivity.NONE)
|
||||
abstract val vanillaMappings: RegularFileProperty
|
||||
|
||||
@get:InputFile
|
||||
@get:PathSensitive(PathSensitivity.NONE)
|
||||
abstract val paramMappings: 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() {
|
||||
generateMappings(
|
||||
vanillaJar.path,
|
||||
libraries.files.map { it.toPath() },
|
||||
vanillaMappings.path,
|
||||
paramMappings.path,
|
||||
outputMappings.path,
|
||||
workerExecutor,
|
||||
launcher.get(),
|
||||
jvmargs.get()
|
||||
)
|
||||
}
|
||||
|
||||
interface GenerateMappingsParams : WorkParameters {
|
||||
val vanillaJar: RegularFileProperty
|
||||
val libraries: ConfigurableFileCollection
|
||||
val vanillaMappings: RegularFileProperty
|
||||
val paramMappings: RegularFileProperty
|
||||
val outputMappings: RegularFileProperty
|
||||
}
|
||||
|
||||
abstract class GenerateMappingsAction : WorkAction<GenerateMappingsParams> {
|
||||
|
||||
override fun execute() {
|
||||
val vanillaMappings = MappingFormats.PROGUARD.createReader(parameters.vanillaMappings.path).use { it.read() }.reverse()
|
||||
|
||||
val paramMappings = parameters.paramMappings.path.openZip().use { fs ->
|
||||
val path = fs.getPath("mappings", "mappings.tiny")
|
||||
MappingFormats.TINY.read(path, "official", "named")
|
||||
}
|
||||
|
||||
val merged = MappingSetMerger.create(
|
||||
vanillaMappings,
|
||||
paramMappings,
|
||||
MergeConfig.builder()
|
||||
.withFieldMergeStrategy(FieldMergeStrategy.STRICT)
|
||||
.withMergeHandler(ParamsMergeHandler())
|
||||
.build()
|
||||
).merge()
|
||||
|
||||
val filledMerged = HypoContext.builder()
|
||||
.withProvider(AsmClassDataProvider.of(ClassProviderRoot.fromJar(parameters.vanillaJar.path)))
|
||||
.withContextProvider(AsmClassDataProvider.of(parameters.libraries.toJarClassProviderRoots()))
|
||||
.withContextProvider(AsmClassDataProvider.of(ClassProviderRoot.ofJdk()))
|
||||
.build().use { hypoContext ->
|
||||
HydrationManager.createDefault()
|
||||
.register(BridgeMethodHydrator.create())
|
||||
.register(SuperConstructorHydrator.create())
|
||||
.hydrate(hypoContext)
|
||||
|
||||
ChangeChain.create()
|
||||
.addLink(RemoveUnusedMappings.create())
|
||||
.addLink(PropagateMappingsUp.create())
|
||||
.addLink(CopyMappingsDown.create())
|
||||
.applyChain(merged, MappingsCompletionManager.create(hypoContext))
|
||||
}
|
||||
|
||||
ensureParentExists(parameters.outputMappings)
|
||||
MappingFormats.TINY.write(filledMerged, parameters.outputMappings.path, OBF_NAMESPACE, DEOBF_NAMESPACE)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ParamsMergeHandler : MappingSetMergerHandler {
|
||||
|
||||
override fun mergeTopLevelClassMappings(
|
||||
left: TopLevelClassMapping,
|
||||
right: TopLevelClassMapping,
|
||||
target: MappingSet,
|
||||
context: MergeContext
|
||||
): MergeResult<TopLevelClassMapping?> {
|
||||
throw IllegalStateException("Unexpectedly merged class: ${left.fullObfuscatedName}")
|
||||
}
|
||||
|
||||
override fun mergeDuplicateTopLevelClassMappings(
|
||||
left: TopLevelClassMapping,
|
||||
right: TopLevelClassMapping,
|
||||
rightContinuation: TopLevelClassMapping?,
|
||||
target: MappingSet,
|
||||
context: MergeContext
|
||||
): MergeResult<TopLevelClassMapping?> {
|
||||
return MergeResult(
|
||||
target.createTopLevelClassMapping(left.obfuscatedName, left.deobfuscatedName),
|
||||
right
|
||||
)
|
||||
}
|
||||
|
||||
override fun mergeInnerClassMappings(
|
||||
left: InnerClassMapping,
|
||||
right: InnerClassMapping,
|
||||
target: ClassMapping<*, *>,
|
||||
context: MergeContext
|
||||
): MergeResult<InnerClassMapping?> {
|
||||
throw IllegalStateException("Unexpectedly merged class: ${left.fullObfuscatedName}")
|
||||
}
|
||||
|
||||
override fun mergeDuplicateInnerClassMappings(
|
||||
left: InnerClassMapping,
|
||||
right: InnerClassMapping,
|
||||
rightContinuation: InnerClassMapping?,
|
||||
target: ClassMapping<*, *>,
|
||||
context: MergeContext
|
||||
): MergeResult<InnerClassMapping?> {
|
||||
return MergeResult(
|
||||
target.createInnerClassMapping(left.obfuscatedName, left.deobfuscatedName),
|
||||
right
|
||||
)
|
||||
}
|
||||
|
||||
override fun mergeFieldMappings(
|
||||
left: FieldMapping,
|
||||
strictRight: FieldMapping?,
|
||||
looseRight: FieldMapping?,
|
||||
target: ClassMapping<*, *>,
|
||||
context: MergeContext
|
||||
): FieldMapping? {
|
||||
throw IllegalStateException("Unexpectedly merged field: ${left.fullObfuscatedName}")
|
||||
}
|
||||
|
||||
override fun mergeDuplicateFieldMappings(
|
||||
left: FieldMapping,
|
||||
strictRightDuplicate: FieldMapping?,
|
||||
looseRightDuplicate: FieldMapping?,
|
||||
strictRightContinuation: FieldMapping?,
|
||||
looseRightContinuation: FieldMapping?,
|
||||
target: ClassMapping<*, *>,
|
||||
context: MergeContext
|
||||
): FieldMapping? {
|
||||
return target.createFieldMapping(left.signature, left.deobfuscatedName)
|
||||
}
|
||||
|
||||
override fun addLeftFieldMapping(
|
||||
left: FieldMapping,
|
||||
target: ClassMapping<*, *>,
|
||||
context: MergeContext
|
||||
): FieldMapping? {
|
||||
return target.createFieldMapping(left.signature, left.deobfuscatedName)
|
||||
}
|
||||
|
||||
override fun mergeMethodMappings(
|
||||
left: MethodMapping,
|
||||
standardRight: MethodMapping?,
|
||||
wiggledRight: MethodMapping?,
|
||||
target: ClassMapping<*, *>,
|
||||
context: MergeContext
|
||||
): MergeResult<MethodMapping?> {
|
||||
throw IllegalStateException("Unexpectedly merged method: ${left.fullObfuscatedName}")
|
||||
}
|
||||
|
||||
override fun mergeDuplicateMethodMappings(
|
||||
left: MethodMapping,
|
||||
standardRightDuplicate: MethodMapping?,
|
||||
wiggledRightDuplicate: MethodMapping?,
|
||||
standardRightContinuation: MethodMapping?,
|
||||
wiggledRightContinuation: MethodMapping?,
|
||||
target: ClassMapping<*, *>,
|
||||
context: MergeContext
|
||||
): MergeResult<MethodMapping?> {
|
||||
return MergeResult(
|
||||
target.createMethodMapping(left.signature, left.deobfuscatedName),
|
||||
listOfNotNull(standardRightDuplicate, wiggledRightDuplicate)
|
||||
)
|
||||
}
|
||||
|
||||
override fun mergeParameterMappings(
|
||||
left: MethodParameterMapping,
|
||||
right: MethodParameterMapping,
|
||||
target: MethodMapping,
|
||||
context: MergeContext
|
||||
): MethodParameterMapping? {
|
||||
throw IllegalStateException("Unexpectedly merged method: ${left.fullObfuscatedName}")
|
||||
}
|
||||
|
||||
// Don't take anything from yarn
|
||||
override fun addRightTopLevelClassMapping(
|
||||
right: TopLevelClassMapping?,
|
||||
target: MappingSet?,
|
||||
context: MergeContext?
|
||||
): MergeResult<TopLevelClassMapping?> {
|
||||
return emptyMergeResult()
|
||||
}
|
||||
|
||||
override fun addRightInnerClassMapping(
|
||||
right: InnerClassMapping?,
|
||||
target: ClassMapping<*, *>?,
|
||||
context: MergeContext?
|
||||
): MergeResult<InnerClassMapping?> {
|
||||
return emptyMergeResult()
|
||||
}
|
||||
|
||||
override fun addRightFieldMapping(
|
||||
right: FieldMapping?,
|
||||
target: ClassMapping<*, *>?,
|
||||
context: MergeContext?
|
||||
): FieldMapping? {
|
||||
return null
|
||||
}
|
||||
|
||||
override fun addRightMethodMapping(
|
||||
right: MethodMapping?,
|
||||
target: ClassMapping<*, *>?,
|
||||
context: MergeContext?
|
||||
): MergeResult<MethodMapping?> {
|
||||
return emptyMergeResult()
|
||||
}
|
||||
}
|
|
@ -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,313 +0,0 @@
|
|||
/*
|
||||
* paperweight is a Gradle plugin for the PaperMC project.
|
||||
*
|
||||
* Copyright (c) 2023 Kyle Wood (DenWav)
|
||||
* Contributors
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation;
|
||||
* version 2.1 only, no later versions.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
|
||||
* USA
|
||||
*/
|
||||
|
||||
package io.papermc.paperweight.tasks
|
||||
|
||||
import io.papermc.paperweight.util.*
|
||||
import io.papermc.paperweight.util.constants.*
|
||||
import org.cadixdev.bombe.type.signature.FieldSignature
|
||||
import org.cadixdev.bombe.type.signature.MethodSignature
|
||||
import org.cadixdev.lorenz.MappingSet
|
||||
import org.cadixdev.lorenz.merge.MappingSetMerger
|
||||
import org.cadixdev.lorenz.merge.MappingSetMergerHandler
|
||||
import org.cadixdev.lorenz.merge.MergeConfig
|
||||
import org.cadixdev.lorenz.merge.MergeContext
|
||||
import org.cadixdev.lorenz.merge.MergeResult
|
||||
import org.cadixdev.lorenz.model.ClassMapping
|
||||
import org.cadixdev.lorenz.model.FieldMapping
|
||||
import org.cadixdev.lorenz.model.InnerClassMapping
|
||||
import org.cadixdev.lorenz.model.MethodMapping
|
||||
import org.cadixdev.lorenz.model.TopLevelClassMapping
|
||||
import org.gradle.api.DefaultTask
|
||||
import org.gradle.api.file.RegularFileProperty
|
||||
import org.gradle.api.tasks.CacheableTask
|
||||
import org.gradle.api.tasks.InputFile
|
||||
import org.gradle.api.tasks.OutputFile
|
||||
import org.gradle.api.tasks.PathSensitive
|
||||
import org.gradle.api.tasks.PathSensitivity
|
||||
import org.gradle.api.tasks.TaskAction
|
||||
|
||||
@CacheableTask
|
||||
abstract class GenerateSpigotMappings : DefaultTask() {
|
||||
|
||||
@get:InputFile
|
||||
@get:PathSensitive(PathSensitivity.NONE)
|
||||
abstract val classMappings: RegularFileProperty
|
||||
|
||||
@get:InputFile
|
||||
@get:PathSensitive(PathSensitivity.NONE)
|
||||
abstract val sourceMappings: RegularFileProperty
|
||||
|
||||
@get:OutputFile
|
||||
abstract val notchToSpigotMappings: RegularFileProperty
|
||||
|
||||
@get:OutputFile
|
||||
abstract val outputMappings: RegularFileProperty
|
||||
|
||||
@get:OutputFile
|
||||
abstract val spigotMemberMappings: RegularFileProperty
|
||||
|
||||
@TaskAction
|
||||
fun run() {
|
||||
val spigotClassMappings = MappingFormats.CSRG.createReader(classMappings.path).use { it.read() }
|
||||
|
||||
val sourceMappings = MappingFormats.TINY.read(
|
||||
sourceMappings.path,
|
||||
OBF_NAMESPACE,
|
||||
DEOBF_NAMESPACE
|
||||
)
|
||||
|
||||
val notchToSpigotSet = MappingSetMerger.create(
|
||||
spigotClassMappings,
|
||||
sourceMappings,
|
||||
MergeConfig.builder()
|
||||
.withMergeHandler(SpigotMappingsMergerHandler)
|
||||
.build()
|
||||
).merge()
|
||||
|
||||
val spigotToNamedSet = notchToSpigotSet.reverse().merge(sourceMappings)
|
||||
|
||||
MappingFormats.TINY.write(
|
||||
notchToSpigotSet,
|
||||
notchToSpigotMappings.path,
|
||||
OBF_NAMESPACE,
|
||||
SPIGOT_NAMESPACE
|
||||
)
|
||||
|
||||
MappingFormats.TINY.write(
|
||||
spigotToNamedSet,
|
||||
outputMappings.path,
|
||||
SPIGOT_NAMESPACE,
|
||||
DEOBF_NAMESPACE
|
||||
)
|
||||
|
||||
val spigotMembers = createSpigotMemberMappings(sourceMappings, spigotClassMappings)
|
||||
MappingFormats.CSRG.write(spigotMembers, spigotMemberMappings.path)
|
||||
}
|
||||
}
|
||||
|
||||
object SpigotMappingsMergerHandler : MappingSetMergerHandler {
|
||||
|
||||
//
|
||||
// TOP LEVEL CLASS
|
||||
//
|
||||
|
||||
override fun mergeTopLevelClassMappings(
|
||||
left: TopLevelClassMapping,
|
||||
right: TopLevelClassMapping,
|
||||
target: MappingSet,
|
||||
context: MergeContext
|
||||
): MergeResult<TopLevelClassMapping?> {
|
||||
throw IllegalStateException("Unexpectedly merged class: ${left.fullObfuscatedName}")
|
||||
}
|
||||
|
||||
override fun mergeDuplicateTopLevelClassMappings(
|
||||
left: TopLevelClassMapping,
|
||||
right: TopLevelClassMapping,
|
||||
rightContinuation: TopLevelClassMapping?,
|
||||
target: MappingSet,
|
||||
context: MergeContext
|
||||
): MergeResult<TopLevelClassMapping?> {
|
||||
// If both are provided, keep spigot
|
||||
return MergeResult(
|
||||
target.createTopLevelClassMapping(left.obfuscatedName, left.deobfuscatedName),
|
||||
right
|
||||
)
|
||||
}
|
||||
|
||||
override fun addLeftTopLevelClassMapping(
|
||||
left: TopLevelClassMapping,
|
||||
target: MappingSet,
|
||||
context: MergeContext
|
||||
): MergeResult<TopLevelClassMapping?> {
|
||||
throw IllegalStateException(
|
||||
"Unexpected added class from Spigot: ${left.fullObfuscatedName} - ${left.fullDeobfuscatedName}"
|
||||
)
|
||||
}
|
||||
|
||||
override fun addRightTopLevelClassMapping(
|
||||
right: TopLevelClassMapping,
|
||||
target: MappingSet,
|
||||
context: MergeContext
|
||||
): MergeResult<TopLevelClassMapping?> {
|
||||
// This is a mapping Spigot is totally missing
|
||||
return MergeResult(
|
||||
target.createTopLevelClassMapping(right.obfuscatedName, right.obfuscatedName),
|
||||
right
|
||||
)
|
||||
}
|
||||
|
||||
//
|
||||
// INNER CLASS
|
||||
//
|
||||
|
||||
override fun mergeInnerClassMappings(
|
||||
left: InnerClassMapping,
|
||||
right: InnerClassMapping,
|
||||
target: ClassMapping<*, *>,
|
||||
context: MergeContext
|
||||
): MergeResult<InnerClassMapping?> {
|
||||
throw IllegalStateException("Unexpectedly merged class: ${left.fullObfuscatedName}")
|
||||
}
|
||||
|
||||
override fun mergeDuplicateInnerClassMappings(
|
||||
left: InnerClassMapping,
|
||||
right: InnerClassMapping,
|
||||
rightContinuation: InnerClassMapping?,
|
||||
target: ClassMapping<*, *>,
|
||||
context: MergeContext
|
||||
): MergeResult<InnerClassMapping?> {
|
||||
return MergeResult(
|
||||
target.createInnerClassMapping(left.obfuscatedName, left.deobfuscatedName),
|
||||
right
|
||||
)
|
||||
}
|
||||
|
||||
override fun addLeftInnerClassMapping(
|
||||
left: InnerClassMapping,
|
||||
target: ClassMapping<*, *>,
|
||||
context: MergeContext
|
||||
): MergeResult<InnerClassMapping> {
|
||||
throw IllegalStateException(
|
||||
"Unexpected added class from Spigot: ${left.fullObfuscatedName} - ${left.fullDeobfuscatedName}"
|
||||
)
|
||||
}
|
||||
|
||||
override fun addRightInnerClassMapping(
|
||||
right: InnerClassMapping,
|
||||
target: ClassMapping<*, *>,
|
||||
context: MergeContext
|
||||
): MergeResult<InnerClassMapping?> {
|
||||
// We want to get all of the inner classes from mojmap, but not the mojmap names
|
||||
return MergeResult(target.createInnerClassMapping(right.obfuscatedName, right.obfuscatedName), right)
|
||||
}
|
||||
|
||||
//
|
||||
// FIELD
|
||||
//
|
||||
|
||||
override fun mergeFieldMappings(
|
||||
left: FieldMapping,
|
||||
strictRight: FieldMapping?,
|
||||
looseRight: FieldMapping?,
|
||||
target: ClassMapping<*, *>,
|
||||
context: MergeContext
|
||||
): FieldMapping {
|
||||
throw IllegalStateException("Unexpectedly merged field: ${left.fullObfuscatedName}")
|
||||
}
|
||||
|
||||
override fun mergeDuplicateFieldMappings(
|
||||
left: FieldMapping,
|
||||
strictRightDuplicate: FieldMapping?,
|
||||
looseRightDuplicate: FieldMapping?,
|
||||
strictRightContinuation: FieldMapping?,
|
||||
looseRightContinuation: FieldMapping?,
|
||||
target: ClassMapping<*, *>,
|
||||
context: MergeContext
|
||||
): FieldMapping {
|
||||
val right = strictRightDuplicate ?: looseRightDuplicate ?: strictRightContinuation ?: looseRightContinuation ?: left
|
||||
return target.createFieldMapping(right.signature, left.deobfuscatedName)
|
||||
}
|
||||
|
||||
override fun addLeftFieldMapping(left: FieldMapping, target: ClassMapping<*, *>, context: MergeContext): FieldMapping? {
|
||||
throw IllegalStateException(
|
||||
"Unexpected added field from Spigot: ${left.fullObfuscatedName} - ${left.fullDeobfuscatedName}"
|
||||
)
|
||||
}
|
||||
|
||||
//
|
||||
// METHOD
|
||||
//
|
||||
|
||||
override fun mergeMethodMappings(
|
||||
left: MethodMapping,
|
||||
standardRight: MethodMapping?,
|
||||
wiggledRight: MethodMapping?,
|
||||
target: ClassMapping<*, *>,
|
||||
context: MergeContext
|
||||
): MergeResult<MethodMapping?> {
|
||||
throw IllegalStateException("Unexpectedly merged method: $left")
|
||||
}
|
||||
|
||||
override fun mergeDuplicateMethodMappings(
|
||||
left: MethodMapping,
|
||||
strictRightDuplicate: MethodMapping?,
|
||||
looseRightDuplicate: MethodMapping?,
|
||||
strictRightContinuation: MethodMapping?,
|
||||
looseRightContinuation: MethodMapping?,
|
||||
target: ClassMapping<*, *>,
|
||||
context: MergeContext
|
||||
): MergeResult<MethodMapping?> {
|
||||
val right = strictRightDuplicate ?: looseRightDuplicate ?: strictRightContinuation ?: looseRightContinuation ?: left
|
||||
return MergeResult(target.createMethodMapping(left.signature, left.deobfuscatedName), right)
|
||||
}
|
||||
|
||||
override fun addLeftMethodMapping(
|
||||
left: MethodMapping,
|
||||
target: ClassMapping<*, *>,
|
||||
context: MergeContext
|
||||
): MergeResult<MethodMapping?> {
|
||||
throw IllegalStateException(
|
||||
"Unexpected added method from Spigot: ${left.fullObfuscatedName} - ${left.fullDeobfuscatedName}"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun createSpigotMemberMappings(mappings: MappingSet, spigotClassMappings: MappingSet): MappingSet {
|
||||
val newMappings = MappingSet.create()
|
||||
|
||||
for (topLevelClassMapping in mappings.topLevelClassMappings) {
|
||||
val name = spigotClassMappings.getTopLevelClassMapping(topLevelClassMapping.obfuscatedName).orElse(topLevelClassMapping).deobfuscatedName
|
||||
val newClassMappings = newMappings.createTopLevelClassMapping(name, name)
|
||||
createSpigotMemberMappings(topLevelClassMapping, newClassMappings, spigotClassMappings)
|
||||
}
|
||||
|
||||
return newMappings
|
||||
}
|
||||
|
||||
private fun createSpigotMemberMappings(old: ClassMapping<*, *>, new: ClassMapping<*, *>, spigotClassMappings: MappingSet) {
|
||||
for (innerClassMapping in old.innerClassMappings) {
|
||||
val name = spigotClassMappings.getClassMapping(innerClassMapping.fullObfuscatedName)
|
||||
.map { it.deobfuscatedName }
|
||||
.orElse(innerClassMapping.obfuscatedName)
|
||||
val newClassMappings = new.createInnerClassMapping(name, name)
|
||||
createSpigotMemberMappings(innerClassMapping, newClassMappings, spigotClassMappings)
|
||||
}
|
||||
|
||||
for (fieldMapping in old.fieldMappings) {
|
||||
new.createFieldMapping(FieldSignature(fieldMapping.obfuscatedName, fieldMapping.type.get()), fieldMapping.deobfuscatedName)
|
||||
}
|
||||
|
||||
for (methodMapping in old.methodMappings) {
|
||||
if (methodMapping.deobfuscatedName.contains("$") ||
|
||||
methodMapping.deobfuscatedName == "<init>" ||
|
||||
methodMapping.deobfuscatedName == "<clinit>"
|
||||
) {
|
||||
continue
|
||||
}
|
||||
|
||||
val desc = spigotClassMappings.deobfuscate(methodMapping.descriptor)
|
||||
new.createMethodMapping(MethodSignature(methodMapping.obfuscatedName, desc)).also {
|
||||
it.deobfuscatedName = methodMapping.deobfuscatedName
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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,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.tasks
|
||||
|
||||
import io.papermc.paperweight.util.*
|
||||
import javax.inject.Inject
|
||||
import org.gradle.api.DefaultTask
|
||||
import org.gradle.api.file.ProjectLayout
|
||||
import org.gradle.api.provider.Property
|
||||
import org.gradle.api.tasks.Input
|
||||
import org.gradle.api.tasks.TaskAction
|
||||
import org.gradle.api.tasks.UntrackedTask
|
||||
|
||||
@Suppress("LeakingThis")
|
||||
@UntrackedTask(because = "Git tracks the state")
|
||||
abstract class InitSubmodules : DefaultTask() {
|
||||
|
||||
@get:Inject
|
||||
abstract val layout: ProjectLayout
|
||||
|
||||
@get:Input
|
||||
abstract val offlineMode: Property<Boolean>
|
||||
|
||||
init {
|
||||
offlineMode.convention(false)
|
||||
}
|
||||
|
||||
@TaskAction
|
||||
fun run() {
|
||||
layout.maybeInitSubmodules(offlineMode.get(), logger)
|
||||
}
|
||||
}
|
|
@ -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,163 +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 java.util.concurrent.ConcurrentLinkedQueue
|
||||
import java.util.concurrent.Executors
|
||||
import java.util.concurrent.Future
|
||||
import javax.inject.Inject
|
||||
import kotlin.io.path.*
|
||||
import org.gradle.api.file.DirectoryProperty
|
||||
import org.gradle.api.provider.Property
|
||||
import org.gradle.api.provider.ProviderFactory
|
||||
import org.gradle.api.tasks.*
|
||||
|
||||
@UntrackedTask(because = "RebuildGitPatches should always run when requested")
|
||||
abstract class RebuildGitPatches : ControllableOutputTask() {
|
||||
|
||||
@get:InputDirectory
|
||||
abstract val inputDir: DirectoryProperty
|
||||
|
||||
@get:Input
|
||||
abstract val baseRef: Property<String>
|
||||
|
||||
@get:OutputDirectory
|
||||
abstract val patchDir: DirectoryProperty
|
||||
|
||||
@get:Input
|
||||
abstract val filterPatches: Property<Boolean>
|
||||
|
||||
@get:Inject
|
||||
abstract val providers: ProviderFactory
|
||||
|
||||
override fun init() {
|
||||
printOutput.convention(true)
|
||||
filterPatches.convention(
|
||||
providers.gradleProperty("paperweight.filter-patches")
|
||||
.map { it.toBoolean() }
|
||||
.orElse(true)
|
||||
)
|
||||
}
|
||||
|
||||
@TaskAction
|
||||
fun run() {
|
||||
val what = inputDir.path.name
|
||||
val patchFolder = patchDir.path
|
||||
if (!patchFolder.exists()) {
|
||||
patchFolder.createDirectories()
|
||||
}
|
||||
|
||||
if (printOutput.get()) {
|
||||
logger.lifecycle("Formatting patches for $what...")
|
||||
}
|
||||
|
||||
if (inputDir.path.resolve(".git/rebase-apply").exists()) {
|
||||
// in middle of a rebase, be smarter
|
||||
if (printOutput.get()) {
|
||||
logger.lifecycle("REBASE DETECTED - PARTIAL SAVE")
|
||||
val last = inputDir.path.resolve(".git/rebase-apply/last").readText().trim().toInt()
|
||||
val next = inputDir.path.resolve(".git/rebase-apply/next").readText().trim().toInt()
|
||||
val orderedFiles = patchFolder.useDirectoryEntries("*.patch") { it.toMutableList() }
|
||||
orderedFiles.sort()
|
||||
|
||||
for (i in 1..last) {
|
||||
if (i < next) {
|
||||
orderedFiles[i].deleteForcefully()
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
patchFolder.deleteRecursive()
|
||||
patchFolder.createDirectories()
|
||||
}
|
||||
|
||||
val git = Git(inputDir.path)
|
||||
git("fetch", "--all", "--prune").runSilently(silenceErr = true)
|
||||
git(
|
||||
"format-patch",
|
||||
"--diff-algorithm=myers", "--zero-commit", "--full-index", "--no-signature", "--no-stat", "-N",
|
||||
"-o", patchFolder.absolutePathString(),
|
||||
baseRef.get()
|
||||
).executeSilently()
|
||||
val patchDirGit = Git(patchFolder)
|
||||
patchDirGit("add", "-A", ".").executeSilently()
|
||||
|
||||
if (filterPatches.get()) {
|
||||
cleanupPatches(patchDirGit)
|
||||
} else {
|
||||
if (printOutput.get()) {
|
||||
val saved = patchFolder.listDirectoryEntries("*.patch").size
|
||||
|
||||
logger.lifecycle("Saved $saved patches for $what to ${layout.projectDirectory.path.relativize(patchFolder)}/")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun cleanupPatches(git: Git) {
|
||||
val patchFiles = patchDir.path.useDirectoryEntries("*.patch") { it.toMutableList() }
|
||||
if (patchFiles.isEmpty()) {
|
||||
return
|
||||
}
|
||||
patchFiles.sort()
|
||||
|
||||
val noChangesPatches = ConcurrentLinkedQueue<Path>()
|
||||
val futures = mutableListOf<Future<*>>()
|
||||
|
||||
// Calling out to git over and over again for each `git diff --staged` command is really slow from the JVM
|
||||
// so to mitigate this we do it parallel
|
||||
val executor = Executors.newWorkStealingPool()
|
||||
try {
|
||||
for (patch in patchFiles) {
|
||||
futures += executor.submit {
|
||||
val hasNoChanges = git("diff", "--diff-algorithm=myers", "--staged", patch.name).getText().lineSequence()
|
||||
.filter { it.startsWith('+') || it.startsWith('-') }
|
||||
.filterNot { it.startsWith("+++") || it.startsWith("---") }
|
||||
.all { it.startsWith("+index") || it.startsWith("-index") }
|
||||
|
||||
if (hasNoChanges) {
|
||||
noChangesPatches.add(patch)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
futures.forEach { it.get() }
|
||||
} finally {
|
||||
executor.shutdownNow()
|
||||
}
|
||||
|
||||
if (noChangesPatches.isNotEmpty()) {
|
||||
for (chunk in noChangesPatches.chunked(50)) {
|
||||
git("reset", "HEAD", *chunk.map { it.name }.toTypedArray()).executeSilently()
|
||||
git("checkout", "--", *chunk.map { it.name }.toTypedArray()).executeSilently()
|
||||
}
|
||||
}
|
||||
|
||||
if (printOutput.get()) {
|
||||
val saved = patchFiles.size - noChangesPatches.size
|
||||
val relDir = layout.projectDirectory.path.relativize(patchDir.path)
|
||||
logger.lifecycle("Saved modified patches ($saved/${patchFiles.size}) for ${inputDir.path.name} to $relDir/")
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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,113 +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 kotlin.io.path.*
|
||||
import org.cadixdev.atlas.Atlas
|
||||
import org.cadixdev.bombe.asm.jar.JarEntryRemappingTransformer
|
||||
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.Internal
|
||||
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.commons.Remapper
|
||||
|
||||
@CacheableTask
|
||||
abstract class RemapJarAtlas : JavaLauncherTask() {
|
||||
|
||||
@get:Classpath
|
||||
abstract val inputJar: RegularFileProperty
|
||||
|
||||
@get:Input
|
||||
abstract val packageVersion: Property<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()
|
||||
|
||||
jvmargs.convention(listOf("-Xmx1G"))
|
||||
}
|
||||
|
||||
@TaskAction
|
||||
fun run() {
|
||||
ensureParentExists(outputJar)
|
||||
ensureDeleted(outputJar)
|
||||
|
||||
val queue = workerExecutor.processIsolation {
|
||||
forkOptions.jvmArgs(jvmargs.get())
|
||||
forkOptions.executable(launcher.get().executablePath.path.absolutePathString())
|
||||
}
|
||||
|
||||
queue.submit(AtlasAction::class) {
|
||||
inputJar.set(this@RemapJarAtlas.inputJar.get())
|
||||
outputJar.set(this@RemapJarAtlas.outputJar.get())
|
||||
packageVersion.set(this@RemapJarAtlas.packageVersion.get())
|
||||
}
|
||||
}
|
||||
|
||||
abstract class AtlasAction : WorkAction<AtlasParameters> {
|
||||
override fun execute() {
|
||||
val oldPack = "net/minecraft"
|
||||
val newPack = "$oldPack/server/v${parameters.packageVersion.get()}"
|
||||
Atlas().let { atlas ->
|
||||
atlas.install { JarEntryRemappingTransformer(PackageRemapper(oldPack, newPack)) }
|
||||
atlas.run(parameters.inputJar.path, parameters.outputJar.path)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface AtlasParameters : WorkParameters {
|
||||
val inputJar: RegularFileProperty
|
||||
val outputJar: RegularFileProperty
|
||||
val packageVersion: Property<String>
|
||||
}
|
||||
}
|
||||
|
||||
class PackageRemapper(private val oldPackage: String, private val newPackage: String) : Remapper() {
|
||||
|
||||
override fun map(internalName: String): String {
|
||||
return if (internalName.startsWith(oldPackage)) {
|
||||
internalName.replaceBeforeLast('/', newPackage)
|
||||
} else {
|
||||
internalName
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,430 +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.Files
|
||||
import java.nio.file.Path
|
||||
import javax.inject.Inject
|
||||
import kotlin.io.path.*
|
||||
import kotlin.streams.asSequence
|
||||
import org.cadixdev.at.AccessTransformSet
|
||||
import org.cadixdev.at.io.AccessTransformFormats
|
||||
import org.cadixdev.mercury.Mercury
|
||||
import org.cadixdev.mercury.RewriteContext
|
||||
import org.cadixdev.mercury.SourceProcessor
|
||||
import org.cadixdev.mercury.SourceRewriter
|
||||
import org.cadixdev.mercury.at.AccessTransformerRewriter
|
||||
import org.cadixdev.mercury.extra.AccessAnalyzerProcessor
|
||||
import org.cadixdev.mercury.remapper.MercuryRemapper
|
||||
import org.eclipse.jdt.core.JavaCore
|
||||
import org.eclipse.jdt.core.dom.*
|
||||
import org.gradle.api.file.ConfigurableFileCollection
|
||||
import org.gradle.api.file.DirectoryProperty
|
||||
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 RemapSources : JavaLauncherTask() {
|
||||
|
||||
@get:CompileClasspath
|
||||
abstract val vanillaJar: RegularFileProperty
|
||||
|
||||
@get:CompileClasspath
|
||||
abstract val mojangMappedVanillaJar: RegularFileProperty
|
||||
|
||||
@get:CompileClasspath
|
||||
abstract val vanillaRemappedSpigotJar: RegularFileProperty
|
||||
|
||||
@get:InputFile
|
||||
@get:PathSensitive(PathSensitivity.NONE)
|
||||
abstract val mappings: RegularFileProperty
|
||||
|
||||
@get:CompileClasspath
|
||||
abstract val spigotDeps: ConfigurableFileCollection
|
||||
|
||||
@get:InputDirectory
|
||||
@get:PathSensitive(PathSensitivity.RELATIVE)
|
||||
abstract val spigotServerDir: DirectoryProperty
|
||||
|
||||
@get:InputDirectory
|
||||
@get:PathSensitive(PathSensitivity.RELATIVE)
|
||||
abstract val spigotApiDir: DirectoryProperty
|
||||
|
||||
@get:Optional
|
||||
@get:InputFile
|
||||
@get:PathSensitive(PathSensitivity.NONE)
|
||||
abstract val additionalAts: RegularFileProperty
|
||||
|
||||
@get:OutputFile
|
||||
abstract val generatedAt: RegularFileProperty
|
||||
|
||||
@get:OutputFile
|
||||
abstract val sourcesOutputZip: RegularFileProperty
|
||||
|
||||
@get:OutputFile
|
||||
abstract val testsOutputZip: RegularFileProperty
|
||||
|
||||
@get:Internal
|
||||
abstract val jvmargs: ListProperty<String>
|
||||
|
||||
@get:Inject
|
||||
abstract val workerExecutor: WorkerExecutor
|
||||
|
||||
@get:OutputFile
|
||||
abstract val spigotRecompiledClasses: RegularFileProperty
|
||||
|
||||
@get:Input
|
||||
abstract val sourceCompatibility: Property<Int>
|
||||
|
||||
override fun init() {
|
||||
super.init()
|
||||
|
||||
jvmargs.convention(listOf("-Xmx2G"))
|
||||
sourcesOutputZip.convention(defaultOutput("$name-sources", "jar"))
|
||||
testsOutputZip.convention(defaultOutput("$name-tests", "jar"))
|
||||
generatedAt.convention(defaultOutput("at"))
|
||||
spigotRecompiledClasses.convention(defaultOutput("spigotRecompiledClasses", "txt"))
|
||||
sourceCompatibility.convention(21)
|
||||
}
|
||||
|
||||
@TaskAction
|
||||
fun run() {
|
||||
val srcOut = findOutputDir(sourcesOutputZip.path).apply { createDirectories() }
|
||||
val testOut = findOutputDir(testsOutputZip.path).apply { createDirectories() }
|
||||
|
||||
try {
|
||||
val queue = workerExecutor.processIsolation {
|
||||
forkOptions.jvmArgs(jvmargs.get())
|
||||
forkOptions.executable(launcher.get().executablePath.path.absolutePathString())
|
||||
}
|
||||
|
||||
val srcDir = spigotServerDir.path.resolve("src/main/java")
|
||||
|
||||
// Remap sources
|
||||
queue.submit(RemapAction::class) {
|
||||
classpath.from(vanillaRemappedSpigotJar.path)
|
||||
classpath.from(mojangMappedVanillaJar.path)
|
||||
classpath.from(vanillaJar.path)
|
||||
classpath.from(spigotApiDir.dir("src/main/java").path)
|
||||
classpath.from(spigotDeps.files.filter { it.toPath().isLibraryJar })
|
||||
additionalAts.set(this@RemapSources.additionalAts.pathOrNull)
|
||||
|
||||
mappings.set(this@RemapSources.mappings.path)
|
||||
inputDir.set(srcDir)
|
||||
|
||||
cacheDir.set(this@RemapSources.layout.cache)
|
||||
|
||||
outputDir.set(srcOut)
|
||||
generatedAtOutput.set(generatedAt.path)
|
||||
|
||||
sourceCompat.set(sourceCompatibility.orNull)
|
||||
}
|
||||
|
||||
val testSrc = spigotServerDir.path.resolve("src/test/java")
|
||||
|
||||
// Remap tests
|
||||
queue.submit(RemapAction::class) {
|
||||
classpath.from(vanillaRemappedSpigotJar.path)
|
||||
classpath.from(mojangMappedVanillaJar.path)
|
||||
classpath.from(vanillaJar.path)
|
||||
classpath.from(spigotApiDir.dir("src/main/java").path)
|
||||
classpath.from(spigotDeps.files.filter { it.toPath().isLibraryJar })
|
||||
classpath.from(srcDir)
|
||||
additionalAts.set(this@RemapSources.additionalAts.pathOrNull)
|
||||
|
||||
mappings.set(this@RemapSources.mappings.path)
|
||||
inputDir.set(testSrc)
|
||||
|
||||
cacheDir.set(this@RemapSources.layout.cache)
|
||||
|
||||
outputDir.set(testOut)
|
||||
|
||||
sourceCompat.set(sourceCompatibility.orNull)
|
||||
}
|
||||
|
||||
queue.await()
|
||||
|
||||
zip(srcOut, sourcesOutputZip)
|
||||
zip(testOut, testsOutputZip)
|
||||
|
||||
writeSpigotRecompiledFiles(srcOut)
|
||||
} finally {
|
||||
srcOut.deleteRecursive()
|
||||
testOut.deleteRecursive()
|
||||
}
|
||||
}
|
||||
|
||||
private fun writeSpigotRecompiledFiles(srcOut: Path) {
|
||||
// Write list of java files spigot recompiles
|
||||
val spigotRecompiled = Files.walk(srcOut).use { stream ->
|
||||
stream.asSequence().mapNotNull {
|
||||
if (!it.isRegularFile()) {
|
||||
return@mapNotNull null
|
||||
}
|
||||
if (!it.fileName.pathString.endsWith(".java")) {
|
||||
return@mapNotNull null
|
||||
}
|
||||
val path = srcOut.relativize(it).pathString
|
||||
if (!path.startsWith("net/minecraft")) {
|
||||
return@mapNotNull null
|
||||
}
|
||||
path.replace(".java", "")
|
||||
}.sorted().joinToString("\n")
|
||||
}
|
||||
spigotRecompiledClasses.path.parent.createDirectories()
|
||||
spigotRecompiledClasses.path.writeText(spigotRecompiled)
|
||||
}
|
||||
|
||||
abstract class RemapAction : WorkAction<RemapParams> {
|
||||
override fun execute() {
|
||||
val mappingSet = MappingFormats.TINY.read(
|
||||
parameters.mappings.path,
|
||||
SPIGOT_NAMESPACE,
|
||||
DEOBF_NAMESPACE
|
||||
)
|
||||
|
||||
val additionalAt = parameters.additionalAts.pathOrNull?.let { AccessTransformFormats.FML.read(it) }
|
||||
|
||||
val processAt = AccessTransformSet.create()
|
||||
val generatedAtOutPath = parameters.generatedAtOutput.pathOrNull
|
||||
|
||||
// Remap any references Spigot maps to mojmap+yarn
|
||||
Mercury().let { merc ->
|
||||
merc.sourceCompatibility = parameters.sourceCompat.map { it.toString() }.orNull ?: JavaCore.VERSION_17
|
||||
merc.isGracefulClasspathChecks = true
|
||||
merc.classPath.addAll(parameters.classpath.map { it.toPath() })
|
||||
|
||||
if (generatedAtOutPath != null) {
|
||||
merc.processors += AccessAnalyzerProcessor.create(processAt, mappingSet)
|
||||
}
|
||||
|
||||
merc.process(parameters.inputDir.path)
|
||||
|
||||
val tempOut = Files.createTempDirectory(parameters.cacheDir.path, "remap")
|
||||
try {
|
||||
merc.processors.clear()
|
||||
merc.processors.addAll(
|
||||
listOf(
|
||||
ExplicitThisAdder,
|
||||
MercuryRemapper.create(mappingSet),
|
||||
AccessTransformerRewriter.create(processAt)
|
||||
)
|
||||
)
|
||||
|
||||
if (generatedAtOutPath != null) {
|
||||
merc.processors.add(AccessTransformerRewriter.create(processAt))
|
||||
}
|
||||
|
||||
merc.rewrite(parameters.inputDir.path, tempOut)
|
||||
|
||||
if (additionalAt != null) {
|
||||
merc.processors.clear()
|
||||
merc.processors += AccessTransformerRewriter.create(additionalAt)
|
||||
|
||||
merc.rewrite(tempOut, parameters.outputDir.path)
|
||||
} else {
|
||||
tempOut.copyRecursivelyTo(parameters.outputDir.path)
|
||||
}
|
||||
} finally {
|
||||
tempOut.deleteRecursive()
|
||||
}
|
||||
}
|
||||
|
||||
if (generatedAtOutPath != null) {
|
||||
AccessTransformFormats.FML.write(generatedAtOutPath, processAt)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface RemapParams : WorkParameters {
|
||||
val classpath: ConfigurableFileCollection
|
||||
val mappings: RegularFileProperty
|
||||
val inputDir: RegularFileProperty
|
||||
val additionalAts: RegularFileProperty
|
||||
|
||||
val cacheDir: RegularFileProperty
|
||||
val generatedAtOutput: RegularFileProperty
|
||||
val outputDir: RegularFileProperty
|
||||
|
||||
val sourceCompat: Property<Int>
|
||||
}
|
||||
|
||||
object ExplicitThisAdder : SourceRewriter {
|
||||
|
||||
override fun getFlags(): Int = SourceProcessor.FLAG_RESOLVE_BINDINGS
|
||||
|
||||
override fun rewrite(context: RewriteContext) {
|
||||
context.compilationUnit.accept(ExplicitThisAdderVisitor(context))
|
||||
}
|
||||
}
|
||||
|
||||
class ExplicitThisAdderVisitor(private val context: RewriteContext) : ASTVisitor() {
|
||||
|
||||
override fun visit(node: SimpleName): Boolean {
|
||||
val binding = node.resolveBinding() ?: return false
|
||||
|
||||
val name = when (val declaringNode = context.compilationUnit.findDeclaringNode(binding)) {
|
||||
is VariableDeclarationFragment -> declaringNode.name
|
||||
is MethodDeclaration -> declaringNode.name
|
||||
null -> null
|
||||
else -> return false
|
||||
}
|
||||
if (name != null && name === node) {
|
||||
// this is the actual declaration
|
||||
return false
|
||||
}
|
||||
|
||||
visit(node, binding)
|
||||
return false
|
||||
}
|
||||
|
||||
private fun visit(node: SimpleName, binding: IBinding) {
|
||||
if (binding.kind != IBinding.VARIABLE && binding.kind != IBinding.METHOD) {
|
||||
return
|
||||
}
|
||||
|
||||
val referringClass = when (binding) {
|
||||
is IVariableBinding -> {
|
||||
if (!binding.isField || binding.isEnumConstant) {
|
||||
return
|
||||
}
|
||||
binding.declaringClass
|
||||
}
|
||||
is IMethodBinding -> {
|
||||
if (binding.isConstructor || binding.isSynthetic) {
|
||||
return
|
||||
}
|
||||
binding.declaringClass
|
||||
}
|
||||
else -> return
|
||||
}
|
||||
val modifiers = when (binding) {
|
||||
is IVariableBinding -> binding.modifiers
|
||||
is IMethodBinding -> binding.modifiers
|
||||
else -> return
|
||||
}
|
||||
|
||||
when (val p = node.parent) {
|
||||
is FieldAccess, is SuperFieldAccess, is ThisExpression, is MethodReference, is SuperMethodInvocation, is MemberValuePair -> return
|
||||
is MethodInvocation -> {
|
||||
if (!p.arguments().contains(node)) {
|
||||
if (p.expression != null && p.expression !== node) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
is QualifiedName -> {
|
||||
if (p.qualifier !== node) {
|
||||
return
|
||||
}
|
||||
}
|
||||
is ClassInstanceCreation -> {
|
||||
if (!p.arguments().contains(node)) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val rewrite = context.createASTRewrite()
|
||||
val fieldAccess = rewrite.ast.newFieldAccess()
|
||||
|
||||
val expr: Expression = if (!Modifier.isStatic(modifiers)) {
|
||||
val accessible = mutableListOf<ITypeBinding>()
|
||||
var curr: ASTNode = node
|
||||
while (true) {
|
||||
if (curr is TypeDeclaration) {
|
||||
accessible += curr.resolveBinding() ?: break
|
||||
} else if (curr is AnonymousClassDeclaration) {
|
||||
accessible += curr.resolveBinding() ?: break
|
||||
}
|
||||
val m = when (curr) {
|
||||
is MethodDeclaration -> curr.modifiers
|
||||
is FieldDeclaration -> curr.modifiers
|
||||
is Initializer -> curr.modifiers
|
||||
is TypeDeclaration -> curr.modifiers
|
||||
else -> null
|
||||
}
|
||||
if (m != null && Modifier.isStatic(m)) {
|
||||
break
|
||||
}
|
||||
curr = curr.parent ?: break
|
||||
}
|
||||
|
||||
rewrite.ast.newThisExpression().also { thisExpr ->
|
||||
if (accessible.size == 1) {
|
||||
return@also
|
||||
}
|
||||
val accessibleTargetCls = accessible.find { referringClass.isCastCompatible(it) }
|
||||
?: return@also
|
||||
if (accessibleTargetCls.isAnonymous) {
|
||||
if (accessible.indexOf(accessibleTargetCls) == 0) {
|
||||
return@also
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if (!accessibleTargetCls.isCastCompatible(accessible[0])) {
|
||||
val name = getNameNode(accessibleTargetCls)
|
||||
?: throw PaperweightException("Could not find name node for ${accessibleTargetCls.qualifiedName}")
|
||||
thisExpr.qualifier = rewrite.createCopyTarget(name) as Name
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// find declaring method
|
||||
var parentNode: ASTNode? = node
|
||||
loop@ while (parentNode != null) {
|
||||
when (parentNode) {
|
||||
is MethodDeclaration, is AnonymousClassDeclaration, is LambdaExpression, is Initializer -> break@loop
|
||||
}
|
||||
parentNode = parentNode.parent
|
||||
}
|
||||
|
||||
if (parentNode is Initializer && Modifier.isStatic(parentNode.modifiers)) {
|
||||
// Can't provide explicit static receiver here
|
||||
return
|
||||
}
|
||||
val name = getNameNode(referringClass) ?: return
|
||||
rewrite.createCopyTarget(name) as Name
|
||||
}
|
||||
|
||||
fieldAccess.expression = expr
|
||||
fieldAccess.name = rewrite.createMoveTarget(node) as SimpleName
|
||||
|
||||
rewrite.replace(node, fieldAccess, null)
|
||||
}
|
||||
|
||||
private fun getNameNode(dec: ITypeBinding): Name? {
|
||||
val typeDec = context.compilationUnit.findDeclaringNode(dec) as? TypeDeclaration ?: return null
|
||||
return typeDec.name
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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,296 +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.patchremap
|
||||
|
||||
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.cadixdev.at.io.AccessTransformFormats
|
||||
import org.gradle.api.file.ConfigurableFileCollection
|
||||
import org.gradle.api.file.DirectoryProperty
|
||||
import org.gradle.api.file.RegularFileProperty
|
||||
import org.gradle.api.provider.Property
|
||||
import org.gradle.api.provider.ProviderFactory
|
||||
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.OutputDirectory
|
||||
import org.gradle.api.tasks.TaskAction
|
||||
import org.gradle.api.tasks.options.Option
|
||||
import org.gradle.kotlin.dsl.*
|
||||
|
||||
abstract class RemapPatches : BaseTask() {
|
||||
|
||||
@get:InputDirectory
|
||||
abstract val inputPatchDir: DirectoryProperty
|
||||
|
||||
@get:InputDirectory
|
||||
abstract val apiPatchDir: DirectoryProperty
|
||||
|
||||
@get:InputFile
|
||||
abstract val mappingsFile: RegularFileProperty
|
||||
|
||||
@get:InputFile
|
||||
abstract val ats: RegularFileProperty
|
||||
|
||||
@get:Classpath
|
||||
abstract val classpathJars: ConfigurableFileCollection
|
||||
|
||||
@get:InputDirectory
|
||||
abstract val spigotApiDir: DirectoryProperty
|
||||
|
||||
@get:InputDirectory
|
||||
abstract val spigotServerDir: DirectoryProperty
|
||||
|
||||
@get:InputFile
|
||||
abstract val spigotDecompJar: RegularFileProperty
|
||||
|
||||
@get:InputDirectory
|
||||
abstract val mcLibrarySourcesDir: DirectoryProperty
|
||||
|
||||
@get:InputFile
|
||||
abstract val devImports: RegularFileProperty
|
||||
|
||||
@get:Input
|
||||
abstract val ignoreGitIgnore: Property<Boolean>
|
||||
|
||||
@get:OutputDirectory
|
||||
abstract val outputPatchDir: DirectoryProperty
|
||||
|
||||
@get:Internal
|
||||
@get:Option(
|
||||
option = "continue-remap",
|
||||
description = "For resuming, don't recreate remap dir and pick up where last run left off"
|
||||
)
|
||||
abstract val continueRemapping: Property<Boolean>
|
||||
|
||||
@get:Internal
|
||||
@get:Option(
|
||||
option = "limit-patches",
|
||||
description = "For testing, you can limit the # of patches (e.g. --limit-patches=10)"
|
||||
)
|
||||
abstract val limitPatches: Property<String>
|
||||
|
||||
@get:Inject
|
||||
abstract val providers: ProviderFactory
|
||||
|
||||
override fun init() {
|
||||
continueRemapping.convention(false)
|
||||
ignoreGitIgnore.convention(Git.ignoreProperty(providers)).finalizeValueOnRead()
|
||||
}
|
||||
|
||||
@TaskAction
|
||||
fun run() {
|
||||
val metaFile = layout.cache / "paperweight" / "remap-meta"
|
||||
val meta = if (metaFile.exists()) {
|
||||
if (continueRemapping.get()) {
|
||||
gson.fromJson<RemapMeta>(metaFile)
|
||||
} else {
|
||||
metaFile.deleteForcefully()
|
||||
null
|
||||
}
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
// Check patches
|
||||
val inputElements = inputPatchDir.path.listDirectoryEntries().sorted()
|
||||
if (inputElements.any { it.isRegularFile() }) {
|
||||
println("Remap patch input directory must only contain directories or patch files, not both")
|
||||
return
|
||||
}
|
||||
if (inputElements.size == 1) {
|
||||
println("No patches to remap, only 1 patch set found")
|
||||
return
|
||||
}
|
||||
|
||||
val patchesToSkip = inputElements.dropLast(1).flatMap { it.listDirectoryEntries("*.patch").sorted() }
|
||||
val patchesToRemap = inputElements.last().listDirectoryEntries("*.patch").sorted()
|
||||
|
||||
if (patchesToRemap.isEmpty()) {
|
||||
println("No input patches to remap found")
|
||||
return
|
||||
}
|
||||
|
||||
val limit = limitPatches.map { it.toInt() }.orElse(patchesToRemap.size).get()
|
||||
|
||||
val mappings = MappingFormats.TINY.read(mappingsFile.path, SPIGOT_NAMESPACE, DEOBF_NAMESPACE)
|
||||
|
||||
// This should pull in any libraries needed for type bindings
|
||||
val configFiles = project.project(":Paper-Server").configurations["runtimeClasspath"].resolve().map { it.toPath() }
|
||||
val classpathFiles = classpathJars.map { it.toPath() } + configFiles
|
||||
|
||||
// Remap output directory, after each output this directory will be re-named to the input directory below for
|
||||
// the next remap operation
|
||||
println("setting up repo")
|
||||
val tempApiDir = createWorkDir("patch-remap-api", source = spigotApiDir.path, recreate = !continueRemapping.get())
|
||||
val tempInputDir = createWorkDirByCloning(
|
||||
"patch-remap-input",
|
||||
source = spigotServerDir.path,
|
||||
recreate = !continueRemapping.get()
|
||||
)
|
||||
val tempOutputDir = createWorkDir("patch-remap-output")
|
||||
|
||||
val sourceInputDir = tempInputDir.resolve("src/main/java")
|
||||
|
||||
PatchSourceRemapWorker(
|
||||
mappings,
|
||||
AccessTransformFormats.FML.read(ats.path),
|
||||
listOf(*classpathFiles.toTypedArray(), tempApiDir.resolve("src/main/java")),
|
||||
sourceInputDir,
|
||||
tempOutputDir
|
||||
).let { remapper ->
|
||||
val patchApplier = PatchApplier("remapped", "old", ignoreGitIgnore.get(), tempInputDir)
|
||||
|
||||
if (!continueRemapping.get()) {
|
||||
// first run
|
||||
patchApplier.createBranches()
|
||||
|
||||
// We need to include any missing classes for the patches later on
|
||||
McDev.importMcDev(
|
||||
patches = patchesToSkip + patchesToRemap,
|
||||
decompJar = spigotDecompJar.path,
|
||||
importsFile = devImports.path,
|
||||
targetDir = tempInputDir.resolve("src/main/java"),
|
||||
librariesDirs = listOf(mcLibrarySourcesDir.path)
|
||||
)
|
||||
|
||||
patchApplier.commitPlain("McDev imports")
|
||||
}
|
||||
|
||||
if (meta == null || meta.stage == RemapStage.PRE_REMAP) {
|
||||
var foundResume = false
|
||||
val patchesToApply = patchesToSkip.dropWhile { patch ->
|
||||
when {
|
||||
meta == null -> false
|
||||
meta.patchSet == patch.parent.name && meta.patchName == patch.name -> {
|
||||
foundResume = true
|
||||
true
|
||||
}
|
||||
else -> !foundResume
|
||||
}
|
||||
}
|
||||
println("Applying ${patchesToApply.size} patches before remapping")
|
||||
for (patch in patchesToApply) {
|
||||
metaFile.deleteForcefully()
|
||||
metaFile.bufferedWriter().use { writer ->
|
||||
gson.toJson(RemapMeta(RemapStage.PRE_REMAP, patch.parent.name, patch.name), writer)
|
||||
}
|
||||
patchApplier.applyPatch(patch)
|
||||
}
|
||||
|
||||
patchApplier.checkoutRemapped() // Switch to remapped branch without checking out files
|
||||
|
||||
remapper.remap() // Remap to new mappings
|
||||
patchApplier.commitInitialRemappedSource() // Initial commit of pre-remap sources mapped to new mappings
|
||||
patchApplier.checkoutOld() // Normal checkout back to pre-remap mappings branch
|
||||
} else if (patchApplier.isUnfinishedPatch()) {
|
||||
println("===========================")
|
||||
println("Finishing current patch")
|
||||
println("===========================")
|
||||
patchApplier.recordCommit()
|
||||
patchApplier.checkoutRemapped()
|
||||
remapper.remap()
|
||||
patchApplier.commitChanges()
|
||||
patchApplier.checkoutOld()
|
||||
println("===========================")
|
||||
println("done with current patch")
|
||||
println("===========================")
|
||||
}
|
||||
|
||||
// Repo setup is done, we can begin the patch loop now
|
||||
var counter = 0
|
||||
var remapSkip = meta != null && meta.stage == RemapStage.REMAP
|
||||
for (patch in patchesToRemap) {
|
||||
if (remapSkip && meta != null) {
|
||||
if (meta.patchSet == patch.parent.name && meta.patchName == patch.name) {
|
||||
remapSkip = false
|
||||
continue
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
metaFile.deleteForcefully()
|
||||
metaFile.bufferedWriter().use { writer ->
|
||||
gson.toJson(RemapMeta(RemapStage.REMAP, patch.parent.name, patch.name), writer)
|
||||
}
|
||||
|
||||
println("===========================")
|
||||
println("attempting to remap $patch")
|
||||
println("===========================")
|
||||
patchApplier.applyPatch(patch) // Apply patch on Spigot mappings
|
||||
patchApplier.recordCommit() // Keep track of commit author, message, and time
|
||||
patchApplier.checkoutRemapped() // Switch to remapped branch without checkout out files
|
||||
remapper.remap() // Remap to new mappings
|
||||
patchApplier.commitChanges() // Commit the changes
|
||||
patchApplier.checkoutOld() // Normal checkout back to Spigot mappings branch
|
||||
println("===========================")
|
||||
println("done remapping patch $patch")
|
||||
println("===========================")
|
||||
|
||||
counter++
|
||||
if (counter >= limit) {
|
||||
break
|
||||
}
|
||||
}
|
||||
patchApplier.generatePatches(outputPatchDir.path)
|
||||
}
|
||||
}
|
||||
|
||||
private fun createWorkDir(name: String, source: Path? = null, recreate: Boolean = true): Path {
|
||||
return layout.cache.resolve("paperweight").resolve(name).apply {
|
||||
if (recreate) {
|
||||
deleteRecursive()
|
||||
createDirectories()
|
||||
source?.copyRecursivelyTo(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun createWorkDirByCloning(name: String, source: Path, recreate: Boolean = true): Path {
|
||||
val workDir = layout.cache.resolve("paperweight")
|
||||
return workDir.resolve(name).apply {
|
||||
if (recreate) {
|
||||
deleteRecursive()
|
||||
createDirectories()
|
||||
Git(workDir)("clone", source.absolutePathString(), this.absolutePathString()).executeSilently()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data class RemapMeta(
|
||||
val stage: RemapStage,
|
||||
val patchSet: String,
|
||||
val patchName: String
|
||||
)
|
||||
|
||||
enum class RemapStage {
|
||||
PRE_REMAP,
|
||||
REMAP
|
||||
}
|
||||
}
|
|
@ -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.util
|
||||
|
||||
import java.nio.file.FileSystem
|
||||
import java.nio.file.Path
|
||||
import org.objectweb.asm.tree.ClassNode
|
||||
|
||||
interface ClassNodeCache {
|
||||
fun findClass(name: String?): ClassNode?
|
||||
|
||||
companion object {
|
||||
fun create(
|
||||
jarFile: FileSystem,
|
||||
vararg fallbackJars: FileSystem?
|
||||
): ClassNodeCache {
|
||||
return ClassNodeCacheImpl(jarFile, fallbackJars.toList())
|
||||
}
|
||||
|
||||
fun create(
|
||||
jarFile: FileSystem,
|
||||
fallbackJars: List<FileSystem>,
|
||||
fallbackDirs: List<Path>
|
||||
): ClassNodeCache {
|
||||
return ClassNodeCacheImpl(jarFile, fallbackJars.toList(), fallbackDirs)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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,88 +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 org.cadixdev.at.io.AccessTransformFormats
|
||||
import org.cadixdev.mercury.Mercury
|
||||
import org.cadixdev.mercury.at.AccessTransformerRewriter
|
||||
import org.gradle.api.file.ConfigurableFileCollection
|
||||
import org.gradle.api.file.DirectoryProperty
|
||||
import org.gradle.api.file.RegularFileProperty
|
||||
import org.gradle.kotlin.dsl.*
|
||||
import org.gradle.workers.WorkAction
|
||||
import org.gradle.workers.WorkParameters
|
||||
import org.gradle.workers.WorkerExecutor
|
||||
|
||||
object PaperAt {
|
||||
|
||||
fun apply(workerExecutor: WorkerExecutor, apiDir: Path, serverDir: Path, atFile: Path?) {
|
||||
if (atFile == null) {
|
||||
return
|
||||
}
|
||||
|
||||
val queue = workerExecutor.processIsolation {
|
||||
forkOptions.jvmArgs("-Xmx2G")
|
||||
}
|
||||
|
||||
val srcDir = serverDir.resolve("src/main/java")
|
||||
|
||||
// Remap sources
|
||||
queue.submit(AtAction::class) {
|
||||
classpath.from(apiDir.resolve("src/main/java"))
|
||||
|
||||
inputDir.set(srcDir)
|
||||
outputDir.set(srcDir)
|
||||
ats.set(atFile)
|
||||
}
|
||||
|
||||
queue.await()
|
||||
}
|
||||
|
||||
interface AtParams : WorkParameters {
|
||||
val classpath: ConfigurableFileCollection
|
||||
val inputDir: DirectoryProperty
|
||||
val outputDir: DirectoryProperty
|
||||
val ats: RegularFileProperty
|
||||
}
|
||||
|
||||
abstract class AtAction : WorkAction<AtParams> {
|
||||
override fun execute() {
|
||||
Mercury().let { merc ->
|
||||
merc.classPath.addAll(parameters.classpath.map { it.toPath() })
|
||||
merc.isGracefulClasspathChecks = true
|
||||
|
||||
merc.process(parameters.inputDir.path)
|
||||
|
||||
merc.processors.clear()
|
||||
merc.processors.addAll(
|
||||
listOf(
|
||||
AccessTransformerRewriter.create(AccessTransformFormats.FML.read(parameters.ats.path))
|
||||
)
|
||||
)
|
||||
|
||||
merc.rewrite(parameters.inputDir.path, parameters.outputDir.path)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,133 +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.LabelNode
|
||||
import org.objectweb.asm.tree.LineNumberNode
|
||||
import org.objectweb.asm.tree.MethodInsnNode
|
||||
import org.objectweb.asm.tree.MethodNode
|
||||
import org.objectweb.asm.tree.TypeInsnNode
|
||||
import org.objectweb.asm.tree.VarInsnNode
|
||||
|
||||
object SyntheticUtil : AsmUtil {
|
||||
|
||||
fun findBaseMethod(node: MethodNode, className: String, methods: List<MethodNode> = emptyList()): MethodDesc {
|
||||
if (node.access !in Opcodes.ACC_SYNTHETIC) {
|
||||
return MethodDesc(node.name, node.desc)
|
||||
}
|
||||
|
||||
return checkMethodNode(node, className, methods) ?: MethodDesc(node.name, node.desc)
|
||||
}
|
||||
|
||||
private enum class State {
|
||||
IN_PARAMS,
|
||||
INVOKE,
|
||||
RETURN,
|
||||
OTHER_INSN
|
||||
}
|
||||
|
||||
// This tries to match the behavior of SpecialSource2's SyntheticFinder.addSynthetics() method
|
||||
private fun checkMethodNode(node: MethodNode, className: String, methods: List<MethodNode>): MethodDesc? {
|
||||
var state = State.IN_PARAMS
|
||||
var nextLvt = 0
|
||||
|
||||
var invokeInsn: MethodInsnNode? = null
|
||||
|
||||
for (insn in node.instructions) {
|
||||
if (insn is LabelNode || insn is LineNumberNode || insn is TypeInsnNode) {
|
||||
continue
|
||||
}
|
||||
|
||||
if (state == State.IN_PARAMS) {
|
||||
if (insn !is VarInsnNode || insn.`var` != nextLvt) {
|
||||
state = State.INVOKE
|
||||
}
|
||||
}
|
||||
|
||||
when (state) {
|
||||
State.IN_PARAMS -> {
|
||||
nextLvt++
|
||||
if (insn.opcode == Opcodes.LLOAD || insn.opcode == Opcodes.DLOAD) {
|
||||
nextLvt++
|
||||
}
|
||||
}
|
||||
State.INVOKE -> {
|
||||
// Must be a virtual or interface invoke instruction
|
||||
if ((insn.opcode != Opcodes.INVOKEVIRTUAL && insn.opcode != Opcodes.INVOKEINTERFACE) || insn !is MethodInsnNode) {
|
||||
return null
|
||||
}
|
||||
|
||||
invokeInsn = insn
|
||||
state = State.RETURN
|
||||
}
|
||||
State.RETURN -> {
|
||||
// The next instruction must be a return
|
||||
if (insn.opcode !in Opcodes.IRETURN..Opcodes.RETURN) {
|
||||
return null
|
||||
}
|
||||
|
||||
state = State.OTHER_INSN
|
||||
}
|
||||
State.OTHER_INSN -> {
|
||||
// We shouldn't see any other instructions
|
||||
return null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val invoke = invokeInsn ?: return null
|
||||
|
||||
// Must be a method in the same class with a different signature
|
||||
if (className != invoke.owner || (node.name == invoke.name && node.desc == invoke.desc)) {
|
||||
return null
|
||||
}
|
||||
|
||||
// The descriptors need to be the same size
|
||||
if (Type.getArgumentTypes(node.desc).size != Type.getArgumentTypes(invoke.desc).size) {
|
||||
return null
|
||||
}
|
||||
|
||||
for (otherMethod in methods) {
|
||||
if (node.name == otherMethod.name && invoke.desc == otherMethod.desc && otherMethod.signature != null) {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
// Add this method as a synthetic accessor for insn.name
|
||||
return MethodDesc(invoke.name, invoke.desc)
|
||||
}
|
||||
}
|
||||
|
||||
data class MethodDesc(val name: String, val desc: String)
|
||||
|
||||
interface AsmUtil {
|
||||
companion object {
|
||||
const val RESET_ACCESS: Int = (Opcodes.ACC_PUBLIC or Opcodes.ACC_PRIVATE or Opcodes.ACC_PROTECTED).inv()
|
||||
}
|
||||
|
||||
operator fun Int.contains(value: Int): Boolean {
|
||||
return value and this != 0
|
||||
}
|
||||
}
|
|
@ -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,30 +0,0 @@
|
|||
/*
|
||||
* paperweight is a Gradle plugin for the PaperMC project.
|
||||
*
|
||||
* Copyright (c) 2023 Kyle Wood (DenWav)
|
||||
* Contributors
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation;
|
||||
* version 2.1 only, no later versions.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
|
||||
* USA
|
||||
*/
|
||||
|
||||
package io.papermc.paperweight.util.data
|
||||
|
||||
data class LibraryChange(
|
||||
val inputId: ModuleId,
|
||||
val inputPath: String,
|
||||
val outputId: ModuleId,
|
||||
val outputPath: String
|
||||
)
|
|
@ -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,215 +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 io.papermc.paperweight.util.constants.*
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.io.IOException
|
||||
import java.io.OutputStream
|
||||
import java.nio.charset.Charset
|
||||
import java.nio.file.Path
|
||||
import kotlin.io.path.*
|
||||
import org.gradle.api.provider.Provider
|
||||
import org.gradle.api.provider.ProviderFactory
|
||||
|
||||
class Git(private val repo: Path, private val env: Map<String, String> = emptyMap()) {
|
||||
|
||||
@Suppress("unused")
|
||||
constructor(repo: Any) : this(repo.convertToPath())
|
||||
|
||||
init {
|
||||
if (!repo.exists()) {
|
||||
throw PaperweightException("Git directory does not exist: $repo")
|
||||
}
|
||||
}
|
||||
|
||||
fun withEnv(env: Map<String, String>): Git = Git(repo, env)
|
||||
|
||||
operator fun invoke(vararg args: String): Command {
|
||||
val cmd = arrayOf("git", "-c", "commit.gpgsign=false", "-c", "core.safecrlf=false", *args)
|
||||
return try {
|
||||
val builder = ProcessBuilder(*cmd).directory(repo)
|
||||
builder.environment().putAll(env)
|
||||
|
||||
val commandText = builder.command().joinToString(" ") { arg ->
|
||||
if (arg.codePoints().anyMatch { Character.isWhitespace(it) }) {
|
||||
"'$arg'"
|
||||
} else {
|
||||
arg
|
||||
}
|
||||
}
|
||||
Command(builder, commandText)
|
||||
} catch (e: IOException) {
|
||||
throw PaperweightException("Failed to execute command: ${cmd.joinToString(separator = " ")}", e)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val IGNORE_GITIGNORE_PROPERTY_NAME = "paperweight.ignore-gitignore"
|
||||
|
||||
var ignorePropertyField: Provider<Boolean>? = null
|
||||
fun ignoreProperty(providers: ProviderFactory): Provider<Boolean> {
|
||||
var current = ignorePropertyField
|
||||
if (current != null) {
|
||||
return current
|
||||
}
|
||||
current = providers.gradleProperty(IGNORE_GITIGNORE_PROPERTY_NAME).map { it.toBoolean() }.orElse(false)
|
||||
ignorePropertyField = current
|
||||
return current
|
||||
}
|
||||
|
||||
fun add(ignoreGitIgnore: Provider<Boolean>, vararg args: String): Array<String> {
|
||||
return add(ignoreGitIgnore.get(), *args)
|
||||
}
|
||||
|
||||
fun add(ignoreGitIgnore: Boolean, vararg args: String): Array<String> {
|
||||
return if (ignoreGitIgnore) {
|
||||
arrayOf("add", "--force", *args)
|
||||
} else {
|
||||
arrayOf("add", *args)
|
||||
}
|
||||
}
|
||||
|
||||
fun checkForGit() {
|
||||
try {
|
||||
val proc = ProcessBuilder("git", "--version").redirectErrorStream(true).start()
|
||||
proc.inputStream.copyTo(UselessOutputStream)
|
||||
if (proc.waitFor() == 0) {
|
||||
return
|
||||
}
|
||||
} catch (_: Exception) {}
|
||||
|
||||
throw PaperweightException("You must have git installed and available on your PATH in order to use paperweight.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class Command(private val processBuilder: ProcessBuilder, private val command: String) {
|
||||
|
||||
private var outStream: OutputStream = UselessOutputStream
|
||||
private var errStream: OutputStream = UselessOutputStream
|
||||
|
||||
fun run(): Int {
|
||||
if (System.getProperty(PAPERWEIGHT_DEBUG, "false") == "true") {
|
||||
// Override all settings for debug
|
||||
setup(DelegatingOutputStream(outStream, System.out), DelegatingOutputStream(errStream, System.err))
|
||||
println()
|
||||
println("$ (pwd) ${processBuilder.directory().absolutePath}")
|
||||
println("$ $command")
|
||||
}
|
||||
try {
|
||||
val process = processBuilder.start()
|
||||
|
||||
val input = process.inputStream
|
||||
val error = process.errorStream
|
||||
val buffer = ByteArray(1000)
|
||||
|
||||
while (process.isAlive) {
|
||||
// Read both stdout and stderr on the same thread
|
||||
// This is important for how Gradle outputs the logs
|
||||
if (input.available() > 0) {
|
||||
val count = input.read(buffer)
|
||||
outStream.write(buffer, 0, count)
|
||||
}
|
||||
if (error.available() > 0) {
|
||||
val count = error.read(buffer)
|
||||
errStream.write(buffer, 0, count)
|
||||
}
|
||||
Thread.sleep(1)
|
||||
}
|
||||
// Catch any other output we may have missed
|
||||
outStream.write(input.readBytes())
|
||||
errStream.write(error.readBytes())
|
||||
return process.waitFor()
|
||||
} catch (e: Exception) {
|
||||
throw PaperweightException("Failed to call git command: $command", e)
|
||||
}
|
||||
}
|
||||
|
||||
fun runSilently(silenceOut: Boolean = true, silenceErr: Boolean = false): Int {
|
||||
silence(silenceOut, silenceErr)
|
||||
return run()
|
||||
}
|
||||
|
||||
fun runOut(): Int {
|
||||
setup(System.out, System.err)
|
||||
return run()
|
||||
}
|
||||
|
||||
fun execute() {
|
||||
val res = run()
|
||||
if (res != 0) {
|
||||
throw PaperweightException("Command finished with $res exit code: $command")
|
||||
}
|
||||
}
|
||||
|
||||
fun executeSilently(silenceOut: Boolean = true, silenceErr: Boolean = false) {
|
||||
silence(silenceOut, silenceErr)
|
||||
execute()
|
||||
}
|
||||
|
||||
private fun silence(silenceOut: Boolean, silenceErr: Boolean) {
|
||||
val out = if (silenceOut) null else System.out
|
||||
val err = if (silenceErr) null else System.err
|
||||
setup(out, err)
|
||||
}
|
||||
|
||||
fun executeOut() {
|
||||
setup(System.out, System.err)
|
||||
execute()
|
||||
}
|
||||
|
||||
fun setup(out: OutputStream? = null, err: OutputStream? = null): Command {
|
||||
outStream = out ?: UselessOutputStream
|
||||
errStream = err ?: UselessOutputStream
|
||||
return this
|
||||
}
|
||||
|
||||
fun getText(): String {
|
||||
val out = ByteArrayOutputStream()
|
||||
setup(out, System.err)
|
||||
execute()
|
||||
return String(out.toByteArray(), Charset.defaultCharset())
|
||||
}
|
||||
|
||||
@Suppress("unused")
|
||||
fun readText(): String? {
|
||||
val out = ByteArrayOutputStream()
|
||||
setup(out, System.err)
|
||||
return if (run() == 0) String(out.toByteArray(), Charset.defaultCharset()) else null
|
||||
}
|
||||
|
||||
class Result(val exit: Int, val out: String)
|
||||
|
||||
fun captureOut(logOut: Boolean): Result = run {
|
||||
val out = ByteArrayOutputStream()
|
||||
if (logOut) {
|
||||
val combined = DelegatingOutputStream(System.out, out)
|
||||
setup(combined, combined)
|
||||
} else {
|
||||
setup(out, out)
|
||||
}
|
||||
Result(run(), String(out.toByteArray()))
|
||||
}
|
||||
}
|
|
@ -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,102 +0,0 @@
|
|||
/*
|
||||
* paperweight is a Gradle plugin for the PaperMC project.
|
||||
*
|
||||
* Copyright (c) 2023 Kyle Wood (DenWav)
|
||||
* Contributors
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation;
|
||||
* version 2.1 only, no later versions.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
|
||||
* USA
|
||||
*/
|
||||
|
||||
package io.papermc.paperweight.util
|
||||
|
||||
import io.papermc.paperweight.PaperweightException
|
||||
import java.io.OutputStream
|
||||
import java.util.concurrent.TimeUnit
|
||||
import java.util.jar.JarFile
|
||||
import kotlin.io.path.*
|
||||
import org.gradle.api.file.FileCollection
|
||||
import org.gradle.api.provider.Provider
|
||||
import org.gradle.jvm.toolchain.JavaLauncher
|
||||
|
||||
fun JavaLauncher.runJar(
|
||||
classpath: FileCollection,
|
||||
workingDir: Any,
|
||||
logFile: Any?,
|
||||
jvmArgs: List<String> = listOf(),
|
||||
vararg args: String
|
||||
) {
|
||||
var mainClass: String? = null
|
||||
for (file in classpath.files) {
|
||||
mainClass = JarFile(file).use { jarFile ->
|
||||
jarFile.manifest.mainAttributes.getValue("Main-Class")
|
||||
} ?: continue
|
||||
break
|
||||
}
|
||||
if (mainClass == null) {
|
||||
throw PaperweightException("Could not determine main class name for ${classpath.asPath}")
|
||||
}
|
||||
|
||||
val dir = workingDir.convertToPath()
|
||||
|
||||
val (logFilePath, output) = when {
|
||||
logFile is OutputStream -> Pair(null, logFile)
|
||||
logFile != null -> {
|
||||
val log = logFile.convertToPath()
|
||||
log.parent.createDirectories()
|
||||
Pair(log, log.outputStream().buffered())
|
||||
}
|
||||
else -> Pair(null, UselessOutputStream)
|
||||
}
|
||||
|
||||
val processBuilder = ProcessBuilder(
|
||||
this.executablePath.path.absolutePathString(),
|
||||
*jvmArgs.toTypedArray(),
|
||||
"-classpath",
|
||||
classpath.asPath,
|
||||
mainClass,
|
||||
*args
|
||||
).directory(dir)
|
||||
|
||||
output.writer().let {
|
||||
it.appendLine("Command: ${processBuilder.command().joinToString(" ")}")
|
||||
it.flush()
|
||||
}
|
||||
|
||||
val process = processBuilder.start()
|
||||
|
||||
output.use {
|
||||
val outFuture = redirect(process.inputStream, it)
|
||||
val errFuture = redirect(process.errorStream, it)
|
||||
|
||||
val exit = process.waitFor()
|
||||
outFuture.get(500L, TimeUnit.MILLISECONDS)
|
||||
errFuture.get(500L, TimeUnit.MILLISECONDS)
|
||||
if (exit != 0) {
|
||||
val logMsg = logFilePath?.let { p -> " Log file: ${p.absolutePathString()}" } ?: ""
|
||||
throw PaperweightException("Execution of '$mainClass' failed with exit code $exit.$logMsg Classpath: ${classpath.asPath}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun Provider<JavaLauncher>.runJar(
|
||||
classpath: FileCollection,
|
||||
workingDir: Any,
|
||||
logFile: Any?,
|
||||
jvmArgs: List<String> = listOf(),
|
||||
vararg args: String
|
||||
) {
|
||||
get().runJar(classpath, workingDir, logFile, jvmArgs, *args)
|
||||
}
|
|
@ -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,80 +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.FileVisitResult
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.SimpleFileVisitor
|
||||
import java.nio.file.attribute.BasicFileAttributes
|
||||
import java.util.concurrent.ThreadLocalRandom
|
||||
import kotlin.io.path.*
|
||||
|
||||
fun unzip(zip: Any, target: Any? = null): Path {
|
||||
val input = zip.convertToPath()
|
||||
val outputDir = target?.convertToPath()
|
||||
?: input.resolveSibling("${input.name}-" + ThreadLocalRandom.current().nextInt())
|
||||
|
||||
input.openZip().use { fs ->
|
||||
fs.walk().use { stream ->
|
||||
stream.forEach { p ->
|
||||
val targetFile = outputDir.resolve(p.absolutePathString().substring(1))
|
||||
targetFile.parent.createDirectories()
|
||||
p.copyTo(targetFile)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return outputDir
|
||||
}
|
||||
|
||||
fun zip(inputDir: Any, zip: Any) {
|
||||
val outputZipFile = zip.convertToPath()
|
||||
try {
|
||||
outputZipFile.deleteIfExists()
|
||||
} catch (e: Exception) {
|
||||
throw PaperweightException("Could not delete $outputZipFile", e)
|
||||
}
|
||||
|
||||
val dirPath = inputDir.convertToPath()
|
||||
|
||||
outputZipFile.writeZip().use { fs ->
|
||||
Files.walkFileTree(
|
||||
dirPath,
|
||||
object : SimpleFileVisitor<Path>() {
|
||||
override fun preVisitDirectory(dir: Path, attrs: BasicFileAttributes): FileVisitResult {
|
||||
if (dir != dirPath) {
|
||||
Files.createDirectories(fs.getPath(dirPath.relativize(dir).toString()))
|
||||
}
|
||||
return FileVisitResult.CONTINUE
|
||||
}
|
||||
|
||||
override fun visitFile(file: Path, attrs: BasicFileAttributes): FileVisitResult {
|
||||
Files.copy(file, fs.getPath(dirPath.relativize(file).toString()))
|
||||
return FileVisitResult.CONTINUE
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
|
@ -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,104 +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.patcher.upstream.DefaultPaperRepoPatcherUpstream
|
||||
import io.papermc.paperweight.patcher.upstream.DefaultPatcherUpstream
|
||||
import io.papermc.paperweight.patcher.upstream.DefaultRepoPatcherUpstream
|
||||
import io.papermc.paperweight.patcher.upstream.PaperRepoPatcherUpstream
|
||||
import io.papermc.paperweight.patcher.upstream.PatcherUpstream
|
||||
import io.papermc.paperweight.patcher.upstream.RepoPatcherUpstream
|
||||
import io.papermc.paperweight.util.*
|
||||
import io.papermc.paperweight.util.constants.*
|
||||
import java.util.Locale
|
||||
import org.gradle.api.Action
|
||||
import org.gradle.api.ExtensiblePolymorphicDomainObjectContainer
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.file.Directory
|
||||
import org.gradle.api.file.DirectoryProperty
|
||||
import org.gradle.api.file.ProjectLayout
|
||||
import org.gradle.api.file.RegularFileProperty
|
||||
import org.gradle.api.model.ObjectFactory
|
||||
import org.gradle.api.provider.ListProperty
|
||||
import org.gradle.api.provider.Property
|
||||
import org.gradle.api.provider.Provider
|
||||
import org.gradle.api.tasks.TaskContainer
|
||||
import org.gradle.kotlin.dsl.*
|
||||
|
||||
@Suppress("unused", "MemberVisibilityCanBePrivate")
|
||||
open class PaperweightPatcherExtension(project: Project, private val objects: ObjectFactory, layout: ProjectLayout, tasks: TaskContainer) {
|
||||
|
||||
val serverProject: Property<Project> = objects.property()
|
||||
|
||||
val mcDevSourceDir: DirectoryProperty = objects.directoryProperty().convention(serverProject.map { it.layout.cacheDir(MC_DEV_SOURCES_DIR) })
|
||||
|
||||
val buildDataDir: DirectoryProperty = objects.dirWithDefault(layout, "build-data")
|
||||
val devImports: RegularFileProperty = objects.fileFrom(buildDataDir, "dev-imports.txt")
|
||||
val reobfMappingsPatch: RegularFileProperty = objects.fileFrom(buildDataDir, "reobf-mappings-patch.tiny")
|
||||
val reobfPackagesToFix: ListProperty<String> = objects.listProperty()
|
||||
|
||||
val mainClass: Property<String> = objects.property<String>().convention("org.bukkit.craftbukkit.Main")
|
||||
val bundlerJarName: Property<String> = objects.property<String>().convention(project.name.lowercase(Locale.ENGLISH))
|
||||
|
||||
val decompileRepo: Property<String> = objects.property()
|
||||
val remapRepo: Property<String> = objects.property()
|
||||
|
||||
val upstreams: ExtensiblePolymorphicDomainObjectContainer<PatcherUpstream> = objects.polymorphicDomainObjectContainer(PatcherUpstream::class)
|
||||
|
||||
/**
|
||||
* The directory upstreams should be checked out in. Paperweight will use the directory specified in the
|
||||
* following order, whichever is set first:
|
||||
*
|
||||
* 1. The value of the Gradle property `paperweightUpstreamWorkDir`.
|
||||
* 2. The value of this [upstreamsDir] property.
|
||||
* 3. The default location of <project_root>/.gradle/caches/paperweight/upstreams
|
||||
*
|
||||
* This means a project which is several upstreams deep will all use the upstreams directory defined by the root project.
|
||||
*/
|
||||
val upstreamsDir: Property<Directory> = objects.directoryProperty().convention(layout.cacheDir(UPSTREAMS))
|
||||
|
||||
init {
|
||||
upstreams.registerFactory(PatcherUpstream::class.java) { name -> DefaultPatcherUpstream(name, objects, tasks) }
|
||||
upstreams.registerFactory(RepoPatcherUpstream::class.java) { name -> DefaultRepoPatcherUpstream(name, objects, tasks, layout) }
|
||||
upstreams.registerFactory(PaperRepoPatcherUpstream::class.java) { name -> DefaultPaperRepoPatcherUpstream(name, objects, tasks, layout) }
|
||||
}
|
||||
|
||||
fun usePaperUpstream(refProvider: Provider<String>, action: Action<PaperRepoPatcherUpstream>) {
|
||||
upstreams {
|
||||
register<PaperRepoPatcherUpstream>("paper") {
|
||||
url.set(github("PaperMC", "Paper"))
|
||||
ref.set(refProvider)
|
||||
|
||||
action.execute(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun useStandardUpstream(name: String, action: Action<RepoPatcherUpstream>) {
|
||||
upstreams {
|
||||
register<RepoPatcherUpstream>(name) {
|
||||
action.execute(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,119 +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.util.*
|
||||
import kotlin.io.path.*
|
||||
import org.gradle.api.DefaultTask
|
||||
import org.gradle.api.file.DirectoryProperty
|
||||
import org.gradle.api.provider.Property
|
||||
import org.gradle.api.tasks.Input
|
||||
import org.gradle.api.tasks.Internal
|
||||
import org.gradle.api.tasks.OutputDirectory
|
||||
import org.gradle.api.tasks.TaskAction
|
||||
import org.gradle.api.tasks.UntrackedTask
|
||||
|
||||
@UntrackedTask(because = "Git tracks the state")
|
||||
abstract class CheckoutRepo : DefaultTask() {
|
||||
|
||||
@get:Input
|
||||
abstract val repoName: Property<String>
|
||||
|
||||
@get:Input
|
||||
abstract val url: Property<String>
|
||||
|
||||
@get:Input
|
||||
abstract val ref: Property<String>
|
||||
|
||||
@get:Input
|
||||
abstract val shallowClone: Property<Boolean>
|
||||
|
||||
@get:Input
|
||||
abstract val initializeSubmodules: Property<Boolean>
|
||||
|
||||
@get:Input
|
||||
abstract val initializeSubmodulesShallow: Property<Boolean>
|
||||
|
||||
@get:Internal
|
||||
abstract val workDir: DirectoryProperty
|
||||
|
||||
@get:OutputDirectory
|
||||
abstract val outputDir: DirectoryProperty
|
||||
|
||||
init {
|
||||
@Suppress("LeakingThis")
|
||||
run {
|
||||
repoName.finalizeValueOnRead()
|
||||
url.finalizeValueOnRead()
|
||||
ref.finalizeValueOnRead()
|
||||
shallowClone.convention(true).finalizeValueOnRead()
|
||||
initializeSubmodules.convention(true).finalizeValueOnRead()
|
||||
initializeSubmodulesShallow.convention(false).finalizeValueOnRead()
|
||||
|
||||
outputDir.convention(workDir.dir(repoName)).finalizeValueOnRead()
|
||||
}
|
||||
}
|
||||
|
||||
@TaskAction
|
||||
fun run() {
|
||||
Git.checkForGit()
|
||||
|
||||
val dir = outputDir.path
|
||||
val urlText = url.get().trim()
|
||||
|
||||
if (dir.resolve(".git").notExists()) {
|
||||
dir.deleteRecursive()
|
||||
dir.createDirectories()
|
||||
|
||||
Git(dir)("init", "--quiet").executeSilently()
|
||||
}
|
||||
|
||||
val git = Git(dir)
|
||||
git("remote", "remove", "origin").runSilently(silenceErr = true) // can fail
|
||||
git("remote", "add", "origin", urlText).executeSilently(silenceErr = true)
|
||||
git.fetch()
|
||||
|
||||
git("checkout", "-f", "FETCH_HEAD").executeSilently(silenceErr = true)
|
||||
git("clean", "-fqd").executeSilently(silenceErr = true)
|
||||
|
||||
if (initializeSubmodules.get()) {
|
||||
git.updateSubmodules()
|
||||
}
|
||||
}
|
||||
|
||||
private fun Git.fetch() {
|
||||
if (shallowClone.get()) {
|
||||
this("fetch", "--depth", "1", "origin", ref.get()).executeSilently(silenceErr = true)
|
||||
} else {
|
||||
this("fetch", "origin", ref.get()).executeSilently(silenceErr = true)
|
||||
}
|
||||
}
|
||||
|
||||
private fun Git.updateSubmodules() {
|
||||
if (initializeSubmodulesShallow.get()) {
|
||||
this("submodule", "update", "--init", "--recursive", "--depth", "1").executeSilently(silenceErr = true)
|
||||
} else {
|
||||
this("submodule", "update", "--init", "--recursive").executeSilently(silenceErr = true)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue