Compare commits
484 commits
Author | SHA1 | Date | |
---|---|---|---|
49e471e0f8 | |||
95585adea7 | |||
671d4ff2b2 | |||
ecb210d3ef | |||
|
91fac7484b | ||
|
4e08a00d65 | ||
|
4b9a233416 | ||
|
b3467d9f8d | ||
|
40f78222c0 | ||
|
33a8f201da | ||
|
a788a9770d | ||
|
a5ae66c59a | ||
|
4d47004311 | ||
|
eb14faff2b | ||
|
007ef94235 | ||
|
d3a5f5fc4c | ||
|
c3c104c363 | ||
|
f964c5bc0b | ||
|
b26c85b988 | ||
|
6495ae0a8d | ||
|
a3256368ed | ||
|
b47a75a601 | ||
|
381e557a2f | ||
|
3711175570 | ||
|
e54e9fce13 | ||
|
0b9e07345f | ||
|
f391c0c900 | ||
|
668e516a41 | ||
|
8828b4adc1 | ||
|
073ed85850 | ||
|
095a3210d2 | ||
|
10b0c49ee8 | ||
|
78f033b9c2 | ||
|
0eaa60eeff | ||
|
cfcb6ad8ef | ||
|
cf4fcd8953 | ||
|
17b3734bb1 | ||
|
e92795886c | ||
|
114dc611e0 | ||
|
c6d776d6f4 | ||
|
ca387c97ce | ||
|
c8f6e05472 | ||
|
4a2e036152 | ||
|
2a02316725 | ||
|
d972b76e9d | ||
|
9026a4ba27 | ||
|
ad931e0d37 | ||
|
b7a64b0134 | ||
|
754562481d | ||
|
8c3c62d89d | ||
|
374131ff4f | ||
|
2fba0a0647 | ||
|
bf78951adc | ||
|
0d47c66448 | ||
|
dafc134ef6 | ||
|
21f771d3e8 | ||
|
0b3688ca5b | ||
|
4bc9495f03 | ||
|
63838710fc | ||
|
15224af96b | ||
|
7e631a2a9d | ||
|
8230d7d997 | ||
|
f1548964e7 | ||
|
b356b4d4de | ||
|
fc0ead8047 | ||
|
4e97731a49 | ||
|
6a36cfa316 | ||
|
76dad969e0 | ||
|
7669316eac | ||
|
87e8db20d0 | ||
|
47ec175e27 | ||
|
3be0f6cff7 | ||
|
7d2ae18db3 | ||
|
1d88d33cc5 | ||
|
daab4fa35e | ||
|
5225a4d132 | ||
|
c7d0820fa1 | ||
|
609bec2f68 | ||
|
87205250d2 | ||
|
ee61ec1e07 | ||
|
00d9ba22ed | ||
|
fa8945f1a8 | ||
|
45aaed91c7 | ||
|
f940a23453 | ||
|
2b1404179e | ||
|
5389e61eee | ||
|
b1011c70fa | ||
|
340ee3582f | ||
|
2dd3e6f3b7 | ||
|
c80a23cf39 | ||
|
3a4ea86b77 | ||
|
586f6a7d49 | ||
|
4803719449 | ||
|
88edb5ce16 | ||
|
157abbb72f | ||
|
23d9406d2c | ||
|
3806539949 | ||
|
8faebd1767 | ||
|
d23909c7d8 | ||
|
28786b0947 | ||
|
de151674ee | ||
|
c2d4fcb084 | ||
|
3f6c7a5feb | ||
|
57566dd7da | ||
|
ce9df1c5f6 | ||
|
a3033c6d60 | ||
|
329b5663e9 | ||
|
33b8a1b12a | ||
|
92f922f291 | ||
|
9f602b892a | ||
|
972de2e850 | ||
|
d3d7eec0c5 | ||
|
d82fddd8d7 | ||
|
c095ab0265 | ||
|
934e245fdf | ||
|
a260d33b42 | ||
|
11ba59068f | ||
|
b56b118555 | ||
|
aa146cdd5f | ||
|
30f1b60fed | ||
|
4e63b8041d | ||
|
ac168e288b | ||
|
4b5d7c0f18 | ||
|
298ae45990 | ||
|
219df998ac | ||
|
0bad385a38 | ||
|
d5ef36b535 | ||
|
d655180e7a | ||
|
12fecf6315 | ||
|
90384770ba | ||
|
dd1b1e28ed | ||
|
f7528b6f12 | ||
|
bed0081cad | ||
|
4e8233d0ae | ||
|
70e0b7b7bb | ||
|
e17043314f | ||
|
6b158a2b7a | ||
|
307fc2e606 | ||
|
6619d202aa | ||
|
0e759beba4 | ||
|
7c0dda0ed3 | ||
|
7d4f0cb889 | ||
|
8a9f7c7462 | ||
|
668e7a0c7c | ||
|
fe82b94a2b | ||
|
e1fcff715d | ||
|
dca41f43c5 | ||
|
6283956ab9 | ||
|
026347a88b | ||
|
ca8767bc2d | ||
|
71402ecbc0 | ||
|
ccd22ea127 | ||
|
08a5f4a9c9 | ||
|
3afff25383 | ||
|
1756148d32 | ||
|
505a11044b | ||
|
33f0d78506 | ||
|
178e1ba293 | ||
|
4c805951b2 | ||
|
c280119859 | ||
|
50b4ca92cf | ||
|
fec6119c16 | ||
|
3f82b9aa48 | ||
|
b887043915 | ||
|
35b5e68aca | ||
|
f2e5ab5077 | ||
|
3f89a135ba | ||
|
ddd552b03f | ||
|
0e51fe634e | ||
|
7c32ad2d62 | ||
|
1bba114771 | ||
|
967c17e0ec | ||
|
e9750ebf7c | ||
|
41ce4d8b9b | ||
|
e52ef621ff | ||
|
98de95e9c0 | ||
|
850aaf8ac4 | ||
|
6deed39825 | ||
|
c83d1fef94 | ||
|
9b7ef249e8 | ||
|
ae6aeed7b5 | ||
|
4a7c4f9842 | ||
|
57970ab0bb | ||
|
e510c2ffcc | ||
|
b361bb3ec6 | ||
|
7cab978aff | ||
|
79db91fa46 | ||
|
05c5d2f758 | ||
|
c4d0c5cb24 | ||
|
1daa1948c1 | ||
|
729ff50a6c | ||
|
9de125ba88 | ||
|
1943b3353c | ||
|
7b68885368 | ||
|
49031b3f19 | ||
|
fea6f5c494 | ||
|
94962163a6 | ||
|
1ac512a5b3 | ||
|
52817caec7 | ||
|
058a807735 | ||
|
a5cda84e7f | ||
|
091a3a2a76 | ||
|
c1bd340a72 | ||
|
9c98431634 | ||
|
db3487afb8 | ||
|
964fa21134 | ||
|
1dd0766422 | ||
|
bb9fdebccb | ||
|
5cfef1b1a5 | ||
|
8f755267dd | ||
|
f038894430 | ||
|
ff68ede186 | ||
|
d1647fa2cc | ||
|
1f50d6696f | ||
|
1a8465dcf0 | ||
|
931993e212 | ||
|
638d21638b | ||
|
762221763b | ||
|
d1290e3e84 | ||
|
b3ff269b57 | ||
|
f1a54fe926 | ||
|
2ad4af9365 | ||
|
ed3112a32c | ||
|
ae902edac2 | ||
|
b305ef9553 | ||
|
c839029bc4 | ||
|
516f1cdace | ||
|
bfa8e20ff8 | ||
|
27957e759a | ||
|
6c9ab905dc | ||
|
5b772c14fb | ||
|
777d41fc69 | ||
|
8d47aaa469 | ||
|
681cc42bd9 | ||
|
11066cb1ba | ||
|
5988b6c42a | ||
|
235a0320f7 | ||
|
ad0c94aff6 | ||
|
21bc470c31 | ||
|
9ff8f17d8b | ||
|
ca9bc917bb | ||
|
0254dd1e1e | ||
|
cf10510466 | ||
|
2d9d7b4d47 | ||
|
d7ad0cd46b | ||
|
1777b820e7 | ||
|
782dec5128 | ||
|
6bc03c39d9 | ||
|
9e15ea3ce3 | ||
|
7799a11289 | ||
|
79ccae2de2 | ||
|
7e88acb91a | ||
|
489ab48d99 | ||
|
c296e3d621 | ||
|
6a3961610e | ||
|
32d8d14faf | ||
|
0d27315301 | ||
|
7de5af8681 | ||
|
311fad8f1f | ||
|
d79a5eb21a | ||
|
d1c5911412 | ||
|
4895a3e32f | ||
|
ee77d0c026 | ||
|
a4a7fb8ed0 | ||
|
b70515b3d4 | ||
|
a110bc2055 | ||
|
ba82b6a0e1 | ||
|
895da03fdc | ||
|
71ff3a00e5 | ||
|
be6c16206d | ||
|
5787341579 | ||
|
7a6d57c43d | ||
|
d261be9f82 | ||
|
835ea19220 | ||
|
e7c19a8c3e | ||
|
2cb7246128 | ||
|
6215e929c1 | ||
|
2948777623 | ||
|
a1b1b140a2 | ||
|
d76e04dae4 | ||
|
00e63c1716 | ||
|
b4115d1363 | ||
|
938bd3a38a | ||
|
f633ab45fc | ||
|
0777a1ce85 | ||
|
91f1190772 | ||
|
b4a8980717 | ||
|
abfb71758d | ||
|
a6f0a96a0d | ||
|
893d2e65d0 | ||
|
699b849951 | ||
|
0a90aa2879 | ||
|
1e3d63a29b | ||
|
5b6f778557 | ||
|
24512fe2e3 | ||
|
2672151036 | ||
|
e5da125708 | ||
|
5f5e44d341 | ||
|
d7e97c0ae1 | ||
|
3695c2ebaf | ||
|
2fe2f9de7f | ||
|
80ab8588f7 | ||
|
82b42acf85 | ||
|
852f3bef3d | ||
|
bbca9901a2 | ||
|
8cea4a9172 | ||
|
0953e65e3f | ||
|
d330bd1b1b | ||
|
621fed978f | ||
|
4c4fad3f1f | ||
|
45f444c0ff | ||
|
55b37e7671 | ||
|
8fdcfa09ab | ||
|
b7800816c8 | ||
|
99d657b170 | ||
|
39f64d8cc0 | ||
|
a17e068923 | ||
|
52e713826f | ||
|
aea7613389 | ||
|
1b15e3708c | ||
|
53b8d8e975 | ||
|
0e4edf85ea | ||
|
d47988d0bc | ||
|
6dee585d50 | ||
|
9cc446e04c | ||
|
ca165dc4b8 | ||
|
f26c0a7643 | ||
|
2b07f488ab | ||
|
03bd820f8d | ||
|
4b329b2e8c | ||
|
bb0ba357f9 | ||
|
6d6f5e4aba | ||
|
fc5f69d579 | ||
|
a8194efde7 | ||
|
24a8b06ad2 | ||
|
54e712a6f6 | ||
|
c2805037f2 | ||
|
87e05b0843 | ||
|
9e4a702d8c | ||
|
3e266190bd | ||
|
102219b035 | ||
|
59c469af45 | ||
|
f2be248c88 | ||
|
dd2cd269fd | ||
|
aba3695bb0 | ||
|
481bbdfc18 | ||
|
3dcafbb69d | ||
|
93eff06e08 | ||
|
51368f2f46 | ||
|
1ecccc4667 | ||
|
3cd511f6df | ||
|
f5d09e8d1d | ||
|
00db3813d5 | ||
|
0938b9e579 | ||
|
3fbb57516a | ||
|
b4a6a098bf | ||
|
dfb3f6d88e | ||
|
f68b7016d1 | ||
|
0da32ff145 | ||
|
200a6edddb | ||
|
5e7f49c63f | ||
|
6970deb6f3 | ||
|
c67ebf0907 | ||
|
a1ca75456a | ||
|
6ff1f7d8d2 | ||
|
c5bcd09cf4 | ||
|
163582fa5d | ||
|
96a37ff164 | ||
|
16727e4555 | ||
|
45c18c1fb4 | ||
|
a20fb06e82 | ||
|
566a138440 | ||
|
3d16820c3c | ||
|
e0ac40d687 | ||
|
6de57d0a96 | ||
|
2d71260a90 | ||
|
685abfa47e | ||
|
f155d849f5 | ||
|
9f2e97c2bd | ||
|
2507973a4e | ||
|
dce96139e7 | ||
|
e24891ba33 | ||
|
2ce26f2fb1 | ||
|
be349eb335 | ||
|
0cef58eace | ||
|
bb377a631f | ||
|
e5f79b3cf3 | ||
|
2c8a13d17b | ||
|
fbe7faa524 | ||
|
e575e5369c | ||
|
77fe06c637 | ||
|
b88846c519 | ||
|
4a24323970 | ||
|
a55034a39c | ||
|
b0cbf7ce9a | ||
|
7d1da0b2cb | ||
|
23e8ad60fe | ||
|
a039727122 | ||
|
7013e8c220 | ||
|
ec35b28283 | ||
|
98b0aa7ae8 | ||
|
462fdcc26e | ||
|
e0d4f19857 | ||
|
ebefb33061 | ||
|
be84234e8d | ||
|
38d7bc2a79 | ||
|
6c4f5c0022 | ||
|
e988e70817 | ||
|
9734ac1a56 | ||
|
d5753c0c6f | ||
|
745018d4b4 | ||
|
970938e158 | ||
|
2f745e4287 | ||
|
784690a92a | ||
|
46a816d5f0 | ||
|
3e5cce729c | ||
|
ee4194f682 | ||
|
b7b6b3d270 | ||
|
ccafe9a3da | ||
|
bf61f803f3 | ||
|
788e691dc3 | ||
|
26b8e25f8a | ||
|
ed900662d2 | ||
|
cfcb0ef378 | ||
|
cb32d78534 | ||
|
22010b0473 | ||
|
ba606503f0 | ||
|
89a2b1612c | ||
|
f6a071c138 | ||
|
4205a4fa46 | ||
|
36186fca7a | ||
|
5212332f40 | ||
|
2fea06ded4 | ||
|
4f1e0f6fdf | ||
|
2c9e0bd64c | ||
|
5f0989c035 | ||
|
c6dfaa3b03 | ||
|
a84caebca1 | ||
|
075f71b02e | ||
|
58fd8ccded | ||
|
e8128c09de | ||
|
d989202788 | ||
|
228946811d | ||
|
57a29a114f | ||
|
d73f6d6519 | ||
|
ec46558598 | ||
|
5a4ed40c3e | ||
|
3fe8607ce9 | ||
|
f115f63dba | ||
|
64de449fab | ||
|
8952ed266c | ||
|
f7100e7e78 | ||
|
0664751fef | ||
|
6a3fdbe961 | ||
|
3dfbb4b1a0 | ||
|
2cbae7a1e7 | ||
|
6406e3c1b4 | ||
|
fe3d49f586 | ||
|
046a54d1ba | ||
|
caccbb1570 | ||
|
b4f5e44ba2 | ||
|
33842f05ed | ||
|
204729678a | ||
|
db46cc1ddb | ||
|
a384d7eef4 | ||
|
6b22ccdf03 | ||
|
217f2ef61d | ||
|
14018872ca | ||
|
b4baf33b02 | ||
|
ef81ce64e1 | ||
|
7510adddb4 | ||
|
27cfd2070f | ||
|
2bf60cded4 | ||
|
bb8978d0ca | ||
|
c38cb87411 | ||
|
bb83b90c47 | ||
|
3ffc238bbb | ||
|
98f2794d18 | ||
|
37932ac17b | ||
|
e9b2021d4a | ||
|
7d200300c2 | ||
|
2aba740750 | ||
|
bbe536e51b | ||
|
5c07640131 |
186 changed files with 16426 additions and 4547 deletions
|
@ -1,4 +1,85 @@
|
|||
[*.{kt,kts}]
|
||||
max_line_length=150
|
||||
# noinspection EditorConfigKeyCorrectness
|
||||
kotlin_imports_layout=ascii
|
||||
[*.{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
|
||||
|
|
19
.github/workflows/build.yml
vendored
19
.github/workflows/build.yml
vendored
|
@ -1,19 +0,0 @@
|
|||
name: Build
|
||||
on: [push, pull_request]
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-20.04
|
||||
container:
|
||||
image: adoptopenjdk:11-jdk-hotspot-focal
|
||||
options: --user root
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- uses: actions/cache@v2
|
||||
with:
|
||||
path: |
|
||||
~/.gradle/caches
|
||||
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-gradle-
|
||||
- uses: gradle/wrapper-validation-action@v1
|
||||
- run: ./gradlew build --no-daemon --stacktrace
|
35
.github/workflows/deploy-snapshot.yml
vendored
Normal file
35
.github/workflows/deploy-snapshot.yml
vendored
Normal file
|
@ -0,0 +1,35 @@
|
|||
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 }}
|
38
.github/workflows/deploy.yml
vendored
38
.github/workflows/deploy.yml
vendored
|
@ -1,24 +1,30 @@
|
|||
name: Deploy
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
tags: [ 'v*' ]
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
runs-on: ubuntu-20.04
|
||||
container:
|
||||
image: adoptopenjdk:11-jdk-hotspot-focal
|
||||
options: --user root
|
||||
name: Deploy
|
||||
runs-on: 'ubuntu-latest'
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- uses: actions/cache@v2
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-java@v3
|
||||
with:
|
||||
path: |
|
||||
~/.gradle/caches
|
||||
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-gradle-
|
||||
- uses: gradle/wrapper-validation-action@v1
|
||||
- run: ./gradlew publish --no-daemon --stacktrace -PdemonwavUsername=paperweight
|
||||
java-version: 17
|
||||
distribution: 'temurin'
|
||||
- name: Setup Gradle
|
||||
uses: gradle/gradle-build-action@v2
|
||||
- name: Deploy release
|
||||
run: ./gradlew publishPlugins --no-daemon --stacktrace
|
||||
env:
|
||||
ORG_GRADLE_PROJECT_demonwavPassword: ${{ secrets.paperweightToken }}
|
||||
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
Normal file
22
.github/workflows/test.yml
vendored
Normal file
|
@ -0,0 +1,22 @@
|
|||
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)
|
||||
|
|
274
build.gradle.kts
274
build.gradle.kts
|
@ -1,273 +1,9 @@
|
|||
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
|
||||
import org.gradle.api.publish.maven.MavenPom
|
||||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||
|
||||
plugins {
|
||||
idea
|
||||
eclipse
|
||||
`kotlin-dsl`
|
||||
`maven-publish`
|
||||
id("net.minecrell.licenser") version "0.4.1"
|
||||
id("com.github.johnrengelman.shadow") version "6.0.0"
|
||||
id("org.jlleitschuh.gradle.ktlint") version "9.4.1"
|
||||
}
|
||||
|
||||
group = "io.papermc.paperweight"
|
||||
version = "1.0.0-SNAPSHOT"
|
||||
|
||||
java {
|
||||
toolchain {
|
||||
languageVersion.set(JavaLanguageVersion.of(11))
|
||||
}
|
||||
withSourcesJar()
|
||||
}
|
||||
|
||||
val sourcesJar by tasks.existing
|
||||
|
||||
tasks.withType<KotlinCompile> {
|
||||
kotlinOptions.jvmTarget = "1.8"
|
||||
kotlinOptions.freeCompilerArgs = listOf("-Xjvm-default=enable")
|
||||
}
|
||||
|
||||
gradlePlugin {
|
||||
// we handle publications ourselves
|
||||
isAutomatedPublishing = false
|
||||
}
|
||||
|
||||
val shade: Configuration by configurations.creating
|
||||
configurations.implementation {
|
||||
extendsFrom(shade)
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
maven("https://oss.sonatype.org/content/repositories/snapshots/") {
|
||||
mavenContent {
|
||||
includeGroup("org.cadixdev")
|
||||
}
|
||||
}
|
||||
maven("https://repo.demonwav.com/snapshots/") {
|
||||
mavenContent {
|
||||
includeGroup("org.cadixdev")
|
||||
}
|
||||
}
|
||||
maven("https://maven.fabricmc.net/") {
|
||||
mavenContent {
|
||||
includeGroup("net.fabricmc")
|
||||
}
|
||||
tasks.register("printVersion") {
|
||||
doFirst {
|
||||
println(version)
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
shade("org.apache.httpcomponents:httpclient:4.5.13")
|
||||
shade("com.github.salomonbrys.kotson:kotson:2.5.0")
|
||||
|
||||
// ASM for inspection
|
||||
val asmVersion = "9.0"
|
||||
shade("org.ow2.asm:asm:$asmVersion")
|
||||
shade("org.ow2.asm:asm-tree:$asmVersion")
|
||||
|
||||
// Cadix
|
||||
val lorenzVersion = "0.5.6"
|
||||
shade("org.cadixdev:lorenz:$lorenzVersion")
|
||||
shade("org.cadixdev:lorenz-asm:$lorenzVersion")
|
||||
shade("org.cadixdev:lorenz-io-proguard:$lorenzVersion")
|
||||
shade("org.cadixdev:atlas:0.2.0")
|
||||
shade("org.cadixdev:at:0.1.0-rc1")
|
||||
shade("org.cadixdev:mercury:0.1.0-rc2-PW-SNAPSHOT")
|
||||
|
||||
shade("net.fabricmc:lorenz-tiny:3.0.0")
|
||||
|
||||
shade("io.sigpipe:jbsdiff:1.0")
|
||||
}
|
||||
|
||||
ktlint {
|
||||
enableExperimentalRules.set(true)
|
||||
|
||||
disabledRules.add("no-wildcard-imports")
|
||||
}
|
||||
|
||||
tasks.register("format") {
|
||||
group = "formatting"
|
||||
description = "Formats source code according to project style"
|
||||
dependsOn(tasks.licenseFormat, tasks.ktlintFormat)
|
||||
}
|
||||
|
||||
license {
|
||||
header = file("license/copyright.txt")
|
||||
include("**/*.kt")
|
||||
}
|
||||
|
||||
idea {
|
||||
module {
|
||||
isDownloadSources = true
|
||||
}
|
||||
}
|
||||
|
||||
fun ShadowJar.configureStandard() {
|
||||
configurations = listOf(shade)
|
||||
|
||||
dependencies {
|
||||
exclude(dependency("org.jetbrains.kotlin:.*:.*"))
|
||||
}
|
||||
|
||||
mergeServiceFiles()
|
||||
}
|
||||
|
||||
tasks.shadowJar {
|
||||
configureStandard()
|
||||
|
||||
val prefix = "paper.libs"
|
||||
listOf(
|
||||
"com.github.salomonbrys.kotson",
|
||||
"com.google.gson",
|
||||
"io.sigpipe",
|
||||
"me.jamiemansfield",
|
||||
"net.fabricmc",
|
||||
"org.apache.commons.codec",
|
||||
"org.apache.commons.compress",
|
||||
"org.apache.commons.logging",
|
||||
"org.apache.felix",
|
||||
"org.apache.http",
|
||||
"org.cadixdev",
|
||||
"org.eclipse",
|
||||
"org.objectweb",
|
||||
"org.osgi",
|
||||
"org.tukaani"
|
||||
).forEach { pack ->
|
||||
relocate(pack, "$prefix.$pack")
|
||||
}
|
||||
}
|
||||
|
||||
val devShadowJar by tasks.registering(ShadowJar::class) {
|
||||
configureStandard()
|
||||
|
||||
from(project.sourceSets.main.get().output)
|
||||
|
||||
archiveClassifier.set("dev")
|
||||
}
|
||||
|
||||
val isSnapshot = version().endsWith("-SNAPSHOT")
|
||||
|
||||
publishing {
|
||||
publications {
|
||||
register<MavenPublication>("shadow") {
|
||||
pluginConfig(version())
|
||||
artifact(tasks.shadowJar) {
|
||||
classifier = null
|
||||
}
|
||||
}
|
||||
register<MavenPublication>("maven") {
|
||||
standardConfig(version())
|
||||
}
|
||||
register<MavenPublication>("shadowLocal") {
|
||||
pluginConfig(localVersion())
|
||||
artifact(devShadowJar) {
|
||||
classifier = null
|
||||
}
|
||||
}
|
||||
register<MavenPublication>("mavenLocal") {
|
||||
standardConfig(localVersion())
|
||||
}
|
||||
|
||||
repositories {
|
||||
val url = if (isSnapshot) {
|
||||
"https://repo.demonwav.com/snapshots"
|
||||
} else {
|
||||
"https://repo.demonwav.com/releases"
|
||||
}
|
||||
maven(url) {
|
||||
credentials(PasswordCredentials::class)
|
||||
name = "demonwav"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tasks.withType(PublishToMavenRepository::class).configureEach {
|
||||
onlyIf {
|
||||
!publication.name.endsWith("Local")
|
||||
}
|
||||
}
|
||||
tasks.withType(PublishToMavenLocal::class).configureEach {
|
||||
onlyIf {
|
||||
publication.name.endsWith("Local")
|
||||
}
|
||||
}
|
||||
|
||||
fun MavenPublication.standardConfig(versionName: String) {
|
||||
groupId = project.group.toString()
|
||||
artifactId = project.name
|
||||
version = versionName
|
||||
|
||||
from(components["java"])
|
||||
artifact(devShadowJar)
|
||||
|
||||
withoutBuildIdentifier()
|
||||
pom {
|
||||
pomConfig()
|
||||
}
|
||||
}
|
||||
|
||||
fun MavenPublication.pluginConfig(versionName: String) {
|
||||
groupId = project.group.toString()
|
||||
artifactId = "io.papermc.paperweight.gradle.plugin"
|
||||
version = versionName
|
||||
|
||||
artifact(sourcesJar)
|
||||
|
||||
withoutBuildIdentifier()
|
||||
pom {
|
||||
pomConfig()
|
||||
}
|
||||
}
|
||||
|
||||
fun MavenPom.pomConfig() {
|
||||
val repoPath = "PaperMC/paperweight"
|
||||
val repoUrl = "https://github.com/$repoPath"
|
||||
|
||||
name.set("paperweight")
|
||||
description.set("Gradle plugin for the PaperMC project")
|
||||
url.set(repoUrl)
|
||||
inceptionYear.set("2020")
|
||||
packaging = "jar"
|
||||
|
||||
licenses {
|
||||
license {
|
||||
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("DemonWav")
|
||||
name.set("Kyle Wood")
|
||||
email.set("demonwav@gmail.com")
|
||||
url.set("https://github.com/DemonWav")
|
||||
}
|
||||
}
|
||||
|
||||
scm {
|
||||
url.set(repoUrl)
|
||||
connection.set("scm:git:$repoUrl.git")
|
||||
developerConnection.set("scm:git:git@github.com:$repoPath.git")
|
||||
}
|
||||
}
|
||||
|
||||
fun version(): String {
|
||||
return project.version.toString()
|
||||
}
|
||||
fun localVersion(): String {
|
||||
return if (isSnapshot) {
|
||||
version().substringBefore('-') + "-LOCAL-SNAPSHOT"
|
||||
} else {
|
||||
version() + "-LOCAL"
|
||||
}
|
||||
tasks.wrapper {
|
||||
distributionType = Wrapper.DistributionType.ALL
|
||||
}
|
||||
|
|
22
buildSrc/build.gradle.kts
Normal file
22
buildSrc/build.gradle.kts
Normal file
|
@ -0,0 +1,22 @@
|
|||
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" }
|
||||
}
|
9
buildSrc/settings.gradle.kts
Normal file
9
buildSrc/settings.gradle.kts
Normal file
|
@ -0,0 +1,9 @@
|
|||
rootProject.name = "buildSrc"
|
||||
|
||||
dependencyResolutionManagement {
|
||||
versionCatalogs {
|
||||
create("libs") {
|
||||
from(files("../gradle/libs.versions.toml"))
|
||||
}
|
||||
}
|
||||
}
|
117
buildSrc/src/main/kotlin/config-kotlin.gradle.kts
Normal file
117
buildSrc/src/main/kotlin/config-kotlin.gradle.kts
Normal file
|
@ -0,0 +1,117 @@
|
|||
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
|
||||
}
|
||||
}
|
157
buildSrc/src/main/kotlin/config-publish.gradle.kts
Normal file
157
buildSrc/src/main/kotlin/config-publish.gradle.kts
Normal file
|
@ -0,0 +1,157 @@
|
|||
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")
|
||||
}
|
||||
}
|
5
gradle.properties
Normal file
5
gradle.properties
Normal file
|
@ -0,0 +1,5 @@
|
|||
group = com.zontreck.nugget
|
||||
version = 1.7.4-SNAPSHOT
|
||||
|
||||
org.gradle.caching = true
|
||||
org.gradle.parallel = true
|
46
gradle/libs.versions.toml
Normal file
46
gradle/libs.versions.toml
Normal file
|
@ -0,0 +1,46 @@
|
|||
[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,5 +1,7 @@
|
|||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-all.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
|
294
gradlew
vendored
294
gradlew
vendored
|
@ -1,7 +1,7 @@
|
|||
#!/usr/bin/env sh
|
||||
#!/bin/sh
|
||||
|
||||
#
|
||||
# Copyright 2015 the original author or authors.
|
||||
# Copyright © 2015-2021 the original authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
|
@ -17,67 +17,99 @@
|
|||
#
|
||||
|
||||
##############################################################################
|
||||
##
|
||||
## Gradle start up script for UN*X
|
||||
##
|
||||
#
|
||||
# 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/.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
|
||||
# Resolve links: $0 may be a link
|
||||
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
|
||||
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
|
||||
done
|
||||
SAVED="`pwd`"
|
||||
cd "`dirname \"$PRG\"`/" >/dev/null
|
||||
APP_HOME="`pwd -P`"
|
||||
cd "$SAVED" >/dev/null
|
||||
|
||||
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"'
|
||||
# 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
|
||||
|
||||
# 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
|
||||
;;
|
||||
MINGW* )
|
||||
msys=true
|
||||
;;
|
||||
NONSTOP* )
|
||||
nonstop=true
|
||||
;;
|
||||
case "$( uname )" in #(
|
||||
CYGWIN* ) cygwin=true ;; #(
|
||||
Darwin* ) darwin=true ;; #(
|
||||
MSYS* | MINGW* ) msys=true ;; #(
|
||||
NONSTOP* ) nonstop=true ;;
|
||||
esac
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
|
@ -87,9 +119,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
|
||||
|
@ -98,88 +130,120 @@ Please set the JAVA_HOME variable in your environment to match the
|
|||
location of your Java installation."
|
||||
fi
|
||||
else
|
||||
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.
|
||||
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.
|
||||
|
||||
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" = "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
|
||||
|
||||
# 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" = "true" -o "$msys" = "true" ] ; then
|
||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||
|
||||
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||
|
||||
# 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" ;;
|
||||
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
|
||||
fi
|
||||
|
||||
# Escape application args
|
||||
save () {
|
||||
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||
echo " "
|
||||
}
|
||||
APP_ARGS=`save "$@"`
|
||||
# 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.
|
||||
|
||||
# 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"
|
||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||
if "$cygwin" || "$msys" ; then
|
||||
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
|
||||
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
|
||||
|
||||
JAVACMD=$( cygpath --unix "$JAVACMD" )
|
||||
|
||||
# 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
|
||||
done
|
||||
fi
|
||||
|
||||
|
||||
# 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' ' '
|
||||
)" '"$@"'
|
||||
|
||||
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,7 +25,8 @@
|
|||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
if "%DIRNAME%"=="" set DIRNAME=.
|
||||
@rem This is normally unused
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
|
@ -40,13 +41,13 @@ if defined JAVA_HOME goto findJavaFromJavaHome
|
|||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto execute
|
||||
if %ERRORLEVEL% equ 0 goto execute
|
||||
|
||||
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.
|
||||
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
|
||||
|
||||
goto fail
|
||||
|
||||
|
@ -56,11 +57,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
|||
|
||||
if exist "%JAVA_EXE%" goto execute
|
||||
|
||||
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.
|
||||
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
|
||||
|
||||
goto fail
|
||||
|
||||
|
@ -75,13 +76,15 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
|||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||
if %ERRORLEVEL% equ 0 goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||
exit /b 1
|
||||
set EXIT_CODE=%ERRORLEVEL%
|
||||
if %EXIT_CODE% equ 0 set EXIT_CODE=1
|
||||
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
|
||||
exit /b %EXIT_CODE%
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
paperweight is a Gradle plugin for the PaperMC project.
|
||||
|
||||
Copyright (c) 2020 Kyle Wood (DemonWav)
|
||||
Copyright (c) 2023 Kyle Wood (DenWav)
|
||||
Contributors
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
|
|
17
nugget-core/build.gradle.kts
Normal file
17
nugget-core/build.gradle.kts
Normal file
|
@ -0,0 +1,17 @@
|
|||
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"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,244 @@
|
|||
/*
|
||||
* 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,7 +1,7 @@
|
|||
/*
|
||||
* paperweight is a Gradle plugin for the PaperMC project.
|
||||
*
|
||||
* Copyright (c) 2020 Kyle Wood (DemonWav)
|
||||
* Copyright (c) 2023 Kyle Wood (DenWav)
|
||||
* Contributors
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
|
@ -20,8 +20,10 @@
|
|||
* USA
|
||||
*/
|
||||
|
||||
package io.papermc.paperweight.ext
|
||||
package io.papermc.paperweight.core.extension
|
||||
|
||||
import io.papermc.paperweight.util.*
|
||||
import kotlin.io.path.*
|
||||
import org.gradle.api.file.DirectoryProperty
|
||||
import org.gradle.api.file.RegularFileProperty
|
||||
import org.gradle.api.model.ObjectFactory
|
||||
|
@ -45,16 +47,14 @@ open class CraftBukkitExtension(objects: ObjectFactory, workDir: DirectoryProper
|
|||
val specialSourceJar: RegularFileProperty = objects.fileFrom(buildDataBinDir, "SpecialSource.jar")
|
||||
val specialSource2Jar: RegularFileProperty = objects.fileFrom(buildDataBinDir, "SpecialSource-2.jar")
|
||||
|
||||
private fun ObjectFactory.bukkitFileFrom(base: DirectoryProperty, extension: String): RegularFileProperty =
|
||||
fileProperty().convention(
|
||||
base.flatMap { dir ->
|
||||
val file = dir.asFile.listFiles()?.firstOrNull { it.name.endsWith(extension) }
|
||||
if (file != null) {
|
||||
mappingsDir.file(file.name)
|
||||
} else {
|
||||
// empty
|
||||
fileProperty()
|
||||
}
|
||||
private fun ObjectFactory.bukkitFileFrom(base: DirectoryProperty, extension: String): RegularFileProperty = fileProperty().convention(
|
||||
base.flatMap { dir ->
|
||||
val file = dir.path.useDirectoryEntries { it.filter { f -> f.name.endsWith(extension) }.singleOrNull() }
|
||||
if (file != null) {
|
||||
mappingsDir.file(file.name)
|
||||
} else {
|
||||
fileProperty()
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* paperweight is a Gradle plugin for the PaperMC project.
|
||||
*
|
||||
* Copyright (c) 2020 Kyle Wood (DemonWav)
|
||||
* Copyright (c) 2023 Kyle Wood (DenWav)
|
||||
* Contributors
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
|
@ -20,33 +20,38 @@
|
|||
* USA
|
||||
*/
|
||||
|
||||
package io.papermc.paperweight.ext
|
||||
package io.papermc.paperweight.core.extension
|
||||
|
||||
import io.papermc.paperweight.util.*
|
||||
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.kotlin.dsl.*
|
||||
|
||||
open class PaperExtension(objects: ObjectFactory, layout: ProjectLayout) {
|
||||
|
||||
@Suppress("MemberVisibilityCanBePrivate")
|
||||
val baseTargetDir: DirectoryProperty = objects.dirWithDefault(layout, ".")
|
||||
val spigotApiPatchDir: DirectoryProperty = objects.dirFrom(baseTargetDir, "Spigot-API-Patches")
|
||||
val spigotServerPatchDir: DirectoryProperty = objects.dirFrom(baseTargetDir, "Spigot-Server-Patches")
|
||||
val remappedSpigotServerPatchDir: DirectoryProperty = objects.dirFrom(
|
||||
baseTargetDir,
|
||||
"Spigot-Server-Patches-Remapped"
|
||||
)
|
||||
val unmappedSpigotServerPatchDir: DirectoryProperty = objects.dirFrom(
|
||||
baseTargetDir,
|
||||
"Spigot-Server-Patches-Unmapped"
|
||||
)
|
||||
val spigotApiPatchDir: DirectoryProperty = objects.dirFrom(baseTargetDir, "patches/api")
|
||||
val spigotServerPatchDir: DirectoryProperty = objects.dirFrom(baseTargetDir, "patches/server")
|
||||
val remappedSpigotServerPatchDir: DirectoryProperty = objects.dirFrom(baseTargetDir, "patches/server-remapped")
|
||||
val unmappedSpigotServerPatchDir: DirectoryProperty = objects.dirFrom(baseTargetDir, "patches/server-unmapped")
|
||||
val paperApiDir: DirectoryProperty = objects.dirFrom(baseTargetDir, "Paper-API")
|
||||
val paperServerDir: DirectoryProperty = objects.dirFrom(baseTargetDir, "Paper-Server")
|
||||
|
||||
@Suppress("MemberVisibilityCanBePrivate")
|
||||
val buildDataDir: DirectoryProperty = objects.dirWithDefault(layout, "build-data")
|
||||
val additionalSpigotClassMappings: RegularFileProperty = objects.fileProperty()
|
||||
val additionalSpigotMemberMappings: RegularFileProperty = objects.fileProperty()
|
||||
val libraryClassImports: RegularFileProperty = objects.fileFrom(buildDataDir, "library-imports.txt")
|
||||
val devImports: RegularFileProperty = objects.fileFrom(buildDataDir, "dev-imports.txt")
|
||||
val additionalAts: RegularFileProperty = objects.fileFrom(buildDataDir, "paper.at")
|
||||
val reobfMappingsPatch: RegularFileProperty = objects.fileProperty()
|
||||
val mappingsPatch: RegularFileProperty = objects.fileProperty()
|
||||
|
||||
val craftBukkitPatchPatchesDir: DirectoryProperty = objects.directoryProperty()
|
||||
val spigotServerPatchPatchesDir: DirectoryProperty = objects.directoryProperty()
|
||||
val spigotApiPatchPatchesDir: DirectoryProperty = objects.directoryProperty()
|
||||
|
||||
val reobfPackagesToFix: ListProperty<String> = objects.listProperty()
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* paperweight is a Gradle plugin for the PaperMC project.
|
||||
*
|
||||
* Copyright (c) 2020 Kyle Wood (DemonWav)
|
||||
* Copyright (c) 2023 Kyle Wood (DenWav)
|
||||
* Contributors
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
|
@ -20,37 +20,57 @@
|
|||
* USA
|
||||
*/
|
||||
|
||||
package io.papermc.paperweight.ext
|
||||
package io.papermc.paperweight.core.extension
|
||||
|
||||
import io.papermc.paperweight.util.*
|
||||
import io.papermc.paperweight.util.constants.*
|
||||
import java.util.Locale
|
||||
import org.gradle.api.Action
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.file.DirectoryProperty
|
||||
import org.gradle.api.file.ProjectLayout
|
||||
import org.gradle.api.model.ObjectFactory
|
||||
import org.gradle.api.provider.ListProperty
|
||||
import org.gradle.api.provider.Property
|
||||
import org.gradle.kotlin.dsl.property
|
||||
import org.gradle.kotlin.dsl.*
|
||||
|
||||
open class PaperweightExtension(objects: ObjectFactory, layout: ProjectLayout) {
|
||||
open class PaperweightCoreExtension(project: Project, objects: ObjectFactory, layout: ProjectLayout) {
|
||||
|
||||
@Suppress("MemberVisibilityCanBePrivate")
|
||||
val workDir: DirectoryProperty = objects.dirWithDefault(layout, "work")
|
||||
|
||||
val minecraftVersion: Property<String> = objects.property()
|
||||
val versionPackage: Property<String> = objects.property()
|
||||
val serverProject: Property<Project> = objects.property()
|
||||
|
||||
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 mcDevSourceDir: DirectoryProperty = objects.directoryProperty().convention(serverProject.map { it.layout.cacheDir(MC_DEV_SOURCES_DIR) })
|
||||
|
||||
val paramMappingsRepo: Property<String> = objects.property()
|
||||
val decompileRepo: Property<String> = objects.property()
|
||||
val remapRepo: Property<String> = objects.property()
|
||||
|
||||
val vanillaJarIncludes: ListProperty<String> = objects.listProperty<String>().convention(
|
||||
listOf("/*.class", "/net/minecraft/**", "/com/mojang/math/**")
|
||||
)
|
||||
|
||||
@Suppress("MemberVisibilityCanBePrivate")
|
||||
val craftBukkit = CraftBukkitExtension(objects, workDir)
|
||||
val spigot = SpigotExtension(objects, workDir)
|
||||
val paper = PaperExtension(objects, layout)
|
||||
|
||||
@Suppress("unused")
|
||||
fun craftBukkit(action: Action<in CraftBukkitExtension>) {
|
||||
action.execute(craftBukkit)
|
||||
}
|
||||
|
||||
@Suppress("unused")
|
||||
fun spigot(action: Action<in SpigotExtension>) {
|
||||
action.execute(spigot)
|
||||
}
|
||||
|
||||
@Suppress("unused")
|
||||
fun paper(action: Action<in PaperExtension>) {
|
||||
action.execute(paper)
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* paperweight is a Gradle plugin for the PaperMC project.
|
||||
*
|
||||
* Copyright (c) 2020 Kyle Wood (DemonWav)
|
||||
* Copyright (c) 2023 Kyle Wood (DenWav)
|
||||
* Contributors
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
|
@ -20,12 +20,14 @@
|
|||
* USA
|
||||
*/
|
||||
|
||||
package io.papermc.paperweight.ext
|
||||
package io.papermc.paperweight.core.extension
|
||||
|
||||
import io.papermc.paperweight.util.*
|
||||
import org.gradle.api.file.DirectoryProperty
|
||||
import org.gradle.api.model.ObjectFactory
|
||||
|
||||
open class SpigotExtension(objects: ObjectFactory, workDir: DirectoryProperty) {
|
||||
|
||||
@Suppress("MemberVisibilityCanBePrivate")
|
||||
val spigotDir: DirectoryProperty = objects.dirFrom(workDir, "Spigot")
|
||||
val spigotApiDir: DirectoryProperty = objects.dirFrom(spigotDir, "Spigot-API")
|
|
@ -0,0 +1,183 @@
|
|||
/*
|
||||
* 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))
|
||||
}
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
* 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 })
|
||||
}
|
||||
}
|
|
@ -0,0 +1,98 @@
|
|||
/*
|
||||
* 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))
|
||||
}
|
||||
}
|
|
@ -0,0 +1,205 @@
|
|||
/*
|
||||
* 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 })
|
||||
}
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* 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 })
|
||||
}
|
||||
}
|
|
@ -0,0 +1,146 @@
|
|||
/*
|
||||
* 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)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* 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
|
27
nugget-lib/build.gradle.kts
Normal file
27
nugget-lib/build.gradle.kts
Normal file
|
@ -0,0 +1,27 @@
|
|||
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,7 +1,7 @@
|
|||
/*
|
||||
* paperweight is a Gradle plugin for the PaperMC project.
|
||||
*
|
||||
* Copyright (c) 2020 Kyle Wood (DemonWav)
|
||||
* Copyright (c) 2023 Kyle Wood (DenWav)
|
||||
* Contributors
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
|
@ -22,14 +22,16 @@
|
|||
|
||||
package io.papermc.paperweight
|
||||
|
||||
import io.papermc.paperweight.util.convertToFile
|
||||
import io.papermc.paperweight.util.convertToUrl
|
||||
import java.io.File
|
||||
import io.papermc.paperweight.util.*
|
||||
import java.net.URL
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.attribute.FileTime
|
||||
import java.time.Instant
|
||||
import java.time.ZoneOffset
|
||||
import java.time.format.DateTimeFormatter
|
||||
import java.util.Locale
|
||||
import java.util.concurrent.TimeUnit
|
||||
import kotlin.io.path.*
|
||||
import org.apache.http.HttpHost
|
||||
import org.apache.http.HttpStatus
|
||||
import org.apache.http.client.config.CookieSpecs
|
||||
|
@ -39,34 +41,67 @@ import org.apache.http.client.methods.HttpGet
|
|||
import org.apache.http.client.utils.DateUtils
|
||||
import org.apache.http.impl.client.CloseableHttpClient
|
||||
import org.apache.http.impl.client.HttpClientBuilder
|
||||
import org.gradle.api.logging.Logger
|
||||
import org.gradle.api.logging.Logging
|
||||
import org.gradle.api.services.BuildService
|
||||
import org.gradle.api.services.BuildServiceParameters
|
||||
|
||||
abstract class DownloadService : BuildService<BuildServiceParameters.None>, AutoCloseable {
|
||||
|
||||
private companion object {
|
||||
val LOGGER: Logger = Logging.getLogger(DownloadService::class.java)
|
||||
}
|
||||
|
||||
private val httpClient: CloseableHttpClient = HttpClientBuilder.create().let { builder ->
|
||||
builder.setRetryHandler { _, count, _ -> count < 3 }
|
||||
builder.useSystemProperties()
|
||||
builder.build()
|
||||
}
|
||||
|
||||
fun download(source: Any, target: Any) {
|
||||
fun download(source: Any, target: Any, hash: Hash? = null) {
|
||||
val url = source.convertToUrl()
|
||||
val file = target.convertToFile()
|
||||
download(url, file)
|
||||
val file = target.convertToPath()
|
||||
download(url, file, hash)
|
||||
}
|
||||
|
||||
private fun download(source: URL, target: File) {
|
||||
target.parentFile.mkdirs()
|
||||
private fun download(source: URL, target: Path, hash: Hash?, retry: Boolean = false) {
|
||||
download(source, target)
|
||||
if (hash == null) {
|
||||
return
|
||||
}
|
||||
val dlHash = target.hashFile(hash.algorithm).asHexString().lowercase(Locale.ENGLISH)
|
||||
if (dlHash == hash.valueLower) {
|
||||
return
|
||||
}
|
||||
LOGGER.warn(
|
||||
"{} hash of downloaded file '{}' does not match what was expected! (expected: '{}', got: '{}')",
|
||||
hash.algorithm.name,
|
||||
target,
|
||||
hash.valueLower,
|
||||
dlHash
|
||||
)
|
||||
if (retry) {
|
||||
throw PaperweightException("Failed to download file '$target' from '$source'.")
|
||||
}
|
||||
LOGGER.warn("Re-attempting download once before giving up.")
|
||||
target.deleteIfExists()
|
||||
download(source, target, hash, true)
|
||||
}
|
||||
|
||||
val etagFile = target.resolveSibling(target.name + ".etag")
|
||||
private fun download(source: URL, target: Path) {
|
||||
target.parent.createDirectories()
|
||||
|
||||
val etagDir = target.resolveSibling("etags")
|
||||
etagDir.createDirectories()
|
||||
|
||||
val etagFile = etagDir.resolve(target.name + ".etag")
|
||||
val etag = if (etagFile.exists()) etagFile.readText() else null
|
||||
|
||||
val host = HttpHost(source.host, source.port, source.protocol)
|
||||
val time = if (target.exists()) target.lastModified() else 0
|
||||
val time = if (target.exists()) target.getLastModifiedTime().toInstant() else Instant.EPOCH
|
||||
|
||||
val httpGet = HttpGet(source.file)
|
||||
// Super high timeout, reduce chances of weird things going wrong
|
||||
// high timeout, reduce chances of weird things going wrong
|
||||
val timeouts = TimeUnit.MINUTES.toMillis(5).toInt()
|
||||
|
||||
httpGet.config = RequestConfig.custom()
|
||||
|
@ -76,46 +111,45 @@ abstract class DownloadService : BuildService<BuildServiceParameters.None>, Auto
|
|||
.setCookieSpec(CookieSpecs.STANDARD)
|
||||
.build()
|
||||
|
||||
if (time > 0) {
|
||||
val value = DateTimeFormatter.RFC_1123_DATE_TIME.format(Instant.ofEpochMilli(time).atZone(ZoneOffset.UTC))
|
||||
httpGet.setHeader("If-Modified-Since", value)
|
||||
}
|
||||
if (etag != null) {
|
||||
httpGet.setHeader("If-None-Match", etag)
|
||||
if (target.exists()) {
|
||||
if (time != Instant.EPOCH) {
|
||||
val value = DateTimeFormatter.RFC_1123_DATE_TIME.format(time.atZone(ZoneOffset.UTC))
|
||||
httpGet.setHeader("If-Modified-Since", value)
|
||||
}
|
||||
if (etag != null) {
|
||||
httpGet.setHeader("If-None-Match", etag)
|
||||
}
|
||||
}
|
||||
|
||||
httpClient.execute(host, httpGet).use { response ->
|
||||
val code = response.statusLine.statusCode
|
||||
if ((code < 200 || code > 299) && code != HttpStatus.SC_NOT_MODIFIED) {
|
||||
if (code !in 200..299 && code != HttpStatus.SC_NOT_MODIFIED) {
|
||||
val reason = response.statusLine.reasonPhrase
|
||||
throw PaperweightException("Download failed, HTTP code: $code; URL: $source; Reason: $reason")
|
||||
}
|
||||
|
||||
val lastModified = handleResponse(response, time, target)
|
||||
val lastModified = handleResponse(response, target)
|
||||
saveEtag(response, lastModified, target, etagFile)
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleResponse(response: CloseableHttpResponse, time: Long, target: File): Long {
|
||||
private fun handleResponse(response: CloseableHttpResponse, target: Path): Instant {
|
||||
val lastModified = with(response.getLastHeader("Last-Modified")) {
|
||||
if (this == null) {
|
||||
return@with 0
|
||||
return@with Instant.EPOCH
|
||||
}
|
||||
if (value.isNullOrBlank()) {
|
||||
return@with 0
|
||||
return@with Instant.EPOCH
|
||||
}
|
||||
val date = DateUtils.parseDate(value) ?: return@with 0
|
||||
return@with date.time
|
||||
return@with DateUtils.parseDate(value).toInstant() ?: Instant.EPOCH
|
||||
}
|
||||
if (response.statusLine.statusCode == HttpStatus.SC_NOT_MODIFIED) {
|
||||
if (lastModified != 0L && time >= lastModified) {
|
||||
return lastModified
|
||||
}
|
||||
return lastModified
|
||||
}
|
||||
|
||||
val entity = response.entity ?: return lastModified
|
||||
entity.content.use { input ->
|
||||
target.outputStream().buffered().use { output ->
|
||||
target.outputStream().use { output ->
|
||||
entity.content.use { input ->
|
||||
input.copyTo(output)
|
||||
}
|
||||
}
|
||||
|
@ -123,9 +157,9 @@ abstract class DownloadService : BuildService<BuildServiceParameters.None>, Auto
|
|||
return lastModified
|
||||
}
|
||||
|
||||
private fun saveEtag(response: CloseableHttpResponse, lastModified: Long, target: File, etagFile: File) {
|
||||
if (lastModified > 0) {
|
||||
target.setLastModified(lastModified)
|
||||
private fun saveEtag(response: CloseableHttpResponse, lastModified: Instant, target: Path, etagFile: Path) {
|
||||
if (lastModified != Instant.EPOCH) {
|
||||
target.setLastModifiedTime(FileTime.from(lastModified))
|
||||
}
|
||||
|
||||
val header = response.getFirstHeader("ETag") ?: return
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* paperweight is a Gradle plugin for the PaperMC project.
|
||||
*
|
||||
* Copyright (c) 2020 Kyle Wood (DemonWav)
|
||||
* Copyright (c) 2023 Kyle Wood (DenWav)
|
||||
* Contributors
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
|
@ -24,5 +24,5 @@ package io.papermc.paperweight
|
|||
|
||||
class PaperweightException : Exception {
|
||||
constructor(message: String) : super(message)
|
||||
constructor(message: String, cause: Throwable) : super(message, cause)
|
||||
constructor(message: String, cause: Throwable?) : super(message, cause)
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
/*
|
||||
* 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())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* 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()
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* 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)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,156 @@
|
|||
/*
|
||||
* 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)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,123 @@
|
|||
/*
|
||||
* 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,7 +1,7 @@
|
|||
/*
|
||||
* paperweight is a Gradle plugin for the PaperMC project.
|
||||
*
|
||||
* Copyright (c) 2020 Kyle Wood (DemonWav)
|
||||
* Copyright (c) 2023 Kyle Wood (DenWav)
|
||||
* Contributors
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
|
@ -22,55 +22,49 @@
|
|||
|
||||
package io.papermc.paperweight.tasks
|
||||
|
||||
import io.papermc.paperweight.util.defaultOutput
|
||||
import io.papermc.paperweight.util.file
|
||||
import io.papermc.paperweight.util.fileOrNull
|
||||
import java.io.File
|
||||
import io.papermc.paperweight.util.*
|
||||
import java.nio.file.Path
|
||||
import kotlin.io.path.*
|
||||
import org.gradle.api.file.RegularFileProperty
|
||||
import org.gradle.api.tasks.CacheableTask
|
||||
import org.gradle.api.tasks.InputFile
|
||||
import org.gradle.api.tasks.Optional
|
||||
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 AddAdditionalSpigotMappings : BaseTask() {
|
||||
|
||||
@get:InputFile
|
||||
@get:PathSensitive(PathSensitivity.NONE)
|
||||
abstract val classSrg: RegularFileProperty
|
||||
@get:InputFile
|
||||
abstract val memberSrg: RegularFileProperty
|
||||
|
||||
@get:Optional
|
||||
@get:InputFile
|
||||
@get:PathSensitive(PathSensitivity.NONE)
|
||||
abstract val additionalClassEntriesSrg: RegularFileProperty
|
||||
@get:Optional
|
||||
@get:InputFile
|
||||
abstract val additionalMemberEntriesSrg: RegularFileProperty
|
||||
|
||||
@get:OutputFile
|
||||
abstract val outputClassSrg: RegularFileProperty
|
||||
@get:OutputFile
|
||||
abstract val outputMemberSrg: RegularFileProperty
|
||||
|
||||
override fun init() {
|
||||
outputClassSrg.convention(defaultOutput("class.csrg"))
|
||||
outputMemberSrg.convention(defaultOutput("member.csrg"))
|
||||
}
|
||||
|
||||
@TaskAction
|
||||
fun run() {
|
||||
addLines(classSrg.file, additionalClassEntriesSrg.fileOrNull, outputClassSrg.file)
|
||||
addLines(memberSrg.file, additionalMemberEntriesSrg.fileOrNull, outputMemberSrg.file)
|
||||
addLines(classSrg.path, additionalClassEntriesSrg.pathOrNull, outputClassSrg.path)
|
||||
}
|
||||
|
||||
private fun addLines(inFile: File, appendFile: File?, outputFile: File) {
|
||||
private fun addLines(inFile: Path, appendFile: Path?, outputFile: Path) {
|
||||
val lines = mutableListOf<String>()
|
||||
inFile.useLines { seq ->
|
||||
seq.filter { it.startsWith("") }
|
||||
}
|
||||
inFile.forEachLine { line -> lines += line }
|
||||
inFile.useLines { seq -> seq.forEach { line -> lines += line } }
|
||||
appendFile?.useLines { seq -> seq.forEach { lines.add(it) } }
|
||||
lines.sort()
|
||||
outputFile.bufferedWriter().use { writer ->
|
||||
lines.forEach { writer.appendln(it) }
|
||||
lines.forEach { writer.appendLine(it) }
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* paperweight is a Gradle plugin for the PaperMC project.
|
||||
*
|
||||
* Copyright (c) 2020 Kyle Wood (DemonWav)
|
||||
* Copyright (c) 2023 Kyle Wood (DenWav)
|
||||
* Contributors
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
|
@ -20,32 +20,33 @@
|
|||
* USA
|
||||
*/
|
||||
|
||||
package io.papermc.paperweight.tasks.patchremap
|
||||
package io.papermc.paperweight.tasks
|
||||
|
||||
import io.papermc.paperweight.tasks.BaseTask
|
||||
import io.papermc.paperweight.util.defaultOutput
|
||||
import io.papermc.paperweight.util.ensureDeleted
|
||||
import io.papermc.paperweight.util.ensureParentExists
|
||||
import io.papermc.paperweight.util.file
|
||||
import io.papermc.paperweight.util.orNull
|
||||
import io.papermc.paperweight.util.path
|
||||
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.tasks.InputFile
|
||||
import org.gradle.api.tasks.OutputFile
|
||||
import org.gradle.api.tasks.TaskAction
|
||||
import org.gradle.kotlin.dsl.submit
|
||||
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
|
||||
|
@ -54,38 +55,67 @@ import org.objectweb.asm.FieldVisitor
|
|||
import org.objectweb.asm.MethodVisitor
|
||||
import org.objectweb.asm.Opcodes
|
||||
|
||||
abstract class ApplyAccessTransform : BaseTask() {
|
||||
fun applyAccessTransform(
|
||||
inputJarPath: Path,
|
||||
outputJarPath: Path,
|
||||
atFilePath: Path,
|
||||
jvmArgs: List<String> = listOf("-Xmx1G"),
|
||||
workerExecutor: WorkerExecutor,
|
||||
launcher: JavaLauncher
|
||||
): WorkQueue {
|
||||
ensureParentExists(outputJarPath)
|
||||
ensureDeleted(outputJarPath)
|
||||
|
||||
@get:InputFile
|
||||
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() {
|
||||
ensureParentExists(outputJar.file)
|
||||
ensureDeleted(outputJar.file)
|
||||
|
||||
val queue = workerExecutor.processIsolation {
|
||||
forkOptions.jvmArgs("-Xmx1G")
|
||||
}
|
||||
|
||||
queue.submit(AtlasAction::class) {
|
||||
inputJar.set(this@ApplyAccessTransform.inputJar.file)
|
||||
atFile.set(this@ApplyAccessTransform.atFile.file)
|
||||
outputJar.set(this@ApplyAccessTransform.outputJar.file)
|
||||
}
|
||||
applyAccessTransform(
|
||||
inputJarPath = inputJar.path,
|
||||
outputJarPath = outputJar.path,
|
||||
atFilePath = atFile.path,
|
||||
jvmArgs = jvmargs.get(),
|
||||
workerExecutor = workerExecutor,
|
||||
launcher = launcher.get()
|
||||
)
|
||||
}
|
||||
|
||||
abstract class AtlasAction : WorkAction<AtlasParameters> {
|
||||
|
@ -94,7 +124,21 @@ abstract class ApplyAccessTransform : BaseTask() {
|
|||
|
||||
Atlas().apply {
|
||||
install {
|
||||
AtJarEntryTransformer(at)
|
||||
// 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)
|
||||
}
|
||||
|
@ -108,21 +152,25 @@ abstract class ApplyAccessTransform : BaseTask() {
|
|||
}
|
||||
}
|
||||
|
||||
class AtJarEntryTransformer(private val at: AccessTransformSet) : JarEntryTransformer {
|
||||
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, writer), 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.ASM7, writer) {
|
||||
) : ClassVisitor(Opcodes.ASM9, writer) {
|
||||
|
||||
private var classTransform: AccessTransformSet.Class? = null
|
||||
private lateinit var classTransform: AccessTransformSet.Class
|
||||
|
||||
override fun visit(
|
||||
version: Int,
|
||||
|
@ -132,8 +180,8 @@ class AccessTransformerVisitor(
|
|||
superName: String?,
|
||||
interfaces: Array<out String>?
|
||||
) {
|
||||
classTransform = at.getClass(name).orNull
|
||||
super.visit(version, classTransform?.get().apply(access), name, signature, superName, interfaces)
|
||||
classTransform = completedClassAt(name)
|
||||
super.visit(version, classTransform.get().apply(access), name, signature, superName, interfaces)
|
||||
}
|
||||
|
||||
override fun visitField(
|
||||
|
@ -143,7 +191,7 @@ class AccessTransformerVisitor(
|
|||
signature: String?,
|
||||
value: Any?
|
||||
): FieldVisitor {
|
||||
return super.visitField(classTransform?.getField(name).apply(access), name, descriptor, signature, value)
|
||||
return super.visitField(classTransform.getField(name).apply(access), name, descriptor, signature, value)
|
||||
}
|
||||
|
||||
override fun visitMethod(
|
||||
|
@ -153,23 +201,26 @@ class AccessTransformerVisitor(
|
|||
signature: String?,
|
||||
exceptions: Array<out String>?
|
||||
): MethodVisitor {
|
||||
val newAccess = classTransform?.getMethod(MethodSignature.of(name, descriptor)).apply(access)
|
||||
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, at.getClass(name).orNull?.get().apply(access))
|
||||
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) }
|
||||
}
|
||||
}
|
||||
|
||||
private const val RESET_ACCESS: Int = (Opcodes.ACC_PUBLIC or Opcodes.ACC_PRIVATE or Opcodes.ACC_PROTECTED).inv()
|
||||
fun AccessTransform?.apply(currentModifier: Int): Int {
|
||||
if (this == null) {
|
||||
return currentModifier
|
||||
}
|
||||
var value = currentModifier
|
||||
if (this.access != AccessChange.NONE) {
|
||||
value = value and RESET_ACCESS
|
||||
value = value and AsmUtil.RESET_ACCESS
|
||||
value = value or this.access.modifier
|
||||
}
|
||||
when (this.final) {
|
||||
|
@ -179,7 +230,8 @@ fun AccessTransform?.apply(currentModifier: Int): Int {
|
|||
ModifierChange.ADD -> {
|
||||
value = value or Opcodes.ACC_FINAL
|
||||
}
|
||||
else -> {}
|
||||
else -> {
|
||||
}
|
||||
}
|
||||
return value
|
||||
}
|
|
@ -0,0 +1,157 @@
|
|||
/*
|
||||
* 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)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,220 @@
|
|||
/*
|
||||
* 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()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,179 @@
|
|||
/*
|
||||
* 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
|
||||
}
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
* 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,7 +1,7 @@
|
|||
/*
|
||||
* paperweight is a Gradle plugin for the PaperMC project.
|
||||
*
|
||||
* Copyright (c) 2020 Kyle Wood (DemonWav)
|
||||
* Copyright (c) 2023 Kyle Wood (DenWav)
|
||||
* Contributors
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
|
@ -33,10 +33,13 @@ abstract class BaseTask : DefaultTask() {
|
|||
|
||||
@get:Inject
|
||||
abstract val objects: ObjectFactory
|
||||
|
||||
@get:Inject
|
||||
abstract val layout: ProjectLayout
|
||||
|
||||
@get:Inject
|
||||
abstract val fs: FileSystemOperations
|
||||
|
||||
@get:Inject
|
||||
abstract val archives: ArchiveOperations
|
||||
|
|
@ -0,0 +1,135 @@
|
|||
/*
|
||||
* 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
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,336 @@
|
|||
/*
|
||||
* 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,92 @@
|
|||
/*
|
||||
* 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,7 +1,7 @@
|
|||
/*
|
||||
* paperweight is a Gradle plugin for the PaperMC project.
|
||||
*
|
||||
* Copyright (c) 2020 Kyle Wood (DemonWav)
|
||||
* Copyright (c) 2023 Kyle Wood (DenWav)
|
||||
* Contributors
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
|
@ -22,8 +22,7 @@
|
|||
|
||||
package io.papermc.paperweight.tasks
|
||||
|
||||
import io.papermc.paperweight.util.Command
|
||||
import io.papermc.paperweight.util.UselessOutputStream
|
||||
import io.papermc.paperweight.util.*
|
||||
import org.gradle.api.provider.Property
|
||||
import org.gradle.api.tasks.Console
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* paperweight is a Gradle plugin for the PaperMC project.
|
||||
*
|
||||
* Copyright (c) 2020 Kyle Wood (DemonWav)
|
||||
* Copyright (c) 2023 Kyle Wood (DenWav)
|
||||
* Contributors
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
|
@ -22,31 +22,50 @@
|
|||
|
||||
package io.papermc.paperweight.tasks
|
||||
|
||||
import io.papermc.paperweight.util.file
|
||||
import io.papermc.paperweight.util.zip
|
||||
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.InputFile
|
||||
import org.gradle.api.tasks.OutputFile
|
||||
import org.gradle.api.tasks.TaskAction
|
||||
|
||||
@CacheableTask
|
||||
abstract class CopyResources : BaseTask() {
|
||||
@get:InputFile
|
||||
|
||||
@get:Classpath
|
||||
abstract val inputJar: RegularFileProperty
|
||||
@get:InputFile
|
||||
|
||||
@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.file
|
||||
val out = outputJar.path
|
||||
val target = out.resolveSibling("${out.name}.dir")
|
||||
target.mkdirs()
|
||||
target.createDirectories()
|
||||
|
||||
fs.copy {
|
||||
from(archives.zipTree(vanillaJar)) {
|
||||
|
@ -54,12 +73,11 @@ abstract class CopyResources : BaseTask() {
|
|||
include(inc)
|
||||
}
|
||||
}
|
||||
into(target)
|
||||
from(archives.zipTree(inputJar))
|
||||
into(target)
|
||||
}
|
||||
|
||||
zip(target, outputJar)
|
||||
target.deleteRecursively()
|
||||
target.deleteRecursive()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,210 @@
|
|||
/*
|
||||
* 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}")
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* 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)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,325 @@
|
|||
/*
|
||||
* 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,7 +1,7 @@
|
|||
/*
|
||||
* paperweight is a Gradle plugin for the PaperMC project.
|
||||
*
|
||||
* Copyright (c) 2020 Kyle Wood (DemonWav)
|
||||
* Copyright (c) 2023 Kyle Wood (DenWav)
|
||||
* Contributors
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
|
@ -23,24 +23,16 @@
|
|||
package io.papermc.paperweight.tasks
|
||||
|
||||
import io.papermc.paperweight.DownloadService
|
||||
import io.papermc.paperweight.PaperweightException
|
||||
import io.papermc.paperweight.util.defaultOutput
|
||||
import io.papermc.paperweight.util.file
|
||||
import java.math.BigInteger
|
||||
import java.security.MessageDigest
|
||||
import io.papermc.paperweight.util.*
|
||||
import org.gradle.api.file.RegularFileProperty
|
||||
import org.gradle.api.provider.Property
|
||||
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.api.tasks.*
|
||||
|
||||
// Not cached since this is Mojang's server jar
|
||||
abstract class DownloadServerJar : BaseTask() {
|
||||
|
||||
@get:Input
|
||||
abstract val downloadUrl: Property<String>
|
||||
@get:Input
|
||||
abstract val hash: Property<String>
|
||||
|
||||
@get:OutputFile
|
||||
abstract val outputJar: RegularFileProperty
|
||||
|
@ -48,25 +40,14 @@ abstract class DownloadServerJar : BaseTask() {
|
|||
@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() {
|
||||
val file = outputJar.asFile.get()
|
||||
outputJar.file.parentFile.mkdirs()
|
||||
|
||||
downloader.get().download(downloadUrl, outputJar)
|
||||
|
||||
val digest = MessageDigest.getInstance("MD5")
|
||||
|
||||
val data = file.readBytes()
|
||||
val hashResult = digest.digest(data)
|
||||
val hashText = String.format("%032x", BigInteger(1, hashResult))
|
||||
|
||||
if (hash.get() != hashText) {
|
||||
throw PaperweightException("Checksum failed, expected ${hash.get()}, actually got $hashText")
|
||||
}
|
||||
}
|
||||
fun run() = downloader.get().download(downloadUrl, outputJar, expectedHash.orNull)
|
||||
}
|
|
@ -0,0 +1,130 @@
|
|||
/*
|
||||
* 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,7 +1,7 @@
|
|||
/*
|
||||
* paperweight is a Gradle plugin for the PaperMC project.
|
||||
*
|
||||
* Copyright (c) 2020 Kyle Wood (DemonWav)
|
||||
* Copyright (c) 2023 Kyle Wood (DenWav)
|
||||
* Contributors
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
|
@ -22,20 +22,21 @@
|
|||
|
||||
package io.papermc.paperweight.tasks
|
||||
|
||||
import io.papermc.paperweight.util.defaultOutput
|
||||
import io.papermc.paperweight.util.file
|
||||
import io.papermc.paperweight.util.zip
|
||||
import io.papermc.paperweight.util.*
|
||||
import org.gradle.api.file.RegularFileProperty
|
||||
import org.gradle.api.provider.ListProperty
|
||||
import org.gradle.api.tasks.CacheableTask
|
||||
import org.gradle.api.tasks.Classpath
|
||||
import org.gradle.api.tasks.Input
|
||||
import org.gradle.api.tasks.InputFile
|
||||
import org.gradle.api.tasks.OutputFile
|
||||
import org.gradle.api.tasks.TaskAction
|
||||
|
||||
@CacheableTask
|
||||
abstract class FilterJar : BaseTask() {
|
||||
|
||||
@get:InputFile
|
||||
@get:Classpath
|
||||
abstract val inputJar: RegularFileProperty
|
||||
|
||||
@get:Input
|
||||
abstract val includes: ListProperty<String>
|
||||
|
||||
|
@ -47,21 +48,11 @@ abstract class FilterJar : BaseTask() {
|
|||
}
|
||||
|
||||
@TaskAction
|
||||
fun run() {
|
||||
val out = outputJar.file
|
||||
val target = out.resolveSibling("${out.name}.dir")
|
||||
target.mkdirs()
|
||||
|
||||
fs.copy {
|
||||
from(archives.zipTree(inputJar)) {
|
||||
for (inc in this@FilterJar.includes.get()) {
|
||||
include(inc)
|
||||
}
|
||||
}
|
||||
into(target)
|
||||
}
|
||||
|
||||
zip(target, outputJar)
|
||||
target.deleteRecursively()
|
||||
open fun run() {
|
||||
filterJar(
|
||||
inputJar.path,
|
||||
outputJar.path,
|
||||
includes.get()
|
||||
)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,161 @@
|
|||
/*
|
||||
* 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,7 +1,7 @@
|
|||
/*
|
||||
* paperweight is a Gradle plugin for the PaperMC project.
|
||||
*
|
||||
* Copyright (c) 2020 Kyle Wood (DemonWav)
|
||||
* Copyright (c) 2023 Kyle Wood (DenWav)
|
||||
* Contributors
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
|
@ -22,32 +22,33 @@
|
|||
|
||||
package io.papermc.paperweight.tasks
|
||||
|
||||
import io.papermc.paperweight.util.file
|
||||
import java.io.File
|
||||
import io.papermc.paperweight.util.*
|
||||
import java.nio.file.Path
|
||||
import kotlin.io.path.*
|
||||
import org.gradle.api.file.RegularFileProperty
|
||||
import org.gradle.api.tasks.CacheableTask
|
||||
import org.gradle.api.tasks.InputFile
|
||||
import org.gradle.api.tasks.PathSensitive
|
||||
import org.gradle.api.tasks.PathSensitivity
|
||||
|
||||
/**
|
||||
* Because Spigot doesn't remap all classes, there are class and package name clashes if we don't do this in the source
|
||||
* remap step. Other than that, we don't need this jar
|
||||
*/
|
||||
@CacheableTask
|
||||
abstract class FilterSpigotExcludes : ZippedTask() {
|
||||
|
||||
@get:InputFile
|
||||
@get:PathSensitive(PathSensitivity.NONE)
|
||||
abstract val excludesFile: RegularFileProperty
|
||||
|
||||
override fun run(rootDir: File) {
|
||||
excludesFile.file.useLines { lines ->
|
||||
override fun run(rootDir: Path) {
|
||||
excludesFile.path.useLines { lines ->
|
||||
for (line in lines) {
|
||||
if (line.startsWith('#') || line.isBlank()) {
|
||||
continue
|
||||
}
|
||||
val file = if (line.contains('/')) {
|
||||
rootDir.resolve("$line.class")
|
||||
} else {
|
||||
rootDir.resolve("net/minecraft/server/$line.class")
|
||||
}
|
||||
file.delete()
|
||||
rootDir.resolve("$line.class").deleteForcefully()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,229 @@
|
|||
/*
|
||||
* 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
|
||||
}
|
||||
}
|
|
@ -0,0 +1,218 @@
|
|||
/*
|
||||
* 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
|
||||
}
|
||||
}
|
|
@ -0,0 +1,396 @@
|
|||
/*
|
||||
* 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,7 +1,7 @@
|
|||
/*
|
||||
* paperweight is a Gradle plugin for the PaperMC project.
|
||||
*
|
||||
* Copyright (c) 2020 Kyle Wood (DemonWav)
|
||||
* Copyright (c) 2023 Kyle Wood (DenWav)
|
||||
* Contributors
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
|
@ -22,19 +22,23 @@
|
|||
|
||||
package io.papermc.paperweight.tasks
|
||||
|
||||
import io.papermc.paperweight.util.Constants
|
||||
import io.papermc.paperweight.util.MappingFormats
|
||||
import io.papermc.paperweight.util.emptyMergeResult
|
||||
import io.papermc.paperweight.util.ensureParentExists
|
||||
import io.papermc.paperweight.util.file
|
||||
import io.papermc.paperweight.util.path
|
||||
import java.nio.file.FileSystems
|
||||
import java.nio.file.Files
|
||||
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 org.cadixdev.atlas.Atlas
|
||||
import org.cadixdev.bombe.asm.jar.JarEntryRemappingTransformer
|
||||
import kotlin.io.path.*
|
||||
import org.cadixdev.lorenz.MappingSet
|
||||
import org.cadixdev.lorenz.asm.LorenzRemapper
|
||||
import org.cadixdev.lorenz.merge.FieldMergeStrategy
|
||||
import org.cadixdev.lorenz.merge.MappingSetMerger
|
||||
import org.cadixdev.lorenz.merge.MappingSetMergerHandler
|
||||
|
@ -47,102 +51,137 @@ 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.DefaultTask
|
||||
import org.gradle.api.file.ConfigurableFileCollection
|
||||
import org.gradle.api.file.RegularFileProperty
|
||||
import org.gradle.api.tasks.InputFile
|
||||
import org.gradle.api.tasks.OutputFile
|
||||
import org.gradle.api.tasks.TaskAction
|
||||
import org.gradle.kotlin.dsl.submit
|
||||
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
|
||||
|
||||
abstract class GenerateMappings : DefaultTask() {
|
||||
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())
|
||||
}
|
||||
|
||||
@get:InputFile
|
||||
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() {
|
||||
val vanillaMappings = MappingFormats.PROGUARD.createReader(vanillaMappings.path).use { it.read() }.reverse()
|
||||
|
||||
val paramMappings = FileSystems.newFileSystem(paramMappings.path, null).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()
|
||||
|
||||
// Fill out any missing inheritance info in the mappings
|
||||
val tempMappingsFile = Files.createTempFile("mappings", "tiny")
|
||||
val tempMappingsOutputFile = Files.createTempFile("mappings-out", "tiny")
|
||||
|
||||
val filledMerged = try {
|
||||
MappingFormats.TINY.write(merged, tempMappingsFile, Constants.OBF_NAMESPACE, Constants.DEOBF_NAMESPACE)
|
||||
|
||||
val queue = workerExecutor.processIsolation {
|
||||
forkOptions.jvmArgs("-Xmx1G")
|
||||
}
|
||||
|
||||
queue.submit(AtlasAction::class) {
|
||||
inputJar.set(vanillaJar.file)
|
||||
mappingsFile.set(tempMappingsFile.toFile())
|
||||
outputMappingsFile.set(tempMappingsOutputFile.toFile())
|
||||
}
|
||||
|
||||
queue.await()
|
||||
|
||||
MappingFormats.TINY.read(tempMappingsOutputFile, Constants.OBF_NAMESPACE, Constants.DEOBF_NAMESPACE)
|
||||
} finally {
|
||||
Files.deleteIfExists(tempMappingsFile)
|
||||
Files.deleteIfExists(tempMappingsOutputFile)
|
||||
}
|
||||
|
||||
ensureParentExists(outputMappings)
|
||||
MappingFormats.TINY.write(filledMerged, outputMappings.path, Constants.OBF_NAMESPACE, Constants.DEOBF_NAMESPACE)
|
||||
generateMappings(
|
||||
vanillaJar.path,
|
||||
libraries.files.map { it.toPath() },
|
||||
vanillaMappings.path,
|
||||
paramMappings.path,
|
||||
outputMappings.path,
|
||||
workerExecutor,
|
||||
launcher.get(),
|
||||
jvmargs.get()
|
||||
)
|
||||
}
|
||||
|
||||
abstract class AtlasAction : WorkAction<AtlasParameters> {
|
||||
override fun execute() {
|
||||
val mappings = MappingFormats.TINY.read(parameters.mappingsFile.path, Constants.OBF_NAMESPACE, Constants.DEOBF_NAMESPACE)
|
||||
interface GenerateMappingsParams : WorkParameters {
|
||||
val vanillaJar: RegularFileProperty
|
||||
val libraries: ConfigurableFileCollection
|
||||
val vanillaMappings: RegularFileProperty
|
||||
val paramMappings: RegularFileProperty
|
||||
val outputMappings: RegularFileProperty
|
||||
}
|
||||
|
||||
val tempOut = Files.createTempFile("remapped", "jar")
|
||||
try {
|
||||
Atlas().let { atlas ->
|
||||
atlas.install { ctx -> JarEntryRemappingTransformer(LorenzRemapper(mappings, ctx.inheritanceProvider())) }
|
||||
atlas.run(parameters.inputJar.path, tempOut)
|
||||
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))
|
||||
}
|
||||
|
||||
MappingFormats.TINY.write(mappings, parameters.outputMappingsFile.path, Constants.OBF_NAMESPACE, Constants.DEOBF_NAMESPACE)
|
||||
} finally {
|
||||
Files.deleteIfExists(tempOut)
|
||||
}
|
||||
ensureParentExists(parameters.outputMappings)
|
||||
MappingFormats.TINY.write(filledMerged, parameters.outputMappings.path, OBF_NAMESPACE, DEOBF_NAMESPACE)
|
||||
}
|
||||
}
|
||||
|
||||
interface AtlasParameters : WorkParameters {
|
||||
val inputJar: RegularFileProperty
|
||||
val mappingsFile: RegularFileProperty
|
||||
val outputMappingsFile: RegularFileProperty
|
||||
}
|
||||
}
|
||||
|
||||
class ParamsMergeHandler : MappingSetMergerHandler {
|
||||
|
@ -155,6 +194,7 @@ class ParamsMergeHandler : MappingSetMergerHandler {
|
|||
): MergeResult<TopLevelClassMapping?> {
|
||||
throw IllegalStateException("Unexpectedly merged class: ${left.fullObfuscatedName}")
|
||||
}
|
||||
|
||||
override fun mergeDuplicateTopLevelClassMappings(
|
||||
left: TopLevelClassMapping,
|
||||
right: TopLevelClassMapping,
|
||||
|
@ -176,6 +216,7 @@ class ParamsMergeHandler : MappingSetMergerHandler {
|
|||
): MergeResult<InnerClassMapping?> {
|
||||
throw IllegalStateException("Unexpectedly merged class: ${left.fullObfuscatedName}")
|
||||
}
|
||||
|
||||
override fun mergeDuplicateInnerClassMappings(
|
||||
left: InnerClassMapping,
|
||||
right: InnerClassMapping,
|
||||
|
@ -198,6 +239,7 @@ class ParamsMergeHandler : MappingSetMergerHandler {
|
|||
): FieldMapping? {
|
||||
throw IllegalStateException("Unexpectedly merged field: ${left.fullObfuscatedName}")
|
||||
}
|
||||
|
||||
override fun mergeDuplicateFieldMappings(
|
||||
left: FieldMapping,
|
||||
strictRightDuplicate: FieldMapping?,
|
||||
|
@ -209,6 +251,7 @@ class ParamsMergeHandler : MappingSetMergerHandler {
|
|||
): FieldMapping? {
|
||||
return target.createFieldMapping(left.signature, left.deobfuscatedName)
|
||||
}
|
||||
|
||||
override fun addLeftFieldMapping(
|
||||
left: FieldMapping,
|
||||
target: ClassMapping<*, *>,
|
||||
|
@ -226,6 +269,7 @@ class ParamsMergeHandler : MappingSetMergerHandler {
|
|||
): MergeResult<MethodMapping?> {
|
||||
throw IllegalStateException("Unexpectedly merged method: ${left.fullObfuscatedName}")
|
||||
}
|
||||
|
||||
override fun mergeDuplicateMethodMappings(
|
||||
left: MethodMapping,
|
||||
standardRightDuplicate: MethodMapping?,
|
||||
|
@ -258,6 +302,7 @@ class ParamsMergeHandler : MappingSetMergerHandler {
|
|||
): MergeResult<TopLevelClassMapping?> {
|
||||
return emptyMergeResult()
|
||||
}
|
||||
|
||||
override fun addRightInnerClassMapping(
|
||||
right: InnerClassMapping?,
|
||||
target: ClassMapping<*, *>?,
|
||||
|
@ -265,6 +310,7 @@ class ParamsMergeHandler : MappingSetMergerHandler {
|
|||
): MergeResult<InnerClassMapping?> {
|
||||
return emptyMergeResult()
|
||||
}
|
||||
|
||||
override fun addRightFieldMapping(
|
||||
right: FieldMapping?,
|
||||
target: ClassMapping<*, *>?,
|
||||
|
@ -272,6 +318,7 @@ class ParamsMergeHandler : MappingSetMergerHandler {
|
|||
): FieldMapping? {
|
||||
return null
|
||||
}
|
||||
|
||||
override fun addRightMethodMapping(
|
||||
right: MethodMapping?,
|
||||
target: ClassMapping<*, *>?,
|
|
@ -0,0 +1,156 @@
|
|||
/*
|
||||
* 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)}"
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,315 @@
|
|||
/*
|
||||
* 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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,313 @@
|
|||
/*
|
||||
* 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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
* 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())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* 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)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,225 @@
|
|||
/*
|
||||
* 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,7 +1,7 @@
|
|||
/*
|
||||
* paperweight is a Gradle plugin for the PaperMC project.
|
||||
*
|
||||
* Copyright (c) 2020 Kyle Wood (DemonWav)
|
||||
* Copyright (c) 2023 Kyle Wood (DenWav)
|
||||
* Contributors
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
|
@ -22,21 +22,31 @@
|
|||
|
||||
package io.papermc.paperweight.tasks
|
||||
|
||||
import io.papermc.paperweight.util.defaultOutput
|
||||
import io.papermc.paperweight.util.path
|
||||
import io.papermc.paperweight.util.*
|
||||
import kotlin.io.path.*
|
||||
import org.cadixdev.at.AccessTransformSet
|
||||
import org.cadixdev.at.io.AccessTransformFormats
|
||||
import org.gradle.api.file.RegularFile
|
||||
import org.gradle.api.file.RegularFileProperty
|
||||
import org.gradle.api.provider.ListProperty
|
||||
import org.gradle.api.tasks.InputFiles
|
||||
import org.gradle.api.tasks.CacheableTask
|
||||
import org.gradle.api.tasks.InputFile
|
||||
import org.gradle.api.tasks.Optional
|
||||
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 MergeAccessTransforms : BaseTask() {
|
||||
|
||||
@get:InputFiles
|
||||
abstract val inputFiles: ListProperty<RegularFile>
|
||||
@get:Optional
|
||||
@get:InputFile
|
||||
@get:PathSensitive(PathSensitivity.NONE)
|
||||
abstract val firstFile: RegularFileProperty
|
||||
|
||||
@get:Optional
|
||||
@get:InputFile
|
||||
@get:PathSensitive(PathSensitivity.NONE)
|
||||
abstract val secondFile: RegularFileProperty
|
||||
|
||||
@get:OutputFile
|
||||
abstract val outputFile: RegularFileProperty
|
||||
|
@ -47,8 +57,10 @@ abstract class MergeAccessTransforms : BaseTask() {
|
|||
|
||||
@TaskAction
|
||||
fun run() {
|
||||
val ats = inputFiles.get()
|
||||
.map { AccessTransformFormats.FML.read(it.asFile.toPath()) }
|
||||
val ats = sequenceOf(firstFile.pathOrNull, secondFile.pathOrNull)
|
||||
.filterNotNull()
|
||||
.filter { it.exists() }
|
||||
.map { AccessTransformFormats.FML.read(it) }
|
||||
|
||||
val outputAt = AccessTransformSet.create()
|
||||
for (at in ats) {
|
|
@ -0,0 +1,109 @@
|
|||
/*
|
||||
* 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,7 +1,7 @@
|
|||
/*
|
||||
* paperweight is a Gradle plugin for the PaperMC project.
|
||||
*
|
||||
* Copyright (c) 2020 Kyle Wood (DemonWav)
|
||||
* Copyright (c) 2023 Kyle Wood (DenWav)
|
||||
* Contributors
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
|
@ -22,99 +22,107 @@
|
|||
|
||||
package io.papermc.paperweight.tasks
|
||||
|
||||
import io.papermc.paperweight.util.Git
|
||||
import io.papermc.paperweight.util.file
|
||||
import java.io.File
|
||||
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.tasks.Console
|
||||
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.options.Option
|
||||
import org.gradle.api.provider.ProviderFactory
|
||||
import org.gradle.api.tasks.*
|
||||
|
||||
abstract class RebuildPaperPatches : ControllableOutputTask() {
|
||||
@UntrackedTask(because = "RebuildGitPatches should always run when requested")
|
||||
abstract class RebuildGitPatches : ControllableOutputTask() {
|
||||
|
||||
@get:InputDirectory
|
||||
abstract val inputDir: DirectoryProperty
|
||||
@get:Console
|
||||
abstract val server: Property<Boolean>
|
||||
|
||||
@get:Input
|
||||
abstract val baseRef: Property<String>
|
||||
|
||||
@get:OutputDirectory
|
||||
abstract val patchDir: DirectoryProperty
|
||||
|
||||
@get:Internal
|
||||
@get:Option(option = "filter-patches", description = "Controls if patches should be cleaned up, defaults to true")
|
||||
@get:Input
|
||||
abstract val filterPatches: Property<Boolean>
|
||||
|
||||
@get:Inject
|
||||
abstract val providers: ProviderFactory
|
||||
|
||||
override fun init() {
|
||||
printOutput.convention(true)
|
||||
filterPatches.convention(true)
|
||||
server.convention(false)
|
||||
filterPatches.convention(
|
||||
providers.gradleProperty("paperweight.filter-patches")
|
||||
.map { it.toBoolean() }
|
||||
.orElse(true)
|
||||
)
|
||||
}
|
||||
|
||||
@TaskAction
|
||||
fun run() {
|
||||
val what = inputDir.file.name
|
||||
val patchFolder = patchDir.file
|
||||
val what = inputDir.path.name
|
||||
val patchFolder = patchDir.path
|
||||
if (!patchFolder.exists()) {
|
||||
patchFolder.mkdirs()
|
||||
patchFolder.createDirectories()
|
||||
}
|
||||
|
||||
if (printOutput.get()) {
|
||||
println("Formatting patches for $what...")
|
||||
logger.lifecycle("Formatting patches for $what...")
|
||||
}
|
||||
|
||||
if (inputDir.file.resolve(".git/rebase-apply").exists()) {
|
||||
if (inputDir.path.resolve(".git/rebase-apply").exists()) {
|
||||
// in middle of a rebase, be smarter
|
||||
if (printOutput.get()) {
|
||||
println("REBASE DETECTED - PARTIAL SAVE")
|
||||
val last = inputDir.file.resolve(".git/rebase-apply/last").readText().trim().toInt()
|
||||
val next = inputDir.file.resolve(".git/rebase-apply/next").readText().trim().toInt()
|
||||
val orderedFiles = patchFolder.listFiles { f -> f.name.endsWith(".patch") }!!
|
||||
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].delete()
|
||||
orderedFiles[i].deleteForcefully()
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
patchFolder.deleteRecursively()
|
||||
patchFolder.mkdirs()
|
||||
patchFolder.deleteRecursive()
|
||||
patchFolder.createDirectories()
|
||||
}
|
||||
|
||||
Git(inputDir.file)(
|
||||
val git = Git(inputDir.path)
|
||||
git("fetch", "--all", "--prune").runSilently(silenceErr = true)
|
||||
git(
|
||||
"format-patch",
|
||||
"--zero-commit", "--full-index", "--no-signature", "--no-stat", "-N",
|
||||
"-o", patchFolder.absolutePath,
|
||||
if (server.get()) "base" else "upstream/upstream"
|
||||
"--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
|
||||
|
||||
if (printOutput.get()) {
|
||||
println(" Patches saved for $what to ${patchFolder.name}/")
|
||||
logger.lifecycle("Saved $saved patches for $what to ${layout.projectDirectory.path.relativize(patchFolder)}/")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun cleanupPatches(git: Git) {
|
||||
val patchFiles = patchDir.file.listFiles { f -> f.name.endsWith(".patch") } ?: emptyArray()
|
||||
val patchFiles = patchDir.path.useDirectoryEntries("*.patch") { it.toMutableList() }
|
||||
if (patchFiles.isEmpty()) {
|
||||
return
|
||||
}
|
||||
patchFiles.sortBy { it.name }
|
||||
patchFiles.sort()
|
||||
|
||||
val noChangesPatches = ConcurrentLinkedQueue<File>()
|
||||
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
|
||||
|
@ -123,13 +131,13 @@ abstract class RebuildPaperPatches : ControllableOutputTask() {
|
|||
try {
|
||||
for (patch in patchFiles) {
|
||||
futures += executor.submit {
|
||||
val hasNoChanges = git("diff", "--staged", patch.name).getText().lineSequence()
|
||||
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 += patch
|
||||
noChangesPatches.add(patch)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -140,14 +148,16 @@ abstract class RebuildPaperPatches : ControllableOutputTask() {
|
|||
}
|
||||
|
||||
if (noChangesPatches.isNotEmpty()) {
|
||||
git("reset", "HEAD", *noChangesPatches.map { it.name }.toTypedArray()).executeSilently()
|
||||
git("checkout", "--", *noChangesPatches.map { it.name }.toTypedArray()).executeSilently()
|
||||
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()) {
|
||||
for (patch in patchFiles) {
|
||||
println(patch.name)
|
||||
}
|
||||
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/")
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,145 @@
|
|||
/*
|
||||
* 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,7 +1,7 @@
|
|||
/*
|
||||
* paperweight is a Gradle plugin for the PaperMC project.
|
||||
*
|
||||
* Copyright (c) 2020 Kyle Wood (DemonWav)
|
||||
* Copyright (c) 2023 Kyle Wood (DenWav)
|
||||
* Contributors
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
|
@ -22,21 +22,26 @@
|
|||
|
||||
package io.papermc.paperweight.tasks
|
||||
|
||||
import io.papermc.paperweight.util.Constants
|
||||
import io.papermc.paperweight.util.MappingFormats
|
||||
import io.papermc.paperweight.util.defaultOutput
|
||||
import io.papermc.paperweight.util.path
|
||||
import io.papermc.paperweight.util.*
|
||||
import io.papermc.paperweight.util.constants.*
|
||||
import org.cadixdev.at.io.AccessTransformFormats
|
||||
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 RemapAccessTransform : BaseTask() {
|
||||
|
||||
@get:InputFile
|
||||
@get:PathSensitive(PathSensitivity.NONE)
|
||||
abstract val inputFile: RegularFileProperty
|
||||
|
||||
@get:InputFile
|
||||
@get:PathSensitive(PathSensitivity.NONE)
|
||||
abstract val mappings: RegularFileProperty
|
||||
|
||||
@get:OutputFile
|
||||
|
@ -49,7 +54,7 @@ abstract class RemapAccessTransform : BaseTask() {
|
|||
@TaskAction
|
||||
fun run() {
|
||||
val at = AccessTransformFormats.FML.read(inputFile.path)
|
||||
val mappingSet = MappingFormats.TINY.read(mappings.path, Constants.SPIGOT_NAMESPACE, Constants.DEOBF_NAMESPACE)
|
||||
val mappingSet = MappingFormats.TINY.read(mappings.path, SPIGOT_NAMESPACE, DEOBF_NAMESPACE)
|
||||
|
||||
val resultAt = at.remap(mappingSet)
|
||||
AccessTransformFormats.FML.write(outputFile.path, resultAt)
|
|
@ -0,0 +1,229 @@
|
|||
/*
|
||||
* 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,7 +1,7 @@
|
|||
/*
|
||||
* paperweight is a Gradle plugin for the PaperMC project.
|
||||
*
|
||||
* Copyright (c) 2020 Kyle Wood (DemonWav)
|
||||
* Copyright (c) 2023 Kyle Wood (DenWav)
|
||||
* Contributors
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
|
@ -22,51 +22,48 @@
|
|||
|
||||
package io.papermc.paperweight.tasks
|
||||
|
||||
import io.papermc.paperweight.util.MappingFormats
|
||||
import io.papermc.paperweight.util.defaultOutput
|
||||
import io.papermc.paperweight.util.ensureDeleted
|
||||
import io.papermc.paperweight.util.ensureParentExists
|
||||
import io.papermc.paperweight.util.file
|
||||
import io.papermc.paperweight.util.path
|
||||
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.cadixdev.lorenz.asm.LorenzRemapper
|
||||
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.OutputFile
|
||||
import org.gradle.api.tasks.TaskAction
|
||||
import org.gradle.kotlin.dsl.submit
|
||||
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
|
||||
|
||||
abstract class RemapJarAtlas : BaseTask() {
|
||||
@CacheableTask
|
||||
abstract class RemapJarAtlas : JavaLauncherTask() {
|
||||
|
||||
@get:InputFile
|
||||
@get:Classpath
|
||||
abstract val inputJar: RegularFileProperty
|
||||
|
||||
@get:InputFile
|
||||
abstract val mappingsFile: RegularFileProperty
|
||||
@get:Input
|
||||
abstract val packageVersion: Property<String>
|
||||
|
||||
@get:Input
|
||||
abstract val fromNamespace: Property<String>
|
||||
@get:Input
|
||||
abstract val toNamespace: Property<String>
|
||||
|
||||
@get:OutputFile
|
||||
abstract val outputJar: RegularFileProperty
|
||||
|
||||
@get:Internal
|
||||
abstract val jvmargs: ListProperty<String>
|
||||
|
||||
@get:Inject
|
||||
abstract val workerExecutor: WorkerExecutor
|
||||
|
||||
override fun init() {
|
||||
outputJar.convention(defaultOutput())
|
||||
super.init()
|
||||
|
||||
jvmargs.convention(listOf("-Xmx1G"))
|
||||
}
|
||||
|
||||
@TaskAction
|
||||
|
@ -75,27 +72,22 @@ abstract class RemapJarAtlas : BaseTask() {
|
|||
ensureDeleted(outputJar)
|
||||
|
||||
val queue = workerExecutor.processIsolation {
|
||||
forkOptions.jvmArgs("-Xmx1G")
|
||||
forkOptions.jvmArgs(jvmargs.get())
|
||||
forkOptions.executable(launcher.get().executablePath.path.absolutePathString())
|
||||
}
|
||||
|
||||
queue.submit(AtlasAction::class) {
|
||||
inputJar.set(this@RemapJarAtlas.inputJar.file)
|
||||
outputJar.set(this@RemapJarAtlas.outputJar.file)
|
||||
mappingsFile.set(this@RemapJarAtlas.mappingsFile.file)
|
||||
inputJar.set(this@RemapJarAtlas.inputJar.get())
|
||||
outputJar.set(this@RemapJarAtlas.outputJar.get())
|
||||
packageVersion.set(this@RemapJarAtlas.packageVersion.get())
|
||||
toNamespace.set(this@RemapJarAtlas.toNamespace.get())
|
||||
fromNamespace.set(this@RemapJarAtlas.fromNamespace.get())
|
||||
}
|
||||
}
|
||||
|
||||
abstract class AtlasAction : WorkAction<AtlasParameters> {
|
||||
override fun execute() {
|
||||
val mappings = MappingFormats.TINY.read(parameters.mappingsFile.path, parameters.fromNamespace.get(), parameters.toNamespace.get())
|
||||
|
||||
val oldPack = "net/minecraft/server"
|
||||
val newPack = "$oldPack/v${parameters.packageVersion.get()}"
|
||||
val oldPack = "net/minecraft"
|
||||
val newPack = "$oldPack/server/v${parameters.packageVersion.get()}"
|
||||
Atlas().let { atlas ->
|
||||
atlas.install { ctx -> JarEntryRemappingTransformer(LorenzRemapper(mappings, ctx.inheritanceProvider())) }
|
||||
atlas.install { JarEntryRemappingTransformer(PackageRemapper(oldPack, newPack)) }
|
||||
atlas.run(parameters.inputJar.path, parameters.outputJar.path)
|
||||
}
|
||||
|
@ -105,9 +97,6 @@ abstract class RemapJarAtlas : BaseTask() {
|
|||
interface AtlasParameters : WorkParameters {
|
||||
val inputJar: RegularFileProperty
|
||||
val outputJar: RegularFileProperty
|
||||
val mappingsFile: RegularFileProperty
|
||||
val fromNamespace: Property<String>
|
||||
val toNamespace: Property<String>
|
||||
val packageVersion: Property<String>
|
||||
}
|
||||
}
|
||||
|
@ -116,7 +105,7 @@ class PackageRemapper(private val oldPackage: String, private val newPackage: St
|
|||
|
||||
override fun map(internalName: String): String {
|
||||
return if (internalName.startsWith(oldPackage)) {
|
||||
internalName.replaceFirst(oldPackage, newPackage)
|
||||
internalName.replaceBeforeLast('/', newPackage)
|
||||
} else {
|
||||
internalName
|
||||
}
|
|
@ -0,0 +1,430 @@
|
|||
/*
|
||||
* 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,7 +1,7 @@
|
|||
/*
|
||||
* paperweight is a Gradle plugin for the PaperMC project.
|
||||
*
|
||||
* Copyright (c) 2020 Kyle Wood (DemonWav)
|
||||
* Copyright (c) 2023 Kyle Wood (DenWav)
|
||||
* Contributors
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
|
@ -22,12 +22,9 @@
|
|||
|
||||
package io.papermc.paperweight.tasks
|
||||
|
||||
import io.papermc.paperweight.util.Constants
|
||||
import io.papermc.paperweight.util.MappingFormats
|
||||
import io.papermc.paperweight.util.defaultOutput
|
||||
import io.papermc.paperweight.util.file
|
||||
import io.papermc.paperweight.util.path
|
||||
import java.util.jar.JarFile
|
||||
import io.papermc.paperweight.util.*
|
||||
import io.papermc.paperweight.util.constants.*
|
||||
import kotlin.io.path.*
|
||||
import org.cadixdev.at.AccessChange
|
||||
import org.cadixdev.at.AccessTransform
|
||||
import org.cadixdev.at.AccessTransformSet
|
||||
|
@ -36,17 +33,26 @@ import org.cadixdev.at.io.AccessTransformFormats
|
|||
import org.cadixdev.bombe.type.MethodDescriptor
|
||||
import org.cadixdev.bombe.type.signature.MethodSignature
|
||||
import org.gradle.api.file.RegularFileProperty
|
||||
import org.gradle.api.tasks.CacheableTask
|
||||
import org.gradle.api.tasks.Classpath
|
||||
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 RemapSpigotAt : BaseTask() {
|
||||
|
||||
@get:InputFile
|
||||
@get:Classpath
|
||||
abstract val inputJar: RegularFileProperty
|
||||
|
||||
@get:InputFile
|
||||
@get:PathSensitive(PathSensitivity.NONE)
|
||||
abstract val spigotAt: RegularFileProperty
|
||||
|
||||
@get:InputFile
|
||||
@get:PathSensitive(PathSensitivity.NONE)
|
||||
abstract val mapping: RegularFileProperty
|
||||
|
||||
@get:OutputFile
|
||||
|
@ -60,8 +66,8 @@ abstract class RemapSpigotAt : BaseTask() {
|
|||
fun run() {
|
||||
val outputAt = AccessTransformSet.create()
|
||||
|
||||
spigotAt.file.useLines { lines ->
|
||||
JarFile(inputJar.file).use { jarFile ->
|
||||
spigotAt.path.useLines { lines ->
|
||||
inputJar.path.openZip().use { jarFile ->
|
||||
for (line in lines) {
|
||||
if (line.isBlank() || line.startsWith('#')) {
|
||||
continue
|
||||
|
@ -84,7 +90,7 @@ abstract class RemapSpigotAt : BaseTask() {
|
|||
)
|
||||
} else {
|
||||
// either field or class
|
||||
if (jarFile.getJarEntry("$desc.class") == null) {
|
||||
if (jarFile.getPath("$desc.class").notExists()) {
|
||||
// field
|
||||
val index = desc.lastIndexOf('/')
|
||||
val className = desc.substring(0, index)
|
||||
|
@ -99,7 +105,7 @@ abstract class RemapSpigotAt : BaseTask() {
|
|||
}
|
||||
}
|
||||
|
||||
val mappings = MappingFormats.TINY.read(mapping.path, Constants.SPIGOT_NAMESPACE, Constants.DEOBF_NAMESPACE)
|
||||
val mappings = MappingFormats.TINY.read(mapping.path, SPIGOT_NAMESPACE, DEOBF_NAMESPACE)
|
||||
val remappedAt = outputAt.remap(mappings)
|
||||
|
||||
AccessTransformFormats.FML.write(outputFile.path, remappedAt)
|
|
@ -0,0 +1,169 @@
|
|||
/*
|
||||
* 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()
|
||||
)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,170 @@
|
|||
/*
|
||||
* 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)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,115 @@
|
|||
/*
|
||||
* 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}"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,86 @@
|
|||
/*
|
||||
* 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,7 +1,7 @@
|
|||
/*
|
||||
* paperweight is a Gradle plugin for the PaperMC project.
|
||||
*
|
||||
* Copyright (c) 2020 Kyle Wood (DemonWav)
|
||||
* Copyright (c) 2023 Kyle Wood (DenWav)
|
||||
* Contributors
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
|
@ -22,14 +22,18 @@
|
|||
|
||||
package io.papermc.paperweight.tasks
|
||||
|
||||
import io.papermc.paperweight.util.file
|
||||
import io.papermc.paperweight.util.*
|
||||
import java.nio.file.Path
|
||||
import kotlin.io.path.*
|
||||
import org.gradle.api.DefaultTask
|
||||
import org.gradle.api.file.RegularFileProperty
|
||||
import org.gradle.api.provider.ListProperty
|
||||
import org.gradle.api.tasks.CacheableTask
|
||||
import org.gradle.api.tasks.Input
|
||||
import org.gradle.api.tasks.OutputFile
|
||||
import org.gradle.api.tasks.TaskAction
|
||||
|
||||
@CacheableTask
|
||||
abstract class SetupMcLibraries : DefaultTask() {
|
||||
|
||||
@get:Input
|
||||
|
@ -40,12 +44,16 @@ abstract class SetupMcLibraries : DefaultTask() {
|
|||
|
||||
@TaskAction
|
||||
fun run() {
|
||||
val list = dependencies.get().sorted()
|
||||
setupMinecraftLibraries(dependencies.get(), outputFile.path)
|
||||
}
|
||||
}
|
||||
|
||||
outputFile.file.bufferedWriter().use { writer ->
|
||||
for (line in list) {
|
||||
writer.appendln(line)
|
||||
}
|
||||
fun setupMinecraftLibraries(dependencies: List<String>, outputFile: Path) {
|
||||
val list = dependencies.sorted()
|
||||
|
||||
outputFile.bufferedWriter().use { writer ->
|
||||
for (line in list) {
|
||||
writer.appendLine(line)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* paperweight is a Gradle plugin for the PaperMC project.
|
||||
*
|
||||
* Copyright (c) 2020 Kyle Wood (DemonWav)
|
||||
* Copyright (c) 2023 Kyle Wood (DenWav)
|
||||
* Contributors
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
|
@ -23,61 +23,77 @@
|
|||
package io.papermc.paperweight.tasks
|
||||
|
||||
import io.papermc.paperweight.PaperweightException
|
||||
import io.papermc.paperweight.util.Constants.paperTaskOutput
|
||||
import io.papermc.paperweight.util.cache
|
||||
import io.papermc.paperweight.util.defaultOutput
|
||||
import io.papermc.paperweight.util.ensureDeleted
|
||||
import io.papermc.paperweight.util.runJar
|
||||
import io.papermc.paperweight.util.*
|
||||
import io.papermc.paperweight.util.constants.*
|
||||
import java.util.concurrent.ThreadLocalRandom
|
||||
import kotlin.io.path.*
|
||||
import org.gradle.api.file.RegularFileProperty
|
||||
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.OutputFile
|
||||
import org.gradle.api.tasks.TaskAction
|
||||
|
||||
abstract class SpigotDecompileJar : BaseTask() {
|
||||
@CacheableTask
|
||||
abstract class SpigotDecompileJar : JavaLauncherTask() {
|
||||
|
||||
@get:InputFile
|
||||
@get:Classpath
|
||||
abstract val inputJar: RegularFileProperty
|
||||
@get:InputFile
|
||||
|
||||
@get:Classpath
|
||||
abstract val fernFlowerJar: RegularFileProperty
|
||||
|
||||
@get:Input
|
||||
abstract val decompileCommand: Property<String>
|
||||
|
||||
@get:OutputFile
|
||||
abstract val outputJar: RegularFileProperty
|
||||
|
||||
@get:Input
|
||||
abstract val memory: Property<String>
|
||||
|
||||
override fun init() {
|
||||
super.init()
|
||||
|
||||
memory.convention("4G")
|
||||
outputJar.convention(defaultOutput())
|
||||
}
|
||||
|
||||
@TaskAction
|
||||
fun run() {
|
||||
val inputJarFile = inputJar.asFile.get()
|
||||
val inputJarPath = inputJarFile.canonicalPath
|
||||
val inputJarFile = inputJar.path
|
||||
val inputJarPath = inputJarFile.absolutePathString()
|
||||
|
||||
val outputJarFile = outputJar.asFile.get()
|
||||
val outputJarFile = outputJar.path
|
||||
val decomp = outputJarFile.resolveSibling("decomp" + ThreadLocalRandom.current().nextInt())
|
||||
|
||||
try {
|
||||
if (!decomp.exists() && !decomp.mkdirs()) {
|
||||
throw PaperweightException("Failed to create output directory: $decomp")
|
||||
try {
|
||||
decomp.createDirectories()
|
||||
} catch (e: Exception) {
|
||||
throw PaperweightException("Failed to create output directory: $decomp", e)
|
||||
}
|
||||
|
||||
val cmd = decompileCommand.get().split(" ").let { it.subList(3, it.size - 2) }.toMutableList()
|
||||
cmd += inputJarPath
|
||||
cmd += decomp.canonicalPath
|
||||
cmd += decomp.absolutePathString()
|
||||
|
||||
val logFile = layout.cache.resolve(paperTaskOutput("log"))
|
||||
logFile.delete()
|
||||
logFile.deleteForcefully()
|
||||
|
||||
runJar(fernFlowerJar, workingDir = layout.cache, logFile = logFile, args = *cmd.toTypedArray())
|
||||
launcher.runJar(
|
||||
objects.fileCollection().from(fernFlowerJar),
|
||||
workingDir = layout.cache,
|
||||
logFile = logFile,
|
||||
jvmArgs = listOf("-Xmx${memory.get()}"),
|
||||
args = cmd.toTypedArray()
|
||||
)
|
||||
|
||||
ensureDeleted(outputJarFile)
|
||||
decomp.resolve(inputJarFile.name).renameTo(outputJarFile)
|
||||
decomp.resolve(inputJarFile.name).moveTo(outputJarFile)
|
||||
} finally {
|
||||
decomp.deleteRecursively()
|
||||
decomp.deleteRecursive()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* paperweight is a Gradle plugin for the PaperMC project.
|
||||
*
|
||||
* Copyright (c) 2020 Kyle Wood (DemonWav)
|
||||
* Copyright (c) 2023 Kyle Wood (DenWav)
|
||||
* Contributors
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
|
@ -23,39 +23,49 @@
|
|||
package io.papermc.paperweight.tasks
|
||||
|
||||
import io.papermc.paperweight.PaperweightException
|
||||
import io.papermc.paperweight.util.Constants.paperTaskOutput
|
||||
import io.papermc.paperweight.util.cache
|
||||
import io.papermc.paperweight.util.defaultOutput
|
||||
import io.papermc.paperweight.util.runJar
|
||||
import io.papermc.paperweight.util.*
|
||||
import io.papermc.paperweight.util.constants.*
|
||||
import kotlin.io.path.*
|
||||
import org.gradle.api.file.RegularFileProperty
|
||||
import org.gradle.api.provider.Property
|
||||
import org.gradle.api.tasks.Input
|
||||
import org.gradle.api.tasks.InputFile
|
||||
import org.gradle.api.tasks.OutputFile
|
||||
import org.gradle.api.tasks.TaskAction
|
||||
import org.gradle.api.tasks.*
|
||||
|
||||
abstract class SpigotRemapJar : BaseTask() {
|
||||
@CacheableTask
|
||||
abstract class SpigotRemapJar : JavaLauncherTask() {
|
||||
|
||||
@get:InputFile
|
||||
@get:Classpath
|
||||
abstract val inputJar: RegularFileProperty
|
||||
|
||||
@get:InputFile
|
||||
@get:PathSensitive(PathSensitivity.NONE)
|
||||
abstract val classMappings: RegularFileProperty
|
||||
|
||||
@get:InputFile
|
||||
@get:PathSensitive(PathSensitivity.NONE)
|
||||
abstract val memberMappings: RegularFileProperty
|
||||
|
||||
@get:Input
|
||||
abstract val mcVersion: Property<String>
|
||||
|
||||
@get:InputFile
|
||||
abstract val packageMappings: RegularFileProperty
|
||||
@get:InputFile
|
||||
@get:PathSensitive(PathSensitivity.NONE)
|
||||
abstract val accessTransformers: RegularFileProperty
|
||||
|
||||
@get:Input
|
||||
abstract val workDirName: Property<String>
|
||||
@get:InputFile
|
||||
|
||||
@get:Classpath
|
||||
abstract val specialSourceJar: RegularFileProperty
|
||||
@get:InputFile
|
||||
|
||||
@get:Classpath
|
||||
abstract val specialSource2Jar: RegularFileProperty
|
||||
|
||||
@get:Input
|
||||
abstract val classMapCommand: Property<String>
|
||||
|
||||
@get:Input
|
||||
abstract val memberMapCommand: Property<String>
|
||||
|
||||
@get:Input
|
||||
abstract val finalMapCommand: Property<String>
|
||||
|
||||
|
@ -63,37 +73,42 @@ abstract class SpigotRemapJar : BaseTask() {
|
|||
abstract val outputJar: RegularFileProperty
|
||||
|
||||
override fun init() {
|
||||
super.init()
|
||||
|
||||
outputJar.convention(defaultOutput())
|
||||
}
|
||||
|
||||
@TaskAction
|
||||
fun run() {
|
||||
val inputJarPath = inputJar.asFile.get().canonicalPath
|
||||
val inputJarPath = inputJar.path.absolutePathString()
|
||||
|
||||
val outputJarFile = outputJar.asFile.get()
|
||||
val outputJarPath = outputJarFile.canonicalPath
|
||||
val outputJarFile = outputJar.path
|
||||
val outputJarPath = outputJarFile.absolutePathString()
|
||||
|
||||
val classJarFile = outputJarFile.resolveSibling(outputJarFile.name + ".classes")
|
||||
val membersJarFile = outputJarFile.resolveSibling(outputJarFile.name + ".members")
|
||||
val classJarPath = classJarFile.canonicalPath
|
||||
val membersJarPath = membersJarFile.canonicalPath
|
||||
val classJarPath = classJarFile.absolutePathString()
|
||||
val membersJarPath = membersJarFile.absolutePathString()
|
||||
|
||||
val classMappingPath = classMappings.asFile.get().canonicalPath
|
||||
val memberMappingsPath = memberMappings.asFile.get().canonicalPath
|
||||
val packageMappingsPath = packageMappings.asFile.get().canonicalPath
|
||||
val accessTransformersPath = accessTransformers.asFile.get().canonicalPath
|
||||
val classMappingPath = classMappings.path.absolutePathString()
|
||||
val accessTransformersPath = accessTransformers.path.absolutePathString()
|
||||
|
||||
val spigotMembersPath = memberMappings.path.absolutePathString()
|
||||
|
||||
val work = layout.projectDirectory.file(workDirName.get())
|
||||
|
||||
val spigotEmptyMappings = layout.cache.resolve("spigot-empty-package-mappings.csrg")
|
||||
spigotEmptyMappings.writeText("")
|
||||
|
||||
try {
|
||||
try {
|
||||
val logFile = layout.cache.resolve(paperTaskOutput("class.log"))
|
||||
logFile.delete()
|
||||
runJar(
|
||||
specialSource2Jar,
|
||||
logFile.deleteForcefully()
|
||||
launcher.runJar(
|
||||
objects.fileCollection().from(specialSource2Jar),
|
||||
workingDir = work,
|
||||
logFile = logFile,
|
||||
args = *doReplacements(classMapCommand.get(), inputJarPath, classMappingPath, classJarPath) {
|
||||
args = doReplacements(classMapCommand.get(), inputJarPath, classMappingPath, classJarPath) {
|
||||
// ignore excludes, we actually want to map every class
|
||||
it != "-e"
|
||||
}
|
||||
|
@ -104,12 +119,12 @@ abstract class SpigotRemapJar : BaseTask() {
|
|||
|
||||
try {
|
||||
val logFile = layout.cache.resolve(paperTaskOutput("member.log"))
|
||||
logFile.delete()
|
||||
runJar(
|
||||
specialSource2Jar,
|
||||
logFile.deleteForcefully()
|
||||
launcher.runJar(
|
||||
objects.fileCollection().from(specialSource2Jar),
|
||||
workingDir = work,
|
||||
logFile = logFile,
|
||||
args = *doReplacements(memberMapCommand.get(), classJarPath, memberMappingsPath, membersJarPath)
|
||||
args = doReplacements(memberMapCommand.get(), classJarPath, spigotMembersPath, membersJarPath)
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
throw PaperweightException("Failed to apply member mappings", e)
|
||||
|
@ -117,16 +132,16 @@ abstract class SpigotRemapJar : BaseTask() {
|
|||
|
||||
try {
|
||||
val logFile = layout.cache.resolve(paperTaskOutput("final.log"))
|
||||
logFile.delete()
|
||||
runJar(
|
||||
specialSourceJar,
|
||||
logFile.deleteForcefully()
|
||||
launcher.runJar(
|
||||
objects.fileCollection().from(specialSourceJar),
|
||||
workingDir = work,
|
||||
logFile = logFile,
|
||||
args = *doReplacements(
|
||||
args = doReplacements(
|
||||
finalMapCommand.get(),
|
||||
membersJarPath,
|
||||
accessTransformersPath,
|
||||
packageMappingsPath,
|
||||
spigotEmptyMappings.absolutePathString(),
|
||||
outputJarPath
|
||||
)
|
||||
)
|
||||
|
@ -134,8 +149,9 @@ abstract class SpigotRemapJar : BaseTask() {
|
|||
throw PaperweightException("Failed to create remapped jar", e)
|
||||
}
|
||||
} finally {
|
||||
classJarFile.delete()
|
||||
membersJarFile.delete()
|
||||
classJarFile.deleteForcefully()
|
||||
membersJarFile.deleteForcefully()
|
||||
spigotEmptyMappings.deleteForcefully()
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* paperweight is a Gradle plugin for the PaperMC project.
|
||||
*
|
||||
* Copyright (c) 2020 Kyle Wood (DemonWav)
|
||||
* Copyright (c) 2023 Kyle Wood (DenWav)
|
||||
* Contributors
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
|
@ -22,30 +22,25 @@
|
|||
|
||||
package io.papermc.paperweight.tasks
|
||||
|
||||
import io.papermc.paperweight.util.defaultOutput
|
||||
import io.papermc.paperweight.util.ensureDeleted
|
||||
import io.papermc.paperweight.util.file
|
||||
import io.papermc.paperweight.util.fileOrNull
|
||||
import io.papermc.paperweight.util.unzip
|
||||
import io.papermc.paperweight.util.zip
|
||||
import java.io.File
|
||||
import java.util.concurrent.ThreadLocalRandom
|
||||
import io.papermc.paperweight.util.*
|
||||
import java.nio.file.Path
|
||||
import kotlin.io.path.*
|
||||
import org.gradle.api.file.RegularFileProperty
|
||||
import org.gradle.api.tasks.InputFile
|
||||
import org.gradle.api.tasks.Classpath
|
||||
import org.gradle.api.tasks.Optional
|
||||
import org.gradle.api.tasks.OutputFile
|
||||
import org.gradle.api.tasks.TaskAction
|
||||
|
||||
abstract class ZippedTask : BaseTask() {
|
||||
|
||||
@get:InputFile
|
||||
@get:Optional
|
||||
@get:Classpath
|
||||
abstract val inputZip: RegularFileProperty
|
||||
|
||||
@get:OutputFile
|
||||
abstract val outputZip: RegularFileProperty
|
||||
|
||||
abstract fun run(rootDir: File)
|
||||
abstract fun run(rootDir: Path)
|
||||
|
||||
override fun init() {
|
||||
outputZip.convention(defaultOutput("zip"))
|
||||
|
@ -53,15 +48,15 @@ abstract class ZippedTask : BaseTask() {
|
|||
|
||||
@TaskAction
|
||||
fun exec() {
|
||||
val outputZipFile = outputZip.file
|
||||
val outputZipFile = outputZip.path
|
||||
val outputDir = findOutputDir(outputZipFile)
|
||||
|
||||
try {
|
||||
val input = inputZip.fileOrNull
|
||||
val input = inputZip.pathOrNull
|
||||
if (input != null) {
|
||||
unzip(input, outputDir)
|
||||
} else {
|
||||
outputDir.mkdirs()
|
||||
outputDir.createDirectories()
|
||||
}
|
||||
|
||||
run(outputDir)
|
||||
|
@ -70,15 +65,7 @@ abstract class ZippedTask : BaseTask() {
|
|||
|
||||
zip(outputDir, outputZipFile)
|
||||
} finally {
|
||||
outputDir.deleteRecursively()
|
||||
outputDir.deleteRecursive()
|
||||
}
|
||||
}
|
||||
|
||||
private fun findOutputDir(baseFile: File): File {
|
||||
var dir: File
|
||||
do {
|
||||
dir = baseFile.resolveSibling("${baseFile.name}-" + ThreadLocalRandom.current().nextInt())
|
||||
} while (dir.exists())
|
||||
return dir
|
||||
}
|
||||
}
|
|
@ -0,0 +1,417 @@
|
|||
/*
|
||||
* 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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* 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,7 +1,7 @@
|
|||
/*
|
||||
* paperweight is a Gradle plugin for the PaperMC project.
|
||||
*
|
||||
* Copyright (c) 2020 Kyle Wood (DemonWav)
|
||||
* Copyright (c) 2023 Kyle Wood (DenWav)
|
||||
* Contributors
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
|
@ -23,13 +23,15 @@
|
|||
package io.papermc.paperweight.tasks.patchremap
|
||||
|
||||
import io.papermc.paperweight.PaperweightException
|
||||
import io.papermc.paperweight.util.Git
|
||||
import java.io.File
|
||||
import io.papermc.paperweight.util.*
|
||||
import java.nio.file.Path
|
||||
import kotlin.io.path.*
|
||||
|
||||
class PatchApplier(
|
||||
private val remappedBranch: String,
|
||||
private val unmappedBranch: String,
|
||||
targetDir: File
|
||||
private val ignoreGitIgnore: Boolean,
|
||||
targetDir: Path
|
||||
) {
|
||||
|
||||
private val git = Git(targetDir)
|
||||
|
@ -50,63 +52,73 @@ class PatchApplier(
|
|||
git("checkout", unmappedBranch).executeSilently()
|
||||
}
|
||||
|
||||
fun commitInitialSource() {
|
||||
fun commitPlain(message: String) {
|
||||
git(*Git.add(ignoreGitIgnore, ".")).executeSilently()
|
||||
git("commit", "-m", message, "--author=Initial <auto@mated.null>").executeSilently()
|
||||
}
|
||||
|
||||
fun createBranches() {
|
||||
git("checkout", "-b", unmappedBranch).executeSilently()
|
||||
git("add", ".").executeSilently()
|
||||
git("commit", "-m", "Initial Source", "--author=Initial <auto@mated.null>").executeSilently()
|
||||
git("branch", remappedBranch).executeSilently()
|
||||
}
|
||||
|
||||
fun commitInitialRemappedSource() {
|
||||
git("add", ".").executeSilently()
|
||||
git(*Git.add(ignoreGitIgnore, ".")).executeSilently()
|
||||
git("commit", "-m", "Initial Remapped Source", "--author=Initial <auto@mated.null>").executeSilently()
|
||||
git("tag", remappedBaseTag)
|
||||
git("tag", remappedBaseTag).executeSilently()
|
||||
}
|
||||
|
||||
// fun commitRemappingDifferences(remapper: PatchSourceRemapWorker) {
|
||||
// checkoutRemapped() // Switch to remapped branch without checkout out files
|
||||
// remapper.remap() // Remap to new mappings
|
||||
// println("Committing remap")
|
||||
// git("add", ".").executeSilently()
|
||||
// git("commit", "-m", "Remap", "--author=Initial <auto@mated.null>").executeSilently()
|
||||
// checkoutOld()
|
||||
// }
|
||||
|
||||
fun recordCommit() {
|
||||
commitMessage = git("log", "--format=%B", "-n", "1", "HEAD").getText()
|
||||
commitAuthor = git("log", "--format=%an <%ae>", "-n", "1", "HEAD").getText()
|
||||
commitTime = git("log", "--format=%aD", "-n", "1", "HEAD").getText()
|
||||
}
|
||||
|
||||
private fun clearCommit() {
|
||||
commitMessage = null
|
||||
commitAuthor = null
|
||||
commitTime = null
|
||||
}
|
||||
|
||||
fun commitChanges() {
|
||||
println("Committing remapped changes to $remappedBranch")
|
||||
val message = commitMessage ?: throw PaperweightException("commitMessage not set")
|
||||
val author = commitAuthor ?: throw PaperweightException("commitAuthor not set")
|
||||
val time = commitTime ?: throw PaperweightException("commitTime not set")
|
||||
commitMessage = null
|
||||
commitAuthor = null
|
||||
commitTime = null
|
||||
clearCommit()
|
||||
|
||||
git("add", ".").executeSilently()
|
||||
git(*Git.add(ignoreGitIgnore, ".")).executeSilently()
|
||||
git("commit", "-m", message, "--author=$author", "--date=$time").execute()
|
||||
}
|
||||
|
||||
fun applyPatch(patch: File) {
|
||||
println("Applying patch ${patch.name}")
|
||||
val result = git("am", "--3way", "--ignore-whitespace", patch.absolutePath).runOut()
|
||||
fun applyPatch(patch: Path) {
|
||||
val result = git("am", "--3way", "--ignore-whitespace", patch.absolutePathString()).runOut()
|
||||
if (result != 0) {
|
||||
System.err.println("Patch failed to apply: $patch")
|
||||
throw RuntimeException("Patch failed to apply: $patch")
|
||||
}
|
||||
}
|
||||
|
||||
fun generatePatches(target: File) {
|
||||
target.deleteRecursively()
|
||||
target.mkdirs()
|
||||
fun generatePatches(target: Path) {
|
||||
target.deleteRecursive()
|
||||
target.createDirectories()
|
||||
git("checkout", remappedBranch).executeSilently()
|
||||
git(
|
||||
"format-patch", "--zero-commit", "--full-index", "--no-signature", "--no-stat", "-N", "-o",
|
||||
target.absolutePath, remappedBaseTag
|
||||
).runOut()
|
||||
"format-patch", "--diff-algorith=myers", "--zero-commit", "--full-index", "--no-signature", "--no-stat", "-N", "-o",
|
||||
target.absolutePathString(), remappedBaseTag
|
||||
).executeOut()
|
||||
}
|
||||
|
||||
fun isUnfinishedPatch(): Boolean {
|
||||
if (git("branch", "--show-current").getText().trim() != unmappedBranch) {
|
||||
return false
|
||||
}
|
||||
|
||||
git("update-index", "--refresh").executeSilently()
|
||||
if (git("diff-index", "--diff-algorithm=myers", "--quiet", "HEAD", "--").runSilently() == 0) {
|
||||
return git("log", unmappedBranch, "-1", "--pretty=%B").getText().trim() !=
|
||||
git("log", remappedBranch, "-1", "--pretty=%B").getText().trim()
|
||||
}
|
||||
|
||||
throw PaperweightException("Unknown state: repo has uncommitted changes")
|
||||
}
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* paperweight is a Gradle plugin for the PaperMC project.
|
||||
*
|
||||
* Copyright (c) 2020 Kyle Wood (DemonWav)
|
||||
* Copyright (c) 2023 Kyle Wood (DenWav)
|
||||
* Contributors
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
|
@ -22,9 +22,10 @@
|
|||
|
||||
package io.papermc.paperweight.tasks.patchremap
|
||||
|
||||
import io.papermc.paperweight.util.Constants
|
||||
import java.nio.file.Files
|
||||
import io.papermc.paperweight.util.*
|
||||
import io.papermc.paperweight.util.constants.*
|
||||
import java.nio.file.Path
|
||||
import kotlin.io.path.*
|
||||
import org.cadixdev.at.AccessTransformSet
|
||||
import org.cadixdev.lorenz.MappingSet
|
||||
import org.cadixdev.mercury.Mercury
|
||||
|
@ -57,7 +58,7 @@ class PatchSourceRemapWorker(
|
|||
fun remap() {
|
||||
setup()
|
||||
|
||||
println("mapping to ${Constants.DEOBF_NAMESPACE}")
|
||||
println("mapping to $DEOBF_NAMESPACE")
|
||||
|
||||
merc.rewrite(inputDir, outputDir)
|
||||
|
||||
|
@ -65,20 +66,13 @@ class PatchSourceRemapWorker(
|
|||
}
|
||||
|
||||
private fun setup() {
|
||||
outputDir.deleteRecursively()
|
||||
Files.createDirectories(outputDir)
|
||||
outputDir.deleteRecursive()
|
||||
outputDir.createDirectories()
|
||||
}
|
||||
|
||||
private fun cleanup() {
|
||||
inputDir.deleteRecursively()
|
||||
Files.move(outputDir, inputDir)
|
||||
outputDir.deleteRecursively()
|
||||
}
|
||||
|
||||
private fun Path.deleteRecursively() {
|
||||
if (Files.notExists(this)) {
|
||||
return
|
||||
}
|
||||
Files.walk(this).use { stream -> stream.sorted(Comparator.reverseOrder()).forEach(Files::delete) }
|
||||
inputDir.deleteRecursive()
|
||||
outputDir.moveTo(inputDir)
|
||||
outputDir.deleteRecursive()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,296 @@
|
|||
/*
|
||||
* 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,7 +1,7 @@
|
|||
/*
|
||||
* paperweight is a Gradle plugin for the PaperMC project.
|
||||
*
|
||||
* Copyright (c) 2020 Kyle Wood (DemonWav)
|
||||
* Copyright (c) 2023 Kyle Wood (DenWav)
|
||||
* Contributors
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
|
@ -25,11 +25,9 @@ package io.papermc.paperweight.util
|
|||
data class BuildDataInfo(
|
||||
val minecraftVersion: String,
|
||||
val serverUrl: String,
|
||||
val minecraftHash: String,
|
||||
val accessTransforms: String,
|
||||
val classMappings: String,
|
||||
val memberMappings: String,
|
||||
val packageMappings: String,
|
||||
val classMapCommand: String,
|
||||
val memberMapCommand: String,
|
||||
val finalMapCommand: String,
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* paperweight is a Gradle plugin for the PaperMC project.
|
||||
*
|
||||
* Copyright (c) 2020 Kyle Wood (DemonWav)
|
||||
* Copyright (c) 2023 Kyle Wood (DenWav)
|
||||
* Contributors
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
|
@ -22,15 +22,4 @@
|
|||
|
||||
package io.papermc.paperweight.util
|
||||
|
||||
data class MinecraftManifest(
|
||||
internal val latest: Map<String, *>,
|
||||
internal val versions: List<ManifestVersion>
|
||||
)
|
||||
|
||||
data class ManifestVersion(
|
||||
internal val id: String,
|
||||
internal val type: String,
|
||||
internal val time: String,
|
||||
internal val releaseTime: String,
|
||||
internal val url: String
|
||||
)
|
||||
data class ClassNameChange(val obfName: String, val deobfName: String)
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* paperweight is a Gradle plugin for the PaperMC project.
|
||||
*
|
||||
* Copyright (c) 2023 Kyle Wood (DenWav)
|
||||
* Contributors
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation;
|
||||
* version 2.1 only, no later versions.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
|
||||
* USA
|
||||
*/
|
||||
|
||||
package io.papermc.paperweight.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)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,93 @@
|
|||
/*
|
||||
* 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"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,101 @@
|
|||
/*
|
||||
* paperweight is a Gradle plugin for the PaperMC project.
|
||||
*
|
||||
* Copyright (c) 2023 Kyle Wood (DenWav)
|
||||
* Contributors
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation;
|
||||
* version 2.1 only, no later versions.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
|
||||
* USA
|
||||
*/
|
||||
|
||||
package io.papermc.paperweight.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())
|
||||
}
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* 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,7 +1,7 @@
|
|||
/*
|
||||
* paperweight is a Gradle plugin for the PaperMC project.
|
||||
*
|
||||
* Copyright (c) 2020 Kyle Wood (DemonWav)
|
||||
* Copyright (c) 2023 Kyle Wood (DenWav)
|
||||
* Contributors
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
267
nugget-lib/src/main/kotlin/io/papermc/paperweight/util/McDev.kt
Normal file
267
nugget-lib/src/main/kotlin/io/papermc/paperweight/util/McDev.kt
Normal file
|
@ -0,0 +1,267 @@
|
|||
/*
|
||||
* 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)
|
||||
}
|
|
@ -0,0 +1,88 @@
|
|||
/*
|
||||
* 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue