mirror of
https://github.com/SquidDev-CC/CC-Tweaked
synced 2025-10-16 14:37:39 +00:00
Compare commits
396 Commits
v1.20.1-1.
...
v1.21.7-1.
Author | SHA1 | Date | |
---|---|---|---|
![]() |
67412a2b72 | ||
![]() |
76869593f0 | ||
![]() |
fbf994e803 | ||
![]() |
8344c0a5c2 | ||
![]() |
a292d33830 | ||
![]() |
341d1c7bc2 | ||
![]() |
846f9dff03 | ||
![]() |
64d10ad45b | ||
![]() |
531eacfac7 | ||
![]() |
1f3da5205c | ||
![]() |
e3fecb013a | ||
![]() |
798ceefafe | ||
![]() |
7c0f79fc3c | ||
![]() |
b35cefc5dd | ||
![]() |
ec3dd328b3 | ||
![]() |
f3f43191ab | ||
![]() |
89dd521930 | ||
![]() |
9272e2efcd | ||
![]() |
69353a4fcf | ||
![]() |
ff363dca5a | ||
![]() |
1c51282426 | ||
![]() |
4a3a1c9275 | ||
![]() |
2557dd0af9 | ||
![]() |
b5c0c6e104 | ||
![]() |
876fd8ddb8 | ||
![]() |
ee3b1343b5 | ||
![]() |
b440b964b7 | ||
![]() |
5dfc401b45 | ||
![]() |
0790a8346a | ||
![]() |
418c9be7ac | ||
![]() |
b491f6b11f | ||
![]() |
acafc06449 | ||
![]() |
598fd98a8b | ||
![]() |
e13e8ff92e | ||
![]() |
0a0c80db41 | ||
![]() |
4344c3072f | ||
![]() |
c20336286b | ||
![]() |
356366ede8 | ||
![]() |
a1df196673 | ||
![]() |
947001104d | ||
![]() |
8711512769 | ||
![]() |
a939ad8b97 | ||
![]() |
fdae94b3c1 | ||
![]() |
9c0ce27ce6 | ||
![]() |
c458360b18 | ||
![]() |
09ad6c1905 | ||
![]() |
0e1e8a72d3 | ||
![]() |
995a6e7379 | ||
![]() |
ffa6eadc26 | ||
![]() |
7c1e8e1951 | ||
![]() |
b805a34c2d | ||
![]() |
b03546a158 | ||
![]() |
582713467f | ||
![]() |
b6f41a0df5 | ||
![]() |
594738a022 | ||
![]() |
27f2ab364c | ||
![]() |
5a43273757 | ||
![]() |
97e28516fb | ||
![]() |
676fb5fb53 | ||
![]() |
08dc08b5a3 | ||
![]() |
8f4d4038f6 | ||
![]() |
63ba3fe274 | ||
![]() |
749b3df227 | ||
![]() |
b97634b717 | ||
![]() |
8ade1c38ac | ||
![]() |
1b8344d0a3 | ||
![]() |
b42bc0a01a | ||
![]() |
70a7478529 | ||
![]() |
0cff73e2fc | ||
![]() |
05163a4911 | ||
![]() |
a892739f8e | ||
![]() |
9277aa33e9 | ||
![]() |
f8785a092f | ||
![]() |
598fc4aefd | ||
![]() |
dd7e8fcefc | ||
![]() |
29c8f96912 | ||
![]() |
b9267ecbfc | ||
![]() |
9d2c2db22b | ||
![]() |
6660966320 | ||
![]() |
3acb231f01 | ||
![]() |
16324e1eac | ||
![]() |
fa33949113 | ||
![]() |
0c04d9de47 | ||
![]() |
32f5c38485 | ||
![]() |
01fe949b3e | ||
![]() |
c03fce275e | ||
![]() |
0998acaa82 | ||
![]() |
12a44fed6f | ||
![]() |
3f8c3b026a | ||
![]() |
0a8d505323 | ||
![]() |
237a0ac3bb | ||
![]() |
b185d088b3 | ||
![]() |
051c70a731 | ||
![]() |
2e2f308ff3 | ||
![]() |
0f123b5efd | ||
![]() |
1278246cf7 | ||
![]() |
88cb03be6b | ||
![]() |
1e25fa9bc3 | ||
![]() |
74f707aaea | ||
![]() |
9bb62b047a | ||
![]() |
4360485880 | ||
![]() |
b69a44a927 | ||
![]() |
7d8f609c49 | ||
![]() |
e7f56c4d25 | ||
![]() |
fa2140d00b | ||
![]() |
03388149b1 | ||
![]() |
f212861370 | ||
![]() |
4f3663ccc9 | ||
![]() |
53425c1e76 | ||
![]() |
55edced9de | ||
![]() |
dc969c5a78 | ||
![]() |
94ad6dab0e | ||
![]() |
938eb38ad5 | ||
![]() |
6739c4c6c0 | ||
![]() |
d6749f8461 | ||
![]() |
d697c47b80 | ||
![]() |
5ba7f99326 | ||
![]() |
4710ee5bcc | ||
![]() |
62c9e5b08f | ||
![]() |
2ca5850060 | ||
![]() |
ed631b05e7 | ||
![]() |
a2b9490d5c | ||
![]() |
8204944b5f | ||
![]() |
ef0af67e96 | ||
![]() |
9a914e75c4 | ||
![]() |
4a532952d4 | ||
![]() |
546577041b | ||
![]() |
f881c0ced0 | ||
![]() |
0b389e04b0 | ||
![]() |
d3a3ab3c21 | ||
![]() |
22e6c06e59 | ||
![]() |
7337b91692 | ||
![]() |
3c46b8acd7 | ||
![]() |
d9fc1c3a80 | ||
![]() |
479aabdd09 | ||
![]() |
ad74893058 | ||
![]() |
2ba6d5815b | ||
![]() |
7e2f490626 | ||
![]() |
4dc649d5e5 | ||
![]() |
5bab415790 | ||
![]() |
f04c699df6 | ||
![]() |
9bbf3f3e1d | ||
![]() |
a3f8e653d4 | ||
![]() |
7c02979c22 | ||
![]() |
fdb65c9368 | ||
![]() |
ea670cc358 | ||
![]() |
b7396f3796 | ||
![]() |
1963e0160f | ||
![]() |
9a06904634 | ||
![]() |
5eb50ecb06 | ||
![]() |
5e24ad17d7 | ||
![]() |
8b1cb09ddf | ||
![]() |
7af2c14327 | ||
![]() |
d1a6b043c2 | ||
![]() |
cddb8fec11 | ||
![]() |
1d7d8006d4 | ||
![]() |
63bdc2537c | ||
![]() |
f776b17150 | ||
![]() |
0056709999 | ||
![]() |
31da2555cb | ||
![]() |
9b19a93ab9 | ||
![]() |
0c8e757314 | ||
![]() |
f39e86bb10 | ||
![]() |
ad52117f0f | ||
![]() |
bdffabc08e | ||
![]() |
87ce41f251 | ||
![]() |
e7c7919cad | ||
![]() |
4f66ac79d3 | ||
![]() |
ba6da3bc6c | ||
![]() |
b742745854 | ||
![]() |
3293639adf | ||
![]() |
064ff31830 | ||
![]() |
5d473725d5 | ||
![]() |
97a2f2dbdd | ||
![]() |
37c4789fa4 | ||
![]() |
0aaeeeee24 | ||
![]() |
2155ec3d63 | ||
![]() |
0da906fc93 | ||
![]() |
9e5e6a1b60 | ||
![]() |
dcc74e15c7 | ||
![]() |
c271ed7c7f | ||
![]() |
d6a246c122 | ||
![]() |
0bef3ee0d8 | ||
![]() |
bb04df7086 | ||
![]() |
a70baf0d74 | ||
![]() |
86e2f92493 | ||
![]() |
e9aceca1de | ||
![]() |
3042950507 | ||
![]() |
63181e73a1 | ||
![]() |
4f3247a0e2 | ||
![]() |
f7a6aac657 | ||
![]() |
782564e6ab | ||
![]() |
6b8ba8b80b | ||
![]() |
52b76d8886 | ||
![]() |
ba36c69583 | ||
![]() |
370e5f92a0 | ||
![]() |
36d05e4774 | ||
![]() |
89d1be17c9 | ||
![]() |
0069591af9 | ||
![]() |
c36c8605bf | ||
![]() |
3c72a00d46 | ||
![]() |
58aefc8df8 | ||
![]() |
97ddfc2794 | ||
![]() |
4f15f4197b | ||
![]() |
6e4ec86586 | ||
![]() |
0d8ac304c7 | ||
![]() |
fdd5f49369 | ||
![]() |
d24984c1d5 | ||
![]() |
8080dcdd9e | ||
![]() |
d7cea55e2a | ||
![]() |
9b2f974a81 | ||
![]() |
43770fa9bd | ||
![]() |
80c7a54ad4 | ||
![]() |
e57b6fede2 | ||
![]() |
34a2fd039f | ||
![]() |
3299d0e72a | ||
![]() |
b89e2615db | ||
![]() |
cdcd82679c | ||
![]() |
cdfa866760 | ||
![]() |
aa8078ddeb | ||
![]() |
7e53c19d74 | ||
![]() |
b7a8432cfb | ||
![]() |
356c8e8aeb | ||
![]() |
ed283155f7 | ||
![]() |
87dfad026e | ||
![]() |
bb97c465d9 | ||
![]() |
8bd4c3370e | ||
![]() |
3eb84ffedd | ||
![]() |
9484315d37 | ||
![]() |
be59f1a875 | ||
![]() |
bfb28b4710 | ||
![]() |
216f0adb3c | ||
![]() |
dad6874638 | ||
![]() |
77af4bc213 | ||
![]() |
5abab982c7 | ||
![]() |
764e1aa332 | ||
![]() |
c47718b09d | ||
![]() |
45cb597ecc | ||
![]() |
08d4f91c8b | ||
![]() |
b9eac4e509 | ||
![]() |
16577783d3 | ||
![]() |
c179da28f0 | ||
![]() |
dc3d8ea198 | ||
![]() |
cbe075b001 | ||
![]() |
ed0b156e05 | ||
![]() |
2765abf971 | ||
![]() |
4dd0735066 | ||
![]() |
38e516d7c7 | ||
![]() |
70a31855ac | ||
![]() |
6c8e64ffcd | ||
![]() |
7285c32d58 | ||
![]() |
99c60ac54b | ||
![]() |
63e40cf3cb | ||
![]() |
1d45935a25 | ||
![]() |
f80373e7a2 | ||
![]() |
63185629b7 | ||
![]() |
4bfb9ac323 | ||
![]() |
5926b6c994 | ||
![]() |
f5ed43584d | ||
![]() |
2c740bb904 | ||
![]() |
d77f5f135f | ||
![]() |
0e4710a956 | ||
![]() |
aca1d43550 | ||
![]() |
f10e401aea | ||
![]() |
7744d2663b | ||
![]() |
4566cb8273 | ||
![]() |
052e7a7ae5 | ||
![]() |
0895200681 | ||
![]() |
1a1623075f | ||
![]() |
54a95e07a4 | ||
![]() |
09d0f563b7 | ||
![]() |
e188f1d3fa | ||
![]() |
efd9a0f315 | ||
![]() |
28f75a0687 | ||
![]() |
819a4f7231 | ||
![]() |
898cb2a95d | ||
![]() |
03a8f83191 | ||
![]() |
aef92c8ebc | ||
![]() |
571ea794a8 | ||
![]() |
4b102f16b3 | ||
![]() |
e81af93043 | ||
![]() |
bb933d0100 | ||
![]() |
25b8a65c5c | ||
![]() |
e4236824d7 | ||
![]() |
cfd11ffa92 | ||
![]() |
ce133a5e66 | ||
![]() |
038fbc1ed1 | ||
![]() |
c582fb521c | ||
![]() |
af21792844 | ||
![]() |
9fbb1070ef | ||
![]() |
1944995c33 | ||
![]() |
ac851a795b | ||
![]() |
334761788a | ||
![]() |
5af3e15dd5 | ||
![]() |
de078e3037 | ||
![]() |
209b1ddbf9 | ||
![]() |
0c9f9a8652 | ||
![]() |
862d92785e | ||
![]() |
d48b85d50c | ||
![]() |
4d619de357 | ||
![]() |
57c289f173 | ||
![]() |
f63f85921f | ||
![]() |
c7e49d1929 | ||
![]() |
eb584aa94d | ||
![]() |
ad70e2ad90 | ||
![]() |
d9b0cc7075 | ||
![]() |
2c0d8263d3 | ||
![]() |
1e214f329e | ||
![]() |
de930c8d09 | ||
![]() |
94c864759d | ||
![]() |
735e7ce09b | ||
![]() |
2226df7224 | ||
![]() |
959bdaeb61 | ||
![]() |
06ac373e83 | ||
![]() |
0aca6a4dc9 | ||
![]() |
bf203bb1f3 | ||
![]() |
6e9799316a | ||
![]() |
cd9840d1c1 | ||
![]() |
b9a002586c | ||
![]() |
a3b07909b0 | ||
![]() |
d7786ee4b9 | ||
![]() |
4e90240922 | ||
![]() |
1a87d1bf45 | ||
![]() |
188806e8b0 | ||
![]() |
01407544c9 | ||
![]() |
bd2fd9d4c8 | ||
![]() |
00e2e2bd2d | ||
![]() |
7c1f40031b | ||
![]() |
929debd382 | ||
![]() |
4980b7355d | ||
![]() |
5c457950d8 | ||
![]() |
925092add3 | ||
![]() |
550296edc5 | ||
![]() |
0771c4891b | ||
![]() |
776fa00b94 | ||
![]() |
03bb279206 | ||
![]() |
fabd77132d | ||
![]() |
95be0a25bf | ||
![]() |
75f3ecce18 | ||
![]() |
688fdc40a6 | ||
![]() |
22bd5309ba | ||
![]() |
ad49325376 | ||
![]() |
825d45eb26 | ||
![]() |
8b2516abb5 | ||
![]() |
bce099ef32 | ||
![]() |
6d14ce625f | ||
![]() |
c8eadf4011 | ||
![]() |
0c1ab780bb | ||
![]() |
0f623c2cca | ||
![]() |
b9ba2534a4 | ||
![]() |
c764981a40 | ||
![]() |
6363164f2b | ||
![]() |
63580b4acb | ||
![]() |
9af1aa1ecf | ||
![]() |
ad0f551204 | ||
![]() |
0d3e00cc41 | ||
![]() |
836d6b939e | ||
![]() |
0e5248e5e6 | ||
![]() |
e154b0db2a | ||
![]() |
ae767eb5be | ||
![]() |
c50d56d9fa | ||
![]() |
777aa34bb0 | ||
![]() |
286f969f94 | ||
![]() |
7b9a156abc | ||
![]() |
0a9e5c78f3 | ||
![]() |
da5885ef35 | ||
![]() |
57c72711bb | ||
![]() |
cbafbca86b | ||
![]() |
c9caffb10f | ||
![]() |
4675583e1c | ||
![]() |
afe16cc593 | ||
![]() |
0abd107348 | ||
![]() |
cef4b4906b | ||
![]() |
04900dc82f | ||
![]() |
9b63cc81b1 | ||
![]() |
9eead7a0ec | ||
![]() |
ad97b2922b | ||
![]() |
52986f8d73 | ||
![]() |
ab00580389 | ||
![]() |
128ac2f109 | ||
![]() |
5d8c46c7e6 | ||
![]() |
1a5dc92bd4 | ||
![]() |
98b2d3f310 | ||
![]() |
e92c2d02f8 | ||
![]() |
f8ef40d378 | ||
![]() |
61f9b1d0c6 | ||
![]() |
ffb62dfa02 | ||
![]() |
6fb291112d | ||
![]() |
7ee821e9c9 | ||
![]() |
b7df91349a | ||
![]() |
cb8e06af2a | ||
![]() |
6478fca7a2 | ||
![]() |
240528cce5 | ||
![]() |
83f1f86888 | ||
![]() |
9c202bd1c2 | ||
![]() |
fc834cd97f |
@@ -18,11 +18,6 @@ ij_any_if_brace_force = if_multiline
|
|||||||
ij_any_for_brace_force = if_multiline
|
ij_any_for_brace_force = if_multiline
|
||||||
ij_any_spaces_within_array_initializer_braces = true
|
ij_any_spaces_within_array_initializer_braces = true
|
||||||
|
|
||||||
ij_kotlin_allow_trailing_comma = true
|
|
||||||
ij_kotlin_allow_trailing_comma_on_call_site = true
|
|
||||||
ij_kotlin_method_parameters_wrap = off
|
|
||||||
ij_kotlin_call_parameters_wrap = off
|
|
||||||
|
|
||||||
[*.md]
|
[*.md]
|
||||||
trim_trailing_whitespace = false
|
trim_trailing_whitespace = false
|
||||||
|
|
||||||
@@ -31,3 +26,27 @@ indent_size = 2
|
|||||||
|
|
||||||
[*.yml]
|
[*.yml]
|
||||||
indent_size = 2
|
indent_size = 2
|
||||||
|
|
||||||
|
[*.{kt,kts}]
|
||||||
|
ij_kotlin_code_style_defaults = KOTLIN_OFFICIAL
|
||||||
|
ij_kotlin_continuation_indent_size = 4
|
||||||
|
ij_kotlin_spaces_around_equality_operators = true
|
||||||
|
|
||||||
|
ij_kotlin_allow_trailing_comma = true
|
||||||
|
ij_kotlin_allow_trailing_comma_on_call_site = true
|
||||||
|
|
||||||
|
# Prefer to handle these manually
|
||||||
|
ij_kotlin_method_parameters_wrap = off
|
||||||
|
ij_kotlin_call_parameters_wrap = off
|
||||||
|
ij_kotlin_extends_list_wrap = off
|
||||||
|
|
||||||
|
ktlint_code_style = intellij_idea
|
||||||
|
ktlint_standard_class-naming = disabled
|
||||||
|
ktlint_standard_class-signature = disabled
|
||||||
|
ktlint_standard_function-naming = disabled
|
||||||
|
ktlint_standard_no-wildcard-imports = disabled
|
||||||
|
|
||||||
|
# FIXME: These two are disable right now as they're over-eager in putting things
|
||||||
|
# on the same line. We should set max_line_length and handle this properly.
|
||||||
|
ktlint_standard_function-signature = disabled
|
||||||
|
ktlint_standard_function-expression-body = disabled
|
||||||
|
19
.github/ISSUE_TEMPLATE/bug_report.yaml
vendored
19
.github/ISSUE_TEMPLATE/bug_report.yaml
vendored
@@ -1,17 +1,17 @@
|
|||||||
name: Bug report
|
name: Bug report
|
||||||
description: Report some misbehaviour in the mod
|
description: Report some misbehaviour in the mod
|
||||||
labels: [ bug ]
|
labels: [ bug ]
|
||||||
|
type: bug
|
||||||
body:
|
body:
|
||||||
- type: dropdown
|
- type: dropdown
|
||||||
id: mc-version
|
id: mc-version
|
||||||
attributes:
|
attributes:
|
||||||
label: Minecraft Version
|
label: Minecraft Version
|
||||||
description: What version of Minecraft are you using?
|
description: |
|
||||||
|
What version of Minecraft are you using? If your version is not listed, please try to reproduce on one of the supported versions.
|
||||||
options:
|
options:
|
||||||
- 1.16.x
|
- 1.20.1
|
||||||
- 1.18.x
|
- 1.21.x
|
||||||
- 1.19.x
|
|
||||||
- 1.20.x
|
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: input
|
- type: input
|
||||||
@@ -28,8 +28,7 @@ body:
|
|||||||
label: Details
|
label: Details
|
||||||
description: |
|
description: |
|
||||||
Description of the bug. Please include the following:
|
Description of the bug. Please include the following:
|
||||||
- Logs: These will be located in the `logs/` directory of your Minecraft
|
- Logs: These will be located in the `logs/` directory of your Minecraft instance. This is always useful, even if it doesn't include errors, so please upload this!
|
||||||
instance. Please upload them as a gist or directly into this editor.
|
- Detailed reproduction steps: sometimes I can spot a bug pretty easily, but often it's much more obscure. The more information I have to help reproduce it, the quicker it'll get fixed.
|
||||||
- Detailed reproduction steps: sometimes I can spot a bug pretty easily,
|
|
||||||
but often it's much more obscure. The more information I have to help
|

|
||||||
reproduce it, the quicker it'll get fixed.
|
|
||||||
|
1
.github/ISSUE_TEMPLATE/feature_request.md
vendored
1
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@@ -2,6 +2,7 @@
|
|||||||
name: Feature request
|
name: Feature request
|
||||||
about: Suggest an idea or improvement
|
about: Suggest an idea or improvement
|
||||||
labels: enhancement
|
labels: enhancement
|
||||||
|
type: feature
|
||||||
---
|
---
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
|
72
.github/workflows/main-ci.yml
vendored
72
.github/workflows/main-ci.yml
vendored
@@ -9,16 +9,16 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: 📥 Clone repository
|
- name: 📥 Clone repository
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: 📥 Set up Java
|
- name: 📥 Set up Java
|
||||||
uses: actions/setup-java@v3
|
uses: actions/setup-java@v4
|
||||||
with:
|
with:
|
||||||
java-version: 17
|
java-version: 21
|
||||||
distribution: 'temurin'
|
distribution: 'temurin'
|
||||||
|
|
||||||
- name: 📥 Setup Gradle
|
- name: 📥 Setup Gradle
|
||||||
uses: gradle/gradle-build-action@v2
|
uses: gradle/actions/setup-gradle@v3
|
||||||
with:
|
with:
|
||||||
cache-read-only: ${{ !startsWith(github.ref, 'refs/heads/mc-') }}
|
cache-read-only: ${{ !startsWith(github.ref, 'refs/heads/mc-') }}
|
||||||
|
|
||||||
@@ -30,8 +30,28 @@ jobs:
|
|||||||
- name: ⚒️ Build
|
- name: ⚒️ Build
|
||||||
run: ./gradlew assemble || ./gradlew assemble
|
run: ./gradlew assemble || ./gradlew assemble
|
||||||
|
|
||||||
|
- name: 📦 Prepare Jars
|
||||||
|
run: |
|
||||||
|
# Find the main jar and append the git hash onto it.
|
||||||
|
mkdir -p jars
|
||||||
|
find projects/forge/build/libs projects/fabric/build/libs -type f -regex '.*[0-9.]+\(-SNAPSHOT\)?\.jar$' -exec bash -c 'cp {} "jars/$(basename {} .jar)-$(git rev-parse HEAD).jar"' \;
|
||||||
|
|
||||||
|
- name: 📤 Upload Jar
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: CC-Tweaked
|
||||||
|
path: ./jars
|
||||||
|
|
||||||
|
- name: Cache pre-commit
|
||||||
|
uses: actions/cache@v4
|
||||||
|
with:
|
||||||
|
path: ~/.cache/pre-commit
|
||||||
|
key: pre-commit-3|${{ env.pythonLocation }}|${{ hashFiles('.pre-commit-config.yaml') }}
|
||||||
|
|
||||||
- name: 💡 Lint
|
- name: 💡 Lint
|
||||||
uses: pre-commit/action@v3.0.0
|
run: |
|
||||||
|
pipx install pre-commit
|
||||||
|
pre-commit run --show-diff-on-failure --color=always
|
||||||
|
|
||||||
- name: 🧪 Run tests
|
- name: 🧪 Run tests
|
||||||
run: ./gradlew test validateMixinNames checkChangelog
|
run: ./gradlew test validateMixinNames checkChangelog
|
||||||
@@ -42,30 +62,10 @@ jobs:
|
|||||||
- name: 🧪 Run integration tests
|
- name: 🧪 Run integration tests
|
||||||
run: ./gradlew runGametest
|
run: ./gradlew runGametest
|
||||||
|
|
||||||
- name: 🧪 Run client tests
|
|
||||||
run: ./gradlew runGametestClient # Not checkClient, as no point running rendering tests.
|
|
||||||
# These are a little flaky on GH actions: its useful to run them, but don't break the build.
|
|
||||||
continue-on-error: true
|
|
||||||
|
|
||||||
- name: 🧪 Parse test reports
|
- name: 🧪 Parse test reports
|
||||||
run: ./tools/parse-reports.py
|
run: ./tools/parse-reports.py
|
||||||
if: ${{ failure() }}
|
if: ${{ failure() }}
|
||||||
|
|
||||||
- name: 📦 Prepare Jars
|
|
||||||
run: |
|
|
||||||
# Find the main jar and append the git hash onto it.
|
|
||||||
mkdir -p jars
|
|
||||||
find projects/forge/build/libs projects/fabric/build/libs -type f -regex '.*[0-9.]+\(-SNAPSHOT\)?\.jar$' -exec bash -c 'cp {} "jars/$(basename {} .jar)-$(git rev-parse HEAD).jar"' \;
|
|
||||||
|
|
||||||
- name: 📤 Upload Jar
|
|
||||||
uses: actions/upload-artifact@v3
|
|
||||||
with:
|
|
||||||
name: CC-Tweaked
|
|
||||||
path: ./jars
|
|
||||||
|
|
||||||
- name: 📤 Upload coverage
|
|
||||||
uses: codecov/codecov-action@v3
|
|
||||||
|
|
||||||
build-core:
|
build-core:
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
@@ -81,24 +81,28 @@ jobs:
|
|||||||
runs-on: ${{ matrix.uses }}
|
runs-on: ${{ matrix.uses }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Clone repository
|
- name: 📥 Clone repository
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Set up Java
|
- name: 📥 Set up Java
|
||||||
uses: actions/setup-java@v3
|
uses: actions/setup-java@v4
|
||||||
with:
|
with:
|
||||||
java-version: 17
|
java-version: 21
|
||||||
distribution: 'temurin'
|
distribution: 'temurin'
|
||||||
|
|
||||||
- name: Setup Gradle
|
- name: 📥 Setup Gradle
|
||||||
uses: gradle/gradle-build-action@v2
|
uses: gradle/actions/setup-gradle@v3
|
||||||
with:
|
with:
|
||||||
cache-read-only: ${{ !startsWith(github.ref, 'refs/heads/mc-') }}
|
cache-read-only: ${{ !startsWith(github.ref, 'refs/heads/mc-') }}
|
||||||
|
|
||||||
- name: Run tests
|
- name: ⚒️ Build
|
||||||
|
run: |
|
||||||
|
./gradlew --configure-on-demand :core:assemble
|
||||||
|
|
||||||
|
- name: 🧪 Run tests
|
||||||
run: |
|
run: |
|
||||||
./gradlew --configure-on-demand :core:test
|
./gradlew --configure-on-demand :core:test
|
||||||
|
|
||||||
- name: Parse test reports
|
- name: 🧪 Parse test reports
|
||||||
run: python3 ./tools/parse-reports.py
|
run: python3 ./tools/parse-reports.py
|
||||||
if: ${{ failure() }}
|
if: ${{ failure() }}
|
||||||
|
19
.github/workflows/make-doc.sh
vendored
19
.github/workflows/make-doc.sh
vendored
@@ -1,19 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
set -eu
|
|
||||||
|
|
||||||
DEST="${GITHUB_REF#refs/*/}"
|
|
||||||
echo "Uploading docs to https://tweaked.cc/$DEST"
|
|
||||||
|
|
||||||
# Setup ssh key
|
|
||||||
mkdir -p "$HOME/.ssh/"
|
|
||||||
echo "$SSH_KEY" > "$HOME/.ssh/key"
|
|
||||||
chmod 600 "$HOME/.ssh/key"
|
|
||||||
|
|
||||||
# And upload
|
|
||||||
rsync -avc -e "ssh -i $HOME/.ssh/key -o StrictHostKeyChecking=no -p $SSH_PORT" \
|
|
||||||
"$GITHUB_WORKSPACE/projects/web/build/site/" \
|
|
||||||
"$SSH_USER@$SSH_HOST:/$DEST"
|
|
||||||
rsync -avc -e "ssh -i $HOME/.ssh/key -o StrictHostKeyChecking=no -p $SSH_PORT" \
|
|
||||||
"$GITHUB_WORKSPACE/projects/common-api/build/docs/javadoc/" \
|
|
||||||
"$SSH_USER@$SSH_HOST:/$DEST/javadoc"
|
|
36
.github/workflows/make-doc.yml
vendored
36
.github/workflows/make-doc.yml
vendored
@@ -3,8 +3,7 @@ name: Build documentation
|
|||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- mc-1.19.x
|
- mc-*
|
||||||
- mc-1.20.x
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
make_doc:
|
make_doc:
|
||||||
@@ -12,30 +11,25 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Clone repository
|
- name: 📥 Clone repository
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Set up Java
|
- name: 📥 Set up Java
|
||||||
uses: actions/setup-java@v1
|
uses: actions/setup-java@v4
|
||||||
with:
|
with:
|
||||||
java-version: 17
|
java-version: 21
|
||||||
distribution: 'temurin'
|
distribution: 'temurin'
|
||||||
|
|
||||||
- name: Setup Gradle
|
- name: 📥 Setup Gradle
|
||||||
uses: gradle/gradle-build-action@v2
|
uses: gradle/actions/setup-gradle@v3
|
||||||
with:
|
with:
|
||||||
cache-read-only: ${{ !startsWith(github.ref, 'refs/heads/mc-') }}
|
cache-read-only: ${{ !startsWith(github.ref, 'refs/heads/mc-') }}
|
||||||
|
|
||||||
- name: Build with Gradle
|
- name: ⚒️ Generate documentation
|
||||||
run: ./gradlew compileJava --no-daemon || ./gradlew compileJava --no-daemon
|
run: ./gradlew docWebsite --no-daemon
|
||||||
|
|
||||||
- name: Generate documentation
|
- name: 📤 Upload Jar
|
||||||
run: ./gradlew docWebsite :common-api:javadoc --no-daemon
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
- name: Upload documentation
|
name: Documentation
|
||||||
run: .github/workflows/make-doc.sh 2> /dev/null
|
path: ./projects/web/build/site/
|
||||||
env:
|
|
||||||
SSH_KEY: ${{ secrets.SSH_KEY }}
|
|
||||||
SSH_USER: ${{ secrets.SSH_USER }}
|
|
||||||
SSH_HOST: ${{ secrets.SSH_HOST }}
|
|
||||||
SSH_PORT: ${{ secrets.SSH_PORT }}
|
|
||||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@@ -27,6 +27,7 @@
|
|||||||
*.iml
|
*.iml
|
||||||
.idea
|
.idea
|
||||||
.gradle
|
.gradle
|
||||||
|
.kotlin
|
||||||
*.DS_Store
|
*.DS_Store
|
||||||
|
|
||||||
/.classpath
|
/.classpath
|
||||||
|
26
.gitpod.yml
26
.gitpod.yml
@@ -1,26 +0,0 @@
|
|||||||
# SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
|
|
||||||
#
|
|
||||||
# SPDX-License-Identifier: MPL-2.0
|
|
||||||
|
|
||||||
image:
|
|
||||||
file: config/gitpod/Dockerfile
|
|
||||||
|
|
||||||
ports:
|
|
||||||
- port: 25565
|
|
||||||
onOpen: notify
|
|
||||||
|
|
||||||
vscode:
|
|
||||||
extensions:
|
|
||||||
- eamodio.gitlens
|
|
||||||
- github.vscode-pull-request-github
|
|
||||||
- ms-azuretools.vscode-docker
|
|
||||||
- redhat.java
|
|
||||||
- richardwillis.vscode-gradle
|
|
||||||
- vscjava.vscode-java-debug
|
|
||||||
- vscode.github
|
|
||||||
|
|
||||||
tasks:
|
|
||||||
- name: Setup pre-commit hool
|
|
||||||
init: pre-commit install --allow-missing-config
|
|
||||||
- name: Install npm packages
|
|
||||||
init: npm ci
|
|
@@ -6,7 +6,7 @@
|
|||||||
# See https://pre-commit.com/hooks.html for more hooks
|
# See https://pre-commit.com/hooks.html for more hooks
|
||||||
repos:
|
repos:
|
||||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||||
rev: v4.4.0
|
rev: v5.0.0
|
||||||
hooks:
|
hooks:
|
||||||
- id: trailing-whitespace
|
- id: trailing-whitespace
|
||||||
- id: end-of-file-fixer
|
- id: end-of-file-fixer
|
||||||
@@ -27,7 +27,7 @@ repos:
|
|||||||
exclude: "^(.*\\.(bat)|LICENSE)$"
|
exclude: "^(.*\\.(bat)|LICENSE)$"
|
||||||
|
|
||||||
- repo: https://github.com/fsfe/reuse-tool
|
- repo: https://github.com/fsfe/reuse-tool
|
||||||
rev: v2.1.0
|
rev: v5.0.2
|
||||||
hooks:
|
hooks:
|
||||||
- id: reuse
|
- id: reuse
|
||||||
|
|
||||||
@@ -58,6 +58,7 @@ repos:
|
|||||||
exclude: |
|
exclude: |
|
||||||
(?x)^(
|
(?x)^(
|
||||||
projects/[a-z]+/src/generated|
|
projects/[a-z]+/src/generated|
|
||||||
|
projects/[a-z]+/src/examples/generatedResources|
|
||||||
projects/core/src/test/resources/test-rom/data/json-parsing/|
|
projects/core/src/test/resources/test-rom/data/json-parsing/|
|
||||||
.*\.dfpwm
|
.*\.dfpwm
|
||||||
)
|
)
|
||||||
|
100
.reuse/dep5
100
.reuse/dep5
@@ -1,100 +0,0 @@
|
|||||||
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
|
|
||||||
Source: https://github.com/cc-tweaked/cc-tweaked
|
|
||||||
Upstream-Name: CC: Tweaked
|
|
||||||
Upstream-Contact: Jonathan Coates <git@squiddev.cc>
|
|
||||||
|
|
||||||
Files:
|
|
||||||
projects/common/src/main/resources/assets/computercraft/sounds.json
|
|
||||||
projects/common/src/main/resources/assets/computercraft/sounds/empty.ogg
|
|
||||||
projects/common/src/testMod/resources/data/cctest/computercraft/turtle_upgrades/*
|
|
||||||
projects/common/src/testMod/resources/data/cctest/structures/*
|
|
||||||
projects/fabric/src/generated/*
|
|
||||||
projects/forge/src/generated/*
|
|
||||||
projects/web/src/htmlTransform/export/index.json
|
|
||||||
projects/web/src/htmlTransform/export/items/minecraft/*
|
|
||||||
Comment: Generated/data files are CC0.
|
|
||||||
Copyright: The CC: Tweaked Developers
|
|
||||||
License: CC0-1.0
|
|
||||||
|
|
||||||
Files:
|
|
||||||
doc/images/*
|
|
||||||
package.json
|
|
||||||
package-lock.json
|
|
||||||
projects/common/src/client/resources/computercraft-client.mixins.json
|
|
||||||
projects/common/src/main/resources/assets/minecraft/shaders/core/computercraft/monitor_tbo.json
|
|
||||||
projects/common/src/main/resources/computercraft.mixins.json
|
|
||||||
projects/common/src/testMod/resources/computercraft-gametest.mixins.json
|
|
||||||
projects/common/src/testMod/resources/data/computercraft/loot_tables/treasure_disk.json
|
|
||||||
projects/common/src/testMod/resources/pack.mcmeta
|
|
||||||
projects/core/src/main/resources/data/computercraft/lua/rom/modules/command/.ignoreme
|
|
||||||
projects/core/src/main/resources/data/computercraft/lua/rom/modules/main/.ignoreme
|
|
||||||
projects/core/src/main/resources/data/computercraft/lua/rom/modules/turtle/.ignoreme
|
|
||||||
projects/core/src/main/resources/data/computercraft/lua/rom/motd.txt
|
|
||||||
projects/fabric-api/src/main/modJson/fabric.mod.json
|
|
||||||
projects/fabric/src/client/resources/computercraft-client.fabric.mixins.json
|
|
||||||
projects/fabric/src/main/resources/computercraft.fabric.mixins.json
|
|
||||||
projects/fabric/src/main/resources/fabric.mod.json
|
|
||||||
projects/fabric/src/testMod/resources/computercraft-gametest.fabric.mixins.json
|
|
||||||
projects/fabric/src/testMod/resources/fabric.mod.json
|
|
||||||
projects/forge/src/client/resources/computercraft-client.forge.mixins.json
|
|
||||||
projects/web/src/frontend/mount/.settings
|
|
||||||
projects/web/src/frontend/mount/example.nfp
|
|
||||||
projects/web/src/frontend/mount/example.nft
|
|
||||||
projects/web/src/frontend/mount/expr_template.lua
|
|
||||||
projects/web/tsconfig.json
|
|
||||||
Comment: Several assets where it's inconvenient to create a .license file.
|
|
||||||
Copyright: The CC: Tweaked Developers
|
|
||||||
License: MPL-2.0
|
|
||||||
|
|
||||||
Files:
|
|
||||||
doc/logo.png
|
|
||||||
doc/logo-darkmode.png
|
|
||||||
projects/common/src/main/resources/assets/computercraft/models/*
|
|
||||||
projects/common/src/main/resources/assets/computercraft/textures/*
|
|
||||||
projects/common/src/main/resources/pack.mcmeta
|
|
||||||
projects/common/src/main/resources/pack.png
|
|
||||||
projects/core/src/main/resources/assets/computercraft/textures/gui/term_font.png
|
|
||||||
projects/core/src/main/resources/data/computercraft/lua/rom/autorun/.ignoreme
|
|
||||||
projects/core/src/main/resources/data/computercraft/lua/rom/help/*
|
|
||||||
projects/core/src/main/resources/data/computercraft/lua/rom/programs/fun/advanced/levels/*
|
|
||||||
projects/web/src/htmlTransform/export/items/computercraft/*
|
|
||||||
Comment: Bulk-license original assets as CCPL.
|
|
||||||
Copyright: 2011 Daniel Ratcliffe
|
|
||||||
License: LicenseRef-CCPL
|
|
||||||
|
|
||||||
Files:
|
|
||||||
projects/common/src/main/resources/assets/computercraft/lang/cs_cz.json
|
|
||||||
projects/common/src/main/resources/assets/computercraft/lang/ko_kr.json
|
|
||||||
projects/common/src/main/resources/assets/computercraft/lang/pl_pl.json
|
|
||||||
projects/common/src/main/resources/assets/computercraft/lang/pt_br.json
|
|
||||||
projects/common/src/main/resources/assets/computercraft/lang/ru_ru.json
|
|
||||||
projects/common/src/main/resources/assets/computercraft/lang/uk_ua.json
|
|
||||||
projects/common/src/main/resources/assets/computercraft/lang/zh_cn.json
|
|
||||||
Comment: Community-contributed license files
|
|
||||||
Copyright: 2017 The CC: Tweaked Developers
|
|
||||||
License: LicenseRef-CCPL
|
|
||||||
|
|
||||||
Files:
|
|
||||||
projects/common/src/main/resources/assets/computercraft/lang/*
|
|
||||||
Comment: Community-contributed license files
|
|
||||||
Copyright: 2017 The CC: Tweaked Developers
|
|
||||||
License: MPL-2.0
|
|
||||||
|
|
||||||
Files:
|
|
||||||
.github/*
|
|
||||||
Comment:
|
|
||||||
GitHub build scripts are CC0. While we could add a header to each file,
|
|
||||||
it's unclear if it will break actions or issue templates in some way.
|
|
||||||
Copyright: Jonathan Coates <git@squiddev.cc>
|
|
||||||
License: CC0-1.0
|
|
||||||
|
|
||||||
Files:
|
|
||||||
gradle/wrapper/*
|
|
||||||
gradlew
|
|
||||||
gradlew.bat
|
|
||||||
Copyright: Gradle Inc
|
|
||||||
License: Apache-2.0
|
|
||||||
|
|
||||||
Files: projects/core/src/test/resources/test-rom/data/json-parsing/*
|
|
||||||
Copyright: 2016 Nicolas Seriot
|
|
||||||
License: MIT
|
|
@@ -22,16 +22,15 @@ If you have a bug, suggestion, or other feedback, the best thing to do is [file
|
|||||||
use the issue templates - they provide a useful hint on what information to provide.
|
use the issue templates - they provide a useful hint on what information to provide.
|
||||||
|
|
||||||
## Translations
|
## Translations
|
||||||
Translations are managed through [Weblate], an online interface for managing language strings. This is synced
|
Translations are managed through [CrowdIn], an online interface for managing language strings.
|
||||||
automatically with GitHub, so please don't submit PRs adding/changing translations!
|
|
||||||
|
|
||||||
## Setting up a development environment
|
## Setting up a development environment
|
||||||
In order to develop CC: Tweaked, you'll need to download the source code and then run it.
|
In order to develop CC: Tweaked, you'll need to download the source code and then run it.
|
||||||
|
|
||||||
- Make sure you've got the following software installed:
|
- Make sure you've got the following software installed:
|
||||||
- Java Development Kit (JDK) installed. This can be downloaded from [Adoptium].
|
- Java Development Kit 21 (JDK). This can be downloaded from [Adoptium].
|
||||||
- [Git](https://git-scm.com/).
|
- [Git](https://git-scm.com/).
|
||||||
- If you want to work on documentation, [NodeJS][node].
|
- [NodeJS 20 or later][node].
|
||||||
|
|
||||||
- Download CC: Tweaked's source code:
|
- Download CC: Tweaked's source code:
|
||||||
```
|
```
|
||||||
@@ -49,9 +48,12 @@ If you want to run CC:T in a normal Minecraft instance, run `./gradlew assemble`
|
|||||||
`projects/forge/build/libs` (for Forge) or `projects/fabric/build/libs` (for Fabric).
|
`projects/forge/build/libs` (for Forge) or `projects/fabric/build/libs` (for Fabric).
|
||||||
|
|
||||||
## Developing CC: Tweaked
|
## Developing CC: Tweaked
|
||||||
Before making any major changes to CC: Tweaked, I'd recommend you have a read of the [the architecture
|
Before making any major changes to CC: Tweaked, I'd recommend starting opening an issue or starting a discussion on
|
||||||
document][architecture] first. While it's not a comprehensive document, it gives a good hint of where you should start
|
GitHub first. It's often helpful to discuss features before spending time developing them!
|
||||||
looking to make your changes. As always, if you're not sure, [do ask the community][community]!
|
|
||||||
|
Once you're ready to start programming, have a read of the [the architecture document][architecture] first. While it's
|
||||||
|
not a comprehensive document, it gives a good hint of where you should start looking to make your changes. As always, if
|
||||||
|
you're not sure, [do ask the community][community]!
|
||||||
|
|
||||||
### Testing
|
### Testing
|
||||||
When making larger changes, it may be useful to write a test to make sure your code works as expected.
|
When making larger changes, it may be useful to write a test to make sure your code works as expected.
|
||||||
@@ -86,8 +88,8 @@ You'll first need to [set up a development environment as above](#setting-up-a-d
|
|||||||
|
|
||||||
Once this is set up, you can now run `./gradlew docWebsite`. This generates documentation from our Lua and Java code,
|
Once this is set up, you can now run `./gradlew docWebsite`. This generates documentation from our Lua and Java code,
|
||||||
writing the resulting HTML into `./projects/web/build/site`, which can then be opened in a browser. When iterating on
|
writing the resulting HTML into `./projects/web/build/site`, which can then be opened in a browser. When iterating on
|
||||||
documentation, you can instead run `./gradlew docWebsite -t`, which will rebuild documentation every time you change a
|
documentation, you can instead run `./gradlew :web:assemble -x :web:compileTeaVM -t`, which will rebuild documentation
|
||||||
file.
|
every time you change a file.
|
||||||
|
|
||||||
Documentation is built using [illuaminate] which, while not currently documented (somewhat ironic), is largely the same
|
Documentation is built using [illuaminate] which, while not currently documented (somewhat ironic), is largely the same
|
||||||
as [ldoc][ldoc]. Documentation comments are written in Markdown, though note that we do not support many GitHub-specific
|
as [ldoc][ldoc]. Documentation comments are written in Markdown, though note that we do not support many GitHub-specific
|
||||||
@@ -101,10 +103,10 @@ about how you can build on that until you've covered everything!
|
|||||||
[community]: README.md#community "Get in touch with the community."
|
[community]: README.md#community "Get in touch with the community."
|
||||||
[Adoptium]: https://adoptium.net/temurin/releases?version=17 "Download OpenJDK 17"
|
[Adoptium]: https://adoptium.net/temurin/releases?version=17 "Download OpenJDK 17"
|
||||||
[illuaminate]: https://github.com/SquidDev/illuaminate/ "Illuaminate on GitHub"
|
[illuaminate]: https://github.com/SquidDev/illuaminate/ "Illuaminate on GitHub"
|
||||||
[weblate]: https://i18n.tweaked.cc/projects/cc-tweaked/minecraft/ "CC: Tweaked weblate instance"
|
|
||||||
[docs]: https://tweaked.cc/ "CC: Tweaked documentation"
|
[docs]: https://tweaked.cc/ "CC: Tweaked documentation"
|
||||||
[ldoc]: http://stevedonovan.github.io/ldoc/ "ldoc, a Lua documentation generator."
|
[ldoc]: http://stevedonovan.github.io/ldoc/ "ldoc, a Lua documentation generator."
|
||||||
[mc-test]: https://www.youtube.com/watch?v=vXaWOJTCYNg
|
[mc-test]: https://www.youtube.com/watch?v=vXaWOJTCYNg
|
||||||
[busted]: https://github.com/Olivine-Labs/busted "busted: Elegant Lua unit testing."
|
[busted]: https://github.com/Olivine-Labs/busted "busted: Elegant Lua unit testing."
|
||||||
[node]: https://nodejs.org/en/ "Node.js"
|
[node]: https://nodejs.org/en/ "Node.js"
|
||||||
[architecture]: projects/ARCHITECTURE.md
|
[architecture]: projects/ARCHITECTURE.md
|
||||||
|
[Crowdin]: https://crowdin.com/project/cc-tweaked/
|
||||||
|
33
README.md
33
README.md
@@ -11,14 +11,13 @@ SPDX-License-Identifier: MPL-2.0
|
|||||||
</picture>
|
</picture>
|
||||||
|
|
||||||
[](https://github.com/cc-tweaked/CC-Tweaked/actions "Current build status")
|
[](https://github.com/cc-tweaked/CC-Tweaked/actions "Current build status")
|
||||||
[][CurseForge]
|
|
||||||
[][Modrinth]
|
[][Modrinth]
|
||||||
|
|
||||||
CC: Tweaked is a mod for Minecraft which adds programmable computers, turtles and more to the game. A fork of the
|
CC: Tweaked is a mod for Minecraft which adds programmable computers, turtles and more to the game. A fork of the
|
||||||
much-beloved [ComputerCraft], it continues its legacy with improved performance and stability, along with a wealth of
|
much-beloved [ComputerCraft], it continues its legacy with improved performance and stability, along with a wealth of
|
||||||
new features.
|
new features.
|
||||||
|
|
||||||
CC: Tweaked can be installed from [CurseForge] or [Modrinth]. It runs on both [Minecraft Forge] and [Fabric].
|
CC: Tweaked can be installed from [Modrinth]. It runs on both [Minecraft Forge] and [Fabric].
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
Any contribution is welcome, be that using the mod, reporting bugs or contributing code. If you want to get started
|
Any contribution is welcome, be that using the mod, reporting bugs or contributing code. If you want to get started
|
||||||
@@ -26,8 +25,9 @@ developing the mod, [check out the instructions here](CONTRIBUTING.md#developing
|
|||||||
|
|
||||||
## Community
|
## Community
|
||||||
If you need help getting started with CC: Tweaked, want to show off your latest project, or just want to chat about
|
If you need help getting started with CC: Tweaked, want to show off your latest project, or just want to chat about
|
||||||
ComputerCraft, do check out our [forum] and [GitHub discussions page][GitHub discussions]! There's also a fairly
|
ComputerCraft, do check out our [GitHub discussions page][GitHub discussions]! There's also a fairly populated,
|
||||||
populated, albeit quiet [IRC channel][irc], if that's more your cup of tea.
|
albeit quiet IRC channel on [EsperNet], if that's more your cup of tea. You can join `#computercraft` through your
|
||||||
|
desktop client, or online using [KiwiIRC].
|
||||||
|
|
||||||
We also host fairly comprehensive documentation at [tweaked.cc](https://tweaked.cc/ "The CC: Tweaked website").
|
We also host fairly comprehensive documentation at [tweaked.cc](https://tweaked.cc/ "The CC: Tweaked website").
|
||||||
|
|
||||||
@@ -39,7 +39,7 @@ on is present.
|
|||||||
```groovy
|
```groovy
|
||||||
repositories {
|
repositories {
|
||||||
maven {
|
maven {
|
||||||
url "https://squiddev.cc/maven/"
|
url "https://maven.squiddev.cc"
|
||||||
content {
|
content {
|
||||||
includeGroup("cc.tweaked")
|
includeGroup("cc.tweaked")
|
||||||
}
|
}
|
||||||
@@ -51,9 +51,8 @@ dependencies {
|
|||||||
compileOnly("cc.tweaked:cc-tweaked-$mcVersion-common-api:$cctVersion")
|
compileOnly("cc.tweaked:cc-tweaked-$mcVersion-common-api:$cctVersion")
|
||||||
|
|
||||||
// Forge Gradle
|
// Forge Gradle
|
||||||
compileOnly("cc.tweaked:cc-tweaked-$mcVersion-core-api:$cctVersion")
|
compileOnly("cc.tweaked:cc-tweaked-$mcVersion-forge-api:$cctVersion")
|
||||||
compileOnly(fg.deobf("cc.tweaked:cc-tweaked-$mcVersion-forge-api:$cctVersion"))
|
runtimeOnly("cc.tweaked:cc-tweaked-$mcVersion-forge:$cctVersion")
|
||||||
runtimeOnly(fg.deobf("cc.tweaked:cc-tweaked-$mcVersion-forge:$cctVersion"))
|
|
||||||
|
|
||||||
// Fabric Loom
|
// Fabric Loom
|
||||||
modCompileOnly("cc.tweaked:cc-tweaked-$mcVersion-fabric-api:$cctVersion")
|
modCompileOnly("cc.tweaked:cc-tweaked-$mcVersion-fabric-api:$cctVersion")
|
||||||
@@ -61,19 +60,6 @@ dependencies {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
When using ForgeGradle, you may also need to add the following:
|
|
||||||
|
|
||||||
```groovy
|
|
||||||
minecraft {
|
|
||||||
runs {
|
|
||||||
configureEach {
|
|
||||||
property 'mixin.env.remapRefMap', 'true'
|
|
||||||
property 'mixin.env.refMapRemappingFile', "${buildDir}/createSrgToMcp/output.srg"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
You should also be careful to only use classes within the `dan200.computercraft.api` package. Non-API classes are
|
You should also be careful to only use classes within the `dan200.computercraft.api` package. Non-API classes are
|
||||||
subject to change at any point. If you depend on functionality outside the API (or need to mixin to CC:T), please file
|
subject to change at any point. If you depend on functionality outside the API (or need to mixin to CC:T), please file
|
||||||
an issue to let me know!
|
an issue to let me know!
|
||||||
@@ -82,10 +68,9 @@ We bundle the API sources with the jar, so documentation should be easily viewab
|
|||||||
the generated documentation [can be browsed online](https://tweaked.cc/javadoc/).
|
the generated documentation [can be browsed online](https://tweaked.cc/javadoc/).
|
||||||
|
|
||||||
[computercraft]: https://github.com/dan200/ComputerCraft "ComputerCraft on GitHub"
|
[computercraft]: https://github.com/dan200/ComputerCraft "ComputerCraft on GitHub"
|
||||||
[curseforge]: https://minecraft.curseforge.com/projects/cc-tweaked "Download CC: Tweaked from CurseForge"
|
|
||||||
[modrinth]: https://modrinth.com/mod/gu7yAYhd "Download CC: Tweaked from Modrinth"
|
[modrinth]: https://modrinth.com/mod/gu7yAYhd "Download CC: Tweaked from Modrinth"
|
||||||
[Minecraft Forge]: https://files.minecraftforge.net/ "Download Minecraft Forge."
|
[Minecraft Forge]: https://files.minecraftforge.net/ "Download Minecraft Forge."
|
||||||
[Fabric]: https://fabricmc.net/use/installer/ "Download Fabric."
|
[Fabric]: https://fabricmc.net/use/installer/ "Download Fabric."
|
||||||
[forum]: https://forums.computercraft.cc/
|
|
||||||
[GitHub Discussions]: https://github.com/cc-tweaked/CC-Tweaked/discussions
|
[GitHub Discussions]: https://github.com/cc-tweaked/CC-Tweaked/discussions
|
||||||
[IRC]: https://webchat.esper.net/?channels=computercraft "#computercraft on EsperNet"
|
[EsperNet]: https://www.esper.net/
|
||||||
|
[KiwiIRC]: https://kiwiirc.com/nextclient/#irc://irc.esper.net:+6697/#computercraft "#computercraft on EsperNet"
|
||||||
|
100
REUSE.toml
Normal file
100
REUSE.toml
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
# SPDX-FileCopyrightText: 2017 The CC: Tweaked Developers
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
|
version = 1
|
||||||
|
SPDX-PackageName = "CC: Tweaked"
|
||||||
|
SPDX-PackageSupplier = "Jonathan Coates <git@squiddev.cc>"
|
||||||
|
SPDX-PackageDownloadLocation = "https://github.com/cc-tweaked/cc-tweaked"
|
||||||
|
|
||||||
|
[[annotations]]
|
||||||
|
SPDX-FileCopyrightText = "The CC: Tweaked Developers"
|
||||||
|
SPDX-License-Identifier = "CC0-1.0"
|
||||||
|
path = [
|
||||||
|
# Generated/data files are CC0.
|
||||||
|
"gradle/gradle-daemon-jvm.properties",
|
||||||
|
"projects/common/src/main/resources/assets/computercraft/sounds.json",
|
||||||
|
"projects/common/src/main/resources/assets/computercraft/sounds/empty.ogg",
|
||||||
|
"projects/common/src/testMod/resources/data/cctest/computercraft/turtle_upgrade/**",
|
||||||
|
"projects/common/src/testMod/resources/data/cctest/structures/**",
|
||||||
|
"projects/*/src/generated/**",
|
||||||
|
"projects/web/src/htmlTransform/export/index.json",
|
||||||
|
"projects/web/src/htmlTransform/export/items/minecraft/**",
|
||||||
|
# GitHub build scripts are CC0. While we could add a header to each file,
|
||||||
|
# it's unclear if it will break actions or issue templates in some way.
|
||||||
|
".github/**",
|
||||||
|
# Example mod is CC0.
|
||||||
|
"projects/*/src/examples/**"
|
||||||
|
]
|
||||||
|
|
||||||
|
[[annotations]]
|
||||||
|
# Several assets where it's inconvenient to create a .license file.
|
||||||
|
SPDX-FileCopyrightText = "The CC: Tweaked Developers"
|
||||||
|
SPDX-License-Identifier = "MPL-2.0"
|
||||||
|
path = [
|
||||||
|
"doc/images/**",
|
||||||
|
"package.json",
|
||||||
|
"package-lock.json",
|
||||||
|
"projects/*/src/*/resources/*.mixins.json",
|
||||||
|
"projects/fabric/src/*/resources/fabric.mod.json",
|
||||||
|
"projects/common/src/main/resources/assets/minecraft/shaders/core/computercraft/monitor_tbo.json",
|
||||||
|
"projects/common/src/testMod/resources/data/computercraft/loot_tables/treasure_disk.json",
|
||||||
|
"projects/common/src/testMod/resources/pack.mcmeta",
|
||||||
|
"projects/core/src/main/resources/data/computercraft/lua/rom/modules/command/.ignoreme",
|
||||||
|
"projects/core/src/main/resources/data/computercraft/lua/rom/modules/main/.ignoreme",
|
||||||
|
"projects/core/src/main/resources/data/computercraft/lua/rom/modules/turtle/.ignoreme",
|
||||||
|
"projects/core/src/main/resources/data/computercraft/lua/rom/motd.txt",
|
||||||
|
"projects/web/src/frontend/mount/.settings",
|
||||||
|
"projects/web/src/frontend/mount/example.nfp",
|
||||||
|
"projects/web/src/frontend/mount/example.nft",
|
||||||
|
"projects/web/src/frontend/mount/expr_template.lua",
|
||||||
|
"projects/web/tsconfig.json",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[annotations]]
|
||||||
|
# Bulk-license original assets as CCPL.
|
||||||
|
SPDX-FileCopyrightText = "2011 Daniel Ratcliffe"
|
||||||
|
SPDX-License-Identifier = "LicenseRef-CCPL"
|
||||||
|
path = [
|
||||||
|
"doc/logo.png",
|
||||||
|
"doc/logo-darkmode.png",
|
||||||
|
"projects/common/src/main/resources/assets/computercraft/models/**",
|
||||||
|
"projects/common/src/main/resources/assets/computercraft/textures/**",
|
||||||
|
"projects/common/src/main/resources/pack.mcmeta",
|
||||||
|
"projects/common/src/main/resources/pack.png",
|
||||||
|
"projects/core/src/main/resources/assets/computercraft/textures/gui/term_font.png",
|
||||||
|
"projects/core/src/main/resources/data/computercraft/lua/rom/autorun/.ignoreme",
|
||||||
|
"projects/core/src/main/resources/data/computercraft/lua/rom/help/**",
|
||||||
|
"projects/core/src/main/resources/data/computercraft/lua/rom/programs/fun/advanced/levels/**",
|
||||||
|
"projects/web/src/htmlTransform/export/items/computercraft/**",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[annotations]]
|
||||||
|
# Community-contributed language files
|
||||||
|
SPDX-FileCopyrightText = "2017 The CC: Tweaked Developers"
|
||||||
|
SPDX-License-Identifier = "LicenseRef-CCPL"
|
||||||
|
path = [
|
||||||
|
"projects/common/src/main/resources/assets/computercraft/lang/cs_cz.json",
|
||||||
|
"projects/common/src/main/resources/assets/computercraft/lang/ko_kr.json",
|
||||||
|
"projects/common/src/main/resources/assets/computercraft/lang/pl_pl.json",
|
||||||
|
"projects/common/src/main/resources/assets/computercraft/lang/pt_br.json",
|
||||||
|
"projects/common/src/main/resources/assets/computercraft/lang/ru_ru.json",
|
||||||
|
"projects/common/src/main/resources/assets/computercraft/lang/uk_ua.json",
|
||||||
|
"projects/common/src/main/resources/assets/computercraft/lang/zh_cn.json",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[annotations]]
|
||||||
|
# Community-contributed language files
|
||||||
|
SPDX-FileCopyrightText = "2017 The CC: Tweaked Developers"
|
||||||
|
SPDX-License-Identifier = "MPL-2.0"
|
||||||
|
path = "projects/common/src/main/resources/assets/computercraft/lang/**"
|
||||||
|
|
||||||
|
[[annotations]]
|
||||||
|
path = ["gradle/wrapper/**"]
|
||||||
|
SPDX-FileCopyrightText = "Gradle Inc"
|
||||||
|
SPDX-License-Identifier = "Apache-2.0"
|
||||||
|
|
||||||
|
[[annotations]]
|
||||||
|
path = "projects/core/src/test/resources/test-rom/data/json-parsing/**"
|
||||||
|
SPDX-FileCopyrightText = "2016 Nicolas Seriot"
|
||||||
|
SPDX-License-Identifier = "MIT"
|
@@ -5,9 +5,8 @@
|
|||||||
import cc.tweaked.gradle.JUnitExt
|
import cc.tweaked.gradle.JUnitExt
|
||||||
import net.fabricmc.loom.api.LoomGradleExtensionAPI
|
import net.fabricmc.loom.api.LoomGradleExtensionAPI
|
||||||
import net.fabricmc.loom.util.gradle.SourceSetHelper
|
import net.fabricmc.loom.util.gradle.SourceSetHelper
|
||||||
import org.jetbrains.gradle.ext.compiler
|
import org.jetbrains.gradle.ext.*
|
||||||
import org.jetbrains.gradle.ext.runConfigurations
|
import org.jetbrains.gradle.ext.Application
|
||||||
import org.jetbrains.gradle.ext.settings
|
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
publishing
|
publishing
|
||||||
@@ -25,21 +24,19 @@ val mcVersion: String by extra
|
|||||||
|
|
||||||
githubRelease {
|
githubRelease {
|
||||||
token(findProperty("githubApiKey") as String? ?: "")
|
token(findProperty("githubApiKey") as String? ?: "")
|
||||||
owner.set("cc-tweaked")
|
owner = "cc-tweaked"
|
||||||
repo.set("CC-Tweaked")
|
repo = "CC-Tweaked"
|
||||||
targetCommitish.set(cct.gitBranch)
|
targetCommitish = cct.gitBranch
|
||||||
|
|
||||||
tagName.set("v$mcVersion-$modVersion")
|
tagName = "v$mcVersion-$modVersion"
|
||||||
releaseName.set("[$mcVersion] $modVersion")
|
releaseName = "[$mcVersion] $modVersion"
|
||||||
body.set(
|
body = provider {
|
||||||
provider {
|
|
||||||
"## " + project(":core").file("src/main/resources/data/computercraft/lua/rom/help/whatsnew.md")
|
"## " + project(":core").file("src/main/resources/data/computercraft/lua/rom/help/whatsnew.md")
|
||||||
.readLines()
|
.readLines()
|
||||||
.takeWhile { it != "Type \"help changelog\" to see the full version history." }
|
.takeWhile { it != "Type \"help changelog\" to see the full version history." }
|
||||||
.joinToString("\n").trim()
|
.joinToString("\n").trim()
|
||||||
},
|
}
|
||||||
)
|
prerelease = isUnstable
|
||||||
prerelease.set(isUnstable)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.publish { dependsOn(tasks.githubRelease) }
|
tasks.publish { dependsOn(tasks.githubRelease) }
|
||||||
@@ -86,6 +83,19 @@ idea.project.settings.runConfigurations {
|
|||||||
moduleName = "${idea.project.name}.forge.test"
|
moduleName = "${idea.project.name}.forge.test"
|
||||||
packageName = ""
|
packageName = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
register<Application>("Standalone") {
|
||||||
|
moduleName = "${idea.project.name}.standalone.main"
|
||||||
|
mainClass = "cc.tweaked.standalone.Main"
|
||||||
|
programParameters = "--resources=projects/core/src/main/resources --term=80x30 --allow-local-domains"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build with the IntelliJ, rather than through Gradle. This may require setting the "Compiler Output" option in
|
||||||
|
// "Project Structure".
|
||||||
|
idea.project.settings.delegateActions {
|
||||||
|
delegateBuildRunToGradle = false
|
||||||
|
testRunner = ActionDelegationConfig.TestRunner.PLATFORM
|
||||||
}
|
}
|
||||||
|
|
||||||
idea.project.settings.compiler.javac {
|
idea.project.settings.compiler.javac {
|
||||||
@@ -106,7 +116,7 @@ idea.project.settings.compiler.javac {
|
|||||||
}
|
}
|
||||||
|
|
||||||
versionCatalogUpdate {
|
versionCatalogUpdate {
|
||||||
sortByKey.set(false)
|
sortByKey = false
|
||||||
pin { versions.addAll("fastutil", "guava", "netty", "slf4j") }
|
pin { versions.addAll("fastutil", "guava", "netty", "slf4j") }
|
||||||
keep { keepUnusedLibraries.set(true) }
|
keep { keepUnusedLibraries = true }
|
||||||
}
|
}
|
||||||
|
@@ -14,18 +14,10 @@ repositories {
|
|||||||
mavenCentral()
|
mavenCentral()
|
||||||
gradlePluginPortal()
|
gradlePluginPortal()
|
||||||
|
|
||||||
maven("https://maven.minecraftforge.net") {
|
maven("https://maven.neoforged.net") {
|
||||||
name = "Forge"
|
name = "NeoForge"
|
||||||
content {
|
content {
|
||||||
includeGroup("net.minecraftforge")
|
includeGroup("net.neoforged")
|
||||||
includeGroup("net.minecraftforge.gradle")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
maven("https://maven.parchmentmc.org") {
|
|
||||||
name = "Librarian"
|
|
||||||
content {
|
|
||||||
includeGroupByRegex("^org\\.parchmentmc.*")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -36,7 +28,7 @@ repositories {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
maven("https://squiddev.cc/maven") {
|
maven("https://maven.squiddev.cc") {
|
||||||
name = "SquidDev"
|
name = "SquidDev"
|
||||||
content {
|
content {
|
||||||
includeGroup("cc.tweaked.vanilla-extract")
|
includeGroup("cc.tweaked.vanilla-extract")
|
||||||
@@ -49,12 +41,10 @@ dependencies {
|
|||||||
implementation(libs.kotlin.plugin)
|
implementation(libs.kotlin.plugin)
|
||||||
implementation(libs.spotless)
|
implementation(libs.spotless)
|
||||||
|
|
||||||
implementation(libs.curseForgeGradle)
|
|
||||||
implementation(libs.fabric.loom)
|
implementation(libs.fabric.loom)
|
||||||
implementation(libs.forgeGradle)
|
|
||||||
implementation(libs.ideaExt)
|
implementation(libs.ideaExt)
|
||||||
implementation(libs.librarian)
|
|
||||||
implementation(libs.minotaur)
|
implementation(libs.minotaur)
|
||||||
|
implementation(libs.modDevGradle)
|
||||||
implementation(libs.vanillaExtract)
|
implementation(libs.vanillaExtract)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,7 +68,7 @@ gradlePlugin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
versionCatalogUpdate {
|
versionCatalogUpdate {
|
||||||
sortByKey.set(false)
|
sortByKey = false
|
||||||
keep { keepUnusedLibraries.set(true) }
|
keep { keepUnusedLibraries = true }
|
||||||
catalogFile.set(file("../gradle/libs.versions.toml"))
|
catalogFile = file("../gradle/libs.versions.toml")
|
||||||
}
|
}
|
||||||
|
@@ -4,10 +4,7 @@
|
|||||||
|
|
||||||
/** Default configuration for Fabric projects. */
|
/** Default configuration for Fabric projects. */
|
||||||
|
|
||||||
import cc.tweaked.gradle.CCTweakedExtension
|
import cc.tweaked.gradle.*
|
||||||
import cc.tweaked.gradle.CCTweakedPlugin
|
|
||||||
import cc.tweaked.gradle.IdeaRunConfigurations
|
|
||||||
import cc.tweaked.gradle.MinecraftConfigurations
|
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
`java-library`
|
`java-library`
|
||||||
@@ -30,7 +27,7 @@ repositories {
|
|||||||
|
|
||||||
loom {
|
loom {
|
||||||
splitEnvironmentSourceSets()
|
splitEnvironmentSourceSets()
|
||||||
splitModDependencies.set(true)
|
splitModDependencies = true
|
||||||
}
|
}
|
||||||
|
|
||||||
MinecraftConfigurations.setup(project)
|
MinecraftConfigurations.setup(project)
|
||||||
@@ -67,3 +64,9 @@ dependencies {
|
|||||||
tasks.ideaSyncTask {
|
tasks.ideaSyncTask {
|
||||||
doLast { IdeaRunConfigurations(project).patch() }
|
doLast { IdeaRunConfigurations(project).patch() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tasks.named("checkDependencyConsistency", DependencyCheck::class.java) {
|
||||||
|
val libs = project.extensions.getByType<VersionCatalogsExtension>().named("libs")
|
||||||
|
// Minecraft depends on asm, but Fabric forces it to a more recent version
|
||||||
|
override(libs.findLibrary("asm").get(), "9.8")
|
||||||
|
}
|
||||||
|
@@ -6,25 +6,25 @@
|
|||||||
|
|
||||||
import cc.tweaked.gradle.CCTweakedExtension
|
import cc.tweaked.gradle.CCTweakedExtension
|
||||||
import cc.tweaked.gradle.CCTweakedPlugin
|
import cc.tweaked.gradle.CCTweakedPlugin
|
||||||
import cc.tweaked.gradle.IdeaRunConfigurations
|
|
||||||
import cc.tweaked.gradle.MinecraftConfigurations
|
import cc.tweaked.gradle.MinecraftConfigurations
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
id("net.minecraftforge.gradle")
|
|
||||||
// We must apply java-convention after Forge, as we need the fg extension to be present.
|
|
||||||
id("cc-tweaked.java-convention")
|
id("cc-tweaked.java-convention")
|
||||||
id("org.parchmentmc.librarian.forgegradle")
|
id("net.neoforged.moddev")
|
||||||
}
|
}
|
||||||
|
|
||||||
plugins.apply(CCTweakedPlugin::class.java)
|
plugins.apply(CCTweakedPlugin::class.java)
|
||||||
|
|
||||||
val mcVersion: String by extra
|
val mcVersion: String by extra
|
||||||
|
|
||||||
minecraft {
|
neoForge {
|
||||||
val libs = project.extensions.getByType<VersionCatalogsExtension>().named("libs")
|
val libs = project.extensions.getByType<VersionCatalogsExtension>().named("libs")
|
||||||
mappings("parchment", "${libs.findVersion("parchmentMc").get()}-${libs.findVersion("parchment").get()}-$mcVersion")
|
version = libs.findVersion("neoForge").get().toString()
|
||||||
|
|
||||||
accessTransformer(project(":forge").file("src/main/resources/META-INF/accesstransformer.cfg"))
|
parchment {
|
||||||
|
minecraftVersion = libs.findVersion("parchmentMc").get().toString()
|
||||||
|
mappingsVersion = libs.findVersion("parchment").get().toString()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MinecraftConfigurations.setup(project)
|
MinecraftConfigurations.setup(project)
|
||||||
@@ -32,13 +32,3 @@ MinecraftConfigurations.setup(project)
|
|||||||
extensions.configure(CCTweakedExtension::class.java) {
|
extensions.configure(CCTweakedExtension::class.java) {
|
||||||
linters(minecraft = true, loader = "forge")
|
linters(minecraft = true, loader = "forge")
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
|
||||||
val libs = project.extensions.getByType<VersionCatalogsExtension>().named("libs")
|
|
||||||
"minecraft"("net.minecraftforge:forge:$mcVersion-${libs.findVersion("forge").get()}")
|
|
||||||
}
|
|
||||||
|
|
||||||
tasks.configureEach {
|
|
||||||
// genIntellijRuns isn't registered until much later, so we need this silly hijinks.
|
|
||||||
if (name == "genIntellijRuns") doLast { IdeaRunConfigurations(project).patch() }
|
|
||||||
}
|
|
||||||
|
@@ -29,7 +29,7 @@ base.archivesName.convention("cc-tweaked-$mcVersion-${project.name}")
|
|||||||
|
|
||||||
java {
|
java {
|
||||||
toolchain {
|
toolchain {
|
||||||
languageVersion.set(CCTweakedPlugin.JAVA_VERSION)
|
languageVersion = CCTweakedPlugin.JAVA_VERSION
|
||||||
}
|
}
|
||||||
|
|
||||||
withSourcesJar()
|
withSourcesJar()
|
||||||
@@ -38,27 +38,17 @@ java {
|
|||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
|
|
||||||
val mainMaven = maven("https://squiddev.cc/maven") {
|
val mainMaven = maven("https://maven.squiddev.cc/mirror") {
|
||||||
name = "SquidDev"
|
name = "SquidDev"
|
||||||
content {
|
|
||||||
// Until https://github.com/SpongePowered/Mixin/pull/593 is merged
|
|
||||||
includeModule("org.spongepowered", "mixin")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
exclusiveContent {
|
exclusiveContent {
|
||||||
forRepositories(mainMaven)
|
forRepositories(mainMaven)
|
||||||
|
|
||||||
// Include the ForgeGradle repository if present. This requires that ForgeGradle is already present, which we
|
|
||||||
// enforce in our Forge overlay.
|
|
||||||
val fg =
|
|
||||||
project.extensions.findByType(net.minecraftforge.gradle.userdev.DependencyManagementExtension::class.java)
|
|
||||||
if (fg != null) forRepositories(fg.repository)
|
|
||||||
|
|
||||||
filter {
|
filter {
|
||||||
includeGroup("cc.tweaked")
|
includeGroup("cc.tweaked")
|
||||||
// Things we mirror
|
// Things we mirror
|
||||||
includeGroup("commoble.morered")
|
includeGroup("com.simibubi.create")
|
||||||
|
includeGroup("net.commoble.morered")
|
||||||
includeGroup("dev.architectury")
|
includeGroup("dev.architectury")
|
||||||
includeGroup("dev.emi")
|
includeGroup("dev.emi")
|
||||||
includeGroup("maven.modrinth")
|
includeGroup("maven.modrinth")
|
||||||
@@ -67,7 +57,6 @@ repositories {
|
|||||||
includeGroup("mezz.jei")
|
includeGroup("mezz.jei")
|
||||||
includeGroup("org.teavm")
|
includeGroup("org.teavm")
|
||||||
includeModule("com.terraformersmc", "modmenu")
|
includeModule("com.terraformersmc", "modmenu")
|
||||||
includeModule("me.lucko", "fabric-permissions-api")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -89,20 +78,28 @@ dependencies {
|
|||||||
// Configure default JavaCompile tasks with our arguments.
|
// Configure default JavaCompile tasks with our arguments.
|
||||||
sourceSets.all {
|
sourceSets.all {
|
||||||
tasks.named(compileJavaTaskName, JavaCompile::class.java) {
|
tasks.named(compileJavaTaskName, JavaCompile::class.java) {
|
||||||
|
|
||||||
|
options.compilerArgs.addAll(
|
||||||
|
listOf(
|
||||||
|
"-Xlint",
|
||||||
// Processing just gives us "No processor claimed any of these annotations", so skip that!
|
// Processing just gives us "No processor claimed any of these annotations", so skip that!
|
||||||
options.compilerArgs.addAll(listOf("-Xlint", "-Xlint:-processing"))
|
"-Xlint:-processing",
|
||||||
|
// We violate this pattern too often for it to be a helpful warning. Something to improve one day!
|
||||||
|
"-Xlint:-this-escape",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
options.errorprone {
|
options.errorprone {
|
||||||
check("InvalidBlockTag", CheckSeverity.OFF) // Broken by @cc.xyz
|
check("InvalidBlockTag", CheckSeverity.OFF) // Broken by @cc.xyz
|
||||||
check("InvalidParam", CheckSeverity.OFF) // Broken by records.
|
|
||||||
check("InlineMeSuggester", CheckSeverity.OFF) // Minecraft uses @Deprecated liberally
|
check("InlineMeSuggester", CheckSeverity.OFF) // Minecraft uses @Deprecated liberally
|
||||||
// Too many false positives right now. Maybe we need an indirection for it later on.
|
// Too many false positives right now. Maybe we need an indirection for it later on.
|
||||||
|
check("AssignmentExpression", CheckSeverity.OFF) // I'm a bad person.
|
||||||
check("ReferenceEquality", CheckSeverity.OFF)
|
check("ReferenceEquality", CheckSeverity.OFF)
|
||||||
check("UnusedVariable", CheckSeverity.OFF) // Too many false positives with records.
|
check("EnumOrdinal", CheckSeverity.OFF) // For now. We could replace most of these with EnumMap.
|
||||||
check("OperatorPrecedence", CheckSeverity.OFF) // For now.
|
check("OperatorPrecedence", CheckSeverity.OFF) // For now.
|
||||||
check("AlreadyChecked", CheckSeverity.OFF) // Seems to be broken?
|
|
||||||
check("NonOverridingEquals", CheckSeverity.OFF) // Peripheral.equals makes this hard to avoid
|
check("NonOverridingEquals", CheckSeverity.OFF) // Peripheral.equals makes this hard to avoid
|
||||||
check("FutureReturnValueIgnored", CheckSeverity.OFF) // Too many false positives with Netty
|
check("FutureReturnValueIgnored", CheckSeverity.OFF) // Too many false positives with Netty
|
||||||
|
check("InvalidInlineTag", CheckSeverity.OFF) // Triggered by @snippet. Can be removed on Java 21.
|
||||||
|
|
||||||
check("NullAway", CheckSeverity.ERROR)
|
check("NullAway", CheckSeverity.ERROR)
|
||||||
option(
|
option(
|
||||||
@@ -113,6 +110,8 @@ sourceSets.all {
|
|||||||
option("NullAway:CastToNonNullMethod", "dan200.computercraft.core.util.Nullability.assertNonNull")
|
option("NullAway:CastToNonNullMethod", "dan200.computercraft.core.util.Nullability.assertNonNull")
|
||||||
option("NullAway:CheckOptionalEmptiness")
|
option("NullAway:CheckOptionalEmptiness")
|
||||||
option("NullAway:AcknowledgeRestrictiveAnnotations")
|
option("NullAway:AcknowledgeRestrictiveAnnotations")
|
||||||
|
|
||||||
|
excludedPaths = ".*/jmh_generated/.*"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -123,7 +122,6 @@ tasks.compileTestJava {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
tasks.withType(JavaCompile::class.java).configureEach {
|
tasks.withType(JavaCompile::class.java).configureEach {
|
||||||
options.encoding = "UTF-8"
|
options.encoding = "UTF-8"
|
||||||
}
|
}
|
||||||
@@ -136,8 +134,8 @@ tasks.processResources {
|
|||||||
tasks.withType(AbstractArchiveTask::class.java).configureEach {
|
tasks.withType(AbstractArchiveTask::class.java).configureEach {
|
||||||
isPreserveFileTimestamps = false
|
isPreserveFileTimestamps = false
|
||||||
isReproducibleFileOrder = true
|
isReproducibleFileOrder = true
|
||||||
dirMode = Integer.valueOf("755", 8)
|
filePermissions {}
|
||||||
fileMode = Integer.valueOf("664", 8)
|
dirPermissions {}
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.jar {
|
tasks.jar {
|
||||||
@@ -157,7 +155,7 @@ tasks.javadoc {
|
|||||||
options {
|
options {
|
||||||
val stdOptions = this as StandardJavadocDocletOptions
|
val stdOptions = this as StandardJavadocDocletOptions
|
||||||
stdOptions.addBooleanOption("Xdoclint:all,-missing", true)
|
stdOptions.addBooleanOption("Xdoclint:all,-missing", true)
|
||||||
stdOptions.links("https://docs.oracle.com/en/java/javase/17/docs/api/")
|
stdOptions.links("https://docs.oracle.com/en/java/javase/21/docs/api/")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -171,8 +169,8 @@ tasks.test {
|
|||||||
}
|
}
|
||||||
|
|
||||||
tasks.withType(JacocoReport::class.java).configureEach {
|
tasks.withType(JacocoReport::class.java).configureEach {
|
||||||
reports.xml.required.set(true)
|
reports.xml.required = true
|
||||||
reports.html.required.set(true)
|
reports.html.required = true
|
||||||
}
|
}
|
||||||
|
|
||||||
project.plugins.withType(CCTweakedPlugin::class.java) {
|
project.plugins.withType(CCTweakedPlugin::class.java) {
|
||||||
@@ -196,30 +194,23 @@ spotless {
|
|||||||
fun FormatExtension.defaults() {
|
fun FormatExtension.defaults() {
|
||||||
endWithNewline()
|
endWithNewline()
|
||||||
trimTrailingWhitespace()
|
trimTrailingWhitespace()
|
||||||
indentWithSpaces(4)
|
leadingTabsToSpaces(4)
|
||||||
}
|
}
|
||||||
|
|
||||||
java {
|
java {
|
||||||
defaults()
|
defaults()
|
||||||
|
importOrder("", "javax|java", "\\#")
|
||||||
removeUnusedImports()
|
removeUnusedImports()
|
||||||
}
|
}
|
||||||
|
|
||||||
val ktlintConfig = mapOf(
|
|
||||||
"ktlint_standard_no-wildcard-imports" to "disabled",
|
|
||||||
"ktlint_standard_class-naming" to "disabled",
|
|
||||||
"ktlint_standard_function-naming" to "disabled",
|
|
||||||
"ij_kotlin_allow_trailing_comma" to "true",
|
|
||||||
"ij_kotlin_allow_trailing_comma_on_call_site" to "true",
|
|
||||||
)
|
|
||||||
|
|
||||||
kotlinGradle {
|
kotlinGradle {
|
||||||
defaults()
|
defaults()
|
||||||
ktlint().editorConfigOverride(ktlintConfig)
|
ktlint()
|
||||||
}
|
}
|
||||||
|
|
||||||
kotlin {
|
kotlin {
|
||||||
defaults()
|
defaults()
|
||||||
ktlint().editorConfigOverride(ktlintConfig)
|
ktlint()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -228,6 +219,5 @@ idea.module {
|
|||||||
|
|
||||||
// Force Gradle to write to inherit the output directory from the parent, instead of writing to out/xxx/classes.
|
// Force Gradle to write to inherit the output directory from the parent, instead of writing to out/xxx/classes.
|
||||||
// This is required for Loom, and we patch Forge's run configurations to work there.
|
// This is required for Loom, and we patch Forge's run configurations to work there.
|
||||||
// TODO: Submit a patch to Forge to support ProjectRootManager.
|
|
||||||
inheritOutputDirs = true
|
inheritOutputDirs = true
|
||||||
}
|
}
|
||||||
|
@@ -1,25 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: 2022 The CC: Tweaked Developers
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: MPL-2.0
|
|
||||||
|
|
||||||
import cc.tweaked.gradle.CCTweakedPlugin
|
|
||||||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
|
||||||
|
|
||||||
plugins {
|
|
||||||
kotlin("jvm")
|
|
||||||
}
|
|
||||||
|
|
||||||
kotlin {
|
|
||||||
jvmToolchain {
|
|
||||||
languageVersion.set(CCTweakedPlugin.JAVA_VERSION)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
tasks.withType(KotlinCompile::class.java).configureEach {
|
|
||||||
// So technically we shouldn't need to do this as the toolchain sets it above. However, the option only appears
|
|
||||||
// to be set when the task executes, so doesn't get picked up by IDEs.
|
|
||||||
kotlinOptions.jvmTarget = when {
|
|
||||||
CCTweakedPlugin.JAVA_VERSION.asInt() > 8 -> CCTweakedPlugin.JAVA_VERSION.toString()
|
|
||||||
else -> "1.${CCTweakedPlugin.JAVA_VERSION.asInt()}"
|
|
||||||
}
|
|
||||||
}
|
|
@@ -2,11 +2,9 @@
|
|||||||
//
|
//
|
||||||
// SPDX-License-Identifier: MPL-2.0
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
import net.darkhax.curseforgegradle.TaskPublishCurseForge
|
|
||||||
import cc.tweaked.gradle.setProvider
|
import cc.tweaked.gradle.setProvider
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
id("net.darkhax.curseforgegradle")
|
|
||||||
id("com.modrinth.minotaur")
|
id("com.modrinth.minotaur")
|
||||||
id("cc-tweaked.publishing")
|
id("cc-tweaked.publishing")
|
||||||
}
|
}
|
||||||
@@ -25,34 +23,17 @@ val isUnstable = project.properties["isUnstable"] == "true"
|
|||||||
val modVersion: String by extra
|
val modVersion: String by extra
|
||||||
val mcVersion: String by extra
|
val mcVersion: String by extra
|
||||||
|
|
||||||
val publishCurseForge by tasks.registering(TaskPublishCurseForge::class) {
|
|
||||||
group = PublishingPlugin.PUBLISH_TASK_GROUP
|
|
||||||
description = "Upload artifacts to CurseForge"
|
|
||||||
|
|
||||||
apiToken = findProperty("curseForgeApiKey") ?: ""
|
|
||||||
enabled = apiToken != ""
|
|
||||||
|
|
||||||
val mainFile = upload("282001", modPublishing.output.get().archiveFile)
|
|
||||||
mainFile.changelog =
|
|
||||||
"Release notes can be found on the [GitHub repository](https://github.com/cc-tweaked/CC-Tweaked/releases/tag/v$mcVersion-$modVersion)."
|
|
||||||
mainFile.changelogType = "markdown"
|
|
||||||
mainFile.releaseType = if (isUnstable) "alpha" else "release"
|
|
||||||
mainFile.gameVersions.add(mcVersion)
|
|
||||||
}
|
|
||||||
|
|
||||||
tasks.publish { dependsOn(publishCurseForge) }
|
|
||||||
|
|
||||||
modrinth {
|
modrinth {
|
||||||
token.set(findProperty("modrinthApiKey") as String? ?: "")
|
token = findProperty("modrinthApiKey") as String? ?: ""
|
||||||
projectId.set("gu7yAYhd")
|
projectId = "gu7yAYhd"
|
||||||
versionNumber.set(modVersion)
|
versionNumber = modVersion
|
||||||
versionName.set(modVersion)
|
versionName = modVersion
|
||||||
versionType.set(if (isUnstable) "alpha" else "release")
|
versionType = if (isUnstable) "alpha" else "release"
|
||||||
uploadFile.setProvider(modPublishing.output)
|
uploadFile.setProvider(modPublishing.output)
|
||||||
gameVersions.add(mcVersion)
|
gameVersions.add(mcVersion)
|
||||||
changelog.set("Release notes can be found on the [GitHub repository](https://github.com/cc-tweaked/CC-Tweaked/releases/tag/v$mcVersion-$modVersion).")
|
changelog = "Release notes can be found on the [GitHub repository](https://github.com/cc-tweaked/CC-Tweaked/releases/tag/v$mcVersion-$modVersion)."
|
||||||
|
|
||||||
syncBodyFrom.set(provider { rootProject.file("doc/mod-page.md").readText() })
|
syncBodyFrom = provider { rootProject.file("doc/mod-page.md").readText() }
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.publish { dependsOn(tasks.modrinth) }
|
tasks.publish { dependsOn(tasks.modrinth) }
|
||||||
|
@@ -2,44 +2,34 @@
|
|||||||
//
|
//
|
||||||
// SPDX-License-Identifier: MPL-2.0
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
import cc.tweaked.gradle.clientClasses
|
|
||||||
import cc.tweaked.gradle.commonClasses
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets up the configurations for writing game tests.
|
* Sets up the configurations for writing game tests.
|
||||||
*
|
*
|
||||||
* See notes in [cc.tweaked.gradle.MinecraftConfigurations] for the general design behind these cursed ideas.
|
* See notes in [cc.tweaked.gradle.MinecraftConfigurations] for the general design behind these cursed ideas.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import cc.tweaked.gradle.MinecraftConfigurations
|
||||||
|
import cc.tweaked.gradle.clientClasses
|
||||||
|
import cc.tweaked.gradle.commonClasses
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
id("cc-tweaked.kotlin-convention")
|
kotlin("jvm")
|
||||||
id("cc-tweaked.java-convention")
|
id("cc-tweaked.java-convention")
|
||||||
}
|
}
|
||||||
|
|
||||||
val main = sourceSets["main"]
|
val main = sourceSets["main"]
|
||||||
val client = sourceSets["client"]
|
val client = sourceSets["client"]
|
||||||
|
|
||||||
// Both testMod and testFixtures inherit from the main and client classpath, just so we have access to Minecraft classes.
|
MinecraftConfigurations.createDerivedConfiguration(project, MinecraftConfigurations.DATAGEN)
|
||||||
val testMod by sourceSets.creating {
|
MinecraftConfigurations.createDerivedConfiguration(project, MinecraftConfigurations.EXAMPLES)
|
||||||
compileClasspath += main.compileClasspath + client.compileClasspath
|
MinecraftConfigurations.createDerivedConfiguration(project, MinecraftConfigurations.TEST_MOD)
|
||||||
runtimeClasspath += main.runtimeClasspath + client.runtimeClasspath
|
|
||||||
}
|
|
||||||
|
|
||||||
configurations {
|
// Set up generated resources
|
||||||
named(testMod.compileClasspathConfigurationName) {
|
sourceSets.main { resources.srcDir("src/generated/resources") }
|
||||||
shouldResolveConsistentlyWith(compileClasspath.get())
|
sourceSets.named("examples") { resources.srcDir("src/examples/generatedResources") }
|
||||||
}
|
|
||||||
|
|
||||||
named(testMod.runtimeClasspathConfigurationName) {
|
// Make sure our examples compile.
|
||||||
shouldResolveConsistentlyWith(runtimeClasspath.get())
|
tasks.check { dependsOn(tasks.named("compileExamplesJava")) }
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Like the main test configurations, we're safe to depend on source set outputs.
|
|
||||||
dependencies {
|
|
||||||
add(testMod.implementationConfigurationName, main.output)
|
|
||||||
add(testMod.implementationConfigurationName, client.output)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Similar to java-test-fixtures, but tries to avoid putting the obfuscated jar on the classpath.
|
// Similar to java-test-fixtures, but tries to avoid putting the obfuscated jar on the classpath.
|
||||||
|
|
@@ -12,25 +12,26 @@ publishing {
|
|||||||
register<MavenPublication>("maven") {
|
register<MavenPublication>("maven") {
|
||||||
artifactId = base.archivesName.get()
|
artifactId = base.archivesName.get()
|
||||||
from(components["java"])
|
from(components["java"])
|
||||||
|
suppressAllPomMetadataWarnings()
|
||||||
|
|
||||||
pom {
|
pom {
|
||||||
name.set("CC: Tweaked")
|
name = "CC: Tweaked"
|
||||||
description.set("CC: Tweaked is a fork of ComputerCraft, adding programmable computers, turtles and more to Minecraft.")
|
description = "CC: Tweaked is a fork of ComputerCraft, adding programmable computers, turtles and more to Minecraft."
|
||||||
url.set("https://github.com/cc-tweaked/CC-Tweaked")
|
url = "https://github.com/cc-tweaked/CC-Tweaked"
|
||||||
|
|
||||||
scm {
|
scm {
|
||||||
url.set("https://github.com/cc-tweaked/CC-Tweaked.git")
|
url = "https://github.com/cc-tweaked/CC-Tweaked.git"
|
||||||
}
|
}
|
||||||
|
|
||||||
issueManagement {
|
issueManagement {
|
||||||
system.set("github")
|
system = "github"
|
||||||
url.set("https://github.com/cc-tweaked/CC-Tweaked/issues")
|
url = "https://github.com/cc-tweaked/CC-Tweaked/issues"
|
||||||
}
|
}
|
||||||
|
|
||||||
licenses {
|
licenses {
|
||||||
license {
|
license {
|
||||||
name.set("ComputerCraft Public License, Version 1.0")
|
name = "ComputerCraft Public License, Version 1.0"
|
||||||
url.set("https://github.com/cc-tweaked/CC-Tweaked/blob/HEAD/LICENSE")
|
url = "https://github.com/cc-tweaked/CC-Tweaked/blob/HEAD/LICENSE"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -38,7 +39,7 @@ publishing {
|
|||||||
}
|
}
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
maven("https://squiddev.cc/maven") {
|
maven("https://maven.squiddev.cc") {
|
||||||
name = "SquidDev"
|
name = "SquidDev"
|
||||||
|
|
||||||
credentials(PasswordCredentials::class)
|
credentials(PasswordCredentials::class)
|
||||||
|
@@ -10,23 +10,16 @@ import org.gradle.api.GradleException
|
|||||||
import org.gradle.api.NamedDomainObjectProvider
|
import org.gradle.api.NamedDomainObjectProvider
|
||||||
import org.gradle.api.Project
|
import org.gradle.api.Project
|
||||||
import org.gradle.api.Task
|
import org.gradle.api.Task
|
||||||
import org.gradle.api.artifacts.Dependency
|
|
||||||
import org.gradle.api.attributes.TestSuiteType
|
|
||||||
import org.gradle.api.file.FileSystemOperations
|
|
||||||
import org.gradle.api.plugins.JavaPluginExtension
|
import org.gradle.api.plugins.JavaPluginExtension
|
||||||
import org.gradle.api.provider.ListProperty
|
|
||||||
import org.gradle.api.provider.Provider
|
import org.gradle.api.provider.Provider
|
||||||
import org.gradle.api.provider.SetProperty
|
import org.gradle.api.provider.SetProperty
|
||||||
import org.gradle.api.reporting.ReportingExtension
|
|
||||||
import org.gradle.api.tasks.SourceSet
|
import org.gradle.api.tasks.SourceSet
|
||||||
import org.gradle.api.tasks.bundling.Jar
|
import org.gradle.api.tasks.bundling.Jar
|
||||||
import org.gradle.api.tasks.compile.JavaCompile
|
import org.gradle.api.tasks.compile.JavaCompile
|
||||||
import org.gradle.api.tasks.javadoc.Javadoc
|
import org.gradle.api.tasks.javadoc.Javadoc
|
||||||
import org.gradle.configurationcache.extensions.capitalized
|
|
||||||
import org.gradle.language.base.plugins.LifecycleBasePlugin
|
import org.gradle.language.base.plugins.LifecycleBasePlugin
|
||||||
import org.gradle.language.jvm.tasks.ProcessResources
|
import org.gradle.language.jvm.tasks.ProcessResources
|
||||||
import org.gradle.process.JavaForkOptions
|
import org.gradle.process.JavaForkOptions
|
||||||
import org.gradle.testing.jacoco.plugins.JacocoCoverageReport
|
|
||||||
import org.gradle.testing.jacoco.plugins.JacocoPluginExtension
|
import org.gradle.testing.jacoco.plugins.JacocoPluginExtension
|
||||||
import org.gradle.testing.jacoco.plugins.JacocoTaskExtension
|
import org.gradle.testing.jacoco.plugins.JacocoTaskExtension
|
||||||
import org.gradle.testing.jacoco.tasks.JacocoReport
|
import org.gradle.testing.jacoco.tasks.JacocoReport
|
||||||
@@ -35,31 +28,22 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
|||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.net.URI
|
import java.net.URI
|
||||||
import java.net.URL
|
|
||||||
import java.util.regex.Pattern
|
import java.util.regex.Pattern
|
||||||
|
|
||||||
abstract class CCTweakedExtension(
|
abstract class CCTweakedExtension(private val project: Project) {
|
||||||
private val project: Project,
|
|
||||||
private val fs: FileSystemOperations,
|
|
||||||
) {
|
|
||||||
/** Get the hash of the latest git commit. */
|
/** Get the hash of the latest git commit. */
|
||||||
val gitHash: Provider<String> = gitProvider(project, "<no git hash>") {
|
val gitHash: Provider<String> =
|
||||||
ProcessHelpers.captureOut("git", "-C", project.rootProject.projectDir.absolutePath, "rev-parse", "HEAD").trim()
|
gitProvider("<no git commit>", listOf("rev-parse", "HEAD")) { it.trim() }
|
||||||
}
|
|
||||||
|
|
||||||
/** Get the current git branch. */
|
/** Get the current git branch. */
|
||||||
val gitBranch: Provider<String> = gitProvider(project, "<no git branch>") {
|
val gitBranch: Provider<String> =
|
||||||
ProcessHelpers.captureOut("git", "-C", project.rootProject.projectDir.absolutePath, "rev-parse", "--abbrev-ref", "HEAD")
|
gitProvider("<no git branch>", listOf("rev-parse", "--abbrev-ref", "HEAD")) { it.trim() }
|
||||||
.trim()
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Get a list of all contributors to the project. */
|
/** Get a list of all contributors to the project. */
|
||||||
val gitContributors: Provider<List<String>> = gitProvider(project, listOf()) {
|
val gitContributors: Provider<List<String>> =
|
||||||
ProcessHelpers.captureLines(
|
gitProvider(listOf(), listOf("shortlog", "-ns", "--group=author", "--group=trailer:co-authored-by", "HEAD")) { input ->
|
||||||
"git", "-C", project.rootProject.projectDir.absolutePath, "shortlog", "-ns",
|
input.lineSequence()
|
||||||
"--group=author", "--group=trailer:co-authored-by", "HEAD",
|
.filter { it.isNotEmpty() }
|
||||||
)
|
|
||||||
.asSequence()
|
|
||||||
.map {
|
.map {
|
||||||
val matcher = COMMIT_COUNTS.matcher(it)
|
val matcher = COMMIT_COUNTS.matcher(it)
|
||||||
matcher.find()
|
matcher.find()
|
||||||
@@ -75,17 +59,11 @@ abstract class CCTweakedExtension(
|
|||||||
*/
|
*/
|
||||||
val sourceDirectories: SetProperty<SourceSetReference> = project.objects.setProperty(SourceSetReference::class.java)
|
val sourceDirectories: SetProperty<SourceSetReference> = project.objects.setProperty(SourceSetReference::class.java)
|
||||||
|
|
||||||
/**
|
|
||||||
* Dependencies excluded from published artifacts.
|
|
||||||
*/
|
|
||||||
private val excludedDeps: ListProperty<Dependency> = project.objects.listProperty(Dependency::class.java)
|
|
||||||
|
|
||||||
/** All source sets referenced by this project. */
|
/** All source sets referenced by this project. */
|
||||||
val sourceSets = sourceDirectories.map { x -> x.map { it.sourceSet } }
|
val sourceSets = sourceDirectories.map { x -> x.map { it.sourceSet } }
|
||||||
|
|
||||||
init {
|
init {
|
||||||
sourceDirectories.finalizeValueOnRead()
|
sourceDirectories.finalizeValueOnRead()
|
||||||
excludedDeps.finalizeValueOnRead()
|
|
||||||
project.afterEvaluate { sourceDirectories.disallowChanges() }
|
project.afterEvaluate { sourceDirectories.disallowChanges() }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -111,14 +89,13 @@ abstract class CCTweakedExtension(
|
|||||||
val otherJava = otherProject.extensions.getByType(JavaPluginExtension::class.java)
|
val otherJava = otherProject.extensions.getByType(JavaPluginExtension::class.java)
|
||||||
val main = otherJava.sourceSets.getByName("main")
|
val main = otherJava.sourceSets.getByName("main")
|
||||||
val client = otherJava.sourceSets.getByName("client")
|
val client = otherJava.sourceSets.getByName("client")
|
||||||
val testMod = otherJava.sourceSets.findByName("testMod")
|
|
||||||
val testFixtures = otherJava.sourceSets.findByName("testFixtures")
|
|
||||||
|
|
||||||
// Pull in sources from the other project.
|
// Pull in sources from the other project.
|
||||||
extendSourceSet(otherProject, main)
|
extendSourceSet(otherProject, main)
|
||||||
extendSourceSet(otherProject, client)
|
extendSourceSet(otherProject, client)
|
||||||
if (testMod != null) extendSourceSet(otherProject, testMod)
|
for (sourceSet in listOf(MinecraftConfigurations.DATAGEN, MinecraftConfigurations.EXAMPLES, MinecraftConfigurations.TEST_MOD, "testFixtures")) {
|
||||||
if (testFixtures != null) extendSourceSet(otherProject, testFixtures)
|
otherJava.sourceSets.findByName(sourceSet)?.let { extendSourceSet(otherProject, it) }
|
||||||
|
}
|
||||||
|
|
||||||
// The extra source-processing tasks should include these files too.
|
// The extra source-processing tasks should include these files too.
|
||||||
project.tasks.named(main.javadocTaskName, Javadoc::class.java) { source(main.allJava, client.allJava) }
|
project.tasks.named(main.javadocTaskName, Javadoc::class.java) { source(main.allJava, client.allJava) }
|
||||||
@@ -181,23 +158,19 @@ abstract class CCTweakedExtension(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun <T> jacoco(task: NamedDomainObjectProvider<T>) where T : Task, T : JavaForkOptions {
|
fun <T> jacoco(task: NamedDomainObjectProvider<T>) where T : Task, T : JavaForkOptions {
|
||||||
val classDump = project.layout.buildDirectory.dir("jacocoClassDump/${task.name}")
|
val reportTaskName = "jacoco${task.name.capitalise()}Report"
|
||||||
val reportTaskName = "jacoco${task.name.capitalized()}Report"
|
|
||||||
|
|
||||||
val jacoco = project.extensions.getByType(JacocoPluginExtension::class.java)
|
val jacoco = project.extensions.getByType(JacocoPluginExtension::class.java)
|
||||||
task.configure {
|
task.configure {
|
||||||
finalizedBy(reportTaskName)
|
finalizedBy(reportTaskName)
|
||||||
|
|
||||||
doFirst("Clean class dump directory") { fs.delete { delete(classDump) } }
|
|
||||||
|
|
||||||
jacoco.applyTo(this)
|
jacoco.applyTo(this)
|
||||||
|
|
||||||
extensions.configure(JacocoTaskExtension::class.java) {
|
extensions.configure(JacocoTaskExtension::class.java) {
|
||||||
includes = listOf("dan200.computercraft.*")
|
includes = listOf("dan200.computercraft.*")
|
||||||
classDumpDir = classDump.get().asFile
|
excludes = listOf(
|
||||||
|
"dan200.computercraft.mixin.*", // Exclude mixins, as they're not executed at runtime.
|
||||||
// Older versions of modlauncher don't include a protection domain (and thus no code
|
"dan200.computercraft.shared.Capabilities$*", // Exclude capability tokens, as Forge rewrites them.
|
||||||
// source). Jacoco skips such classes by default, so we need to explicitly include them.
|
)
|
||||||
isIncludeNoLocationClasses = true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -206,15 +179,11 @@ abstract class CCTweakedExtension(
|
|||||||
description = "Generates code coverage report for the ${task.name} task."
|
description = "Generates code coverage report for the ${task.name} task."
|
||||||
|
|
||||||
executionData(task.get())
|
executionData(task.get())
|
||||||
classDirectories.from(classDump)
|
|
||||||
|
|
||||||
// Don't want to use sourceSets(...) here as we have a custom class directory.
|
// Don't want to use sourceSets(...) here as we don't use all class directories.
|
||||||
for (ref in sourceSets.get()) sourceDirectories.from(ref.allSource.sourceDirectories)
|
for (ref in this@CCTweakedExtension.sourceDirectories.get()) {
|
||||||
}
|
sourceDirectories.from(ref.sourceSet.allSource.sourceDirectories)
|
||||||
|
if (ref.classes) classDirectories.from(ref.sourceSet.output)
|
||||||
project.extensions.configure(ReportingExtension::class.java) {
|
|
||||||
reports.register("${task.name}CodeCoverageReport", JacocoCoverageReport::class.java) {
|
|
||||||
testType.set(TestSuiteType.INTEGRATION_TEST)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -226,12 +195,12 @@ abstract class CCTweakedExtension(
|
|||||||
* where possible.
|
* where possible.
|
||||||
*/
|
*/
|
||||||
fun downloadFile(label: String, url: String): File {
|
fun downloadFile(label: String, url: String): File {
|
||||||
val url = URL(url)
|
val uri = URI(url)
|
||||||
val path = File(url.path)
|
val path = File(uri.path)
|
||||||
|
|
||||||
project.repositories.ivy {
|
project.repositories.ivy {
|
||||||
name = label
|
name = label
|
||||||
setUrl(URI(url.protocol, url.userInfo, url.host, url.port, path.parent, null, null))
|
setUrl(URI(uri.scheme, uri.userInfo, uri.host, uri.port, path.parent, null, null))
|
||||||
patternLayout {
|
patternLayout {
|
||||||
artifact("[artifact].[ext]")
|
artifact("[artifact].[ext]")
|
||||||
}
|
}
|
||||||
@@ -254,40 +223,31 @@ abstract class CCTweakedExtension(
|
|||||||
).resolve().single()
|
).resolve().single()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private fun <T> gitProvider(default: T, command: List<String>, process: (String) -> T): Provider<T> {
|
||||||
* Exclude a dependency from being published in Maven.
|
val baseResult = project.providers.exec {
|
||||||
*/
|
commandLine = listOf("git", "-C", project.rootDir.absolutePath) + command
|
||||||
fun exclude(dep: Dependency) {
|
|
||||||
excludedDeps.add(dep)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
return project.provider {
|
||||||
* Configure a [MavenDependencySpec].
|
val res = try {
|
||||||
*/
|
baseResult.standardOutput.asText.get()
|
||||||
fun configureExcludes(spec: MavenDependencySpec) {
|
} catch (e: IOException) {
|
||||||
for (dep in excludedDeps.get()) spec.exclude(dep)
|
project.logger.error("Cannot read Git repository: ${e.message}", e)
|
||||||
|
return@provider default
|
||||||
|
} catch (e: GradleException) {
|
||||||
|
project.logger.error("Cannot read Git repository: ${e.message}", e)
|
||||||
|
return@provider default
|
||||||
|
}
|
||||||
|
process(res)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val COMMIT_COUNTS = Pattern.compile("""^\s*[0-9]+\s+(.*)$""")
|
private val COMMIT_COUNTS = Pattern.compile("""^\s*[0-9]+\s+(.*)$""")
|
||||||
private val IGNORED_USERS = setOf(
|
private val IGNORED_USERS = setOf(
|
||||||
"GitHub", "Daniel Ratcliffe", "Weblate",
|
"GitHub", "Daniel Ratcliffe", "NotSquidDev", "Weblate",
|
||||||
)
|
)
|
||||||
|
|
||||||
private fun <T> gitProvider(project: Project, default: T, supplier: () -> T): Provider<T> {
|
|
||||||
return project.provider {
|
|
||||||
try {
|
|
||||||
supplier()
|
|
||||||
} catch (e: IOException) {
|
|
||||||
project.logger.error("Cannot read Git repository: ${e.message}")
|
|
||||||
default
|
|
||||||
} catch (e: GradleException) {
|
|
||||||
project.logger.error("Cannot read Git repository: ${e.message}")
|
|
||||||
default
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private val isIdeSync: Boolean
|
private val isIdeSync: Boolean
|
||||||
get() = java.lang.Boolean.parseBoolean(System.getProperty("idea.sync.active", "false"))
|
get() = java.lang.Boolean.parseBoolean(System.getProperty("idea.sync.active", "false"))
|
||||||
}
|
}
|
||||||
|
@@ -42,6 +42,6 @@ class CCTweakedPlugin : Plugin<Project> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val JAVA_VERSION = JavaLanguageVersion.of(17)
|
val JAVA_VERSION = JavaLanguageVersion.of(21)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -22,19 +22,19 @@ import org.gradle.language.base.plugins.LifecycleBasePlugin
|
|||||||
|
|
||||||
abstract class DependencyCheck : DefaultTask() {
|
abstract class DependencyCheck : DefaultTask() {
|
||||||
@get:Input
|
@get:Input
|
||||||
abstract val configuration: ListProperty<Configuration>
|
protected abstract val dependencies: ListProperty<DependencyResult>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A mapping of module coordinates (`group:module`) to versions, overriding the requested version.
|
* A mapping of module coordinates (`group:module`) to versions, overriding the requested version.
|
||||||
*/
|
*/
|
||||||
@get:Input
|
@get:Input
|
||||||
abstract val overrides: MapProperty<String, String>
|
protected abstract val overrides: MapProperty<String, String>
|
||||||
|
|
||||||
init {
|
init {
|
||||||
description = "Check :core's dependencies are consistent with Minecraft's."
|
description = "Check :core's dependencies are consistent with Minecraft's."
|
||||||
group = LifecycleBasePlugin.VERIFICATION_GROUP
|
group = LifecycleBasePlugin.VERIFICATION_GROUP
|
||||||
|
|
||||||
configuration.finalizeValueOnRead()
|
dependencies.finalizeValueOnRead()
|
||||||
overrides.finalizeValueOnRead()
|
overrides.finalizeValueOnRead()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -45,13 +45,19 @@ abstract class DependencyCheck : DefaultTask() {
|
|||||||
overrides.putAll(project.provider { mutableMapOf(module.get().module.toString() to version) })
|
overrides.putAll(project.provider { mutableMapOf(module.get().module.toString() to version) })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a configuration to check.
|
||||||
|
*/
|
||||||
|
fun configuration(configuration: Provider<Configuration>) {
|
||||||
|
// We can't store the Configuration in the cache, so store the resolved dependencies instead.
|
||||||
|
dependencies.addAll(configuration.map { it.incoming.resolutionResult.allDependencies })
|
||||||
|
}
|
||||||
|
|
||||||
@TaskAction
|
@TaskAction
|
||||||
fun run() {
|
fun run() {
|
||||||
var ok = true
|
var ok = true
|
||||||
for (configuration in configuration.get()) {
|
for (configuration in dependencies.get()) {
|
||||||
configuration.incoming.resolutionResult.allDependencies {
|
if (!check(configuration)) ok = false
|
||||||
if (!check(this@allDependencies)) ok = false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
|
@@ -5,10 +5,8 @@
|
|||||||
package cc.tweaked.gradle
|
package cc.tweaked.gradle
|
||||||
|
|
||||||
import org.gradle.api.file.DirectoryProperty
|
import org.gradle.api.file.DirectoryProperty
|
||||||
import org.gradle.api.provider.Property
|
|
||||||
import org.gradle.api.tasks.AbstractExecTask
|
import org.gradle.api.tasks.AbstractExecTask
|
||||||
import org.gradle.api.tasks.OutputDirectory
|
import org.gradle.api.tasks.OutputDirectory
|
||||||
import java.io.File
|
|
||||||
|
|
||||||
abstract class ExecToDir : AbstractExecTask<ExecToDir>(ExecToDir::class.java) {
|
abstract class ExecToDir : AbstractExecTask<ExecToDir>(ExecToDir::class.java) {
|
||||||
@get:OutputDirectory
|
@get:OutputDirectory
|
||||||
|
@@ -9,6 +9,7 @@ import org.gradle.api.file.FileSystemLocation
|
|||||||
import org.gradle.api.provider.Property
|
import org.gradle.api.provider.Property
|
||||||
import org.gradle.api.provider.Provider
|
import org.gradle.api.provider.Provider
|
||||||
import org.gradle.api.tasks.JavaExec
|
import org.gradle.api.tasks.JavaExec
|
||||||
|
import org.gradle.kotlin.dsl.getByName
|
||||||
import org.gradle.process.BaseExecSpec
|
import org.gradle.process.BaseExecSpec
|
||||||
import org.gradle.process.JavaExecSpec
|
import org.gradle.process.JavaExecSpec
|
||||||
import org.gradle.process.ProcessForkOptions
|
import org.gradle.process.ProcessForkOptions
|
||||||
@@ -46,6 +47,21 @@ fun JavaExec.copyToFull(spec: JavaExec) {
|
|||||||
copyToExec(spec)
|
copyToExec(spec)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base this [JavaExec] task on an existing task.
|
||||||
|
*/
|
||||||
|
fun JavaExec.copyFromTask(task: JavaExec) {
|
||||||
|
for (dep in task.dependsOn) dependsOn(dep)
|
||||||
|
task.copyToFull(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base this [JavaExec] task on an existing task.
|
||||||
|
*/
|
||||||
|
fun JavaExec.copyFromTask(task: String) {
|
||||||
|
copyFromTask(project.tasks.getByName<JavaExec>(task))
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Copy additional [BaseExecSpec] options which aren't handled by [ProcessForkOptions.copyTo].
|
* Copy additional [BaseExecSpec] options which aren't handled by [ProcessForkOptions.copyTo].
|
||||||
*/
|
*/
|
||||||
@@ -143,7 +159,7 @@ fun getNextVersion(version: String): String {
|
|||||||
val lastIndex = mainVersion.lastIndexOf('.')
|
val lastIndex = mainVersion.lastIndexOf('.')
|
||||||
if (lastIndex < 0) throw IllegalArgumentException("Cannot parse version format \"$version\"")
|
if (lastIndex < 0) throw IllegalArgumentException("Cannot parse version format \"$version\"")
|
||||||
val lastVersion = try {
|
val lastVersion = try {
|
||||||
version.substring(lastIndex + 1).toInt()
|
mainVersion.substring(lastIndex + 1).toInt()
|
||||||
} catch (e: NumberFormatException) {
|
} catch (e: NumberFormatException) {
|
||||||
throw IllegalArgumentException("Cannot parse version format \"$version\"", e)
|
throw IllegalArgumentException("Cannot parse version format \"$version\"", e)
|
||||||
}
|
}
|
||||||
@@ -155,3 +171,15 @@ fun getNextVersion(version: String): String {
|
|||||||
if (dashIndex >= 0) out.append(version, dashIndex, version.length)
|
if (dashIndex >= 0) out.append(version, dashIndex, version.length)
|
||||||
return out.toString()
|
return out.toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Capitalise the first letter of the string.
|
||||||
|
*
|
||||||
|
* This is a replacement for the now deprecated [String.capitalize].
|
||||||
|
*/
|
||||||
|
fun String.capitalise(): String {
|
||||||
|
if (isEmpty()) return this
|
||||||
|
val first = this[0]
|
||||||
|
val firstTitle = first.titlecaseChar()
|
||||||
|
return if (first == firstTitle) this else firstTitle + substring(1)
|
||||||
|
}
|
||||||
|
@@ -1,26 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: MPL-2.0
|
|
||||||
|
|
||||||
package cc.tweaked.gradle
|
|
||||||
|
|
||||||
import net.minecraftforge.gradle.common.util.RunConfig
|
|
||||||
import net.minecraftforge.gradle.common.util.runs.setRunConfigInternal
|
|
||||||
import org.gradle.api.plugins.JavaPluginExtension
|
|
||||||
import org.gradle.api.tasks.JavaExec
|
|
||||||
import org.gradle.jvm.toolchain.JavaToolchainService
|
|
||||||
import java.nio.file.Files
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set [JavaExec] task to run a given [RunConfig].
|
|
||||||
*/
|
|
||||||
fun JavaExec.setRunConfig(config: RunConfig) {
|
|
||||||
dependsOn("prepareRuns")
|
|
||||||
setRunConfigInternal(project, this, config)
|
|
||||||
doFirst("Create working directory") { Files.createDirectories(workingDir.toPath()) }
|
|
||||||
|
|
||||||
javaLauncher.set(
|
|
||||||
project.extensions.getByType(JavaToolchainService::class.java)
|
|
||||||
.launcherFor(project.extensions.getByType(JavaPluginExtension::class.java).toolchain),
|
|
||||||
)
|
|
||||||
}
|
|
@@ -25,7 +25,6 @@ import javax.xml.xpath.XPathFactory
|
|||||||
* Would be good to PR some (or all) of these changes upstream at some point.
|
* Would be good to PR some (or all) of these changes upstream at some point.
|
||||||
*
|
*
|
||||||
* @see net.fabricmc.loom.configuration.ide.idea.IdeaSyncTask
|
* @see net.fabricmc.loom.configuration.ide.idea.IdeaSyncTask
|
||||||
* @see net.minecraftforge.gradle.common.util.runs.IntellijRunGenerator
|
|
||||||
*/
|
*/
|
||||||
internal class IdeaRunConfigurations(project: Project) {
|
internal class IdeaRunConfigurations(project: Project) {
|
||||||
private val rootProject = project.rootProject
|
private val rootProject = project.rootProject
|
||||||
@@ -35,22 +34,6 @@ internal class IdeaRunConfigurations(project: Project) {
|
|||||||
private val writer = TransformerFactory.newInstance().newTransformer()
|
private val writer = TransformerFactory.newInstance().newTransformer()
|
||||||
|
|
||||||
private val ideaDir = rootProject.file(".idea/")
|
private val ideaDir = rootProject.file(".idea/")
|
||||||
private val buildDir: Lazy<String?> = lazy {
|
|
||||||
val ideaMisc = ideaDir.resolve("misc.xml")
|
|
||||||
|
|
||||||
try {
|
|
||||||
val doc = Files.newBufferedReader(ideaMisc.toPath()).use {
|
|
||||||
documentBuilder.parse(InputSource(it))
|
|
||||||
}
|
|
||||||
val node =
|
|
||||||
xpath.evaluate("//component[@name=\"ProjectRootManager\"]/output", doc, XPathConstants.NODE) as Node
|
|
||||||
val attr = node.attributes.getNamedItem("url") as Attr
|
|
||||||
attr.value.removePrefix("file://")
|
|
||||||
} catch (e: Exception) {
|
|
||||||
LOGGER.error("Failed to find root directory", e)
|
|
||||||
null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun patch() = synchronized(LOCK) {
|
fun patch() = synchronized(LOCK) {
|
||||||
val runConfigDir = ideaDir.resolve("runConfigurations")
|
val runConfigDir = ideaDir.resolve("runConfigurations")
|
||||||
@@ -58,10 +41,9 @@ internal class IdeaRunConfigurations(project: Project) {
|
|||||||
|
|
||||||
Files.list(runConfigDir.toPath()).use {
|
Files.list(runConfigDir.toPath()).use {
|
||||||
for (configuration in it) {
|
for (configuration in it) {
|
||||||
val filename = configuration.fileName.toString();
|
val filename = configuration.fileName.toString()
|
||||||
when {
|
when {
|
||||||
filename.endsWith("_fabric.xml") -> patchFabric(configuration)
|
filename.endsWith("_fabric.xml") -> patchFabric(configuration)
|
||||||
filename.startsWith("forge_") && filename.endsWith(".xml") -> patchForge(configuration)
|
|
||||||
else -> {}
|
else -> {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -72,65 +54,6 @@ internal class IdeaRunConfigurations(project: Project) {
|
|||||||
setXml("//configuration", "folderName") { "Fabric" }
|
setXml("//configuration", "folderName") { "Fabric" }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun patchForge(path: Path) = withXml(path) {
|
|
||||||
val configId = path.fileName.toString().removePrefix("forge_").removeSuffix(".xml")
|
|
||||||
val sourceSet = forgeConfigs[configId]
|
|
||||||
if (sourceSet == null) {
|
|
||||||
LOGGER.error("[{}] Cannot map run configuration to a known source set", path)
|
|
||||||
return@withXml
|
|
||||||
}
|
|
||||||
|
|
||||||
setXml("//configuration", "folderName") { "Forge" }
|
|
||||||
setXml("//configuration/module", "name") { "${rootProject.name}.forge.$sourceSet" }
|
|
||||||
|
|
||||||
if (buildDir.value == null) return@withXml
|
|
||||||
setXml("//configuration/envs/env[@name=\"MOD_CLASSES\"]", "value") { classpath ->
|
|
||||||
val classes = classpath!!.split(':')
|
|
||||||
val newClasses = mutableListOf<String>()
|
|
||||||
fun appendUnique(x: String) {
|
|
||||||
if (!newClasses.contains(x)) newClasses.add(x)
|
|
||||||
}
|
|
||||||
|
|
||||||
for (entry in classes) {
|
|
||||||
if (!entry.contains("/out/")) {
|
|
||||||
appendUnique(entry)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
val match = CLASSPATH_ENTRY.matchEntire(entry)
|
|
||||||
if (match != null) {
|
|
||||||
val modId = match.groups["modId"]!!.value
|
|
||||||
val proj = match.groups["proj"]!!.value
|
|
||||||
var component = match.groups["component"]!!.value
|
|
||||||
if (component == "production") component = "main"
|
|
||||||
|
|
||||||
appendUnique(forgeModEntry(modId, proj, component))
|
|
||||||
} else {
|
|
||||||
LOGGER.warn("[{}] Unknown classpath entry {}", path, entry)
|
|
||||||
appendUnique(entry)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure common code is on the classpath
|
|
||||||
for (proj in listOf("common", "common-api")) {
|
|
||||||
for (component in listOf("main", "client")) {
|
|
||||||
appendUnique(forgeModEntry("computercraft", proj, component))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (newClasses.any { it.startsWith("cctest%%") }) {
|
|
||||||
appendUnique(forgeModEntry("cctest", "core", "testFixtures"))
|
|
||||||
appendUnique(forgeModEntry("cctest", "common", "testFixtures"))
|
|
||||||
appendUnique(forgeModEntry("cctest", "common", "testMod"))
|
|
||||||
}
|
|
||||||
|
|
||||||
newClasses.joinToString(":")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun forgeModEntry(mod: String, project: String, component: String) =
|
|
||||||
"$mod%%${buildDir.value}/production/${rootProject.name}.$project.$component"
|
|
||||||
|
|
||||||
private fun LocatedDocument.setXml(xpath: String, attribute: String, value: (String?) -> String) {
|
private fun LocatedDocument.setXml(xpath: String, attribute: String, value: (String?) -> String) {
|
||||||
val node = this@IdeaRunConfigurations.xpath.evaluate(xpath, document, XPathConstants.NODE) as Node?
|
val node = this@IdeaRunConfigurations.xpath.evaluate(xpath, document, XPathConstants.NODE) as Node?
|
||||||
if (node == null) {
|
if (node == null) {
|
||||||
@@ -159,16 +82,5 @@ internal class IdeaRunConfigurations(project: Project) {
|
|||||||
companion object {
|
companion object {
|
||||||
private val LOGGER = Logging.getLogger(IdeaRunConfigurations::class.java)
|
private val LOGGER = Logging.getLogger(IdeaRunConfigurations::class.java)
|
||||||
private val LOCK = Any()
|
private val LOCK = Any()
|
||||||
|
|
||||||
private val CLASSPATH_ENTRY =
|
|
||||||
Regex("(?<modId>[a-z]+)%%\\\$PROJECT_DIR\\\$/projects/(?<proj>[a-z-]+)/out/(?<component>\\w+)/(?<type>[a-z]+)\$")
|
|
||||||
|
|
||||||
private val forgeConfigs = mapOf(
|
|
||||||
"runClient" to "client",
|
|
||||||
"runData" to "main",
|
|
||||||
"runGameTestServer" to "testMod",
|
|
||||||
"runServer" to "main",
|
|
||||||
"runTestClient" to "testMod",
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,73 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: 2022 The CC: Tweaked Developers
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: MPL-2.0
|
|
||||||
|
|
||||||
package cc.tweaked.gradle
|
|
||||||
|
|
||||||
import org.gradle.api.artifacts.Dependency
|
|
||||||
import org.gradle.api.artifacts.MinimalExternalModuleDependency
|
|
||||||
import org.gradle.api.artifacts.ProjectDependency
|
|
||||||
import org.gradle.api.plugins.BasePluginExtension
|
|
||||||
import org.gradle.api.publish.maven.MavenPublication
|
|
||||||
import org.gradle.api.specs.Spec
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A dependency in a POM file.
|
|
||||||
*/
|
|
||||||
data class MavenDependency(val groupId: String?, val artifactId: String?, val version: String?, val scope: String?)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A spec specifying which dependencies to include/exclude.
|
|
||||||
*/
|
|
||||||
class MavenDependencySpec {
|
|
||||||
private val excludeSpecs = mutableListOf<Spec<MavenDependency>>()
|
|
||||||
|
|
||||||
fun exclude(spec: Spec<MavenDependency>) {
|
|
||||||
excludeSpecs.add(spec)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun exclude(dep: Dependency) {
|
|
||||||
exclude {
|
|
||||||
// We have to cheat a little for project dependencies, as the project name doesn't match the artifact group.
|
|
||||||
val name = when (dep) {
|
|
||||||
is ProjectDependency -> dep.dependencyProject.extensions.getByType(BasePluginExtension::class.java).archivesName.get()
|
|
||||||
else -> dep.name
|
|
||||||
}
|
|
||||||
(dep.group.isNullOrEmpty() || dep.group == it.groupId) &&
|
|
||||||
(name.isNullOrEmpty() || name == it.artifactId) &&
|
|
||||||
(dep.version.isNullOrEmpty() || dep.version == it.version)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun exclude(dep: MinimalExternalModuleDependency) {
|
|
||||||
exclude {
|
|
||||||
dep.module.group == it.groupId && dep.module.name == it.artifactId
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun isIncluded(dep: MavenDependency) = !excludeSpecs.any { it.isSatisfiedBy(dep) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Configure dependencies present in this publication's POM file.
|
|
||||||
*
|
|
||||||
* While this approach is very ugly, it's the easiest way to handle it!
|
|
||||||
*/
|
|
||||||
fun MavenPublication.mavenDependencies(action: MavenDependencySpec.() -> Unit) {
|
|
||||||
val spec = MavenDependencySpec()
|
|
||||||
action(spec)
|
|
||||||
|
|
||||||
pom.withXml {
|
|
||||||
val dependencies = XmlUtil.findChild(asNode(), "dependencies") ?: return@withXml
|
|
||||||
dependencies.children().map { it as groovy.util.Node }.forEach {
|
|
||||||
val dep = MavenDependency(
|
|
||||||
groupId = XmlUtil.findChild(it, "groupId")?.text(),
|
|
||||||
artifactId = XmlUtil.findChild(it, "artifactId")?.text(),
|
|
||||||
version = XmlUtil.findChild(it, "version")?.text(),
|
|
||||||
scope = XmlUtil.findChild(it, "scope")?.text(),
|
|
||||||
)
|
|
||||||
|
|
||||||
if (!spec.isIncluded(dep)) it.parent().remove(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
120
buildSrc/src/main/kotlin/cc/tweaked/gradle/MergeTrees.kt
Normal file
120
buildSrc/src/main/kotlin/cc/tweaked/gradle/MergeTrees.kt
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2024 The CC: Tweaked Developers
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
|
package cc.tweaked.gradle
|
||||||
|
|
||||||
|
import cc.tweaked.vanillaextract.core.util.MoreFiles
|
||||||
|
import org.gradle.api.Action
|
||||||
|
import org.gradle.api.DefaultTask
|
||||||
|
import org.gradle.api.GradleException
|
||||||
|
import org.gradle.api.file.*
|
||||||
|
import org.gradle.api.model.ObjectFactory
|
||||||
|
import org.gradle.api.provider.ListProperty
|
||||||
|
import org.gradle.api.tasks.*
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Merge common files across multiple directories into one destination directory.
|
||||||
|
*
|
||||||
|
* This is intended for merging the generated resources from the Forge and Fabric projects. Files common between the two
|
||||||
|
* are written to the global [output] directory, while distinct files are written to the per-source
|
||||||
|
* [MergeTrees.Source.output] directory.
|
||||||
|
*/
|
||||||
|
abstract class MergeTrees : DefaultTask() {
|
||||||
|
/**
|
||||||
|
* A source directory to read from.
|
||||||
|
*/
|
||||||
|
interface Source {
|
||||||
|
/**
|
||||||
|
* The folder contianing all input files.
|
||||||
|
*/
|
||||||
|
@get:InputFiles
|
||||||
|
@get:PathSensitive(PathSensitivity.RELATIVE)
|
||||||
|
val input: ConfigurableFileTree
|
||||||
|
|
||||||
|
fun input(configure: Action<ConfigurableFileTree>) {
|
||||||
|
configure.execute(input)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The folder to write files unique to this folder to.
|
||||||
|
*/
|
||||||
|
@get:OutputDirectory
|
||||||
|
val output: DirectoryProperty
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The list of sources.
|
||||||
|
*/
|
||||||
|
@get:Nested
|
||||||
|
abstract val sources: ListProperty<Source>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add and configure a new source.
|
||||||
|
*/
|
||||||
|
fun source(configure: Action<Source>) {
|
||||||
|
val instance = objectFactory.newInstance(Source::class.java)
|
||||||
|
configure.execute(instance)
|
||||||
|
instance.output.disallowChanges()
|
||||||
|
sources.add(instance)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The directory to write common files to.
|
||||||
|
*/
|
||||||
|
@get:OutputDirectory
|
||||||
|
abstract val output: DirectoryProperty
|
||||||
|
|
||||||
|
@get:Inject
|
||||||
|
protected abstract val objectFactory: ObjectFactory
|
||||||
|
|
||||||
|
@get:Inject
|
||||||
|
protected abstract val fsOperations: FileSystemOperations
|
||||||
|
|
||||||
|
@TaskAction
|
||||||
|
fun run() {
|
||||||
|
val sources = this.sources.get()
|
||||||
|
if (sources.isEmpty()) throw GradleException("Cannot have an empty list of sources")
|
||||||
|
|
||||||
|
val files = mutableMapOf<String, SharedFile>()
|
||||||
|
for (source in sources) {
|
||||||
|
source.input.visit(
|
||||||
|
object : FileVisitor {
|
||||||
|
override fun visitDir(dirDetails: FileVisitDetails) = Unit
|
||||||
|
override fun visitFile(fileDetails: FileVisitDetails) {
|
||||||
|
val path = fileDetails.file.toRelativeString(source.input.dir)
|
||||||
|
val hash = MoreFiles.computeSha1(fileDetails.file.toPath())
|
||||||
|
|
||||||
|
val existing = files[path]
|
||||||
|
if (existing == null) {
|
||||||
|
files[path] = SharedFile(hash, 1)
|
||||||
|
} else if (existing.hash == hash) {
|
||||||
|
existing.found++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
val sharedFiles = files.entries.asSequence().filter { (_, v) -> v.found == sources.size }.map { (k, _) -> k }.toList()
|
||||||
|
|
||||||
|
// Copy shared files to the common directory
|
||||||
|
fsOperations.sync {
|
||||||
|
from(sources[0].input)
|
||||||
|
into(output)
|
||||||
|
include(sharedFiles)
|
||||||
|
}
|
||||||
|
|
||||||
|
// And all other files to their per-source directory
|
||||||
|
for (source in sources) {
|
||||||
|
fsOperations.sync {
|
||||||
|
from(source.input)
|
||||||
|
into(source.output)
|
||||||
|
exclude(sharedFiles)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SharedFile(val hash: String, var found: Int)
|
||||||
|
}
|
@@ -24,7 +24,6 @@ class MinecraftConfigurations private constructor(private val project: Project)
|
|||||||
private val java = project.extensions.getByType(JavaPluginExtension::class.java)
|
private val java = project.extensions.getByType(JavaPluginExtension::class.java)
|
||||||
private val sourceSets = java.sourceSets
|
private val sourceSets = java.sourceSets
|
||||||
private val configurations = project.configurations
|
private val configurations = project.configurations
|
||||||
private val objects = project.objects
|
|
||||||
|
|
||||||
private val main = sourceSets[SourceSet.MAIN_SOURCE_SET_NAME]
|
private val main = sourceSets[SourceSet.MAIN_SOURCE_SET_NAME]
|
||||||
private val test = sourceSets[SourceSet.TEST_SOURCE_SET_NAME]
|
private val test = sourceSets[SourceSet.TEST_SOURCE_SET_NAME]
|
||||||
@@ -37,13 +36,7 @@ class MinecraftConfigurations private constructor(private val project: Project)
|
|||||||
val client = sourceSets.maybeCreate("client")
|
val client = sourceSets.maybeCreate("client")
|
||||||
|
|
||||||
// Ensure the client classpaths behave the same as the main ones.
|
// Ensure the client classpaths behave the same as the main ones.
|
||||||
configurations.named(client.compileClasspathConfigurationName) {
|
consistentWithMain(client)
|
||||||
shouldResolveConsistentlyWith(configurations[main.compileClasspathConfigurationName])
|
|
||||||
}
|
|
||||||
|
|
||||||
configurations.named(client.runtimeClasspathConfigurationName) {
|
|
||||||
shouldResolveConsistentlyWith(configurations[main.runtimeClasspathConfigurationName])
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set up an API configuration for clients (to ensure it's consistent with the main source set).
|
// Set up an API configuration for clients (to ensure it's consistent with the main source set).
|
||||||
val clientApi = configurations.maybeCreate(client.apiConfigurationName).apply {
|
val clientApi = configurations.maybeCreate(client.apiConfigurationName).apply {
|
||||||
@@ -85,6 +78,16 @@ class MinecraftConfigurations private constructor(private val project: Project)
|
|||||||
setupBasic()
|
setupBasic()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun consistentWithMain(sourceSet: SourceSet) {
|
||||||
|
configurations.named(sourceSet.compileClasspathConfigurationName) {
|
||||||
|
shouldResolveConsistentlyWith(configurations[main.compileClasspathConfigurationName])
|
||||||
|
}
|
||||||
|
|
||||||
|
configurations.named(sourceSet.runtimeClasspathConfigurationName) {
|
||||||
|
shouldResolveConsistentlyWith(configurations[main.runtimeClasspathConfigurationName])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun setupBasic() {
|
private fun setupBasic() {
|
||||||
val client = sourceSets["client"]
|
val client = sourceSets["client"]
|
||||||
|
|
||||||
@@ -96,13 +99,30 @@ class MinecraftConfigurations private constructor(private val project: Project)
|
|||||||
val checkDependencyConsistency =
|
val checkDependencyConsistency =
|
||||||
project.tasks.register("checkDependencyConsistency", DependencyCheck::class.java) {
|
project.tasks.register("checkDependencyConsistency", DependencyCheck::class.java) {
|
||||||
// We need to check both the main and client classpath *configurations*, as the actual configuration
|
// We need to check both the main and client classpath *configurations*, as the actual configuration
|
||||||
configuration.add(configurations.named(main.runtimeClasspathConfigurationName))
|
configuration(configurations.named(main.runtimeClasspathConfigurationName))
|
||||||
configuration.add(configurations.named(client.runtimeClasspathConfigurationName))
|
configuration(configurations.named(client.runtimeClasspathConfigurationName))
|
||||||
}
|
}
|
||||||
project.tasks.named("check") { dependsOn(checkDependencyConsistency) }
|
project.tasks.named("check") { dependsOn(checkDependencyConsistency) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new configuration that pulls in the main and client classes from the mod.
|
||||||
|
*/
|
||||||
|
private fun createDerivedConfiguration(name: String) {
|
||||||
|
val client = sourceSets["client"]
|
||||||
|
val sourceSet = sourceSets.create(name)
|
||||||
|
sourceSet.compileClasspath += main.compileClasspath + client.compileClasspath
|
||||||
|
sourceSet.runtimeClasspath += main.runtimeClasspath + client.runtimeClasspath
|
||||||
|
consistentWithMain(sourceSet)
|
||||||
|
project.dependencies.add(sourceSet.implementationConfigurationName, main.output)
|
||||||
|
project.dependencies.add(sourceSet.implementationConfigurationName, client.output)
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
const val DATAGEN = "datagen"
|
||||||
|
const val EXAMPLES = "examples"
|
||||||
|
const val TEST_MOD = "testMod"
|
||||||
|
|
||||||
fun setupBasic(project: Project) {
|
fun setupBasic(project: Project) {
|
||||||
MinecraftConfigurations(project).setupBasic()
|
MinecraftConfigurations(project).setupBasic()
|
||||||
}
|
}
|
||||||
@@ -110,6 +130,10 @@ class MinecraftConfigurations private constructor(private val project: Project)
|
|||||||
fun setup(project: Project) {
|
fun setup(project: Project) {
|
||||||
MinecraftConfigurations(project).setup()
|
MinecraftConfigurations(project).setup()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun createDerivedConfiguration(project: Project, name: String) {
|
||||||
|
MinecraftConfigurations(project).createDerivedConfiguration(name)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
package cc.tweaked.gradle
|
package cc.tweaked.gradle
|
||||||
|
|
||||||
import net.minecraftforge.gradle.common.util.RunConfig
|
import net.neoforged.moddevgradle.internal.RunGameTask
|
||||||
import org.gradle.api.GradleException
|
import org.gradle.api.GradleException
|
||||||
import org.gradle.api.file.FileSystemOperations
|
import org.gradle.api.file.FileSystemOperations
|
||||||
import org.gradle.api.invocation.Gradle
|
import org.gradle.api.invocation.Gradle
|
||||||
@@ -19,6 +19,7 @@ import java.nio.file.Files
|
|||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
import java.util.function.Supplier
|
import java.util.function.Supplier
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
import kotlin.collections.set
|
||||||
import kotlin.random.Random
|
import kotlin.random.Random
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -65,11 +66,19 @@ abstract class ClientJavaExec : JavaExec() {
|
|||||||
setTestProperties()
|
setTestProperties()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun copyFromForge(path: String) = copyFromForge(project.tasks.getByName(path, RunGameTask::class))
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set this task to run a given [RunConfig].
|
* Set this task to run a given [RunGameTask].
|
||||||
*/
|
*/
|
||||||
fun setRunConfig(config: RunConfig) {
|
fun copyFromForge(task: RunGameTask) {
|
||||||
(this as JavaExec).setRunConfig(config)
|
copyFrom(task)
|
||||||
|
|
||||||
|
// Eagerly evaluate the behaviour of RunGameTask.exec
|
||||||
|
environment.putAll(task.environmentProperty.get())
|
||||||
|
classpath(task.classpathProvider)
|
||||||
|
workingDir = task.gameDirectory.get().asFile
|
||||||
|
|
||||||
setTestProperties() // setRunConfig may clobber some properties, ensure everything is set.
|
setTestProperties() // setRunConfig may clobber some properties, ensure everything is set.
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -117,6 +126,23 @@ abstract class ClientJavaExec : JavaExec() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configure Iris to use Complementary Shaders.
|
||||||
|
*/
|
||||||
|
fun withComplementaryShaders() {
|
||||||
|
val cct = project.extensions.getByType(CCTweakedExtension::class.java)
|
||||||
|
|
||||||
|
withFileFrom(workingDir.resolve("shaderpacks/ComplementaryShaders_v4.6.zip")) {
|
||||||
|
cct.downloadFile("Complementary Shaders", "https://edge.forgecdn.net/files/3951/170/ComplementaryShaders_v4.6.zip")
|
||||||
|
}
|
||||||
|
withFileContents(workingDir.resolve("config/iris.properties")) {
|
||||||
|
"""
|
||||||
|
enableShaders=true
|
||||||
|
shaderPack=ComplementaryShaders_v4.6.zip
|
||||||
|
""".trimIndent()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@TaskAction
|
@TaskAction
|
||||||
override fun exec() {
|
override fun exec() {
|
||||||
Files.createDirectories(workingDir.toPath())
|
Files.createDirectories(workingDir.toPath())
|
||||||
|
@@ -11,7 +11,9 @@ import org.gradle.api.file.Directory
|
|||||||
import org.gradle.api.file.DirectoryProperty
|
import org.gradle.api.file.DirectoryProperty
|
||||||
import org.gradle.api.provider.Provider
|
import org.gradle.api.provider.Provider
|
||||||
import org.gradle.api.tasks.*
|
import org.gradle.api.tasks.*
|
||||||
|
import org.gradle.process.ExecOperations
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
class NodePlugin : Plugin<Project> {
|
class NodePlugin : Plugin<Project> {
|
||||||
override fun apply(project: Project) {
|
override fun apply(project: Project) {
|
||||||
@@ -43,10 +45,13 @@ abstract class NpmInstall : DefaultTask() {
|
|||||||
@get:OutputDirectory
|
@get:OutputDirectory
|
||||||
val nodeModules: Provider<Directory> = projectRoot.dir("node_modules")
|
val nodeModules: Provider<Directory> = projectRoot.dir("node_modules")
|
||||||
|
|
||||||
|
@get:Inject
|
||||||
|
protected abstract val execOperations: ExecOperations
|
||||||
|
|
||||||
@TaskAction
|
@TaskAction
|
||||||
fun install() {
|
fun install() {
|
||||||
project.exec {
|
execOperations.exec {
|
||||||
commandLine("npm", "ci")
|
commandLine(ProcessHelpers.getExecutable("npm"), "ci")
|
||||||
workingDir = projectRoot.get().asFile
|
workingDir = projectRoot.get().asFile
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -59,6 +64,6 @@ abstract class NpmInstall : DefaultTask() {
|
|||||||
abstract class NpxExecToDir : ExecToDir() {
|
abstract class NpxExecToDir : ExecToDir() {
|
||||||
init {
|
init {
|
||||||
dependsOn(NpmInstall.TASK_NAME)
|
dependsOn(NpmInstall.TASK_NAME)
|
||||||
executable = "npx"
|
executable = ProcessHelpers.getExecutable("npx")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -4,48 +4,36 @@
|
|||||||
|
|
||||||
package cc.tweaked.gradle
|
package cc.tweaked.gradle
|
||||||
|
|
||||||
import org.codehaus.groovy.runtime.ProcessGroovyMethods
|
|
||||||
import org.gradle.api.GradleException
|
import org.gradle.api.GradleException
|
||||||
import java.io.BufferedReader
|
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.InputStreamReader
|
|
||||||
|
|
||||||
internal object ProcessHelpers {
|
internal object ProcessHelpers {
|
||||||
fun startProcess(vararg command: String): Process {
|
|
||||||
// Something randomly passes in "GIT_DIR=" as an environment variable which clobbers everything else. Don't
|
|
||||||
// inherit the environment array!
|
|
||||||
return ProcessBuilder()
|
|
||||||
.command(*command)
|
|
||||||
.redirectError(ProcessBuilder.Redirect.INHERIT)
|
|
||||||
.also { it.environment().clear() }
|
|
||||||
.start()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun captureOut(vararg command: String): String {
|
|
||||||
val process = startProcess(*command)
|
|
||||||
process.outputStream.close()
|
|
||||||
|
|
||||||
val result = ProcessGroovyMethods.getText(process)
|
|
||||||
process.waitForOrThrow("Failed to run command")
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
fun captureLines(vararg command: String): List<String> {
|
|
||||||
val process = startProcess(*command)
|
|
||||||
process.outputStream.close()
|
|
||||||
|
|
||||||
val out = BufferedReader(InputStreamReader(process.inputStream)).use { reader ->
|
|
||||||
reader.lines().filter { it.isNotEmpty() }.toList()
|
|
||||||
}
|
|
||||||
ProcessGroovyMethods.closeStreams(process)
|
|
||||||
process.waitForOrThrow("Failed to run command")
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
||||||
fun onPath(name: String): Boolean {
|
fun onPath(name: String): Boolean {
|
||||||
val path = System.getenv("PATH") ?: return false
|
val path = System.getenv("PATH") ?: return false
|
||||||
return path.splitToSequence(File.pathSeparator).any { File(it, name).exists() }
|
return path.splitToSequence(File.pathSeparator).any { File(it, name).exists() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Search for an executable on the `PATH` if required.
|
||||||
|
*
|
||||||
|
* [Process]/[ProcessBuilder] does not handle all executable file extensions on Windows (such as `.com). When on
|
||||||
|
* Windows, this function searches `PATH` and `PATHEXT` for an executable matching [name].
|
||||||
|
*/
|
||||||
|
fun getExecutable(name: String): String {
|
||||||
|
if (!System.getProperty("os.name").lowercase().contains("windows")) return name
|
||||||
|
|
||||||
|
val path = (System.getenv("PATH") ?: return name).split(File.pathSeparator)
|
||||||
|
val pathExt = (System.getenv("PATHEXT") ?: return name).split(File.pathSeparator)
|
||||||
|
|
||||||
|
for (pathEntry in path) {
|
||||||
|
for (ext in pathExt) {
|
||||||
|
val resolved = File(pathEntry, name + ext)
|
||||||
|
if (resolved.exists()) return resolved.getAbsolutePath()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return name
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun Process.waitForOrThrow(message: String) {
|
internal fun Process.waitForOrThrow(message: String) {
|
||||||
|
@@ -1,16 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: 2022 The CC: Tweaked Developers
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: MPL-2.0
|
|
||||||
|
|
||||||
package cc.tweaked.gradle
|
|
||||||
|
|
||||||
import groovy.util.Node
|
|
||||||
import groovy.util.NodeList
|
|
||||||
|
|
||||||
object XmlUtil {
|
|
||||||
fun findChild(node: Node, name: String): Node? = when (val child = node.get(name)) {
|
|
||||||
is Node -> child
|
|
||||||
is NodeList -> child.singleOrNull() as Node?
|
|
||||||
else -> null
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,51 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: MPL-2.0
|
|
||||||
|
|
||||||
package net.minecraftforge.gradle.common.util.runs
|
|
||||||
|
|
||||||
import net.minecraftforge.gradle.common.util.RunConfig
|
|
||||||
import org.gradle.api.Project
|
|
||||||
import org.gradle.process.CommandLineArgumentProvider
|
|
||||||
import org.gradle.process.JavaExecSpec
|
|
||||||
import java.io.File
|
|
||||||
import java.util.function.Supplier
|
|
||||||
import java.util.stream.Collectors
|
|
||||||
import java.util.stream.Stream
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set up a [JavaExecSpec] to execute a [RunConfig].
|
|
||||||
*
|
|
||||||
* [MinecraftRunTask] sets up all its properties when the task is executed, rather than when configured. As such, it's
|
|
||||||
* not possible to use [cc.tweaked.gradle.copyToFull] like we do for Fabric. Instead, we set up the task manually.
|
|
||||||
*
|
|
||||||
* Unfortunately most of the functionality we need is package-private, and so we have to put our code into the package.
|
|
||||||
*/
|
|
||||||
internal fun setRunConfigInternal(project: Project, spec: JavaExecSpec, config: RunConfig) {
|
|
||||||
spec.workingDir = File(config.workingDirectory)
|
|
||||||
|
|
||||||
spec.mainClass.set(config.main)
|
|
||||||
for (source in config.allSources) spec.classpath(source.runtimeClasspath)
|
|
||||||
|
|
||||||
val originalTask = project.tasks.named(config.taskName, MinecraftRunTask::class.java)
|
|
||||||
|
|
||||||
// Add argument and JVM argument via providers, to be as lazy as possible with fetching artifacts.
|
|
||||||
val lazyTokens = RunConfigGenerator.configureTokensLazy(
|
|
||||||
project, config, RunConfigGenerator.mapModClassesToGradle(project, config),
|
|
||||||
originalTask.get().minecraftArtifacts,
|
|
||||||
originalTask.get().runtimeClasspathArtifacts,
|
|
||||||
)
|
|
||||||
spec.argumentProviders.add(
|
|
||||||
CommandLineArgumentProvider {
|
|
||||||
RunConfigGenerator.getArgsStream(config, lazyTokens, false).toList()
|
|
||||||
},
|
|
||||||
)
|
|
||||||
spec.jvmArgumentProviders.add(
|
|
||||||
CommandLineArgumentProvider {
|
|
||||||
(if (config.isClient) config.jvmArgs + originalTask.get().additionalClientArgs.get() else config.jvmArgs).map { config.replace(lazyTokens, it) } +
|
|
||||||
config.properties.map { (k, v) -> "-D${k}=${config.replace(lazyTokens, v)}" }
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
for ((key, value) in config.environment) spec.environment(key, config.replace(lazyTokens, value))
|
|
||||||
}
|
|
@@ -13,10 +13,23 @@ SPDX-License-Identifier: MPL-2.0
|
|||||||
<property name="tabWidth" value="4"/>
|
<property name="tabWidth" value="4"/>
|
||||||
<property name="charset" value="UTF-8" />
|
<property name="charset" value="UTF-8" />
|
||||||
|
|
||||||
|
<module name="BeforeExecutionExclusionFileFilter">
|
||||||
|
<property name="fileNamePattern" value="module\-info\.java$"/>
|
||||||
|
</module>
|
||||||
|
|
||||||
<module name="SuppressionFilter">
|
<module name="SuppressionFilter">
|
||||||
<property name="file" value="${config_loc}/suppressions.xml" />
|
<property name="file" value="${config_loc}/suppressions.xml" />
|
||||||
</module>
|
</module>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Checkstyle doesn't support @snippet (https://github.com/checkstyle/checkstyle/issues/11455),
|
||||||
|
so suppress warnings nearby
|
||||||
|
-->
|
||||||
|
<module name="SuppressWithNearbyTextFilter">
|
||||||
|
<property name="nearbyTextPattern" value="@snippet" />
|
||||||
|
<property name="lineRange" value="20" />
|
||||||
|
</module>
|
||||||
|
|
||||||
<module name="BeforeExecutionExclusionFileFilter">
|
<module name="BeforeExecutionExclusionFileFilter">
|
||||||
<property name="fileNamePattern" value="render_old"/>
|
<property name="fileNamePattern" value="render_old"/>
|
||||||
</module>
|
</module>
|
||||||
@@ -88,7 +101,10 @@ SPDX-License-Identifier: MPL-2.0
|
|||||||
<module name="InvalidJavadocPosition" />
|
<module name="InvalidJavadocPosition" />
|
||||||
<module name="JavadocBlockTagLocation" />
|
<module name="JavadocBlockTagLocation" />
|
||||||
<module name="JavadocMethod"/>
|
<module name="JavadocMethod"/>
|
||||||
<module name="JavadocType"/>
|
<module name="JavadocType">
|
||||||
|
<!-- Seems to complain about @hidden!? -->
|
||||||
|
<property name="allowUnknownTags" value="true" />
|
||||||
|
</module>
|
||||||
<module name="JavadocStyle">
|
<module name="JavadocStyle">
|
||||||
<property name="checkHtml" value="false" />
|
<property name="checkHtml" value="false" />
|
||||||
</module>
|
</module>
|
||||||
@@ -113,14 +129,14 @@ SPDX-License-Identifier: MPL-2.0
|
|||||||
<module name="LocalFinalVariableName" />
|
<module name="LocalFinalVariableName" />
|
||||||
<module name="LocalVariableName" />
|
<module name="LocalVariableName" />
|
||||||
<module name="MemberName">
|
<module name="MemberName">
|
||||||
<property name="format" value="^\$?[a-z][a-zA-Z0-9]*$" />
|
<property name="format" value="^(computercraft\$|\$)?[a-z][a-zA-Z0-9]*$" />
|
||||||
</module>
|
</module>
|
||||||
<module name="MethodName">
|
<module name="MethodName">
|
||||||
<property name="format" value="^(computercraft\$)?[a-z][a-zA-Z0-9]*$" />
|
<property name="format" value="^(computercraft\$)?[a-z][a-zA-Z0-9]*$" />
|
||||||
</module>
|
</module>
|
||||||
<module name="MethodTypeParameterName" />
|
<module name="MethodTypeParameterName" />
|
||||||
<module name="PackageName">
|
<module name="PackageName">
|
||||||
<property name="format" value="^(dan200\.computercraft|cc\.tweaked)(\.[a-z][a-z0-9]*)*" />
|
<property name="format" value="^(dan200\.computercraft|cc\.tweaked|com\.example\.examplemod)(\.[a-z][a-z0-9]*)*" />
|
||||||
</module>
|
</module>
|
||||||
<module name="ParameterName" />
|
<module name="ParameterName" />
|
||||||
<module name="StaticVariableName">
|
<module name="StaticVariableName">
|
||||||
@@ -139,7 +155,10 @@ SPDX-License-Identifier: MPL-2.0
|
|||||||
<module name="NoWhitespaceAfter">
|
<module name="NoWhitespaceAfter">
|
||||||
<property name="tokens" value="AT,INC,DEC,UNARY_MINUS,UNARY_PLUS,BNOT,LNOT,DOT,ARRAY_DECLARATOR,INDEX_OP,METHOD_REF" />
|
<property name="tokens" value="AT,INC,DEC,UNARY_MINUS,UNARY_PLUS,BNOT,LNOT,DOT,ARRAY_DECLARATOR,INDEX_OP,METHOD_REF" />
|
||||||
</module>
|
</module>
|
||||||
<module name="NoWhitespaceBefore" />
|
<module name="NoWhitespaceBefore">
|
||||||
|
<!-- Allow whitespace before "..." for @Nullable annotations -->
|
||||||
|
<property name="tokens" value="COMMA,SEMI,POST_INC,POST_DEC,LABELED_STAT" />
|
||||||
|
</module>
|
||||||
<!-- TODO: Decide on an OperatorWrap style. -->
|
<!-- TODO: Decide on an OperatorWrap style. -->
|
||||||
<module name="ParenPad" />
|
<module name="ParenPad" />
|
||||||
<module name="SeparatorWrap">
|
<module name="SeparatorWrap">
|
||||||
|
@@ -21,5 +21,8 @@ SPDX-License-Identifier: MPL-2.0
|
|||||||
<suppress checks="PackageName" files=".*[\\/]T[A-Za-z]+.java" />
|
<suppress checks="PackageName" files=".*[\\/]T[A-Za-z]+.java" />
|
||||||
|
|
||||||
<!-- Allow underscores in our test classes. -->
|
<!-- Allow underscores in our test classes. -->
|
||||||
<suppress checks="MethodName" files=".*Contract.java" />
|
<suppress checks="MethodName" files=".*(Contract|Test).java" />
|
||||||
|
|
||||||
|
<!-- Allow underscores in Mixin classes -->
|
||||||
|
<suppress checks="TypeName" files=".*[\\/]mixin[\\/].*.java" />
|
||||||
</suppressions>
|
</suppressions>
|
||||||
|
@@ -1,12 +0,0 @@
|
|||||||
# SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
|
|
||||||
#
|
|
||||||
# SPDX-License-Identifier: MPL-2.0
|
|
||||||
|
|
||||||
FROM gitpod/workspace-base
|
|
||||||
|
|
||||||
USER gitpod
|
|
||||||
|
|
||||||
RUN sudo apt-get -q update \
|
|
||||||
&& sudo apt-get install -yq openjdk-16-jdk python3-pip npm \
|
|
||||||
&& sudo pip3 install pre-commit \
|
|
||||||
&& sudo update-java-alternatives --set java-1.16.0-openjdk-amd64
|
|
28
crowdin.yml
Normal file
28
crowdin.yml
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
# SPDX-FileCopyrightText: 2017 The CC: Tweaked Developers
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
|
files:
|
||||||
|
- source: projects/common/src/generated/resources/assets/computercraft/lang/en_us.json
|
||||||
|
translation: /projects/common/src/main/resources/assets/computercraft/lang/%locale_with_underscore%.json
|
||||||
|
languages_mapping:
|
||||||
|
locale_with_underscore:
|
||||||
|
cs: cs_cz # Czech
|
||||||
|
da: da_dk # Danish
|
||||||
|
de: de_de # German
|
||||||
|
es-ES: es_es # Spanish
|
||||||
|
fr: fr_fr # French
|
||||||
|
it: it_it # Italian
|
||||||
|
ja: ja_jp # Japanese
|
||||||
|
ko: ko_kr # Korean
|
||||||
|
nb: nb_no # Norwegian Bokmal
|
||||||
|
nl: nl_nl # Dutch
|
||||||
|
pl: pl_pl # Polish
|
||||||
|
pt-BR: pt_br # Portuguese, Brazilian
|
||||||
|
ru: ru_ru # Russian
|
||||||
|
sv-SE: sv_se # Sweedish
|
||||||
|
tok: tok # Toki Pona
|
||||||
|
tr: tr_tr # Turkish
|
||||||
|
uk: uk_ua # Ukraine
|
||||||
|
vi: vi_vn # Vietnamese
|
||||||
|
zh-CN: zh_cn # Chinese Simplified
|
@@ -8,7 +8,7 @@ SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
|
|||||||
SPDX-License-Identifier: MPL-2.0
|
SPDX-License-Identifier: MPL-2.0
|
||||||
-->
|
-->
|
||||||
|
|
||||||
The [`monitor_resize`] event is fired when an adjacent or networked monitor's size is changed.
|
The [`monitor_resize`] event is fired when an adjacent or networked [monitor's][`monitor`] size is changed.
|
||||||
|
|
||||||
## Return Values
|
## Return Values
|
||||||
1. [`string`]: The event name.
|
1. [`string`]: The event name.
|
||||||
|
@@ -8,7 +8,7 @@ SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
|
|||||||
SPDX-License-Identifier: MPL-2.0
|
SPDX-License-Identifier: MPL-2.0
|
||||||
-->
|
-->
|
||||||
|
|
||||||
The [`monitor_touch`] event is fired when an adjacent or networked Advanced Monitor is right-clicked.
|
The [`monitor_touch`] event is fired when an adjacent or networked [Advanced Monitor][`monitor`] is right-clicked.
|
||||||
|
|
||||||
## Return Values
|
## Return Values
|
||||||
1. [`string`]: The event name.
|
1. [`string`]: The event name.
|
||||||
|
@@ -8,7 +8,7 @@ SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
|
|||||||
SPDX-License-Identifier: MPL-2.0
|
SPDX-License-Identifier: MPL-2.0
|
||||||
-->
|
-->
|
||||||
|
|
||||||
The [`event!redstone`] event is fired whenever any redstone inputs on the computer change.
|
The [`event!redstone`] event is fired whenever any redstone inputs on the computer or [relay][`redstone_relay`] change.
|
||||||
|
|
||||||
## Return values
|
## Return values
|
||||||
1. [`string`]: The event name.
|
1. [`string`]: The event name.
|
||||||
@@ -21,3 +21,7 @@ while true do
|
|||||||
print("A redstone input has changed!")
|
print("A redstone input has changed!")
|
||||||
end
|
end
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## See also
|
||||||
|
- [The `redstone` API on computers][`module!redstone`]
|
||||||
|
- [The `redstone_relay` peripheral][`redstone_relay`]
|
||||||
|
@@ -19,7 +19,7 @@ In order to give the best results, a GPS constellation needs at least four compu
|
|||||||
constellation is redundant, but it does not cause problems.
|
constellation is redundant, but it does not cause problems.
|
||||||
|
|
||||||
## Building a GPS constellation
|
## Building a GPS constellation
|
||||||
<img alt="An example GPS constellation." src="/images/gps-constellation-example.png" class="big-image" />
|
<img alt="An example GPS constellation." src="../images/gps-constellation-example.png" class="big-image" />
|
||||||
|
|
||||||
We are going to build our GPS constellation as shown in the image above. You will need 4 computers and either 4 wireless
|
We are going to build our GPS constellation as shown in the image above. You will need 4 computers and either 4 wireless
|
||||||
modems or 4 ender modems. Try not to mix ender and wireless modems together as you might get some odd behavior when your
|
modems or 4 ender modems. Try not to mix ender and wireless modems together as you might get some odd behavior when your
|
||||||
|
@@ -131,7 +131,7 @@ different.
|
|||||||
First, we require the dfpwm module and call [`cc.audio.dfpwm.make_decoder`] to construct a new decoder. This decoder
|
First, we require the dfpwm module and call [`cc.audio.dfpwm.make_decoder`] to construct a new decoder. This decoder
|
||||||
accepts blocks of DFPWM data and converts it to a list of 8-bit amplitudes, which we can then play with our speaker.
|
accepts blocks of DFPWM data and converts it to a list of 8-bit amplitudes, which we can then play with our speaker.
|
||||||
|
|
||||||
As mentioned above, [`speaker.playAudio`] accepts at most 128×1024 samples in one go. DFPMW uses a single bit for each
|
As mentioned above, [`speaker.playAudio`] accepts at most 128×1024 samples in one go. DFPWM uses a single bit for each
|
||||||
sample, which means we want to process our audio in chunks of 16×1024 bytes (16KiB). In order to do this, we use
|
sample, which means we want to process our audio in chunks of 16×1024 bytes (16KiB). In order to do this, we use
|
||||||
[`io.lines`], which provides a nice way to loop over chunks of a file. You can of course just use [`fs.open`] and
|
[`io.lines`], which provides a nice way to loop over chunks of a file. You can of course just use [`fs.open`] and
|
||||||
[`fs.ReadHandle.read`] if you prefer.
|
[`fs.ReadHandle.read`] if you prefer.
|
||||||
@@ -191,7 +191,7 @@ end
|
|||||||
|
|
||||||
> [Confused?][!NOTE]
|
> [Confused?][!NOTE]
|
||||||
> Don't worry if you don't understand this example. It's quite advanced, and does use some ideas that this guide doesn't
|
> Don't worry if you don't understand this example. It's quite advanced, and does use some ideas that this guide doesn't
|
||||||
> cover. That said, don't be afraid to ask on [GitHub Discussions] or [IRC] either!
|
> cover. That said, don't be afraid to ask [the community for help][community].
|
||||||
|
|
||||||
It's worth noting that the examples of audio processing we've mentioned here are about manipulating the _amplitude_ of
|
It's worth noting that the examples of audio processing we've mentioned here are about manipulating the _amplitude_ of
|
||||||
the wave. If you wanted to modify the _frequency_ (for instance, shifting the pitch), things get rather more complex.
|
the wave. If you wanted to modify the _frequency_ (for instance, shifting the pitch), things get rather more complex.
|
||||||
@@ -205,5 +205,4 @@ This is, I'm afraid, left as an exercise to the reader.
|
|||||||
[PCM]: https://en.wikipedia.org/wiki/Pulse-code_modulation "Pulse-code Modulation - Wikipedia"
|
[PCM]: https://en.wikipedia.org/wiki/Pulse-code_modulation "Pulse-code Modulation - Wikipedia"
|
||||||
[Ring Buffer]: https://en.wikipedia.org/wiki/Circular_buffer "Circular buffer - Wikipedia"
|
[Ring Buffer]: https://en.wikipedia.org/wiki/Circular_buffer "Circular buffer - Wikipedia"
|
||||||
[Sine Wave]: https://en.wikipedia.org/wiki/Sine_wave "Sine wave - Wikipedia"
|
[Sine Wave]: https://en.wikipedia.org/wiki/Sine_wave "Sine wave - Wikipedia"
|
||||||
[GitHub Discussions]: https://github.com/cc-tweaked/CC-Tweaked/discussions
|
[Community]: /#community
|
||||||
[IRC]: https://webchat.esper.net/?channels=computercraft "#computercraft on EsperNet"
|
|
||||||
|
BIN
doc/images/computercraft-dump.png
Normal file
BIN
doc/images/computercraft-dump.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 254 KiB |
BIN
doc/images/computercraft-track.png
Normal file
BIN
doc/images/computercraft-track.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 304 KiB |
23
doc/index.md
23
doc/index.md
@@ -4,12 +4,19 @@ SPDX-FileCopyrightText: 2020 The CC: Tweaked Developers
|
|||||||
SPDX-License-Identifier: MPL-2.0
|
SPDX-License-Identifier: MPL-2.0
|
||||||
-->
|
-->
|
||||||
|
|
||||||
# 
|
<h1>
|
||||||
|
<picture>
|
||||||
|
<source media="(prefers-color-scheme: dark)" srcset="logo-darkmode.png">
|
||||||
|
<source media="(prefers-color-scheme: light)" srcset="logo.png">
|
||||||
|
<img alt="CC: Tweaked" src="logo.png">
|
||||||
|
</picture>
|
||||||
|
</h1>
|
||||||
|
|
||||||
CC: Tweaked is a mod for Minecraft which adds programmable computers, turtles and more to the game. A fork of the
|
CC: Tweaked is a mod for Minecraft which adds programmable computers, turtles and more to the game. A fork of the
|
||||||
much-beloved [ComputerCraft], it continues its legacy with improved performance and stability, along with a wealth of
|
much-beloved [ComputerCraft], it continues its legacy with improved performance and stability, along with a wealth of
|
||||||
new features.
|
new features.
|
||||||
|
|
||||||
CC: Tweaked can be installed from [CurseForge] or [Modrinth]. It runs on both [Minecraft Forge] and [Fabric].
|
CC: Tweaked can be installed from [Modrinth]. It runs on both [Minecraft Forge] and [Fabric].
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
Controlled using the [Lua programming language][lua], CC: Tweaked's computers provides all the tools you need to start
|
Controlled using the [Lua programming language][lua], CC: Tweaked's computers provides all the tools you need to start
|
||||||
@@ -38,12 +45,16 @@ little daunting getting started. Thankfully, there's several fantastic tutorials
|
|||||||
|
|
||||||
- [Direwolf20's ComputerCraft tutorials](https://www.youtube.com/watch?v=wrUHUhfCY5A "ComputerCraft Tutorial Episode 1 - HELP! and Hello World")
|
- [Direwolf20's ComputerCraft tutorials](https://www.youtube.com/watch?v=wrUHUhfCY5A "ComputerCraft Tutorial Episode 1 - HELP! and Hello World")
|
||||||
- [Sethbling's ComputerCraft series](https://www.youtube.com/watch?v=DSsx4VSe-Uk "Programming Tutorial with Minecraft Turtles -- Ep. 1: Intro to Turtles and If-Then-Else_End")
|
- [Sethbling's ComputerCraft series](https://www.youtube.com/watch?v=DSsx4VSe-Uk "Programming Tutorial with Minecraft Turtles -- Ep. 1: Intro to Turtles and If-Then-Else_End")
|
||||||
- [Lyqyd's Computer Basics 1](http://www.computercraft.info/forums2/index.php?/topic/15033-computer-basics-i/ "Computer Basics I")
|
- [Lyqyd's Computer Basics 1](https://ccf.squiddev.cc/forums2/index.php?/topic/15033-computer-basics-i/ "Computer Basics I")
|
||||||
|
|
||||||
Once you're a little more familiar with the mod, the sidebar and links below provide more detailed documentation on the
|
Once you're a little more familiar with the mod, the sidebar and links below provide more detailed documentation on the
|
||||||
various APIs and peripherals provided by the mod.
|
various APIs and peripherals provided by the mod.
|
||||||
|
|
||||||
If you get stuck, do [ask a question on GitHub][GitHub Discussions] or pop in to the ComputerCraft's [IRC channel][IRC].
|
<h2 id="community">Community</h2>
|
||||||
|
If you need help getting started with CC: Tweaked, want to show off your latest project, or just want to chat about
|
||||||
|
ComputerCraft, do check out our [GitHub discussions page][GitHub discussions]! There's also a fairly populated,
|
||||||
|
albeit quiet IRC channel on [EsperNet], if that's more your cup of tea. You can join `#computercraft` through your
|
||||||
|
desktop client, or online using [KiwiIRC].
|
||||||
|
|
||||||
## Get Involved
|
## Get Involved
|
||||||
CC: Tweaked lives on [GitHub]. If you've got any ideas, feedback or bugs please do [create an issue][bug].
|
CC: Tweaked lives on [GitHub]. If you've got any ideas, feedback or bugs please do [create an issue][bug].
|
||||||
@@ -51,11 +62,11 @@ CC: Tweaked lives on [GitHub]. If you've got any ideas, feedback or bugs please
|
|||||||
[github]: https://github.com/cc-tweaked/CC-Tweaked/ "CC: Tweaked on GitHub"
|
[github]: https://github.com/cc-tweaked/CC-Tweaked/ "CC: Tweaked on GitHub"
|
||||||
[bug]: https://github.com/cc-tweaked/CC-Tweaked/issues/new/choose
|
[bug]: https://github.com/cc-tweaked/CC-Tweaked/issues/new/choose
|
||||||
[computercraft]: https://github.com/dan200/ComputerCraft "ComputerCraft on GitHub"
|
[computercraft]: https://github.com/dan200/ComputerCraft "ComputerCraft on GitHub"
|
||||||
[curseforge]: https://minecraft.curseforge.com/projects/cc-tweaked "Download CC: Tweaked from CurseForge"
|
|
||||||
[modrinth]: https://modrinth.com/mod/gu7yAYhd "Download CC: Tweaked from Modrinth"
|
[modrinth]: https://modrinth.com/mod/gu7yAYhd "Download CC: Tweaked from Modrinth"
|
||||||
[forge]: https://files.minecraftforge.net/ "Download Minecraft Forge."
|
[forge]: https://files.minecraftforge.net/ "Download Minecraft Forge."
|
||||||
[Minecraft Forge]: https://files.minecraftforge.net/ "Download Minecraft Forge."
|
[Minecraft Forge]: https://files.minecraftforge.net/ "Download Minecraft Forge."
|
||||||
[Fabric]: https://fabricmc.net/use/installer/ "Download Fabric."
|
[Fabric]: https://fabricmc.net/use/installer/ "Download Fabric."
|
||||||
[lua]: https://www.lua.org/ "Lua's main website"
|
[lua]: https://www.lua.org/ "Lua's main website"
|
||||||
[GitHub Discussions]: https://github.com/cc-tweaked/CC-Tweaked/discussions
|
[GitHub Discussions]: https://github.com/cc-tweaked/CC-Tweaked/discussions
|
||||||
[IRC]: https://webchat.esper.net/?channels=computercraft "#computercraft on EsperNet"
|
[EsperNet]: https://www.esper.net/
|
||||||
|
[KiwiIRC]: https://kiwiirc.com/nextclient/#irc://irc.esper.net:+6697/#computercraft "#computercraft on EsperNet"
|
||||||
|
@@ -45,12 +45,16 @@ little daunting getting started. Thankfully, there's several fantastic tutorials
|
|||||||
|
|
||||||
- [Direwolf20's ComputerCraft tutorials](https://www.youtube.com/watch?v=wrUHUhfCY5A "ComputerCraft Tutorial Episode 1 - HELP! and Hello World")
|
- [Direwolf20's ComputerCraft tutorials](https://www.youtube.com/watch?v=wrUHUhfCY5A "ComputerCraft Tutorial Episode 1 - HELP! and Hello World")
|
||||||
- [Sethbling's ComputerCraft series](https://www.youtube.com/watch?v=DSsx4VSe-Uk "Programming Tutorial with Minecraft Turtles -- Ep. 1: Intro to Turtles and If-Then-Else_End")
|
- [Sethbling's ComputerCraft series](https://www.youtube.com/watch?v=DSsx4VSe-Uk "Programming Tutorial with Minecraft Turtles -- Ep. 1: Intro to Turtles and If-Then-Else_End")
|
||||||
- [Lyqyd's Computer Basics 1](http://www.computercraft.info/forums2/index.php?/topic/15033-computer-basics-i/ "Computer Basics I")
|
- [Lyqyd's Computer Basics 1](https://ccf.squiddev.cc/forums2/index.php?/topic/15033-computer-basics-i/ "Computer Basics I")
|
||||||
|
|
||||||
Once you're a little more familiar with the mod, the [wiki](https://tweaked.cc/) provides more detailed documentation on the
|
Once you're a little more familiar with the mod, the [wiki](https://tweaked.cc/) provides more detailed documentation on the
|
||||||
various APIs and peripherals provided by the mod.
|
various APIs and peripherals provided by the mod.
|
||||||
|
|
||||||
If you get stuck, do [ask a question on GitHub][GitHub Discussions] or pop in to the ComputerCraft's [IRC channel][IRC].
|
## Community
|
||||||
|
If you need help getting started with CC: Tweaked, want to show off your latest project, or just want to chat about
|
||||||
|
ComputerCraft, do check out our [GitHub discussions page][GitHub discussions]! There's also a fairly populated,
|
||||||
|
albeit quiet IRC channel on [EsperNet], if that's more your cup of tea. You can join `#computercraft` through your
|
||||||
|
desktop client, or online using [KiwiIRC].
|
||||||
|
|
||||||
## Get Involved
|
## Get Involved
|
||||||
CC: Tweaked lives on [GitHub]. If you've got any ideas, feedback or bugs please do [create an issue][bug].
|
CC: Tweaked lives on [GitHub]. If you've got any ideas, feedback or bugs please do [create an issue][bug].
|
||||||
@@ -60,4 +64,5 @@ CC: Tweaked lives on [GitHub]. If you've got any ideas, feedback or bugs please
|
|||||||
[computercraft]: https://github.com/dan200/ComputerCraft "ComputerCraft on GitHub"
|
[computercraft]: https://github.com/dan200/ComputerCraft "ComputerCraft on GitHub"
|
||||||
[lua]: https://www.lua.org/ "Lua's main website"
|
[lua]: https://www.lua.org/ "Lua's main website"
|
||||||
[GitHub Discussions]: https://github.com/cc-tweaked/CC-Tweaked/discussions
|
[GitHub Discussions]: https://github.com/cc-tweaked/CC-Tweaked/discussions
|
||||||
[IRC]: http://webchat.esper.net/?channels=computercraft "#computercraft on EsperNet"
|
[EsperNet]: https://www.esper.net/
|
||||||
|
[KiwiIRC]: https://kiwiirc.com/nextclient/#irc://irc.esper.net:+6697/#computercraft "#computercraft on EsperNet"
|
||||||
|
@@ -25,13 +25,13 @@ as documentation for breaking changes and "gotchas" one should look out for betw
|
|||||||
|
|
||||||
- Update to Lua 5.2:
|
- Update to Lua 5.2:
|
||||||
- Support for Lua 5.0's pseudo-argument `arg` has been removed. You should always use `...` for varargs.
|
- Support for Lua 5.0's pseudo-argument `arg` has been removed. You should always use `...` for varargs.
|
||||||
- Environments are no longer baked into the runtime, and instead use the `_ENV` local or upvalue. `getfenv`/`setfenv`
|
- Environments are no longer baked into the runtime, and instead use the `_ENV` local or upvalue. [`getfenv`]/[`setfenv`]
|
||||||
now only work on Lua functions with an `_ENV` upvalue. `getfenv` will return the global environment when called
|
now only work on Lua functions with an `_ENV` upvalue. [`getfenv`] will return the global environment when called
|
||||||
with other functions, and `setfenv` will have no effect.
|
with other functions, and [`setfenv`] will have no effect.
|
||||||
- `load`/`loadstring` defaults to using the global environment (`_G`) rather than the current coroutine's
|
- [`load`]/[`loadstring`] defaults to using the global environment (`_G`) rather than the current coroutine's
|
||||||
environment.
|
environment.
|
||||||
- Support for dumping functions (`string.dump`) and loading binary chunks has been removed.
|
- Support for dumping functions ([`string.dump`]) and loading binary chunks has been removed.
|
||||||
- `math.random` now uses Lua 5.4's random number generator.
|
- [`math.random`] now uses Lua 5.4's random number generator.
|
||||||
|
|
||||||
- File handles, HTTP requests and websockets now always use the original bytes rather than encoding/decoding to UTF-8.
|
- File handles, HTTP requests and websockets now always use the original bytes rather than encoding/decoding to UTF-8.
|
||||||
|
|
||||||
@@ -44,7 +44,7 @@ as documentation for breaking changes and "gotchas" one should look out for betw
|
|||||||
`keys.enter` constant was queued when the key was pressed)
|
`keys.enter` constant was queued when the key was pressed)
|
||||||
|
|
||||||
- Minecraft 1.13 removed the concept of item damage and block metadata (see ["The Flattening"][flattening]). As a
|
- Minecraft 1.13 removed the concept of item damage and block metadata (see ["The Flattening"][flattening]). As a
|
||||||
result `turtle.inspect` no longer provides block metadata, and `turtle.getItemDetail` no longer provides damage.
|
result [`turtle.inspect`] no longer provides block metadata, and [`turtle.getItemDetail`] no longer provides damage.
|
||||||
|
|
||||||
- Block states (`turtle.inspect().state`) should provide all the same information as block metadata, but in a much
|
- Block states (`turtle.inspect().state`) should provide all the same information as block metadata, but in a much
|
||||||
more understandable format.
|
more understandable format.
|
||||||
@@ -70,12 +70,12 @@ as documentation for breaking changes and "gotchas" one should look out for betw
|
|||||||
- Unlabelled computers and turtles now keep their ID when broken, meaning that unlabelled computers/items do not stack.
|
- Unlabelled computers and turtles now keep their ID when broken, meaning that unlabelled computers/items do not stack.
|
||||||
|
|
||||||
## ComputerCraft 1.80pr1 {#cc-1.80}
|
## ComputerCraft 1.80pr1 {#cc-1.80}
|
||||||
- Programs run via `shell.run` are now started in their own isolated environment. This means globals set by programs
|
- Programs run via [`shell.run`] are now started in their own isolated environment. This means globals set by programs
|
||||||
will not be accessible outside of this program.
|
will not be accessible outside of this program.
|
||||||
|
|
||||||
- Programs containing `/` are looked up in the current directory and are no longer looked up on the path. For instance,
|
- Programs containing `/` are looked up in the current directory and are no longer looked up on the path. For instance,
|
||||||
you can no longer type `turtle/excavate` to run `/rom/programs/turtle/excavate.lua`.
|
you can no longer type `turtle/excavate` to run `/rom/programs/turtle/excavate.lua`.
|
||||||
|
|
||||||
[flattening]: https://minecraft.wiki/w/Java_Edition_1.13/Flattening
|
[flattening]: https://minecraft.wiki/w/Java_Edition_1.13/Flattening
|
||||||
[legal_data_pack]: https://minecraft.gamepedia.com/Tutorials/Creating_a_data_pack#Legal_characters
|
[legal_data_pack]: https://minecraft.wiki/w/Tutorials/Creating_a_data_pack#Legal_characters
|
||||||
[datapack-example]: https://github.com/cc-tweaked/datapack-example "An example datapack for CC: Tweaked"
|
[datapack-example]: https://github.com/cc-tweaked/datapack-example "An example datapack for CC: Tweaked"
|
||||||
|
140
doc/reference/command.md
Normal file
140
doc/reference/command.md
Normal file
@@ -0,0 +1,140 @@
|
|||||||
|
---
|
||||||
|
module: [kind=reference] computercraft_command
|
||||||
|
---
|
||||||
|
|
||||||
|
<!--
|
||||||
|
SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers
|
||||||
|
|
||||||
|
SPDX-License-Identifier: MPL-2.0
|
||||||
|
-->
|
||||||
|
|
||||||
|
# The `/computercraft` command
|
||||||
|
CC: Tweaked provides a `/computercraft` command for server owners to manage running computers on a server.
|
||||||
|
|
||||||
|
## Permissions {#permissions}
|
||||||
|
As the `/computercraft` command is mostly intended for debugging and administrative purposes, its sub-commands typically
|
||||||
|
require you to have op (or similar).
|
||||||
|
|
||||||
|
- All players have access to the [`queue`] sub-command.
|
||||||
|
- On a multi-player server, all other commands require op.
|
||||||
|
- On a single-player world, the player can run the [`dump`], [`turn-on`]/[`shutdown`], and [`track`] sub-commands, even
|
||||||
|
when cheats are not enabled. The [`tp`] and [`view`] commands require cheats.
|
||||||
|
|
||||||
|
If a permission mod such as [LuckPerms] is installed[^permission], you can configure access to the individual
|
||||||
|
sub-commands. Each sub-command creates a `computercraft.command.NAME` permission node to control which players can
|
||||||
|
execute it.
|
||||||
|
|
||||||
|
[LuckPerms]: https://github.com/LuckPerms/LuckPerms/ "A permissions plugin for Minecraft servers."
|
||||||
|
[fabric-permission-api]: https://github.com/lucko/fabric-permissions-api "A simple permissions API for Fabric"
|
||||||
|
|
||||||
|
[^permission]: This supports any mod which uses Forge's permission API or [fabric-permission-api].
|
||||||
|
|
||||||
|
## Computer selectors {#computer-selectors}
|
||||||
|
Some commands (such as [`tp`] or [`turn-on`]) target a specific computer, or a list of computers. To specify which
|
||||||
|
computers to operate on, you must use "computer selectors".
|
||||||
|
|
||||||
|
Computer selectors are similar to Minecraft's [entity target selectors], but targeting computers instead. They allow
|
||||||
|
you to select one or more computers, based on a set of predicates.
|
||||||
|
|
||||||
|
The following predicates are supported:
|
||||||
|
- `id=<id>`: Select computer(s) with a specific id.
|
||||||
|
- `instance=<id>`: Select the computer with the given instance id.
|
||||||
|
- `family=<normal|advanced|command>`: Select computers based on their type.
|
||||||
|
- `label=<label>`: Select computers with the given label.
|
||||||
|
- `distance=<distance>`: Select computers within a specific distance of the player executing the command. This uses
|
||||||
|
Minecraft's [float range] syntax.
|
||||||
|
|
||||||
|
`#<id>` may also be used as a shorthand for `@c[id=<id>]`, to select computer(s) with a specific id.
|
||||||
|
|
||||||
|
### Examples:
|
||||||
|
- `/computercraft turn-on #12`: Turn on the computer(s) with an id of 12.
|
||||||
|
- `/computercraft shutdown @c[distance=..100]`: Shut down all computers with 100 blocks of the player.
|
||||||
|
|
||||||
|
[entity target selectors]: https://minecraft.wiki/w/Target_selectors "Target Selectors on the Minecraft wiki"
|
||||||
|
[Float range]: https://minecraft.wiki/w/Argument_types#minecraft:float_range
|
||||||
|
|
||||||
|
## Commands {#commands}
|
||||||
|
### `/computercraft dump` {#dump}
|
||||||
|
`/computercraft dump` prints a table of currently loaded computers, including their id, position, and whether they're
|
||||||
|
running. It can also be run with a single computer argument to dump more detailed information about a computer.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Next to the computer id, there are several buttons to either [teleport][`tp`] to the computer, or [open its terminal
|
||||||
|
][`view`].
|
||||||
|
|
||||||
|
Computers are sorted by distance to the player, so nearby computers will appear earlier.
|
||||||
|
|
||||||
|
### `/computercraft turn-on [computers...]` {#turn-on}
|
||||||
|
Turn on one or more computers or, if no run with no arguments, all loaded computers.
|
||||||
|
|
||||||
|
#### Examples
|
||||||
|
- `/computercraft turn-on #0 #2`: Turn on computers with id 0 and 2.
|
||||||
|
- `/computercraft turn-on @c[family=command]`: Turn on all command computers.
|
||||||
|
|
||||||
|
### `/computercraft shutdown [computers...]` {#shutdown}
|
||||||
|
Shutdown one or more computers or, if no run with no arguments, all loaded computers.
|
||||||
|
|
||||||
|
This is sometimes useful when dealing with lag, as a way to ensure that ComputerCraft is not causing problems.
|
||||||
|
|
||||||
|
#### Examples
|
||||||
|
- `/computercraft shutdown`: Shut down all loaded computers.
|
||||||
|
- `/computercraft shutdown @c[distance=..10]`: Shut down all computers in a block radius.
|
||||||
|
|
||||||
|
### `/computercraft tp [computer]` {#tp}
|
||||||
|
Teleport to the given computer.
|
||||||
|
|
||||||
|
This is normally used from via the [`dump`] command interface rather than being invoked directly.
|
||||||
|
|
||||||
|
### `/computercraft view [computer]` {#view}
|
||||||
|
Open a terminal for the specified computer. This allows remotely viewing computers without having to interact with the
|
||||||
|
block.
|
||||||
|
|
||||||
|
This is normally used from via the [`dump`] command interface rather than being invoked directly.
|
||||||
|
|
||||||
|
### `/computercraft track` {#track}
|
||||||
|
The `/computercraft track` command allows you to enable profiling of computers. When a computer runs code, or interacts
|
||||||
|
with the Minecraft world, we time how long that takes. This timing information may then be queried, and used to find
|
||||||
|
computers which may be causing lag.
|
||||||
|
|
||||||
|
To enable the profiler, run `/computercraft track start`. Computers will then start recording metrics. Once enough data
|
||||||
|
has been gathered, run `/computercraft track stop` to stop profiling and display the recorded data.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
The table by default shows the number of times each computer has run, and how long it ran for (in total, and on
|
||||||
|
average). In the above screenshot, we can see one computer was particularly badly behaved, and ran for 7 seconds. The
|
||||||
|
buttons may be used to [teleport][`tp`] to the computer, or [open its terminal ][`view`], and inspect it further.
|
||||||
|
|
||||||
|
`/computercraft track dump` can be used to display this table at any point (including while profiling is still running).
|
||||||
|
|
||||||
|
Computers also record other information, such as how much server-thread time they consume, or their HTTP bandwidth
|
||||||
|
usage. The `dump` subcommand accepts a list of other fields to display, instead of the default timings.
|
||||||
|
|
||||||
|
#### Examples
|
||||||
|
- `/computercraft track dump server_tasks_count server_tasks`: Print the number of server-thread tasks each computer
|
||||||
|
executed, and how long they took in total.
|
||||||
|
- `/computercraft track dump http_upload http_download`: Print the number of bytes uploaded and downloaded by each
|
||||||
|
computer.
|
||||||
|
|
||||||
|
|
||||||
|
### `/computercraft queue` {#queue}
|
||||||
|
The queue subcommand allows non-operator players to queue a `computer_command` event on *command* computers.
|
||||||
|
|
||||||
|
This has a similar purpose to vanilla's [`/trigger`] command. Command computers may choose to listen to this event, and
|
||||||
|
then perform some action.
|
||||||
|
|
||||||
|
[`/trigger`]: https://minecraft.wiki/w/Commands/trigger "/trigger on the Minecraft wiki"
|
||||||
|
|
||||||
|
|
||||||
|
[`dump`]: #dump "/computercraft dump"
|
||||||
|
[`queue`]: #queue "/computercraft queue"
|
||||||
|
[`shutdown`]: #shutdown "/computercraft shutdown"
|
||||||
|
[`tp`]: #tp "/computercraft tp"
|
||||||
|
[`track`]: #track "/computercraft track"
|
||||||
|
[`turn-on`]: #turn-on "/computercraft turn-on"
|
||||||
|
[`view`]: #view "/computercraft view"
|
||||||
|
[computer selectors]: #computer-selectors "Computer selectors"
|
@@ -81,7 +81,7 @@ compatibility for these newer versions.
|
|||||||
| `string.dump` strip argument | ✔ | |
|
| `string.dump` strip argument | ✔ | |
|
||||||
| `string.pack`/`string.unpack`/`string.packsize` | ✔ | |
|
| `string.pack`/`string.unpack`/`string.packsize` | ✔ | |
|
||||||
| `table.move` | ✔ | |
|
| `table.move` | ✔ | |
|
||||||
| `math.atan2` -> `math.atan` | ❌ | |
|
| `math.atan2` -> `math.atan` | 🔶 | `math.atan` supports its two argument form. |
|
||||||
| Removed `math.frexp`, `math.ldexp`, `math.pow`, `math.cosh`, `math.sinh`, `math.tanh` | ❌ | |
|
| Removed `math.frexp`, `math.ldexp`, `math.pow`, `math.cosh`, `math.sinh`, `math.tanh` | ❌ | |
|
||||||
| `math.maxinteger`/`math.mininteger` | ❌ | |
|
| `math.maxinteger`/`math.mininteger` | ❌ | |
|
||||||
| `math.tointeger` | ❌ | |
|
| `math.tointeger` | ❌ | |
|
||||||
|
@@ -2,15 +2,17 @@
|
|||||||
#
|
#
|
||||||
# SPDX-License-Identifier: MPL-2.0
|
# SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
org.gradle.jvmargs=-Xmx3G
|
org.gradle.jvmargs=-Xmx3G -Dfile.encoding=UTF-8
|
||||||
org.gradle.parallel=true
|
org.gradle.parallel=true
|
||||||
|
|
||||||
kotlin.stdlib.default.dependency=false
|
kotlin.stdlib.default.dependency=false
|
||||||
kotlin.jvm.target.validation.mode=error
|
kotlin.jvm.target.validation.mode=error
|
||||||
|
|
||||||
|
neogradle.subsystems.conventions.runs.enabled=false
|
||||||
|
|
||||||
# Mod properties
|
# Mod properties
|
||||||
isUnstable=false
|
isUnstable=true
|
||||||
modVersion=1.109.7
|
modVersion=1.116.0
|
||||||
|
|
||||||
# Minecraft properties: We want to configure this here so we can read it in settings.gradle
|
# Minecraft properties: We want to configure this here so we can read it in settings.gradle
|
||||||
mcVersion=1.20.1
|
mcVersion=1.21.7
|
||||||
|
2
gradle/gradle-daemon-jvm.properties
Normal file
2
gradle/gradle-daemon-jvm.properties
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
#This file is generated by updateDaemonJvm
|
||||||
|
toolchainVersion=21
|
@@ -6,74 +6,76 @@
|
|||||||
|
|
||||||
# Minecraft
|
# Minecraft
|
||||||
# MC version is specified in gradle.properties, as we need that in settings.gradle.
|
# MC version is specified in gradle.properties, as we need that in settings.gradle.
|
||||||
# Remember to update corresponding versions in fabric.mod.json/mods.toml
|
# Remember to update corresponding versions in fabric.mod.json/neoforge.mods.toml
|
||||||
fabric-api = "0.86.1+1.20.1"
|
fabric-api = "0.128.0+1.21.7"
|
||||||
fabric-loader = "0.14.21"
|
fabric-loader = "0.16.14"
|
||||||
forge = "47.1.0"
|
neoForge = "21.7.1-beta"
|
||||||
forgeSpi = "7.0.1"
|
neoMergeTool = "2.0.0"
|
||||||
mixin = "0.8.5"
|
mixin = "0.8.5"
|
||||||
parchment = "2023.08.20"
|
parchment = "2025.06.29"
|
||||||
parchmentMc = "1.20.1"
|
parchmentMc = "1.21.6"
|
||||||
yarn = "1.20.1+build.10"
|
yarn = "1.21.7+build.1"
|
||||||
|
|
||||||
# Core dependencies (these versions are tied to the version Minecraft uses)
|
# Core dependencies (these versions are tied to the version Minecraft uses)
|
||||||
fastutil = "8.5.9"
|
fastutil = "8.5.15"
|
||||||
guava = "31.1-jre"
|
guava = "33.3.1-jre"
|
||||||
netty = "4.1.82.Final"
|
netty = "4.1.118.Final"
|
||||||
slf4j = "2.0.1"
|
slf4j = "2.0.16"
|
||||||
|
|
||||||
# Core dependencies (independent of Minecraft)
|
# Core dependencies (independent of Minecraft)
|
||||||
asm = "9.6"
|
asm = "9.7.1"
|
||||||
autoService = "1.1.1"
|
autoService = "1.1.1"
|
||||||
checkerFramework = "3.42.0"
|
checkerFramework = "3.42.0"
|
||||||
cobalt = "0.9.1"
|
cobalt = { strictly = "0.9.6" }
|
||||||
commonsCli = "1.6.0"
|
commonsCli = "1.6.0"
|
||||||
jetbrainsAnnotations = "24.1.0"
|
jetbrainsAnnotations = "24.1.0"
|
||||||
jsr305 = "3.0.2"
|
jspecify = "1.0.0"
|
||||||
jzlib = "1.1.3"
|
jzlib = "1.1.3"
|
||||||
kotlin = "1.9.21"
|
kotlin = "2.1.10"
|
||||||
kotlin-coroutines = "1.7.3"
|
kotlin-coroutines = "1.10.1"
|
||||||
nightConfig = "3.6.7"
|
nightConfig = "3.8.1"
|
||||||
|
|
||||||
# Minecraft mods
|
# Minecraft mods
|
||||||
emi = "1.0.8+1.20.1"
|
emi = "1.1.7+1.21"
|
||||||
fabricPermissions = "0.3.20230723"
|
fabricPermissions = "0.3.3"
|
||||||
iris = "1.6.4+1.20"
|
iris-fabric = "1.8.11+1.21.5-fabric"
|
||||||
jei = "15.2.0.22"
|
iris-forge = "1.8.11+1.21.5-neoforge"
|
||||||
modmenu = "7.1.0"
|
jei = "19.8.2.99"
|
||||||
moreRed = "4.0.0.4"
|
modmenu = "13.0.2"
|
||||||
oculus = "1.2.5"
|
moreRed = "6.0.0.3"
|
||||||
rei = "12.0.626"
|
rei = "18.0.800"
|
||||||
rubidium = "0.6.1"
|
sodium-fabric = "mc1.21.5-0.6.12-fabric"
|
||||||
sodium = "mc1.20-0.4.10"
|
sodium-forge = "mc1.21.5-0.6.12-neoforge"
|
||||||
|
mixinExtra = "0.3.5"
|
||||||
|
create-forge = "6.0.0-6"
|
||||||
|
create-fabric = "0.5.1-f-build.1467+mc1.20.1"
|
||||||
|
|
||||||
# Testing
|
# Testing
|
||||||
hamcrest = "2.2"
|
hamcrest = "2.2"
|
||||||
jqwik = "1.8.2"
|
jqwik = "1.8.2"
|
||||||
junit = "5.10.1"
|
junit = "5.11.4"
|
||||||
|
junitPlatform = "1.11.4"
|
||||||
jmh = "1.37"
|
jmh = "1.37"
|
||||||
|
|
||||||
# Build tools
|
# Build tools
|
||||||
cctJavadoc = "1.8.2"
|
cctJavadoc = "1.8.5"
|
||||||
checkstyle = "10.12.6"
|
checkstyle = "10.23.1"
|
||||||
curseForgeGradle = "1.0.14"
|
errorProne-core = "2.38.0"
|
||||||
errorProne-core = "2.23.0"
|
errorProne-plugin = "4.1.0"
|
||||||
errorProne-plugin = "3.1.0"
|
fabric-loom = "1.10.4"
|
||||||
fabric-loom = "1.5.7"
|
|
||||||
forgeGradle = "6.0.20"
|
|
||||||
githubRelease = "2.5.2"
|
githubRelease = "2.5.2"
|
||||||
gradleVersions = "0.50.0"
|
gradleVersions = "0.50.0"
|
||||||
ideaExt = "1.1.7"
|
ideaExt = "1.1.7"
|
||||||
illuaminate = "0.1.0-44-g9ee0055"
|
illuaminate = "0.1.0-83-g1131f68"
|
||||||
librarian = "1.+"
|
|
||||||
lwjgl = "3.3.3"
|
lwjgl = "3.3.3"
|
||||||
minotaur = "2.+"
|
minotaur = "2.8.7"
|
||||||
mixinGradle = "0.7.38"
|
modDevGradle = "2.0.95"
|
||||||
nullAway = "0.9.9"
|
nullAway = "0.12.7"
|
||||||
spotless = "6.23.3"
|
shadow = "8.3.1"
|
||||||
|
spotless = "7.0.2"
|
||||||
taskTree = "2.1.1"
|
taskTree = "2.1.1"
|
||||||
teavm = "0.10.0-SQUID.2"
|
teavm = "0.11.0-SQUID.1"
|
||||||
vanillaExtract = "0.1.1"
|
vanillaExtract = "0.2.1"
|
||||||
versionCatalogUpdate = "0.8.1"
|
versionCatalogUpdate = "0.8.1"
|
||||||
|
|
||||||
[libraries]
|
[libraries]
|
||||||
@@ -85,40 +87,44 @@ checkerFramework = { module = "org.checkerframework:checker-qual", version.ref =
|
|||||||
cobalt = { module = "cc.tweaked:cobalt", version.ref = "cobalt" }
|
cobalt = { module = "cc.tweaked:cobalt", version.ref = "cobalt" }
|
||||||
commonsCli = { module = "commons-cli:commons-cli", version.ref = "commonsCli" }
|
commonsCli = { module = "commons-cli:commons-cli", version.ref = "commonsCli" }
|
||||||
fastutil = { module = "it.unimi.dsi:fastutil", version.ref = "fastutil" }
|
fastutil = { module = "it.unimi.dsi:fastutil", version.ref = "fastutil" }
|
||||||
forgeSpi = { module = "net.minecraftforge:forgespi", version.ref = "forgeSpi" }
|
neoMergeTool = { module = "net.neoforged:mergetool", version.ref = "neoMergeTool" }
|
||||||
guava = { module = "com.google.guava:guava", version.ref = "guava" }
|
guava = { module = "com.google.guava:guava", version.ref = "guava" }
|
||||||
jetbrainsAnnotations = { module = "org.jetbrains:annotations", version.ref = "jetbrainsAnnotations" }
|
jetbrainsAnnotations = { module = "org.jetbrains:annotations", version.ref = "jetbrainsAnnotations" }
|
||||||
jsr305 = { module = "com.google.code.findbugs:jsr305", version.ref = "jsr305" }
|
jspecify = { module = "org.jspecify:jspecify", version.ref = "jspecify" }
|
||||||
jzlib = { module = "com.jcraft:jzlib", version.ref = "jzlib" }
|
jzlib = { module = "com.jcraft:jzlib", version.ref = "jzlib" }
|
||||||
kotlin-coroutines = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kotlin-coroutines" }
|
kotlin-coroutines = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kotlin-coroutines" }
|
||||||
kotlin-platform = { module = "org.jetbrains.kotlin:kotlin-bom", version.ref = "kotlin" }
|
kotlin-platform = { module = "org.jetbrains.kotlin:kotlin-bom", version.ref = "kotlin" }
|
||||||
kotlin-stdlib = { module = "org.jetbrains.kotlin:kotlin-stdlib", version.ref = "kotlin" }
|
kotlin-stdlib = { module = "org.jetbrains.kotlin:kotlin-stdlib", version.ref = "kotlin" }
|
||||||
|
netty-codec = { module = "io.netty:netty-codec", version.ref = "netty" }
|
||||||
netty-http = { module = "io.netty:netty-codec-http", version.ref = "netty" }
|
netty-http = { module = "io.netty:netty-codec-http", version.ref = "netty" }
|
||||||
netty-socks = { module = "io.netty:netty-codec-socks", version.ref = "netty" }
|
|
||||||
netty-proxy = { module = "io.netty:netty-handler-proxy", version.ref = "netty" }
|
netty-proxy = { module = "io.netty:netty-handler-proxy", version.ref = "netty" }
|
||||||
|
netty-socks = { module = "io.netty:netty-codec-socks", version.ref = "netty" }
|
||||||
nightConfig-core = { module = "com.electronwill.night-config:core", version.ref = "nightConfig" }
|
nightConfig-core = { module = "com.electronwill.night-config:core", version.ref = "nightConfig" }
|
||||||
nightConfig-toml = { module = "com.electronwill.night-config:toml", version.ref = "nightConfig" }
|
nightConfig-toml = { module = "com.electronwill.night-config:toml", version.ref = "nightConfig" }
|
||||||
slf4j = { module = "org.slf4j:slf4j-api", version.ref = "slf4j" }
|
slf4j = { module = "org.slf4j:slf4j-api", version.ref = "slf4j" }
|
||||||
|
|
||||||
# Minecraft mods
|
# Minecraft mods
|
||||||
fabric-api = { module = "net.fabricmc.fabric-api:fabric-api", version.ref = "fabric-api" }
|
create-fabric = { module = "com.simibubi.create:create-fabric-1.20.1", version.ref = "create-fabric" }
|
||||||
fabric-loader = { module = "net.fabricmc:fabric-loader", version.ref = "fabric-loader" }
|
create-forge = { module = "com.simibubi.create:create-1.21.1", version.ref = "create-forge" }
|
||||||
fabric-junit = { module = "net.fabricmc:fabric-loader-junit", version.ref = "fabric-loader" }
|
|
||||||
fabricPermissions = { module = "me.lucko:fabric-permissions-api", version.ref = "fabricPermissions" }
|
|
||||||
emi = { module = "dev.emi:emi-xplat-mojmap", version.ref = "emi" }
|
emi = { module = "dev.emi:emi-xplat-mojmap", version.ref = "emi" }
|
||||||
iris = { module = "maven.modrinth:iris", version.ref = "iris" }
|
fabric-api = { module = "net.fabricmc.fabric-api:fabric-api", version.ref = "fabric-api" }
|
||||||
jei-api = { module = "mezz.jei:jei-1.20.1-common-api", version.ref = "jei" }
|
fabric-junit = { module = "net.fabricmc:fabric-loader-junit", version.ref = "fabric-loader" }
|
||||||
jei-fabric = { module = "mezz.jei:jei-1.20.1-fabric", version.ref = "jei" }
|
fabric-loader = { module = "net.fabricmc:fabric-loader", version.ref = "fabric-loader" }
|
||||||
jei-forge = { module = "mezz.jei:jei-1.20.1-forge", version.ref = "jei" }
|
fabricPermissions = { module = "me.lucko:fabric-permissions-api", version.ref = "fabricPermissions" }
|
||||||
|
iris-fabric = { module = "maven.modrinth:iris", version.ref = "iris-fabric" }
|
||||||
|
iris-forge = { module = "maven.modrinth:iris", version.ref = "iris-forge" }
|
||||||
|
jei-api = { module = "mezz.jei:jei-1.21-common-api", version.ref = "jei" }
|
||||||
|
jei-fabric = { module = "mezz.jei:jei-1.21-fabric", version.ref = "jei" }
|
||||||
|
jei-forge = { module = "mezz.jei:jei-1.21-neoforge", version.ref = "jei" }
|
||||||
mixin = { module = "org.spongepowered:mixin", version.ref = "mixin" }
|
mixin = { module = "org.spongepowered:mixin", version.ref = "mixin" }
|
||||||
|
mixinExtra = { module = "io.github.llamalad7:mixinextras-common", version.ref = "mixinExtra" }
|
||||||
modmenu = { module = "com.terraformersmc:modmenu", version.ref = "modmenu" }
|
modmenu = { module = "com.terraformersmc:modmenu", version.ref = "modmenu" }
|
||||||
moreRed = { module = "commoble.morered:morered-1.20.1", version.ref = "moreRed" }
|
moreRed = { module = "net.commoble.morered:morered-1.21.1", version.ref = "moreRed" }
|
||||||
oculus = { module = "maven.modrinth:oculus", version.ref = "oculus" }
|
|
||||||
rei-api = { module = "me.shedaniel:RoughlyEnoughItems-api", version.ref = "rei" }
|
rei-api = { module = "me.shedaniel:RoughlyEnoughItems-api", version.ref = "rei" }
|
||||||
rei-builtin = { module = "me.shedaniel:RoughlyEnoughItems-default-plugin", version.ref = "rei" }
|
rei-builtin = { module = "me.shedaniel:RoughlyEnoughItems-default-plugin", version.ref = "rei" }
|
||||||
rei-fabric = { module = "me.shedaniel:RoughlyEnoughItems-fabric", version.ref = "rei" }
|
rei-fabric = { module = "me.shedaniel:RoughlyEnoughItems-fabric", version.ref = "rei" }
|
||||||
rubidium = { module = "maven.modrinth:rubidium", version.ref = "rubidium" }
|
sodium-fabric = { module = "maven.modrinth:sodium", version.ref = "sodium.fabric" }
|
||||||
sodium = { module = "maven.modrinth:sodium", version.ref = "sodium" }
|
sodium-forge = { module = "maven.modrinth:sodium", version.ref = "sodium.forge" }
|
||||||
|
|
||||||
# Testing
|
# Testing
|
||||||
hamcrest = { module = "org.hamcrest:hamcrest", version.ref = "hamcrest" }
|
hamcrest = { module = "org.hamcrest:hamcrest", version.ref = "hamcrest" }
|
||||||
@@ -127,6 +133,7 @@ jqwik-engine = { module = "net.jqwik:jqwik-engine", version.ref = "jqwik" }
|
|||||||
junit-jupiter-api = { module = "org.junit.jupiter:junit-jupiter-api", version.ref = "junit" }
|
junit-jupiter-api = { module = "org.junit.jupiter:junit-jupiter-api", version.ref = "junit" }
|
||||||
junit-jupiter-engine = { module = "org.junit.jupiter:junit-jupiter-engine", version.ref = "junit" }
|
junit-jupiter-engine = { module = "org.junit.jupiter:junit-jupiter-engine", version.ref = "junit" }
|
||||||
junit-jupiter-params = { module = "org.junit.jupiter:junit-jupiter-params", version.ref = "junit" }
|
junit-jupiter-params = { module = "org.junit.jupiter:junit-jupiter-params", version.ref = "junit" }
|
||||||
|
junit-platform-launcher = { module = "org.junit.platform:junit-platform-launcher", version.ref = "junitPlatform" }
|
||||||
slf4j-simple = { module = "org.slf4j:slf4j-simple", version.ref = "slf4j" }
|
slf4j-simple = { module = "org.slf4j:slf4j-simple", version.ref = "slf4j" }
|
||||||
jmh = { module = "org.openjdk.jmh:jmh-core", version.ref = "jmh" }
|
jmh = { module = "org.openjdk.jmh:jmh-core", version.ref = "jmh" }
|
||||||
jmh-processor = { module = "org.openjdk.jmh:jmh-generator-annprocess", version.ref = "jmh" }
|
jmh-processor = { module = "org.openjdk.jmh:jmh-generator-annprocess", version.ref = "jmh" }
|
||||||
@@ -140,21 +147,20 @@ lwjgl-glfw = { module = "org.lwjgl:lwjgl-glfw" }
|
|||||||
# Build tools
|
# Build tools
|
||||||
cctJavadoc = { module = "cc.tweaked:cct-javadoc", version.ref = "cctJavadoc" }
|
cctJavadoc = { module = "cc.tweaked:cct-javadoc", version.ref = "cctJavadoc" }
|
||||||
checkstyle = { module = "com.puppycrawl.tools:checkstyle", version.ref = "checkstyle" }
|
checkstyle = { module = "com.puppycrawl.tools:checkstyle", version.ref = "checkstyle" }
|
||||||
curseForgeGradle = { module = "net.darkhax.curseforgegradle:CurseForgeGradle", version.ref = "curseForgeGradle" }
|
|
||||||
errorProne-annotations = { module = "com.google.errorprone:error_prone_annotations", version.ref = "errorProne-core" }
|
errorProne-annotations = { module = "com.google.errorprone:error_prone_annotations", version.ref = "errorProne-core" }
|
||||||
errorProne-api = { module = "com.google.errorprone:error_prone_check_api", version.ref = "errorProne-core" }
|
errorProne-api = { module = "com.google.errorprone:error_prone_check_api", version.ref = "errorProne-core" }
|
||||||
errorProne-core = { module = "com.google.errorprone:error_prone_core", version.ref = "errorProne-core" }
|
errorProne-core = { module = "com.google.errorprone:error_prone_core", version.ref = "errorProne-core" }
|
||||||
errorProne-plugin = { module = "net.ltgt.gradle:gradle-errorprone-plugin", version.ref = "errorProne-plugin" }
|
errorProne-plugin = { module = "net.ltgt.gradle:gradle-errorprone-plugin", version.ref = "errorProne-plugin" }
|
||||||
errorProne-testHelpers = { module = "com.google.errorprone:error_prone_test_helpers", version.ref = "errorProne-core" }
|
errorProne-testHelpers = { module = "com.google.errorprone:error_prone_test_helpers", version.ref = "errorProne-core" }
|
||||||
fabric-loom = { module = "net.fabricmc:fabric-loom", version.ref = "fabric-loom" }
|
fabric-loom = { module = "net.fabricmc:fabric-loom", version.ref = "fabric-loom" }
|
||||||
forgeGradle = { module = "net.minecraftforge.gradle:ForgeGradle", version.ref = "forgeGradle" }
|
|
||||||
ideaExt = { module = "gradle.plugin.org.jetbrains.gradle.plugin.idea-ext:gradle-idea-ext", version.ref = "ideaExt" }
|
ideaExt = { module = "gradle.plugin.org.jetbrains.gradle.plugin.idea-ext:gradle-idea-ext", version.ref = "ideaExt" }
|
||||||
kotlin-plugin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" }
|
kotlin-plugin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" }
|
||||||
librarian = { module = "org.parchmentmc:librarian", version.ref = "librarian" }
|
|
||||||
minotaur = { module = "com.modrinth.minotaur:Minotaur", version.ref = "minotaur" }
|
minotaur = { module = "com.modrinth.minotaur:Minotaur", version.ref = "minotaur" }
|
||||||
nullAway = { module = "com.uber.nullaway:nullaway", version.ref = "nullAway" }
|
nullAway = { module = "com.uber.nullaway:nullaway", version.ref = "nullAway" }
|
||||||
|
modDevGradle = { module = "net.neoforged:moddev-gradle", version.ref = "modDevGradle" }
|
||||||
spotless = { module = "com.diffplug.spotless:spotless-plugin-gradle", version.ref = "spotless" }
|
spotless = { module = "com.diffplug.spotless:spotless-plugin-gradle", version.ref = "spotless" }
|
||||||
teavm-classlib = { module = "org.teavm:teavm-classlib", version.ref = "teavm" }
|
teavm-classlib = { module = "org.teavm:teavm-classlib", version.ref = "teavm" }
|
||||||
|
teavm-core = { module = "org.teavm:teavm-core", version.ref = "teavm" }
|
||||||
teavm-jso = { module = "org.teavm:teavm-jso", version.ref = "teavm" }
|
teavm-jso = { module = "org.teavm:teavm-jso", version.ref = "teavm" }
|
||||||
teavm-jso-apis = { module = "org.teavm:teavm-jso-apis", version.ref = "teavm" }
|
teavm-jso-apis = { module = "org.teavm:teavm-jso-apis", version.ref = "teavm" }
|
||||||
teavm-jso-impl = { module = "org.teavm:teavm-jso-impl", version.ref = "teavm" }
|
teavm-jso-impl = { module = "org.teavm:teavm-jso-impl", version.ref = "teavm" }
|
||||||
@@ -166,29 +172,27 @@ vanillaExtract = { module = "cc.tweaked.vanilla-extract:plugin", version.ref = "
|
|||||||
yarn = { module = "net.fabricmc:yarn", version.ref = "yarn" }
|
yarn = { module = "net.fabricmc:yarn", version.ref = "yarn" }
|
||||||
|
|
||||||
[plugins]
|
[plugins]
|
||||||
forgeGradle = { id = "net.minecraftforge.gradle", version.ref = "forgeGradle" }
|
|
||||||
githubRelease = { id = "com.github.breadmoirai.github-release", version.ref = "githubRelease" }
|
githubRelease = { id = "com.github.breadmoirai.github-release", version.ref = "githubRelease" }
|
||||||
gradleVersions = { id = "com.github.ben-manes.versions", version.ref = "gradleVersions" }
|
gradleVersions = { id = "com.github.ben-manes.versions", version.ref = "gradleVersions" }
|
||||||
kotlin = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
|
kotlin = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
|
||||||
librarian = { id = "org.parchmentmc.librarian.forgegradle", version.ref = "librarian" }
|
shadow = { id = "com.gradleup.shadow", version.ref = "shadow" }
|
||||||
mixinGradle = { id = "org.spongepowered.mixin", version.ref = "mixinGradle" }
|
|
||||||
taskTree = { id = "com.dorongold.task-tree", version.ref = "taskTree" }
|
taskTree = { id = "com.dorongold.task-tree", version.ref = "taskTree" }
|
||||||
versionCatalogUpdate = { id = "nl.littlerobots.version-catalog-update", version.ref = "versionCatalogUpdate" }
|
versionCatalogUpdate = { id = "nl.littlerobots.version-catalog-update", version.ref = "versionCatalogUpdate" }
|
||||||
|
|
||||||
[bundles]
|
[bundles]
|
||||||
annotations = ["jsr305", "checkerFramework", "jetbrainsAnnotations"]
|
annotations = ["checkerFramework", "jetbrainsAnnotations", "jspecify"]
|
||||||
kotlin = ["kotlin-stdlib", "kotlin-coroutines"]
|
kotlin = ["kotlin-stdlib", "kotlin-coroutines"]
|
||||||
|
|
||||||
# Minecraft
|
# Minecraft
|
||||||
externalMods-common = ["jei-api", "nightConfig-core", "nightConfig-toml"]
|
externalMods-common = ["iris-forge", "jei-api", "nightConfig-core", "nightConfig-toml"]
|
||||||
externalMods-forge-compile = ["moreRed", "oculus", "jei-api"]
|
externalMods-forge-compile = ["moreRed", "iris-forge", "jei-api"]
|
||||||
externalMods-forge-runtime = ["jei-forge"]
|
externalMods-forge-runtime = []
|
||||||
externalMods-fabric-compile = ["fabricPermissions", "iris", "jei-api", "rei-api", "rei-builtin"]
|
externalMods-fabric-compile = ["fabricPermissions", "iris-fabric", "jei-api", "rei-api", "rei-builtin"]
|
||||||
externalMods-fabric-runtime = ["jei-fabric", "modmenu"]
|
externalMods-fabric-runtime = []
|
||||||
|
|
||||||
# Testing
|
# Testing
|
||||||
test = ["junit-jupiter-api", "junit-jupiter-params", "hamcrest", "jqwik-api"]
|
test = ["junit-jupiter-api", "junit-jupiter-params", "hamcrest", "jqwik-api"]
|
||||||
testRuntime = ["junit-jupiter-engine", "jqwik-engine"]
|
testRuntime = ["junit-jupiter-engine", "junit-platform-launcher", "jqwik-engine"]
|
||||||
|
|
||||||
# Build tools
|
# Build tools
|
||||||
teavm-api = ["teavm-jso", "teavm-jso-apis", "teavm-platform", "teavm-classlib", "teavm-metaprogramming-api"]
|
teavm-api = ["teavm-jso", "teavm-jso-apis", "teavm-platform", "teavm-classlib", "teavm-metaprogramming-api"]
|
||||||
|
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,6 +1,6 @@
|
|||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-bin.zip
|
||||||
networkTimeout=10000
|
networkTimeout=10000
|
||||||
validateDistributionUrl=true
|
validateDistributionUrl=true
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
6
gradlew
vendored
6
gradlew
vendored
@@ -15,6 +15,8 @@
|
|||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
#
|
#
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
#
|
||||||
|
|
||||||
##############################################################################
|
##############################################################################
|
||||||
#
|
#
|
||||||
@@ -55,7 +57,7 @@
|
|||||||
# Darwin, MinGW, and NonStop.
|
# Darwin, MinGW, and NonStop.
|
||||||
#
|
#
|
||||||
# (3) This script is generated from the Groovy template
|
# (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
|
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||||
# within the Gradle project.
|
# within the Gradle project.
|
||||||
#
|
#
|
||||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||||
@@ -84,7 +86,7 @@ done
|
|||||||
# shellcheck disable=SC2034
|
# shellcheck disable=SC2034
|
||||||
APP_BASE_NAME=${0##*/}
|
APP_BASE_NAME=${0##*/}
|
||||||
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
|
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
|
||||||
APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
|
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
|
||||||
|
|
||||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||||
MAX_FD=maximum
|
MAX_FD=maximum
|
||||||
|
22
gradlew.bat
vendored
22
gradlew.bat
vendored
@@ -13,6 +13,8 @@
|
|||||||
@rem See the License for the specific language governing permissions and
|
@rem See the License for the specific language governing permissions and
|
||||||
@rem limitations under the License.
|
@rem limitations under the License.
|
||||||
@rem
|
@rem
|
||||||
|
@rem SPDX-License-Identifier: Apache-2.0
|
||||||
|
@rem
|
||||||
|
|
||||||
@if "%DEBUG%"=="" @echo off
|
@if "%DEBUG%"=="" @echo off
|
||||||
@rem ##########################################################################
|
@rem ##########################################################################
|
||||||
@@ -43,11 +45,11 @@ set JAVA_EXE=java.exe
|
|||||||
%JAVA_EXE% -version >NUL 2>&1
|
%JAVA_EXE% -version >NUL 2>&1
|
||||||
if %ERRORLEVEL% equ 0 goto execute
|
if %ERRORLEVEL% equ 0 goto execute
|
||||||
|
|
||||||
echo.
|
echo. 1>&2
|
||||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
|
||||||
echo.
|
echo. 1>&2
|
||||||
echo Please set the JAVA_HOME variable in your environment to match the
|
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||||
echo location of your Java installation.
|
echo location of your Java installation. 1>&2
|
||||||
|
|
||||||
goto fail
|
goto fail
|
||||||
|
|
||||||
@@ -57,11 +59,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
|||||||
|
|
||||||
if exist "%JAVA_EXE%" goto execute
|
if exist "%JAVA_EXE%" goto execute
|
||||||
|
|
||||||
echo.
|
echo. 1>&2
|
||||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
|
||||||
echo.
|
echo. 1>&2
|
||||||
echo Please set the JAVA_HOME variable in your environment to match the
|
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||||
echo location of your Java installation.
|
echo location of your Java installation. 1>&2
|
||||||
|
|
||||||
goto fail
|
goto fail
|
||||||
|
|
||||||
|
1618
package-lock.json
generated
1618
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -12,11 +12,11 @@
|
|||||||
"tslib": "^2.0.3"
|
"tslib": "^2.0.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@rollup/plugin-node-resolve": "^15.2.1",
|
"@rollup/plugin-node-resolve": "^16.0.0",
|
||||||
"@rollup/plugin-typescript": "^11.0.0",
|
"@rollup/plugin-typescript": "^12.0.0 && <12.1.3",
|
||||||
"@rollup/plugin-url": "^8.0.1",
|
"@rollup/plugin-url": "^8.0.1",
|
||||||
"@swc/core": "^1.3.92",
|
"@swc/core": "^1.3.92",
|
||||||
"@types/node": "^20.8.3",
|
"@types/node": "^24.0.0",
|
||||||
"lightningcss": "^1.22.0",
|
"lightningcss": "^1.22.0",
|
||||||
"preact-render-to-string": "^6.2.1",
|
"preact-render-to-string": "^6.2.1",
|
||||||
"rehype": "^13.0.0",
|
"rehype": "^13.0.0",
|
||||||
|
@@ -8,6 +8,8 @@ plugins {
|
|||||||
id("cc-tweaked.vanilla")
|
id("cc-tweaked.vanilla")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val mcVersion: String by extra
|
||||||
|
|
||||||
java {
|
java {
|
||||||
withJavadocJar()
|
withJavadocJar()
|
||||||
}
|
}
|
||||||
@@ -16,9 +18,70 @@ dependencies {
|
|||||||
api(project(":core-api"))
|
api(project(":core-api"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val javadocOverview by tasks.registering(Copy::class) {
|
||||||
|
from("src/overview.html")
|
||||||
|
into(layout.buildDirectory.dir(name))
|
||||||
|
|
||||||
|
expand(
|
||||||
|
mapOf(
|
||||||
|
"mcVersion" to mcVersion,
|
||||||
|
"modVersion" to version,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
tasks.javadoc {
|
tasks.javadoc {
|
||||||
|
title = "CC: Tweaked $version for Minecraft $mcVersion"
|
||||||
include("dan200/computercraft/api/**/*.java")
|
include("dan200/computercraft/api/**/*.java")
|
||||||
|
|
||||||
|
options {
|
||||||
|
(this as StandardJavadocDocletOptions)
|
||||||
|
|
||||||
|
inputs.files(javadocOverview)
|
||||||
|
overview(javadocOverview.get().destinationDir.resolve("overview.html").absolutePath)
|
||||||
|
|
||||||
|
groups = mapOf(
|
||||||
|
"Common" to listOf(
|
||||||
|
"dan200.computercraft.api",
|
||||||
|
"dan200.computercraft.api.lua",
|
||||||
|
"dan200.computercraft.api.peripheral",
|
||||||
|
),
|
||||||
|
"Upgrades" to listOf(
|
||||||
|
"dan200.computercraft.api.client.turtle",
|
||||||
|
"dan200.computercraft.api.pocket",
|
||||||
|
"dan200.computercraft.api.turtle",
|
||||||
|
"dan200.computercraft.api.upgrades",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
addBooleanOption("-allow-script-in-comments", true)
|
||||||
|
bottom(
|
||||||
|
"""
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/prismjs@v1.29.0/components/prism-core.min.js"></script>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/prismjs@v1.29.0/plugins/autoloader/prism-autoloader.min.js"></script>
|
||||||
|
<link href=" https://cdn.jsdelivr.net/npm/prismjs@1.29.0/themes/prism.min.css " rel="stylesheet">
|
||||||
|
""".trimIndent(),
|
||||||
|
)
|
||||||
|
|
||||||
|
val snippetSources = listOf(":common", ":fabric", ":forge").flatMap {
|
||||||
|
project(it).sourceSets["examples"].allSource.sourceDirectories
|
||||||
|
}
|
||||||
|
inputs.files(snippetSources)
|
||||||
|
addPathOption("-snippet-path").value = snippetSources
|
||||||
|
}
|
||||||
|
|
||||||
// Include the core-api in our javadoc export. This is wrong, but it means we can export a single javadoc dump.
|
// Include the core-api in our javadoc export. This is wrong, but it means we can export a single javadoc dump.
|
||||||
source(project(":core-api").sourceSets.main.map { it.allJava })
|
source(project(":core-api").sourceSets.main.map { it.allJava })
|
||||||
|
|
||||||
|
options {
|
||||||
|
this as StandardJavadocDocletOptions
|
||||||
|
addBooleanOption("-allow-script-in-comments", true)
|
||||||
|
bottom(
|
||||||
|
"""
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/prismjs@v1.29.0/components/prism-core.min.js"></script>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/prismjs@v1.29.0/plugins/autoloader/prism-autoloader.min.js"></script>
|
||||||
|
<link href=" https://cdn.jsdelivr.net/npm/prismjs@1.29.0/themes/prism.min.css " rel="stylesheet">
|
||||||
|
""".trimIndent(),
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,43 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: 2022 The CC: Tweaked Developers
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: MPL-2.0
|
|
||||||
|
|
||||||
package dan200.computercraft.api.client;
|
|
||||||
|
|
||||||
import dan200.computercraft.api.client.turtle.TurtleUpgradeModeller;
|
|
||||||
import dan200.computercraft.api.turtle.ITurtleUpgrade;
|
|
||||||
import dan200.computercraft.api.turtle.TurtleUpgradeSerialiser;
|
|
||||||
import dan200.computercraft.impl.client.ComputerCraftAPIClientService;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The public API for client-only code.
|
|
||||||
*
|
|
||||||
* @see dan200.computercraft.api.ComputerCraftAPI The main API
|
|
||||||
*/
|
|
||||||
public final class ComputerCraftAPIClient {
|
|
||||||
private ComputerCraftAPIClient() {
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Register a {@link TurtleUpgradeModeller} for a class of turtle upgrades.
|
|
||||||
* <p>
|
|
||||||
* This may be called at any point after registry creation, though it is recommended to call it within your client
|
|
||||||
* setup step.
|
|
||||||
*
|
|
||||||
* @param serialiser The turtle upgrade serialiser.
|
|
||||||
* @param modeller The upgrade modeller.
|
|
||||||
* @param <T> The type of the turtle upgrade.
|
|
||||||
* @deprecated This method can lead to confusing load behaviour on Forge. Use
|
|
||||||
* {@code dan200.computercraft.api.client.FabricComputerCraftAPIClient#registerTurtleUpgradeModeller} on Fabric, or
|
|
||||||
* {@code dan200.computercraft.api.client.turtle.RegisterTurtleModellersEvent} on Forge.
|
|
||||||
*/
|
|
||||||
@Deprecated(forRemoval = true)
|
|
||||||
public static <T extends ITurtleUpgrade> void registerTurtleUpgradeModeller(TurtleUpgradeSerialiser<T> serialiser, TurtleUpgradeModeller<T> modeller) {
|
|
||||||
// TODO(1.20.4): Remove this
|
|
||||||
getInstance().registerTurtleUpgradeModeller(serialiser, modeller);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static ComputerCraftAPIClientService getInstance() {
|
|
||||||
return ComputerCraftAPIClientService.get();
|
|
||||||
}
|
|
||||||
}
|
|
@@ -0,0 +1,166 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2025 The CC: Tweaked Developers
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
|
package dan200.computercraft.api.client;
|
||||||
|
|
||||||
|
import com.google.common.base.Suppliers;
|
||||||
|
import com.mojang.blaze3d.vertex.PoseStack;
|
||||||
|
import dan200.computercraft.api.client.turtle.TurtleUpgradeModel;
|
||||||
|
import net.minecraft.client.multiplayer.ClientLevel;
|
||||||
|
import net.minecraft.client.renderer.MultiBufferSource;
|
||||||
|
import net.minecraft.client.renderer.RenderType;
|
||||||
|
import net.minecraft.client.renderer.Sheets;
|
||||||
|
import net.minecraft.client.renderer.block.model.BakedQuad;
|
||||||
|
import net.minecraft.client.renderer.item.BlockModelWrapper;
|
||||||
|
import net.minecraft.client.renderer.item.ItemModel;
|
||||||
|
import net.minecraft.client.renderer.item.ItemModelResolver;
|
||||||
|
import net.minecraft.client.renderer.item.ItemStackRenderState;
|
||||||
|
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
|
||||||
|
import net.minecraft.client.resources.model.BlockModelRotation;
|
||||||
|
import net.minecraft.client.resources.model.ModelBaker;
|
||||||
|
import net.minecraft.client.resources.model.ResolvedModel;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import net.minecraft.util.ARGB;
|
||||||
|
import net.minecraft.world.entity.LivingEntity;
|
||||||
|
import net.minecraft.world.item.ItemDisplayContext;
|
||||||
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
import org.joml.Vector3f;
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A standalone model.
|
||||||
|
* <p>
|
||||||
|
* This is very similar to vanilla's {@link BlockModelWrapper}, but suitable for use in both {@link ItemModel}s and
|
||||||
|
* block models. This is primarily intended for use with {@link TurtleUpgradeModel}s.
|
||||||
|
*/
|
||||||
|
public final class StandaloneModel {
|
||||||
|
private final List<BakedQuad> quads;
|
||||||
|
private final boolean useBlockLight;
|
||||||
|
private final TextureAtlasSprite particleIcon;
|
||||||
|
private final RenderType renderType;
|
||||||
|
private final Supplier<Vector3f[]> extents;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a new {@link StandaloneModel}.
|
||||||
|
*
|
||||||
|
* @param quads The list of quads which form this model.
|
||||||
|
* @param usesBlockLight Whether this uses block lighting. See {@link ItemStackRenderState.LayerRenderState#setUsesBlockLight(boolean)}.
|
||||||
|
* @param particleIcon The sprite for the model's particles. See {@link ItemStackRenderState.LayerRenderState#setParticleIcon(TextureAtlasSprite)}.
|
||||||
|
* @param renderType The render type for this model.
|
||||||
|
*/
|
||||||
|
public StandaloneModel(List<BakedQuad> quads, boolean usesBlockLight, TextureAtlasSprite particleIcon, RenderType renderType) {
|
||||||
|
this.quads = quads;
|
||||||
|
this.useBlockLight = usesBlockLight;
|
||||||
|
this.particleIcon = particleIcon;
|
||||||
|
this.renderType = renderType;
|
||||||
|
this.extents = Suppliers.memoize(() -> BlockModelWrapper.computeExtents(quads));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load a model from a {@link ModelBaker} and bake it.
|
||||||
|
*
|
||||||
|
* @param model The model id to load.
|
||||||
|
* @param baker The model baker.
|
||||||
|
* @return The baked {@link StandaloneModel}.
|
||||||
|
*/
|
||||||
|
public static StandaloneModel of(ResourceLocation model, ModelBaker baker) {
|
||||||
|
return of(baker.getModel(model), baker);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bake a {@link ResolvedModel} into a {@link StandaloneModel}.
|
||||||
|
*
|
||||||
|
* @param model The resolved model.
|
||||||
|
* @param baker The model baker.
|
||||||
|
* @return The baked {@link StandaloneModel}.
|
||||||
|
*/
|
||||||
|
public static StandaloneModel of(ResolvedModel model, ModelBaker baker) {
|
||||||
|
return baker.compute(new CacheKey(model));
|
||||||
|
}
|
||||||
|
|
||||||
|
private record CacheKey(ResolvedModel model) implements ModelBaker.SharedOperationKey<StandaloneModel> {
|
||||||
|
@Override
|
||||||
|
public StandaloneModel compute(ModelBaker baker) {
|
||||||
|
return ofUncached(model(), baker);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object other) {
|
||||||
|
return other instanceof CacheKey(var otherModel) && model() == otherModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return System.identityHashCode(model());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static StandaloneModel ofUncached(ResolvedModel model, ModelBaker baker) {
|
||||||
|
var slots = model.getTopTextureSlots();
|
||||||
|
return new StandaloneModel(
|
||||||
|
model.bakeTopGeometry(slots, baker, BlockModelRotation.X0_Y0).getAll(),
|
||||||
|
model.getTopGuiLight().lightLikeBlock(),
|
||||||
|
model.resolveParticleSprite(slots, baker),
|
||||||
|
Sheets.translucentItemSheet()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set up an {@link ItemStackRenderState.LayerRenderState} to render this model.
|
||||||
|
*
|
||||||
|
* @param layer The layer to set up.
|
||||||
|
* @see ItemModel#update(ItemStackRenderState, ItemStack, ItemModelResolver, ItemDisplayContext, ClientLevel, LivingEntity, int)
|
||||||
|
*/
|
||||||
|
public void setupItemLayer(ItemStackRenderState.LayerRenderState layer) {
|
||||||
|
layer.setExtents(extents);
|
||||||
|
layer.setRenderType(renderType);
|
||||||
|
layer.setUsesBlockLight(useBlockLight);
|
||||||
|
layer.setParticleIcon(particleIcon);
|
||||||
|
layer.prepareQuadList().addAll(quads);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render the model directly.
|
||||||
|
*
|
||||||
|
* @param transform The current pose stack transformations.
|
||||||
|
* @param buffers The buffer source to use for rendering.
|
||||||
|
* @param light The current light texture coordinate.
|
||||||
|
* @param overlay The current overlay texture coordinate.
|
||||||
|
*/
|
||||||
|
public void render(PoseStack transform, MultiBufferSource buffers, int light, int overlay) {
|
||||||
|
render(transform, buffers, light, overlay, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render the model directly.
|
||||||
|
*
|
||||||
|
* @param transform The current pose stack transformations.
|
||||||
|
* @param buffers The buffer source to use for rendering.
|
||||||
|
* @param light The current light texture coordinate.
|
||||||
|
* @param overlay The current overlay texture coordinate.
|
||||||
|
* @param tints The tints for this model.
|
||||||
|
*/
|
||||||
|
public void render(PoseStack transform, MultiBufferSource buffers, int light, int overlay, int @Nullable [] tints) {
|
||||||
|
var pose = transform.last();
|
||||||
|
var buffer = buffers.getBuffer(renderType);
|
||||||
|
for (var quad : quads) {
|
||||||
|
float r, g, b, a;
|
||||||
|
var idx = quad.tintIndex();
|
||||||
|
if (tints != null && idx >= 0 && idx < tints.length) {
|
||||||
|
var tint = tints[idx];
|
||||||
|
r = ARGB.red(tint) / 255.0f;
|
||||||
|
g = ARGB.green(tint) / 255.0f;
|
||||||
|
b = ARGB.blue(tint) / 255.0f;
|
||||||
|
a = ARGB.alpha(tint) / 255.0f;
|
||||||
|
} else {
|
||||||
|
r = g = b = a = 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer.putBulkData(pose, quad, r, g, b, a, light, overlay);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -1,57 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: 2020 The CC: Tweaked Developers
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: MPL-2.0
|
|
||||||
|
|
||||||
|
|
||||||
package dan200.computercraft.api.client;
|
|
||||||
|
|
||||||
import com.mojang.math.Transformation;
|
|
||||||
import dan200.computercraft.impl.client.ClientPlatformHelper;
|
|
||||||
import net.minecraft.client.Minecraft;
|
|
||||||
import net.minecraft.client.resources.model.BakedModel;
|
|
||||||
import net.minecraft.client.resources.model.ModelResourceLocation;
|
|
||||||
import net.minecraft.resources.ResourceLocation;
|
|
||||||
import net.minecraft.world.item.ItemStack;
|
|
||||||
|
|
||||||
import java.util.Objects;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A model to render, combined with a transformation matrix to apply.
|
|
||||||
*/
|
|
||||||
public final class TransformedModel {
|
|
||||||
private final BakedModel model;
|
|
||||||
private final Transformation matrix;
|
|
||||||
|
|
||||||
public TransformedModel(BakedModel model, Transformation matrix) {
|
|
||||||
this.model = Objects.requireNonNull(model);
|
|
||||||
this.matrix = Objects.requireNonNull(matrix);
|
|
||||||
}
|
|
||||||
|
|
||||||
public TransformedModel(BakedModel model) {
|
|
||||||
this.model = Objects.requireNonNull(model);
|
|
||||||
matrix = Transformation.identity();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static TransformedModel of(ModelResourceLocation location) {
|
|
||||||
var modelManager = Minecraft.getInstance().getModelManager();
|
|
||||||
return new TransformedModel(modelManager.getModel(location));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static TransformedModel of(ResourceLocation location) {
|
|
||||||
var modelManager = Minecraft.getInstance().getModelManager();
|
|
||||||
return new TransformedModel(ClientPlatformHelper.get().getModel(modelManager, location));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static TransformedModel of(ItemStack item, Transformation transform) {
|
|
||||||
var model = Minecraft.getInstance().getItemRenderer().getItemModelShaper().getItemModel(item);
|
|
||||||
return new TransformedModel(model, transform);
|
|
||||||
}
|
|
||||||
|
|
||||||
public BakedModel getModel() {
|
|
||||||
return model;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Transformation getMatrix() {
|
|
||||||
return matrix;
|
|
||||||
}
|
|
||||||
}
|
|
@@ -0,0 +1,95 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2025 The CC: Tweaked Developers
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
|
package dan200.computercraft.api.client.turtle;
|
||||||
|
|
||||||
|
import com.mojang.blaze3d.vertex.PoseStack;
|
||||||
|
import com.mojang.serialization.MapCodec;
|
||||||
|
import com.mojang.serialization.codecs.RecordCodecBuilder;
|
||||||
|
import dan200.computercraft.api.ComputerCraftAPI;
|
||||||
|
import dan200.computercraft.api.client.StandaloneModel;
|
||||||
|
import dan200.computercraft.api.turtle.ITurtleAccess;
|
||||||
|
import dan200.computercraft.api.turtle.ITurtleUpgrade;
|
||||||
|
import dan200.computercraft.api.turtle.TurtleSide;
|
||||||
|
import dan200.computercraft.api.upgrades.UpgradeData;
|
||||||
|
import net.minecraft.client.renderer.MultiBufferSource;
|
||||||
|
import net.minecraft.client.renderer.block.model.ItemTransform;
|
||||||
|
import net.minecraft.client.renderer.item.BlockModelWrapper;
|
||||||
|
import net.minecraft.client.renderer.item.ItemModelResolver;
|
||||||
|
import net.minecraft.client.renderer.item.ItemStackRenderState;
|
||||||
|
import net.minecraft.client.resources.model.ModelBaker;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link TurtleUpgradeModel} that renders a basic model.
|
||||||
|
* <p>
|
||||||
|
* This is the {@link TurtleUpgradeModel} equivalent of {@link BlockModelWrapper}.
|
||||||
|
*/
|
||||||
|
public final class BasicUpgradeModel implements TurtleUpgradeModel {
|
||||||
|
public static final ResourceLocation ID = ResourceLocation.fromNamespaceAndPath(ComputerCraftAPI.MOD_ID, "sided");
|
||||||
|
public static final MapCodec<? extends TurtleUpgradeModel.Unbaked> CODEC = RecordCodecBuilder.<Unbaked>mapCodec(instance -> instance.group(
|
||||||
|
ResourceLocation.CODEC.fieldOf("left").forGetter(Unbaked::left),
|
||||||
|
ResourceLocation.CODEC.fieldOf("right").forGetter(Unbaked::right)
|
||||||
|
).apply(instance, Unbaked::new));
|
||||||
|
|
||||||
|
private final StandaloneModel left;
|
||||||
|
private final StandaloneModel right;
|
||||||
|
|
||||||
|
private BasicUpgradeModel(StandaloneModel left, StandaloneModel right) {
|
||||||
|
this.left = left;
|
||||||
|
this.right = right;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an unbaked {@link BasicUpgradeModel}.
|
||||||
|
*
|
||||||
|
* @param left The model when equipped on the left.
|
||||||
|
* @param right The model when equipped on the right.
|
||||||
|
* @return The unbaked turtle upgrade model.
|
||||||
|
*/
|
||||||
|
public static TurtleUpgradeModel.Unbaked unbaked(ResourceLocation left, ResourceLocation right) {
|
||||||
|
return new Unbaked(left, right);
|
||||||
|
}
|
||||||
|
|
||||||
|
private StandaloneModel getModel(TurtleSide side) {
|
||||||
|
return switch (side) {
|
||||||
|
case LEFT -> left;
|
||||||
|
case RIGHT -> right;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void renderForItem(UpgradeData<ITurtleUpgrade> upgrade, TurtleSide side, ItemStackRenderState renderer, ItemModelResolver resolver, ItemTransform transform, int seed) {
|
||||||
|
renderer.appendModelIdentityElement(this);
|
||||||
|
renderer.appendModelIdentityElement(side);
|
||||||
|
renderer.appendModelIdentityElement(transform);
|
||||||
|
|
||||||
|
var layer = renderer.newLayer();
|
||||||
|
layer.setTransform(transform);
|
||||||
|
getModel(side).setupItemLayer(layer);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void renderForLevel(UpgradeData<ITurtleUpgrade> upgrade, TurtleSide side, ITurtleAccess turtle, PoseStack transform, MultiBufferSource buffers, int light, int overlay) {
|
||||||
|
getModel(side).render(transform, buffers, light, overlay);
|
||||||
|
}
|
||||||
|
|
||||||
|
private record Unbaked(ResourceLocation left, ResourceLocation right) implements TurtleUpgradeModel.Unbaked {
|
||||||
|
@Override
|
||||||
|
public MapCodec<? extends TurtleUpgradeModel.Unbaked> type() {
|
||||||
|
return CODEC;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TurtleUpgradeModel bake(ModelBaker baker) {
|
||||||
|
return new BasicUpgradeModel(StandaloneModel.of(left(), baker), StandaloneModel.of(right(), baker));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void resolveDependencies(Resolver resolver) {
|
||||||
|
resolver.markDependency(left());
|
||||||
|
resolver.markDependency(right());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,143 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2025 The CC: Tweaked Developers
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
|
package dan200.computercraft.api.client.turtle;
|
||||||
|
|
||||||
|
import com.mojang.blaze3d.vertex.PoseStack;
|
||||||
|
import com.mojang.math.Axis;
|
||||||
|
import com.mojang.math.Transformation;
|
||||||
|
import com.mojang.serialization.MapCodec;
|
||||||
|
import dan200.computercraft.api.ComputerCraftAPI;
|
||||||
|
import dan200.computercraft.api.turtle.ITurtleAccess;
|
||||||
|
import dan200.computercraft.api.turtle.ITurtleUpgrade;
|
||||||
|
import dan200.computercraft.api.turtle.TurtleSide;
|
||||||
|
import dan200.computercraft.api.upgrades.UpgradeData;
|
||||||
|
import net.minecraft.client.Minecraft;
|
||||||
|
import net.minecraft.client.renderer.MultiBufferSource;
|
||||||
|
import net.minecraft.client.renderer.block.model.ItemTransform;
|
||||||
|
import net.minecraft.client.renderer.item.ItemModelResolver;
|
||||||
|
import net.minecraft.client.renderer.item.ItemStackRenderState;
|
||||||
|
import net.minecraft.client.renderer.item.TrackingItemStackRenderState;
|
||||||
|
import net.minecraft.client.renderer.special.SpecialModelRenderer;
|
||||||
|
import net.minecraft.client.resources.model.ModelBaker;
|
||||||
|
import net.minecraft.core.component.DataComponentPatch;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import net.minecraft.util.Mth;
|
||||||
|
import net.minecraft.world.item.ItemDisplayContext;
|
||||||
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
import org.joml.Matrix4f;
|
||||||
|
import org.joml.Vector3f;
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A sic {@link TurtleUpgradeModel} that renders the upgrade's {@linkplain ITurtleUpgrade#getUpgradeItem(DataComponentPatch)
|
||||||
|
* upgrade item}.
|
||||||
|
* <p>
|
||||||
|
* This uses appropriate transformations for "flat" items, namely those extending the {@literal minecraft:item/generated}
|
||||||
|
* model type. It will not appear correct for 3D models with additional depth, such as blocks.
|
||||||
|
*/
|
||||||
|
public final class ItemUpgradeModel implements TurtleUpgradeModel {
|
||||||
|
private static final TurtleUpgradeModel.Unbaked UNBAKED = new Unbaked();
|
||||||
|
private static final TurtleUpgradeModel INSTANCE = new ItemUpgradeModel();
|
||||||
|
|
||||||
|
public static final ResourceLocation ID = ResourceLocation.fromNamespaceAndPath(ComputerCraftAPI.MOD_ID, "item");
|
||||||
|
public static final MapCodec<TurtleUpgradeModel.Unbaked> CODEC = MapCodec.unit(UNBAKED);
|
||||||
|
|
||||||
|
private static final TransformedRenderer LEFT = computeRenderer(TurtleSide.LEFT);
|
||||||
|
private static final TransformedRenderer RIGHT = computeRenderer(TurtleSide.RIGHT);
|
||||||
|
|
||||||
|
private ItemUpgradeModel() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the unbaked {@link ItemUpgradeModel}.
|
||||||
|
*
|
||||||
|
* @return The unbaked item upgrade model.
|
||||||
|
*/
|
||||||
|
public static TurtleUpgradeModel.Unbaked unbaked() {
|
||||||
|
return UNBAKED;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void renderForItem(UpgradeData<ITurtleUpgrade> upgrade, TurtleSide side, ItemStackRenderState renderer, ItemModelResolver resolver, ItemTransform transform, int seed) {
|
||||||
|
renderer.appendModelIdentityElement(this);
|
||||||
|
|
||||||
|
var childState = new TrackingItemStackRenderState();
|
||||||
|
resolver.updateForTopItem(childState, upgrade.getUpgradeItem(), ItemDisplayContext.NONE, null, null, seed);
|
||||||
|
if (!childState.isEmpty()) {
|
||||||
|
renderer.appendModelIdentityElement(childState.getModelIdentity());
|
||||||
|
renderer.appendModelIdentityElement(transform);
|
||||||
|
|
||||||
|
var layer = renderer.newLayer();
|
||||||
|
layer.setTransform(transform);
|
||||||
|
layer.setupSpecialModel(getRenderer(side), childState);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void renderForLevel(UpgradeData<ITurtleUpgrade> upgrade, TurtleSide side, ITurtleAccess turtle, PoseStack transform, MultiBufferSource buffers, int light, int overlay) {
|
||||||
|
transform.mulPose(getRenderer(side).transform().getMatrix());
|
||||||
|
transform.mulPose(Axis.YP.rotation(Mth.PI));
|
||||||
|
Minecraft.getInstance().getItemRenderer().renderStatic(
|
||||||
|
upgrade.getUpgradeItem(), ItemDisplayContext.FIXED, light, overlay, transform, buffers, turtle.getLevel(), 0
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class Unbaked implements TurtleUpgradeModel.Unbaked {
|
||||||
|
@Override
|
||||||
|
public MapCodec<? extends TurtleUpgradeModel.Unbaked> type() {
|
||||||
|
return CODEC;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TurtleUpgradeModel bake(ModelBaker baker) {
|
||||||
|
return INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void resolveDependencies(Resolver resolver) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static TransformedRenderer computeRenderer(TurtleSide side) {
|
||||||
|
var pose = new Matrix4f();
|
||||||
|
pose.translate(0.5f, 0.5f, 0.5f);
|
||||||
|
pose.rotate(Axis.YN.rotationDegrees(90f));
|
||||||
|
pose.rotate(Axis.ZP.rotationDegrees(90f));
|
||||||
|
pose.translate(0.0f, 0.0f, side == TurtleSide.RIGHT ? -0.4065f : 0.4065f);
|
||||||
|
return new TransformedRenderer(new Transformation(pose));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static TransformedRenderer getRenderer(TurtleSide side) {
|
||||||
|
return switch (side) {
|
||||||
|
case LEFT -> LEFT;
|
||||||
|
case RIGHT -> RIGHT;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private record TransformedRenderer(Transformation transform) implements SpecialModelRenderer<ItemStackRenderState> {
|
||||||
|
@Override
|
||||||
|
public void render(
|
||||||
|
@Nullable ItemStackRenderState state, ItemDisplayContext itemDisplayContext, PoseStack poseStack,
|
||||||
|
MultiBufferSource multiBufferSource, int overlay, int light, boolean bl
|
||||||
|
) {
|
||||||
|
if (state == null) return;
|
||||||
|
poseStack.pushPose();
|
||||||
|
poseStack.mulPose(transform.getMatrix());
|
||||||
|
state.render(poseStack, multiBufferSource, overlay, light);
|
||||||
|
poseStack.popPose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void getExtents(Set<Vector3f> set) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable ItemStackRenderState extractArgument(ItemStack itemStack) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,25 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
|
package dan200.computercraft.api.client.turtle;
|
||||||
|
|
||||||
|
import com.mojang.serialization.MapCodec;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A functional interface to register a {@link TurtleUpgradeModel}.
|
||||||
|
* <p>
|
||||||
|
* This interface is largely intended to be used from multi-loader code, to allow sharing registration code between
|
||||||
|
* multiple loaders.
|
||||||
|
*/
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface RegisterTurtleUpgradeModel {
|
||||||
|
/**
|
||||||
|
* Register a {@link TurtleUpgradeModel}.
|
||||||
|
*
|
||||||
|
* @param id The id used for this type of upgrade model.
|
||||||
|
* @param model The codec used to read/decode an upgrade model.
|
||||||
|
*/
|
||||||
|
void register(ResourceLocation id, MapCodec<? extends TurtleUpgradeModel.Unbaked> model);
|
||||||
|
}
|
@@ -1,26 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: MPL-2.0
|
|
||||||
|
|
||||||
package dan200.computercraft.api.client.turtle;
|
|
||||||
|
|
||||||
import dan200.computercraft.api.turtle.ITurtleUpgrade;
|
|
||||||
import dan200.computercraft.api.turtle.TurtleUpgradeSerialiser;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A functional interface to register a {@link TurtleUpgradeModeller} for a class of turtle upgrades.
|
|
||||||
* <p>
|
|
||||||
* This interface is largely intended to be used from multi-loader code, to allow sharing registration code between
|
|
||||||
* multiple loaders.
|
|
||||||
*/
|
|
||||||
@FunctionalInterface
|
|
||||||
public interface RegisterTurtleUpgradeModeller {
|
|
||||||
/**
|
|
||||||
* Register a {@link TurtleUpgradeModeller}.
|
|
||||||
*
|
|
||||||
* @param serialiser The turtle upgrade serialiser.
|
|
||||||
* @param modeller The upgrade modeller.
|
|
||||||
* @param <T> The type of the turtle upgrade.
|
|
||||||
*/
|
|
||||||
<T extends ITurtleUpgrade> void register(TurtleUpgradeSerialiser<T> serialiser, TurtleUpgradeModeller<T> modeller);
|
|
||||||
}
|
|
@@ -0,0 +1,209 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2025 The CC: Tweaked Developers
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
|
package dan200.computercraft.api.client.turtle;
|
||||||
|
|
||||||
|
import com.google.common.collect.HashMultiset;
|
||||||
|
import com.google.common.collect.Multiset;
|
||||||
|
import com.mojang.blaze3d.vertex.PoseStack;
|
||||||
|
import com.mojang.datafixers.util.Pair;
|
||||||
|
import com.mojang.serialization.Codec;
|
||||||
|
import com.mojang.serialization.DataResult;
|
||||||
|
import com.mojang.serialization.MapCodec;
|
||||||
|
import com.mojang.serialization.codecs.RecordCodecBuilder;
|
||||||
|
import dan200.computercraft.api.ComputerCraftAPI;
|
||||||
|
import dan200.computercraft.api.turtle.ITurtleAccess;
|
||||||
|
import dan200.computercraft.api.turtle.ITurtleUpgrade;
|
||||||
|
import dan200.computercraft.api.turtle.TurtleSide;
|
||||||
|
import dan200.computercraft.api.upgrades.UpgradeData;
|
||||||
|
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
||||||
|
import net.minecraft.Util;
|
||||||
|
import net.minecraft.client.renderer.MultiBufferSource;
|
||||||
|
import net.minecraft.client.renderer.block.model.ItemTransform;
|
||||||
|
import net.minecraft.client.renderer.item.ItemModelResolver;
|
||||||
|
import net.minecraft.client.renderer.item.ItemStackRenderState;
|
||||||
|
import net.minecraft.client.renderer.item.SelectItemModel;
|
||||||
|
import net.minecraft.client.resources.model.MissingBlockModel;
|
||||||
|
import net.minecraft.client.resources.model.ModelBaker;
|
||||||
|
import net.minecraft.core.component.DataComponentType;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link TurtleUpgradeModel} which selects between different models based on the value of a component in
|
||||||
|
* {@linkplain UpgradeData#data() the upgrade's data}.
|
||||||
|
* <p>
|
||||||
|
* This is the {@link TurtleUpgradeModel} equivalent of {@link SelectItemModel}.
|
||||||
|
*
|
||||||
|
* @param <T> The type of value to switch on.
|
||||||
|
*/
|
||||||
|
public final class SelectUpgradeModel<T> implements TurtleUpgradeModel {
|
||||||
|
public static final ResourceLocation ID = ResourceLocation.fromNamespaceAndPath(ComputerCraftAPI.MOD_ID, "select");
|
||||||
|
public static final MapCodec<? extends TurtleUpgradeModel.Unbaked> CODEC = RecordCodecBuilder.<Unbaked<?>>mapCodec(instance -> instance.group(
|
||||||
|
Cases.CODEC.forGetter(Unbaked::cases),
|
||||||
|
TurtleUpgradeModel.CODEC.optionalFieldOf("fallback").forGetter(Unbaked::fallback)
|
||||||
|
).apply(instance, Unbaked::new));
|
||||||
|
|
||||||
|
private final DataComponentType<T> component;
|
||||||
|
private final Map<T, TurtleUpgradeModel> cases;
|
||||||
|
private final TurtleUpgradeModel fallback;
|
||||||
|
|
||||||
|
private SelectUpgradeModel(DataComponentType<T> component, Map<T, TurtleUpgradeModel> cases, TurtleUpgradeModel fallback) {
|
||||||
|
this.component = component;
|
||||||
|
this.cases = cases;
|
||||||
|
this.fallback = fallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
private TurtleUpgradeModel getModel(UpgradeData<ITurtleUpgrade> upgrade) {
|
||||||
|
var value = upgrade.get(component);
|
||||||
|
if (value == null) return fallback;
|
||||||
|
|
||||||
|
var model = cases.get(value);
|
||||||
|
return model != null ? model : fallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void renderForItem(UpgradeData<ITurtleUpgrade> upgrade, TurtleSide side, ItemStackRenderState renderer, ItemModelResolver resolver, ItemTransform transform, int seed) {
|
||||||
|
getModel(upgrade).renderForItem(upgrade, side, renderer, resolver, transform, seed);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void renderForLevel(UpgradeData<ITurtleUpgrade> upgrade, TurtleSide side, ITurtleAccess turtle, PoseStack transform, MultiBufferSource buffers, int light, int overlay) {
|
||||||
|
getModel(upgrade).renderForLevel(upgrade, side, turtle, transform, buffers, light, overlay);
|
||||||
|
}
|
||||||
|
|
||||||
|
private record Unbaked<T>(
|
||||||
|
Cases<T> cases,
|
||||||
|
Optional<TurtleUpgradeModel.Unbaked> fallback
|
||||||
|
) implements TurtleUpgradeModel.Unbaked {
|
||||||
|
private static final TurtleUpgradeModel.Unbaked MISSING = BasicUpgradeModel.unbaked(MissingBlockModel.LOCATION, MissingBlockModel.LOCATION);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MapCodec<? extends TurtleUpgradeModel.Unbaked> type() {
|
||||||
|
return CODEC;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TurtleUpgradeModel bake(ModelBaker baker) {
|
||||||
|
Map<T, TurtleUpgradeModel> cases = new Object2ObjectOpenHashMap<>();
|
||||||
|
for (var condition : cases().cases()) {
|
||||||
|
var model = condition.getSecond().bake(baker);
|
||||||
|
for (var when : condition.getFirst()) cases.put(when, model);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new SelectUpgradeModel<>(cases().component(), cases, fallback().orElse(MISSING).bake(baker));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void resolveDependencies(Resolver resolver) {
|
||||||
|
cases().cases().forEach(x -> x.getSecond().resolveDependencies(resolver));
|
||||||
|
fallback().orElse(MISSING).resolveDependencies(resolver);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private record Cases<T>(DataComponentType<T> component, List<Pair<List<T>, TurtleUpgradeModel.Unbaked>> cases) {
|
||||||
|
private static final MapCodec<Cases<?>> CODEC = DataComponentType.CODEC.dispatchMap("property", Cases::component, Util.memoize(Cases::codec));
|
||||||
|
|
||||||
|
private static <T> MapCodec<Cases<T>> codec(DataComponentType<T> component) {
|
||||||
|
return RecordCodecBuilder.mapCodec(instance -> instance.group(
|
||||||
|
MapCodec.unit(component).forGetter(Cases::component),
|
||||||
|
caseCodec(component.codecOrThrow()).listOf().fieldOf("cases").validate(Cases::validate).forGetter(Cases::cases)
|
||||||
|
).apply(instance, Cases<T>::new));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static <T> Codec<Pair<List<T>, TurtleUpgradeModel.Unbaked>> caseCodec(Codec<T> codec) {
|
||||||
|
return RecordCodecBuilder.create(instance -> instance.group(
|
||||||
|
codec.listOf().fieldOf("when").forGetter(Pair::getFirst),
|
||||||
|
TurtleUpgradeModel.CODEC.fieldOf("model").forGetter(Pair::getSecond)
|
||||||
|
).apply(instance, Pair::new));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static <T> DataResult<List<Pair<List<T>, TurtleUpgradeModel.Unbaked>>> validate(List<Pair<List<T>, TurtleUpgradeModel.Unbaked>> cases) {
|
||||||
|
Multiset<T> multiset = HashMultiset.create();
|
||||||
|
for (var condition : cases) multiset.addAll(condition.getFirst());
|
||||||
|
|
||||||
|
if (multiset.isEmpty()) return DataResult.error(() -> "Empty cases");
|
||||||
|
if (multiset.size() != multiset.entrySet().size()) {
|
||||||
|
return DataResult.error(() -> "Duplicate case conditions: " + multiset.entrySet().stream()
|
||||||
|
.filter(x -> x.getCount() > 1)
|
||||||
|
.map(x -> Objects.toString(x.getElement()))
|
||||||
|
.collect(Collectors.joining(", ")));
|
||||||
|
}
|
||||||
|
|
||||||
|
return DataResult.success(cases);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a {@link SelectUpgradeModel} that selects a model based on a component.
|
||||||
|
*
|
||||||
|
* @param component The component to select.
|
||||||
|
* @param <T> The type the component stores.
|
||||||
|
* @return A {@link Builder}.
|
||||||
|
*/
|
||||||
|
public static <T> Builder<T> onComponent(DataComponentType<T> component) {
|
||||||
|
return new Builder<>(component);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A builder for constructing {@link SelectUpgradeModel}s.
|
||||||
|
*
|
||||||
|
* @param <T> The type of value to switch on.
|
||||||
|
*/
|
||||||
|
public static final class Builder<T> {
|
||||||
|
private final DataComponentType<T> component;
|
||||||
|
private final List<Pair<List<T>, TurtleUpgradeModel.Unbaked>> cases = new ArrayList<>();
|
||||||
|
private TurtleUpgradeModel.@Nullable Unbaked fallback;
|
||||||
|
|
||||||
|
private Builder(DataComponentType<T> component) {
|
||||||
|
this.component = component;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a case to our model.
|
||||||
|
*
|
||||||
|
* @param value The value for this case.
|
||||||
|
* @param model The model to use.
|
||||||
|
* @return {@code this}, for chaining.
|
||||||
|
*/
|
||||||
|
public Builder<T> when(T value, TurtleUpgradeModel.Unbaked model) {
|
||||||
|
return when(List.of(value), model);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a case to our model.
|
||||||
|
*
|
||||||
|
* @param values The value(s) for this case.
|
||||||
|
* @param model The model to use.
|
||||||
|
* @return {@code this}, for chaining.
|
||||||
|
*/
|
||||||
|
public Builder<T> when(List<T> values, TurtleUpgradeModel.Unbaked model) {
|
||||||
|
cases.add(Pair.of(values, model));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a fallback value, when no previous value matches or the component is not present.
|
||||||
|
*
|
||||||
|
* @param model The fallback model.
|
||||||
|
* @return {@code this}, for chaining.
|
||||||
|
*/
|
||||||
|
public Builder<T> fallback(TurtleUpgradeModel.Unbaked model) {
|
||||||
|
this.fallback = model;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert this builder into an unbaked model.
|
||||||
|
*
|
||||||
|
* @return The unbaked {@link SelectUpgradeModel}.
|
||||||
|
*/
|
||||||
|
public TurtleUpgradeModel.Unbaked create() {
|
||||||
|
return new Unbaked<>(new Cases<>(component, cases), Optional.ofNullable(fallback));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,110 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2025 The CC: Tweaked Developers
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
|
package dan200.computercraft.api.client.turtle;
|
||||||
|
|
||||||
|
import com.mojang.blaze3d.vertex.PoseStack;
|
||||||
|
import com.mojang.serialization.Codec;
|
||||||
|
import com.mojang.serialization.MapCodec;
|
||||||
|
import dan200.computercraft.api.ComputerCraftAPI;
|
||||||
|
import dan200.computercraft.api.turtle.ITurtleAccess;
|
||||||
|
import dan200.computercraft.api.turtle.ITurtleUpgrade;
|
||||||
|
import dan200.computercraft.api.turtle.TurtleSide;
|
||||||
|
import dan200.computercraft.api.upgrades.UpgradeData;
|
||||||
|
import dan200.computercraft.impl.client.ComputerCraftAPIClientService;
|
||||||
|
import net.minecraft.client.multiplayer.ClientLevel;
|
||||||
|
import net.minecraft.client.renderer.MultiBufferSource;
|
||||||
|
import net.minecraft.client.renderer.block.model.ItemTransform;
|
||||||
|
import net.minecraft.client.renderer.item.ItemModel;
|
||||||
|
import net.minecraft.client.renderer.item.ItemModelResolver;
|
||||||
|
import net.minecraft.client.renderer.item.ItemStackRenderState;
|
||||||
|
import net.minecraft.client.resources.model.ModelBaker;
|
||||||
|
import net.minecraft.client.resources.model.ResolvableModel;
|
||||||
|
import net.minecraft.world.entity.LivingEntity;
|
||||||
|
import net.minecraft.world.item.ItemDisplayContext;
|
||||||
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The model for a {@link ITurtleUpgrade}.
|
||||||
|
* <p>
|
||||||
|
* Turtle upgrade models are very similar to vanilla's {@link ItemModel}. Each upgrade's model is defined in JSON, and
|
||||||
|
* loaded from resource packs with other assets.
|
||||||
|
* <p>
|
||||||
|
* In most cases, upgrades can use one of the existing implementations of {@link TurtleUpgradeModel} (e.g.
|
||||||
|
* {@link BasicUpgradeModel} or {@link ItemUpgradeModel}), and do not need to subclass it. However, in the cases where
|
||||||
|
* a custom model is required, one should use
|
||||||
|
* {@code dan200.computercraft.api.client.FabricComputerCraftAPIClient#registerTurtleUpgradeModel} to register a
|
||||||
|
* model on Fabric and {@code dan200.computercraft.api.client.turtle.RegisterTurtleModelEvent} to register one
|
||||||
|
* on Forge.
|
||||||
|
* <p>
|
||||||
|
* See {@link ITurtleUpgrade} for a full example of registering turtle upgrades and their models.
|
||||||
|
*
|
||||||
|
* @see RegisterTurtleUpgradeModel For multi-loader registration support.
|
||||||
|
* @see ItemUpgradeModel A {@code TurtleUpgradeModel} which uses the upgrade's item.
|
||||||
|
* @see BasicUpgradeModel A {@code TurtleUpgradeModel} which renders a simple model.
|
||||||
|
*/
|
||||||
|
public interface TurtleUpgradeModel {
|
||||||
|
/**
|
||||||
|
* The directory from which turtle upgrade models are loaded. This may be used by data generators.
|
||||||
|
*/
|
||||||
|
String SOURCE = ComputerCraftAPI.MOD_ID + "/turtle_upgrade";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The codec used to read/write {@linkplain TurtleUpgradeModel.Unbaked unbaked upgrade models}.
|
||||||
|
*/
|
||||||
|
Codec<TurtleUpgradeModel.Unbaked> CODEC = Codec.lazyInitialized(() -> ComputerCraftAPIClientService.get().getTurtleUpgradeModelCodec());
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render this upgrade to an {@link ItemStackRenderState}. This is used for rendering the item form of the upgrade.
|
||||||
|
* <p>
|
||||||
|
* Like with {@link ItemModel}, implementations must be careful to call {@link ItemStackRenderState#appendModelIdentityElement}
|
||||||
|
* where appropriate.
|
||||||
|
*
|
||||||
|
* @param upgrade The upgrade being rendered.
|
||||||
|
* @param side Which side of the turtle (left or right) the upgrade is equipped on.
|
||||||
|
* @param renderer The render state to draw to.
|
||||||
|
* @param resolver The model resolver.
|
||||||
|
* @param transform The root model's transformation.
|
||||||
|
* @param seed The current model seed.
|
||||||
|
* @see ItemModel#update(ItemStackRenderState, ItemStack, ItemModelResolver, ItemDisplayContext, ClientLevel, LivingEntity, int)
|
||||||
|
*/
|
||||||
|
void renderForItem(UpgradeData<ITurtleUpgrade> upgrade, TurtleSide side, ItemStackRenderState renderer, ItemModelResolver resolver, ItemTransform transform, int seed);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render this upgrade to a {@link MultiBufferSource}. This is used for rendering the block-entity form of the
|
||||||
|
* upgrade.
|
||||||
|
*
|
||||||
|
* @param upgrade The upgrade being rendered.
|
||||||
|
* @param side Which side of the turtle (left or right) the upgrade is equipped on.
|
||||||
|
* @param turtle Access to the turtle that the upgrade resides on.
|
||||||
|
* @param transform The current pose stack.
|
||||||
|
* @param buffers The buffers to render to.
|
||||||
|
* @param light The lightmap coordinate.
|
||||||
|
* @param overlay The overlay coordinate.
|
||||||
|
*/
|
||||||
|
void renderForLevel(UpgradeData<ITurtleUpgrade> upgrade, TurtleSide side, ITurtleAccess turtle, PoseStack transform, MultiBufferSource buffers, int light, int overlay);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An unbaked turtle model. Much like other unbaked models (e.g. {@link ItemModel.Unbaked}), this should resolve
|
||||||
|
* any dependencies and returned the fully-resolved model.
|
||||||
|
*/
|
||||||
|
interface Unbaked extends ResolvableModel {
|
||||||
|
/**
|
||||||
|
* The {@link MapCodec} used to read/write this unbaked model.
|
||||||
|
*
|
||||||
|
* @return The codec used to read/write this model.
|
||||||
|
* @see ItemModel.Unbaked#type()
|
||||||
|
*/
|
||||||
|
MapCodec<? extends Unbaked> type();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bake this turtle model.
|
||||||
|
*
|
||||||
|
* @param baker The current model baker
|
||||||
|
* @return The baked upgrade model.
|
||||||
|
* @see ItemModel.Unbaked#bake(ItemModel.BakingContext)
|
||||||
|
*/
|
||||||
|
TurtleUpgradeModel bake(ModelBaker baker);
|
||||||
|
}
|
||||||
|
}
|
@@ -1,122 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: 2022 The CC: Tweaked Developers
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: MPL-2.0
|
|
||||||
|
|
||||||
package dan200.computercraft.api.client.turtle;
|
|
||||||
|
|
||||||
import dan200.computercraft.api.client.TransformedModel;
|
|
||||||
import dan200.computercraft.api.turtle.ITurtleAccess;
|
|
||||||
import dan200.computercraft.api.turtle.ITurtleUpgrade;
|
|
||||||
import dan200.computercraft.api.turtle.TurtleSide;
|
|
||||||
import net.minecraft.client.resources.model.ModelResourceLocation;
|
|
||||||
import net.minecraft.client.resources.model.UnbakedModel;
|
|
||||||
import net.minecraft.nbt.CompoundTag;
|
|
||||||
import net.minecraft.resources.ResourceLocation;
|
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Provides models for a {@link ITurtleUpgrade}.
|
|
||||||
* <p>
|
|
||||||
* Use {@code dan200.computercraft.api.client.FabricComputerCraftAPIClient#registerTurtleUpgradeModeller} to register a
|
|
||||||
* modeller on Fabric and {@code dan200.computercraft.api.client.turtle.RegisterTurtleModellersEvent} to register one
|
|
||||||
* on Forge
|
|
||||||
*
|
|
||||||
* @param <T> The type of turtle upgrade this modeller applies to.
|
|
||||||
* @see RegisterTurtleUpgradeModeller For multi-loader registration support.
|
|
||||||
*/
|
|
||||||
public interface TurtleUpgradeModeller<T extends ITurtleUpgrade> {
|
|
||||||
/**
|
|
||||||
* Obtain the model to be used when rendering a turtle peripheral.
|
|
||||||
* <p>
|
|
||||||
* When the current turtle is {@literal null}, this function should be constant for a given upgrade and side.
|
|
||||||
*
|
|
||||||
* @param upgrade The upgrade that you're getting the model for.
|
|
||||||
* @param turtle Access to the turtle that the upgrade resides on. This will be null when getting item models, unless
|
|
||||||
* {@link #getModel(ITurtleUpgrade, CompoundTag, TurtleSide)} is overriden.
|
|
||||||
* @param side Which side of the turtle (left or right) the upgrade resides on.
|
|
||||||
* @return The model that you wish to be used to render your upgrade.
|
|
||||||
*/
|
|
||||||
TransformedModel getModel(T upgrade, @Nullable ITurtleAccess turtle, TurtleSide side);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Obtain the model to be used when rendering a turtle peripheral.
|
|
||||||
* <p>
|
|
||||||
* This is used when rendering the turtle's item model, and so no {@link ITurtleAccess} is available.
|
|
||||||
*
|
|
||||||
* @param upgrade The upgrade that you're getting the model for.
|
|
||||||
* @param data Upgrade data instance for current turtle side.
|
|
||||||
* @param side Which side of the turtle (left or right) the upgrade resides on.
|
|
||||||
* @return The model that you wish to be used to render your upgrade.
|
|
||||||
*/
|
|
||||||
default TransformedModel getModel(T upgrade, CompoundTag data, TurtleSide side) {
|
|
||||||
return getModel(upgrade, (ITurtleAccess) null, side);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get a list of models that this turtle modeller depends on.
|
|
||||||
* <p>
|
|
||||||
* Models included in this list will be loaded and baked alongside item and block models, and so may be referenced
|
|
||||||
* by {@link TransformedModel#of(ResourceLocation)}. You do not need to override this method if you will load models
|
|
||||||
* by other means.
|
|
||||||
*
|
|
||||||
* @return A list of models that this modeller depends on.
|
|
||||||
* @see UnbakedModel#getDependencies()
|
|
||||||
*/
|
|
||||||
default Collection<ResourceLocation> getDependencies() {
|
|
||||||
return List.of();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A basic {@link TurtleUpgradeModeller} which renders using the upgrade's {@linkplain ITurtleUpgrade#getUpgradeItem(CompoundTag)}
|
|
||||||
* upgrade item}.
|
|
||||||
* <p>
|
|
||||||
* This uses appropriate transformations for "flat" items, namely those extending the {@literal minecraft:item/generated}
|
|
||||||
* model type. It will not appear correct for 3D models with additional depth, such as blocks.
|
|
||||||
*
|
|
||||||
* @param <T> The type of the turtle upgrade.
|
|
||||||
* @return The constructed modeller.
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
static <T extends ITurtleUpgrade> TurtleUpgradeModeller<T> flatItem() {
|
|
||||||
return (TurtleUpgradeModeller<T>) TurtleUpgradeModellers.UPGRADE_ITEM;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Construct a {@link TurtleUpgradeModeller} which has a single model for the left and right side.
|
|
||||||
*
|
|
||||||
* @param left The model to use on the left.
|
|
||||||
* @param right The model to use on the right.
|
|
||||||
* @param <T> The type of the turtle upgrade.
|
|
||||||
* @return The constructed modeller.
|
|
||||||
*/
|
|
||||||
static <T extends ITurtleUpgrade> TurtleUpgradeModeller<T> sided(ModelResourceLocation left, ModelResourceLocation right) {
|
|
||||||
// TODO(1.21.0): Remove this.
|
|
||||||
return sided((ResourceLocation) left, right);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Construct a {@link TurtleUpgradeModeller} which has a single model for the left and right side.
|
|
||||||
*
|
|
||||||
* @param left The model to use on the left.
|
|
||||||
* @param right The model to use on the right.
|
|
||||||
* @param <T> The type of the turtle upgrade.
|
|
||||||
* @return The constructed modeller.
|
|
||||||
*/
|
|
||||||
static <T extends ITurtleUpgrade> TurtleUpgradeModeller<T> sided(ResourceLocation left, ResourceLocation right) {
|
|
||||||
return new TurtleUpgradeModeller<>() {
|
|
||||||
@Override
|
|
||||||
public TransformedModel getModel(T upgrade, @Nullable ITurtleAccess turtle, TurtleSide side) {
|
|
||||||
return TransformedModel.of(side == TurtleSide.LEFT ? left : right);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Collection<ResourceLocation> getDependencies() {
|
|
||||||
return List.of(left, right);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,55 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: 2022 The CC: Tweaked Developers
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: MPL-2.0
|
|
||||||
|
|
||||||
package dan200.computercraft.api.client.turtle;
|
|
||||||
|
|
||||||
import com.mojang.math.Transformation;
|
|
||||||
import dan200.computercraft.api.client.TransformedModel;
|
|
||||||
import dan200.computercraft.api.turtle.ITurtleAccess;
|
|
||||||
import dan200.computercraft.api.turtle.ITurtleUpgrade;
|
|
||||||
import dan200.computercraft.api.turtle.TurtleSide;
|
|
||||||
import dan200.computercraft.impl.client.ClientPlatformHelper;
|
|
||||||
import net.minecraft.client.Minecraft;
|
|
||||||
import net.minecraft.nbt.CompoundTag;
|
|
||||||
import net.minecraft.world.item.ItemStack;
|
|
||||||
import org.joml.Matrix4f;
|
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
|
|
||||||
final class TurtleUpgradeModellers {
|
|
||||||
private static final Transformation leftTransform = getMatrixFor(-0.4065f);
|
|
||||||
private static final Transformation rightTransform = getMatrixFor(0.4065f);
|
|
||||||
|
|
||||||
private static Transformation getMatrixFor(float offset) {
|
|
||||||
var matrix = new Matrix4f();
|
|
||||||
matrix.set(new float[]{
|
|
||||||
0.0f, 0.0f, -1.0f, 1.0f + offset,
|
|
||||||
1.0f, 0.0f, 0.0f, 0.0f,
|
|
||||||
0.0f, -1.0f, 0.0f, 1.0f,
|
|
||||||
0.0f, 0.0f, 0.0f, 1.0f,
|
|
||||||
});
|
|
||||||
matrix.transpose();
|
|
||||||
return new Transformation(matrix);
|
|
||||||
}
|
|
||||||
|
|
||||||
static final TurtleUpgradeModeller<ITurtleUpgrade> UPGRADE_ITEM = new UpgradeItemModeller();
|
|
||||||
|
|
||||||
private static final class UpgradeItemModeller implements TurtleUpgradeModeller<ITurtleUpgrade> {
|
|
||||||
@Override
|
|
||||||
public TransformedModel getModel(ITurtleUpgrade upgrade, @Nullable ITurtleAccess turtle, TurtleSide side) {
|
|
||||||
return getModel(turtle == null ? upgrade.getCraftingItem() : upgrade.getUpgradeItem(turtle.getUpgradeNBTData(side)), side);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public TransformedModel getModel(ITurtleUpgrade upgrade, CompoundTag data, TurtleSide side) {
|
|
||||||
return getModel(upgrade.getUpgradeItem(data), side);
|
|
||||||
}
|
|
||||||
|
|
||||||
private TransformedModel getModel(ItemStack stack, TurtleSide side) {
|
|
||||||
var model = Minecraft.getInstance().getItemRenderer().getItemModelShaper().getItemModel(stack);
|
|
||||||
if (stack.hasFoil()) model = ClientPlatformHelper.get().createdFoiledModel(model);
|
|
||||||
return new TransformedModel(model, side == TurtleSide.LEFT ? leftTransform : rightTransform);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,55 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: 2022 The CC: Tweaked Developers
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: MPL-2.0
|
|
||||||
|
|
||||||
package dan200.computercraft.impl.client;
|
|
||||||
|
|
||||||
import dan200.computercraft.impl.Services;
|
|
||||||
import net.minecraft.client.renderer.RenderType;
|
|
||||||
import net.minecraft.client.resources.model.BakedModel;
|
|
||||||
import net.minecraft.client.resources.model.ModelManager;
|
|
||||||
import net.minecraft.client.resources.model.ModelResourceLocation;
|
|
||||||
import net.minecraft.resources.ResourceLocation;
|
|
||||||
import org.jetbrains.annotations.ApiStatus;
|
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
|
|
||||||
@ApiStatus.Internal
|
|
||||||
public interface ClientPlatformHelper {
|
|
||||||
/**
|
|
||||||
* Equivalent to {@link ModelManager#getModel(ModelResourceLocation)} but for arbitrary {@link ResourceLocation}s.
|
|
||||||
*
|
|
||||||
* @param manager The model manager.
|
|
||||||
* @param location The model location.
|
|
||||||
* @return The baked model.
|
|
||||||
*/
|
|
||||||
BakedModel getModel(ModelManager manager, ResourceLocation location);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Wrap this model in a version which renders a foil/enchantment glint.
|
|
||||||
*
|
|
||||||
* @param model The model to wrap.
|
|
||||||
* @return The wrapped model.
|
|
||||||
* @see RenderType#glint()
|
|
||||||
*/
|
|
||||||
BakedModel createdFoiledModel(BakedModel model);
|
|
||||||
|
|
||||||
static ClientPlatformHelper get() {
|
|
||||||
var instance = Instance.INSTANCE;
|
|
||||||
return instance == null ? Services.raise(ClientPlatformHelper.class, Instance.ERROR) : instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
final class Instance {
|
|
||||||
static final @Nullable ClientPlatformHelper INSTANCE;
|
|
||||||
static final @Nullable Throwable ERROR;
|
|
||||||
|
|
||||||
static {
|
|
||||||
var helper = Services.tryLoad(ClientPlatformHelper.class);
|
|
||||||
INSTANCE = helper.instance();
|
|
||||||
ERROR = helper.error();
|
|
||||||
}
|
|
||||||
|
|
||||||
private Instance() {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,20 +1,17 @@
|
|||||||
// SPDX-FileCopyrightText: 2022 The CC: Tweaked Developers
|
// SPDX-FileCopyrightText: 2025 The CC: Tweaked Developers
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: MPL-2.0
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
package dan200.computercraft.impl.client;
|
package dan200.computercraft.impl.client;
|
||||||
|
|
||||||
import dan200.computercraft.api.client.ComputerCraftAPIClient;
|
import com.mojang.serialization.Codec;
|
||||||
import dan200.computercraft.api.client.turtle.TurtleUpgradeModeller;
|
import dan200.computercraft.api.client.turtle.TurtleUpgradeModel;
|
||||||
import dan200.computercraft.api.turtle.ITurtleUpgrade;
|
|
||||||
import dan200.computercraft.api.turtle.TurtleUpgradeSerialiser;
|
|
||||||
import dan200.computercraft.impl.Services;
|
import dan200.computercraft.impl.Services;
|
||||||
import org.jetbrains.annotations.ApiStatus;
|
import org.jetbrains.annotations.ApiStatus;
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
import javax.annotation.Nullable;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Backing interface for {@link ComputerCraftAPIClient}
|
* Bridge between implementation
|
||||||
* <p>
|
* <p>
|
||||||
* Do <strong>NOT</strong> directly reference this class. It exists for internal use by the API.
|
* Do <strong>NOT</strong> directly reference this class. It exists for internal use by the API.
|
||||||
*/
|
*/
|
||||||
@@ -25,7 +22,7 @@ public interface ComputerCraftAPIClientService {
|
|||||||
return instance == null ? Services.raise(ComputerCraftAPIClientService.class, Instance.ERROR) : instance;
|
return instance == null ? Services.raise(ComputerCraftAPIClientService.class, Instance.ERROR) : instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
<T extends ITurtleUpgrade> void registerTurtleUpgradeModeller(TurtleUpgradeSerialiser<T> serialiser, TurtleUpgradeModeller<T> modeller);
|
Codec<TurtleUpgradeModel.Unbaked> getTurtleUpgradeModelCodec();
|
||||||
|
|
||||||
final class Instance {
|
final class Instance {
|
||||||
static final @Nullable ComputerCraftAPIClientService INSTANCE;
|
static final @Nullable ComputerCraftAPIClientService INSTANCE;
|
||||||
|
@@ -4,13 +4,13 @@
|
|||||||
|
|
||||||
package dan200.computercraft.api;
|
package dan200.computercraft.api;
|
||||||
|
|
||||||
|
import dan200.computercraft.api.component.ComputerComponent;
|
||||||
import dan200.computercraft.api.filesystem.Mount;
|
import dan200.computercraft.api.filesystem.Mount;
|
||||||
import dan200.computercraft.api.filesystem.WritableMount;
|
import dan200.computercraft.api.filesystem.WritableMount;
|
||||||
import dan200.computercraft.api.lua.GenericSource;
|
import dan200.computercraft.api.lua.GenericSource;
|
||||||
|
import dan200.computercraft.api.lua.IComputerSystem;
|
||||||
import dan200.computercraft.api.lua.ILuaAPI;
|
import dan200.computercraft.api.lua.ILuaAPI;
|
||||||
import dan200.computercraft.api.lua.ILuaAPIFactory;
|
import dan200.computercraft.api.lua.ILuaAPIFactory;
|
||||||
import dan200.computercraft.api.media.IMedia;
|
|
||||||
import dan200.computercraft.api.media.MediaProvider;
|
|
||||||
import dan200.computercraft.api.network.PacketNetwork;
|
import dan200.computercraft.api.network.PacketNetwork;
|
||||||
import dan200.computercraft.api.network.wired.WiredElement;
|
import dan200.computercraft.api.network.wired.WiredElement;
|
||||||
import dan200.computercraft.api.network.wired.WiredNode;
|
import dan200.computercraft.api.network.wired.WiredNode;
|
||||||
@@ -24,8 +24,7 @@ import net.minecraft.core.Direction;
|
|||||||
import net.minecraft.server.MinecraftServer;
|
import net.minecraft.server.MinecraftServer;
|
||||||
import net.minecraft.world.item.ItemStack;
|
import net.minecraft.world.item.ItemStack;
|
||||||
import net.minecraft.world.level.Level;
|
import net.minecraft.world.level.Level;
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
import javax.annotation.Nullable;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The static entry point to the ComputerCraft API.
|
* The static entry point to the ComputerCraft API.
|
||||||
@@ -141,16 +140,6 @@ public final class ComputerCraftAPI {
|
|||||||
return getInstance().getBundledRedstoneOutput(world, pos, side);
|
return getInstance().getBundledRedstoneOutput(world, pos, side);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Registers a media provider to provide {@link IMedia} implementations for Items.
|
|
||||||
*
|
|
||||||
* @param provider The media provider to register.
|
|
||||||
* @see MediaProvider
|
|
||||||
*/
|
|
||||||
public static void registerMediaProvider(MediaProvider provider) {
|
|
||||||
getInstance().registerMediaProvider(provider);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Attempt to get the game-wide wireless network.
|
* Attempt to get the game-wide wireless network.
|
||||||
*
|
*
|
||||||
@@ -165,7 +154,13 @@ public final class ComputerCraftAPI {
|
|||||||
* Register a custom {@link ILuaAPI}, which may be added onto all computers without requiring a peripheral.
|
* Register a custom {@link ILuaAPI}, which may be added onto all computers without requiring a peripheral.
|
||||||
* <p>
|
* <p>
|
||||||
* Before implementing this interface, consider alternative methods of providing methods. It is generally preferred
|
* Before implementing this interface, consider alternative methods of providing methods. It is generally preferred
|
||||||
* to use peripherals to provide functionality to users.
|
* to use peripherals to provide functionality to users. If an API is <em>required</em>, you may want to consider
|
||||||
|
* using {@link ILuaAPI#getModuleName()} to expose this library as a module instead of as a global.
|
||||||
|
* <p>
|
||||||
|
* This may be used with {@link IComputerSystem#getComponent(ComputerComponent)} to only attach APIs to specific
|
||||||
|
* computers. For example, one can add a new API just to turtles with the following code:
|
||||||
|
*
|
||||||
|
* {@snippet class=com.example.examplemod.ExampleAPI region=register}
|
||||||
*
|
*
|
||||||
* @param factory The factory for your API subclass.
|
* @param factory The factory for your API subclass.
|
||||||
* @see ILuaAPIFactory
|
* @see ILuaAPIFactory
|
||||||
|
@@ -6,10 +6,12 @@ package dan200.computercraft.api;
|
|||||||
|
|
||||||
import net.minecraft.core.registries.Registries;
|
import net.minecraft.core.registries.Registries;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import net.minecraft.tags.ItemTags;
|
||||||
import net.minecraft.tags.TagKey;
|
import net.minecraft.tags.TagKey;
|
||||||
import net.minecraft.world.InteractionHand;
|
import net.minecraft.world.InteractionHand;
|
||||||
import net.minecraft.world.entity.player.Player;
|
import net.minecraft.world.entity.player.Player;
|
||||||
import net.minecraft.world.item.Item;
|
import net.minecraft.world.item.Item;
|
||||||
|
import net.minecraft.world.item.ItemStack;
|
||||||
import net.minecraft.world.item.context.UseOnContext;
|
import net.minecraft.world.item.context.UseOnContext;
|
||||||
import net.minecraft.world.level.Level;
|
import net.minecraft.world.level.Level;
|
||||||
import net.minecraft.world.level.block.Block;
|
import net.minecraft.world.level.block.Block;
|
||||||
@@ -26,6 +28,20 @@ public class ComputerCraftTags {
|
|||||||
public static final TagKey<Item> WIRED_MODEM = make("wired_modem");
|
public static final TagKey<Item> WIRED_MODEM = make("wired_modem");
|
||||||
public static final TagKey<Item> MONITOR = make("monitor");
|
public static final TagKey<Item> MONITOR = make("monitor");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Floppy disks. Both the read/write version, and treasure disks.
|
||||||
|
*
|
||||||
|
* @since 1.116.0
|
||||||
|
*/
|
||||||
|
public static final TagKey<Item> DISKS = make("disks");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* All pocket computers.
|
||||||
|
*
|
||||||
|
* @since 1.116.0
|
||||||
|
*/
|
||||||
|
public static final TagKey<Item> POCKET_COMPUTERS = make("pocket_computers");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Items which can be {@linkplain Item#use(Level, Player, InteractionHand) used} when calling
|
* Items which can be {@linkplain Item#use(Level, Player, InteractionHand) used} when calling
|
||||||
* {@code turtle.place()}.
|
* {@code turtle.place()}.
|
||||||
@@ -35,8 +51,16 @@ public class ComputerCraftTags {
|
|||||||
*/
|
*/
|
||||||
public static final TagKey<Item> TURTLE_CAN_PLACE = make("turtle_can_place");
|
public static final TagKey<Item> TURTLE_CAN_PLACE = make("turtle_can_place");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Items which can be dyed.
|
||||||
|
* <p>
|
||||||
|
* This is similar to {@link ItemTags#DYEABLE}, but allows cleaning the item with a sponge, rather than in a
|
||||||
|
* cauldron.
|
||||||
|
*/
|
||||||
|
public static final TagKey<Item> DYEABLE = make("dyeable");
|
||||||
|
|
||||||
private static TagKey<Item> make(String name) {
|
private static TagKey<Item> make(String name) {
|
||||||
return TagKey.create(Registries.ITEM, new ResourceLocation(ComputerCraftAPI.MOD_ID, name));
|
return TagKey.create(Registries.ITEM, ResourceLocation.fromNamespaceAndPath(ComputerCraftAPI.MOD_ID, name));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -75,13 +99,13 @@ public class ComputerCraftTags {
|
|||||||
public static final TagKey<Block> TURTLE_HOE_BREAKABLE = make("turtle_hoe_harvestable");
|
public static final TagKey<Block> TURTLE_HOE_BREAKABLE = make("turtle_hoe_harvestable");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Block which can be {@linkplain BlockState#use(Level, Player, InteractionHand, BlockHitResult) used} when
|
* Block which can be {@linkplain BlockState#useItemOn(ItemStack, Level, Player, InteractionHand, BlockHitResult) used}
|
||||||
* calling {@code turtle.place()}.
|
* when calling {@code turtle.place()}.
|
||||||
*/
|
*/
|
||||||
public static final TagKey<Block> TURTLE_CAN_USE = make("turtle_can_use");
|
public static final TagKey<Block> TURTLE_CAN_USE = make("turtle_can_use");
|
||||||
|
|
||||||
private static TagKey<Block> make(String name) {
|
private static TagKey<Block> make(String name) {
|
||||||
return TagKey.create(Registries.BLOCK, new ResourceLocation(ComputerCraftAPI.MOD_ID, name));
|
return TagKey.create(Registries.BLOCK, ResourceLocation.fromNamespaceAndPath(ComputerCraftAPI.MOD_ID, name));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,24 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2024 The CC: Tweaked Developers
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
|
package dan200.computercraft.api.component;
|
||||||
|
|
||||||
|
import net.minecraft.commands.CommandSourceStack;
|
||||||
|
import org.jetbrains.annotations.ApiStatus;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A computer which has permission to perform administrative/op commands, such as the command computer.
|
||||||
|
*/
|
||||||
|
@ApiStatus.NonExtendable
|
||||||
|
public interface AdminComputer {
|
||||||
|
/**
|
||||||
|
* The permission level that this computer can operate at.
|
||||||
|
*
|
||||||
|
* @return The permission level for this computer.
|
||||||
|
* @see CommandSourceStack#hasPermission(int)
|
||||||
|
*/
|
||||||
|
default int permissionLevel() {
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,48 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2024 The CC: Tweaked Developers
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
|
package dan200.computercraft.api.component;
|
||||||
|
|
||||||
|
import dan200.computercraft.api.lua.IComputerSystem;
|
||||||
|
import dan200.computercraft.api.lua.ILuaAPIFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A component attached to a computer.
|
||||||
|
* <p>
|
||||||
|
* Components provide a mechanism to attach additional data to a computer, that can then be queried with
|
||||||
|
* {@link IComputerSystem#getComponent(ComputerComponent)}.
|
||||||
|
* <p>
|
||||||
|
* This is largely designed for {@linkplain ILuaAPIFactory custom APIs}, allowing APIs to read additional properties
|
||||||
|
* of the computer, such as its position.
|
||||||
|
*
|
||||||
|
* @param <T> The type of this component.
|
||||||
|
* @see ComputerComponents The built-in components.
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("UnusedTypeParameter")
|
||||||
|
public final class ComputerComponent<T> {
|
||||||
|
private final String id;
|
||||||
|
|
||||||
|
private ComputerComponent(String id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new computer component.
|
||||||
|
* <p>
|
||||||
|
* Mods typically will not need to create their own components.
|
||||||
|
*
|
||||||
|
* @param namespace The namespace of this component. This should be the mod id.
|
||||||
|
* @param id The unique id of this component.
|
||||||
|
* @param <T> The component
|
||||||
|
* @return The newly created component.
|
||||||
|
*/
|
||||||
|
public static <T> ComputerComponent<T> create(String namespace, String id) {
|
||||||
|
return new ComputerComponent<>(namespace + ":" + id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "ComputerComponent(" + id + ")";
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,30 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2024 The CC: Tweaked Developers
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
|
package dan200.computercraft.api.component;
|
||||||
|
|
||||||
|
import dan200.computercraft.api.ComputerCraftAPI;
|
||||||
|
import dan200.computercraft.api.pocket.IPocketAccess;
|
||||||
|
import dan200.computercraft.api.pocket.PocketComputer;
|
||||||
|
import dan200.computercraft.api.turtle.ITurtleAccess;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@link ComputerComponent}s provided by ComputerCraft.
|
||||||
|
*/
|
||||||
|
public class ComputerComponents {
|
||||||
|
/**
|
||||||
|
* The {@link ITurtleAccess} associated with a turtle.
|
||||||
|
*/
|
||||||
|
public static final ComputerComponent<ITurtleAccess> TURTLE = ComputerComponent.create(ComputerCraftAPI.MOD_ID, "turtle");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@link IPocketAccess} associated with a pocket computer.
|
||||||
|
*/
|
||||||
|
public static final ComputerComponent<PocketComputer> POCKET = ComputerComponent.create(ComputerCraftAPI.MOD_ID, "pocket");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This component is only present on "command computers", and other computers with admin capabilities.
|
||||||
|
*/
|
||||||
|
public static final ComputerComponent<AdminComputer> ADMIN_COMPUTER = ComputerComponent.create(ComputerCraftAPI.MOD_ID, "admin_computer");
|
||||||
|
}
|
@@ -6,14 +6,14 @@ package dan200.computercraft.api.detail;
|
|||||||
|
|
||||||
import net.minecraft.world.item.Item;
|
import net.minecraft.world.item.Item;
|
||||||
import net.minecraft.world.item.ItemStack;
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An item detail provider for {@link ItemStack}'s whose {@link Item} has a specific type.
|
* An item detail provider for {@link ItemStack}s whose {@link Item} has a specific type.
|
||||||
*
|
*
|
||||||
* @param <T> The type the stack's item must have.
|
* @param <T> The type the stack's item must have.
|
||||||
*/
|
*/
|
||||||
@@ -22,7 +22,7 @@ public abstract class BasicItemDetailProvider<T> implements DetailProvider<ItemS
|
|||||||
private final @Nullable String namespace;
|
private final @Nullable String namespace;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new item detail provider. Meta will be inserted into a new sub-map named as per {@code namespace}.
|
* Create a new item detail provider. Details will be inserted into a new sub-map named as per {@code namespace}.
|
||||||
*
|
*
|
||||||
* @param itemType The type the stack's item must have.
|
* @param itemType The type the stack's item must have.
|
||||||
* @param namespace The namespace to use for this provider.
|
* @param namespace The namespace to use for this provider.
|
||||||
@@ -34,7 +34,7 @@ public abstract class BasicItemDetailProvider<T> implements DetailProvider<ItemS
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new item detail provider. Meta will be inserted directly into the results.
|
* Create a new item detail provider. Details will be inserted directly into the results.
|
||||||
*
|
*
|
||||||
* @param itemType The type the stack's item must have.
|
* @param itemType The type the stack's item must have.
|
||||||
*/
|
*/
|
||||||
@@ -53,21 +53,18 @@ public abstract class BasicItemDetailProvider<T> implements DetailProvider<ItemS
|
|||||||
* @param stack The item stack to provide details for.
|
* @param stack The item stack to provide details for.
|
||||||
* @param item The item to provide details for.
|
* @param item The item to provide details for.
|
||||||
*/
|
*/
|
||||||
public abstract void provideDetails(
|
public abstract void provideDetails(Map<? super String, Object> data, ItemStack stack, T item);
|
||||||
Map<? super String, Object> data, ItemStack stack, T item
|
|
||||||
);
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void provideDetails(Map<? super String, Object> data, ItemStack stack) {
|
public final void provideDetails(Map<? super String, Object> data, ItemStack stack) {
|
||||||
var item = stack.getItem();
|
var item = stack.getItem();
|
||||||
if (!itemType.isInstance(item)) return;
|
if (!itemType.isInstance(item)) return;
|
||||||
|
|
||||||
// If `namespace` is specified, insert into a new data map instead of the existing one.
|
if (namespace == null) {
|
||||||
Map<? super String, Object> child = namespace == null ? data : new HashMap<>();
|
provideDetails(data, stack, itemType.cast(item));
|
||||||
|
} else {
|
||||||
|
Map<? super String, Object> child = new HashMap<>();
|
||||||
provideDetails(child, stack, itemType.cast(item));
|
provideDetails(child, stack, itemType.cast(item));
|
||||||
|
|
||||||
if (namespace != null) {
|
|
||||||
data.put(namespace, child);
|
data.put(namespace, child);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -8,8 +8,7 @@ import net.minecraft.core.BlockPos;
|
|||||||
import net.minecraft.world.level.Level;
|
import net.minecraft.world.level.Level;
|
||||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||||
import net.minecraft.world.level.block.state.BlockState;
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
import javax.annotation.Nullable;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A reference to a block in the world, used by block detail providers.
|
* A reference to a block in the world, used by block detail providers.
|
||||||
|
@@ -0,0 +1,72 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2024 The CC: Tweaked Developers
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
|
package dan200.computercraft.api.detail;
|
||||||
|
|
||||||
|
import net.minecraft.core.component.DataComponentHolder;
|
||||||
|
import net.minecraft.core.component.DataComponentType;
|
||||||
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An item detail provider for a specific {@linkplain DataComponentType data component} on {@link ItemStack}s or
|
||||||
|
* other {@link DataComponentHolder}.
|
||||||
|
*
|
||||||
|
* @param <T> The type of the component's contents.
|
||||||
|
*/
|
||||||
|
public abstract class ComponentDetailProvider<T> implements DetailProvider<DataComponentHolder> {
|
||||||
|
private final DataComponentType<T> component;
|
||||||
|
private final @Nullable String namespace;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new component detail provider. Details will be inserted into a new sub-map named as per {@code namespace}.
|
||||||
|
*
|
||||||
|
* @param component The data component to provide details for.
|
||||||
|
* @param namespace The namespace to use for this provider.
|
||||||
|
*/
|
||||||
|
public ComponentDetailProvider(@Nullable String namespace, DataComponentType<T> component) {
|
||||||
|
Objects.requireNonNull(component);
|
||||||
|
this.component = component;
|
||||||
|
this.namespace = namespace;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new component detail provider. Details will be inserted directly into the results.
|
||||||
|
*
|
||||||
|
* @param component The data component to provide details for.
|
||||||
|
*/
|
||||||
|
public ComponentDetailProvider(DataComponentType<T> component) {
|
||||||
|
this(null, component);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provide additional details for the given data component. This method is called by {@code turtle.getItemDetail()}.
|
||||||
|
* New properties should be added to the given {@link Map}, {@code data}.
|
||||||
|
* <p>
|
||||||
|
* This method is always called on the server thread, so it is safe to interact with the world here, but you should
|
||||||
|
* take care to avoid long blocking operations as this will stall the server and other computers.
|
||||||
|
*
|
||||||
|
* @param data The full details to be returned for this item stack. New properties should be added to this map.
|
||||||
|
* @param component The component to provide details for.
|
||||||
|
*/
|
||||||
|
public abstract void provideComponentDetails(Map<? super String, Object> data, T component);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final void provideDetails(Map<? super String, Object> data, DataComponentHolder holder) {
|
||||||
|
var value = holder.get(component);
|
||||||
|
if (value == null) return;
|
||||||
|
|
||||||
|
if (namespace == null) {
|
||||||
|
provideComponentDetails(data, value);
|
||||||
|
} else {
|
||||||
|
Map<? super String, Object> child = new HashMap<>();
|
||||||
|
provideComponentDetails(child, value);
|
||||||
|
data.put(namespace, child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -14,6 +14,7 @@ import java.util.Map;
|
|||||||
*
|
*
|
||||||
* @param <T> The type of object that this provider can provide details for.
|
* @param <T> The type of object that this provider can provide details for.
|
||||||
* @see DetailRegistry
|
* @see DetailRegistry
|
||||||
|
* @see dan200.computercraft.api.detail An overview of the detail system.
|
||||||
*/
|
*/
|
||||||
@FunctionalInterface
|
@FunctionalInterface
|
||||||
public interface DetailProvider<T> {
|
public interface DetailProvider<T> {
|
||||||
|
@@ -17,6 +17,7 @@ import java.util.Map;
|
|||||||
* also in this package.
|
* also in this package.
|
||||||
*
|
*
|
||||||
* @param <T> The type of object that this registry provides details for.
|
* @param <T> The type of object that this registry provides details for.
|
||||||
|
* @see dan200.computercraft.api.detail An overview of the detail system.
|
||||||
*/
|
*/
|
||||||
@ApiStatus.NonExtendable
|
@ApiStatus.NonExtendable
|
||||||
public interface DetailRegistry<T> {
|
public interface DetailRegistry<T> {
|
||||||
@@ -26,7 +27,7 @@ public interface DetailRegistry<T> {
|
|||||||
* @param provider The detail provider to register.
|
* @param provider The detail provider to register.
|
||||||
* @see DetailProvider
|
* @see DetailProvider
|
||||||
*/
|
*/
|
||||||
void addProvider(DetailProvider<T> provider);
|
void addProvider(DetailProvider<? super T> provider);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Compute basic details about an object. This is cheaper than computing all details operation, and so is suitable
|
* Compute basic details about an object. This is cheaper than computing all details operation, and so is suitable
|
||||||
|
@@ -17,6 +17,9 @@ public class VanillaDetailRegistries {
|
|||||||
* <p>
|
* <p>
|
||||||
* This instance's {@link DetailRegistry#getBasicDetails(Object)} is thread safe (assuming the stack is immutable)
|
* This instance's {@link DetailRegistry#getBasicDetails(Object)} is thread safe (assuming the stack is immutable)
|
||||||
* and may be called from the computer thread.
|
* and may be called from the computer thread.
|
||||||
|
* <p>
|
||||||
|
* This does not have special handling for {@linkplain ItemStack#isEmpty() empty item stacks}, and so the returned
|
||||||
|
* details will be an empty stack of air. Callers should generally check for empty stacks before calling this.
|
||||||
*/
|
*/
|
||||||
public static final DetailRegistry<ItemStack> ITEM_STACK = ComputerCraftAPIService.get().getItemStackDetailRegistry();
|
public static final DetailRegistry<ItemStack> ITEM_STACK = ComputerCraftAPIService.get().getItemStackDetailRegistry();
|
||||||
|
|
||||||
|
@@ -0,0 +1,48 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2025 The CC: Tweaked Developers
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The detail system provides a standard way for mods to return descriptions of common game objects, such as blocks or
|
||||||
|
* items, as well as registering additional detail to be included in those descriptions.
|
||||||
|
* <p>
|
||||||
|
* For instance, the built-in {@code turtle.getItemDetail()} method uses
|
||||||
|
* {@linkplain dan200.computercraft.api.detail.VanillaDetailRegistries#ITEM_STACK in order to provide information about}
|
||||||
|
* the selected item:
|
||||||
|
*
|
||||||
|
* <pre class="language language-lua">{@code
|
||||||
|
* local item = turtle.getItemDetail(nil, true)
|
||||||
|
* --[[
|
||||||
|
* item = {
|
||||||
|
* name = "minecraft:wheat",
|
||||||
|
* displayName = "Wheat",
|
||||||
|
* count = 1,
|
||||||
|
* maxCount = 64,
|
||||||
|
* tags = {},
|
||||||
|
* }
|
||||||
|
* ]]
|
||||||
|
* }</pre>
|
||||||
|
*
|
||||||
|
* <h2>Built-in detail providers</h2>
|
||||||
|
* While you can define your own detail providers (perhaps for types from your own mod), CC comes with several built-in
|
||||||
|
* detail registries for vanilla and mod-loader objects:
|
||||||
|
*
|
||||||
|
* <ul>
|
||||||
|
* <li>{@link dan200.computercraft.api.detail.VanillaDetailRegistries}, for vanilla objects</li>
|
||||||
|
* <li>{@code dan200.computercraft.api.detail.ForgeDetailRegistries} for Forge-specific objects</li>
|
||||||
|
* <li>{@code dan200.computercraft.api.detail.FabricDetailRegistries} for Fabric-specific objects</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* <h2>Example: Returning details from methods</h2>
|
||||||
|
* Here we define a {@code getHeldItem()} method for pocket computers which finds the currently held item of the player
|
||||||
|
* and returns it to the user using {@link dan200.computercraft.api.detail.VanillaDetailRegistries#ITEM_STACK} and
|
||||||
|
* {@link dan200.computercraft.api.detail.DetailRegistry#getDetails(java.lang.Object)}.
|
||||||
|
*
|
||||||
|
* {@snippet class=com.example.examplemod.ExamplePocketPeripheral region=details}
|
||||||
|
*
|
||||||
|
* <h2>Example: Registering custom detail registries</h2>
|
||||||
|
* Here we define a new detail provider for items that includes the nutrition and saturation values in the returned object.
|
||||||
|
*
|
||||||
|
* {@snippet class=com.example.examplemod.ExampleMod region=details}
|
||||||
|
*/
|
||||||
|
package dan200.computercraft.api.detail;
|
@@ -0,0 +1,58 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2017 The CC: Tweaked Developers
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
|
package dan200.computercraft.api.lua;
|
||||||
|
|
||||||
|
import dan200.computercraft.api.component.ComputerComponent;
|
||||||
|
import dan200.computercraft.api.peripheral.IComputerAccess;
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.server.level.ServerLevel;
|
||||||
|
import org.jetbrains.annotations.ApiStatus;
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An interface passed to {@link ILuaAPIFactory} in order to provide additional information
|
||||||
|
* about a computer.
|
||||||
|
*/
|
||||||
|
@ApiStatus.NonExtendable
|
||||||
|
public interface IComputerSystem extends IComputerAccess {
|
||||||
|
/**
|
||||||
|
* Get the level this computer is currently in.
|
||||||
|
* <p>
|
||||||
|
* This method is not guaranteed to remain the same (even for stationary computers).
|
||||||
|
*
|
||||||
|
* @return The computer's current level.
|
||||||
|
*/
|
||||||
|
ServerLevel getLevel();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the position this computer is currently at.
|
||||||
|
* <p>
|
||||||
|
* This method is not guaranteed to remain the same (even for stationary computers).
|
||||||
|
*
|
||||||
|
* @return The computer's current position.
|
||||||
|
*/
|
||||||
|
BlockPos getPosition();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the label for this computer.
|
||||||
|
*
|
||||||
|
* @return This computer's label, or {@code null} if it is not set.
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
String getLabel();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a component attached to this computer.
|
||||||
|
* <p>
|
||||||
|
* No component is guaranteed to be on a computer, and so this method should always be guarded with a null check.
|
||||||
|
* <p>
|
||||||
|
* This method will always return the same value for a given component, and so may be cached.
|
||||||
|
*
|
||||||
|
* @param component The component to query.
|
||||||
|
* @param <T> The type of the component.
|
||||||
|
* @return The component, if present.
|
||||||
|
*/
|
||||||
|
<T> @Nullable T getComponent(ComputerComponent<T> component);
|
||||||
|
}
|
@@ -4,13 +4,14 @@
|
|||||||
|
|
||||||
package dan200.computercraft.api.lua;
|
package dan200.computercraft.api.lua;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import dan200.computercraft.api.ComputerCraftAPI;
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct an {@link ILuaAPI} for a specific computer.
|
* Construct an {@link ILuaAPI} for a computer.
|
||||||
*
|
*
|
||||||
* @see ILuaAPI
|
* @see ILuaAPI
|
||||||
* @see dan200.computercraft.api.ComputerCraftAPI#registerAPIFactory(ILuaAPIFactory)
|
* @see ComputerCraftAPI#registerAPIFactory(ILuaAPIFactory)
|
||||||
*/
|
*/
|
||||||
@FunctionalInterface
|
@FunctionalInterface
|
||||||
public interface ILuaAPIFactory {
|
public interface ILuaAPIFactory {
|
@@ -7,29 +7,32 @@ package dan200.computercraft.api.media;
|
|||||||
import dan200.computercraft.api.ComputerCraftAPI;
|
import dan200.computercraft.api.ComputerCraftAPI;
|
||||||
import dan200.computercraft.api.filesystem.Mount;
|
import dan200.computercraft.api.filesystem.Mount;
|
||||||
import dan200.computercraft.api.filesystem.WritableMount;
|
import dan200.computercraft.api.filesystem.WritableMount;
|
||||||
|
import net.minecraft.core.Holder;
|
||||||
|
import net.minecraft.core.HolderLookup;
|
||||||
import net.minecraft.server.MinecraftServer;
|
import net.minecraft.server.MinecraftServer;
|
||||||
import net.minecraft.server.level.ServerLevel;
|
import net.minecraft.server.level.ServerLevel;
|
||||||
import net.minecraft.sounds.SoundEvent;
|
|
||||||
import net.minecraft.world.item.Item;
|
import net.minecraft.world.item.Item;
|
||||||
import net.minecraft.world.item.ItemStack;
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
import net.minecraft.world.item.JukeboxSong;
|
||||||
import javax.annotation.Nullable;
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents an item that can be placed in a disk drive and used by a Computer.
|
* Represents an item that can be placed in a disk drive and used by a Computer.
|
||||||
* <p>
|
* <p>
|
||||||
* Implement this interface on your {@link Item} class to allow it to be used in the drive. Alternatively, register
|
* Implement this interface on your {@link Item} class to allow it to be used in the drive, or register via
|
||||||
* a {@link MediaProvider}.
|
* {@code dan200.computercraft.api.media.MediaLookup} (Fabric) or {@code dan200.computercraft.api.media.MediaCapability}
|
||||||
|
* (NeoForge).
|
||||||
*/
|
*/
|
||||||
public interface IMedia {
|
public interface IMedia {
|
||||||
/**
|
/**
|
||||||
* Get a string representing the label of this item. Will be called via {@code disk.getLabel()} in lua.
|
* Get a string representing the label of this item. Will be called via {@code disk.getLabel()} in lua.
|
||||||
*
|
*
|
||||||
|
* @param registries The currently loaded registries.
|
||||||
* @param stack The {@link ItemStack} to inspect.
|
* @param stack The {@link ItemStack} to inspect.
|
||||||
* @return The label. ie: "Dan's Programs".
|
* @return The label. ie: "Dan's Programs".
|
||||||
*/
|
*/
|
||||||
@Nullable
|
@Nullable
|
||||||
String getLabel(ItemStack stack);
|
String getLabel(HolderLookup.Provider registries, ItemStack stack);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set a string representing the label of this item. Will be called vi {@code disk.setLabel()} in lua.
|
* Set a string representing the label of this item. Will be called vi {@code disk.setLabel()} in lua.
|
||||||
@@ -43,26 +46,15 @@ public interface IMedia {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If this disk represents an item with audio (like a record), get the readable name of the audio track. ie:
|
* If this disk represents an item with audio (like a record), get the corresponding {@link JukeboxSong}.
|
||||||
* "Jonathan Coulton - Still Alive"
|
|
||||||
*
|
*
|
||||||
* @param stack The {@link ItemStack} to modify.
|
* @param registries The currently loaded registries.
|
||||||
* @return The name, or null if this item does not represent an item with audio.
|
* @param stack The {@link ItemStack} to query.
|
||||||
|
* @return The song, or null if this item does not represent an item with audio.
|
||||||
*/
|
*/
|
||||||
@Nullable
|
@Nullable
|
||||||
default String getAudioTitle(ItemStack stack) {
|
default Holder<JukeboxSong> getAudio(HolderLookup.Provider registries, ItemStack stack) {
|
||||||
return null;
|
return JukeboxSong.fromStack(registries, stack).orElse(null);
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* If this disk represents an item with audio (like a record), get the resource name of the audio track to play.
|
|
||||||
*
|
|
||||||
* @param stack The {@link ItemStack} to modify.
|
|
||||||
* @return The name, or null if this item does not represent an item with audio.
|
|
||||||
*/
|
|
||||||
@Nullable
|
|
||||||
default SoundEvent getAudio(ItemStack stack) {
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -1,27 +0,0 @@
|
|||||||
// Copyright Daniel Ratcliffe, 2011-2022. This API may be redistributed unmodified and in full only.
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: LicenseRef-CCPL
|
|
||||||
|
|
||||||
package dan200.computercraft.api.media;
|
|
||||||
|
|
||||||
import net.minecraft.world.item.ItemStack;
|
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This interface is used to provide {@link IMedia} implementations for {@link ItemStack}.
|
|
||||||
*
|
|
||||||
* @see dan200.computercraft.api.ComputerCraftAPI#registerMediaProvider(MediaProvider)
|
|
||||||
*/
|
|
||||||
@FunctionalInterface
|
|
||||||
public interface MediaProvider {
|
|
||||||
/**
|
|
||||||
* Produce an IMedia implementation from an ItemStack.
|
|
||||||
*
|
|
||||||
* @param stack The stack from which to extract the media information.
|
|
||||||
* @return An {@link IMedia} implementation, or {@code null} if the item is not something you wish to handle
|
|
||||||
* @see dan200.computercraft.api.ComputerCraftAPI#registerMediaProvider(MediaProvider)
|
|
||||||
*/
|
|
||||||
@Nullable
|
|
||||||
IMedia getMedia(ItemStack stack);
|
|
||||||
}
|
|
@@ -0,0 +1,46 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2025 The CC: Tweaked Developers
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
|
package dan200.computercraft.api.media;
|
||||||
|
|
||||||
|
import dan200.computercraft.impl.ComputerCraftAPIService;
|
||||||
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The contents of a page (or book) created by a ComputerCraft printer.
|
||||||
|
*
|
||||||
|
* @since 1.115
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
public interface PrintoutContents {
|
||||||
|
/**
|
||||||
|
* Get the (possibly empty) title for this printout.
|
||||||
|
*
|
||||||
|
* @return The title of this printout.
|
||||||
|
*/
|
||||||
|
String getTitle();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the text contents of this printout, as a sequence of lines.
|
||||||
|
* <p>
|
||||||
|
* The lines in the printout may include blank lines at the end of the document, as well as trailing spaces on each
|
||||||
|
* line.
|
||||||
|
*
|
||||||
|
* @return The text contents of this printout.
|
||||||
|
*/
|
||||||
|
Stream<String> getTextLines();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the printout contents for a particular stack.
|
||||||
|
*
|
||||||
|
* @param stack The stack to get the contents for.
|
||||||
|
* @return The printout contents, or {@code null} if this is not a printout item.
|
||||||
|
*/
|
||||||
|
static @Nullable PrintoutContents get(ItemStack stack) {
|
||||||
|
return ComputerCraftAPIService.get().getPrintoutContents(stack);
|
||||||
|
}
|
||||||
|
}
|
@@ -14,13 +14,13 @@ import dan200.computercraft.api.ComputerCraftAPI;
|
|||||||
* as a proxy for all network objects. Whilst the node may change networks, an element's node should remain constant
|
* as a proxy for all network objects. Whilst the node may change networks, an element's node should remain constant
|
||||||
* for its lifespan.
|
* for its lifespan.
|
||||||
* <p>
|
* <p>
|
||||||
* Elements are generally tied to a block or tile entity in world. In such as case, one should provide the
|
* Elements are generally tied to a block or block entity in world. In such as case, one should provide the
|
||||||
* {@link WiredElement} capability for the appropriate sides.
|
* {@link WiredElement} capability for the appropriate sides.
|
||||||
*/
|
*/
|
||||||
public interface WiredElement extends WiredSender {
|
public interface WiredElement extends WiredSender {
|
||||||
/**
|
/**
|
||||||
* Called when objects on the network change. This may occur when network nodes are added or removed, or when
|
* Called when peripherals on the network change. This may occur when network nodes are added or removed, or when
|
||||||
* peripherals change.
|
* peripherals are attached or detached from a modem.
|
||||||
*
|
*
|
||||||
* @param change The change which occurred.
|
* @param change The change which occurred.
|
||||||
* @see WiredNetworkChange
|
* @see WiredNetworkChange
|
||||||
|
@@ -1,92 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: 2018 The CC: Tweaked Developers
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: MPL-2.0
|
|
||||||
|
|
||||||
package dan200.computercraft.api.network.wired;
|
|
||||||
|
|
||||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
|
||||||
import org.jetbrains.annotations.ApiStatus;
|
|
||||||
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A wired network is composed of one of more {@link WiredNode}s, a set of connections between them, and a series
|
|
||||||
* of peripherals.
|
|
||||||
* <p>
|
|
||||||
* Networks from a connected graph. This means there is some path between all nodes on the network. Further more, if
|
|
||||||
* there is some path between two nodes then they must be on the same network. {@link WiredNetwork} will automatically
|
|
||||||
* handle the merging and splitting of networks (and thus changing of available nodes and peripherals) as connections
|
|
||||||
* change.
|
|
||||||
* <p>
|
|
||||||
* This does mean one can not rely on the network remaining consistent between subsequent operations. Consequently,
|
|
||||||
* it is generally preferred to use the methods provided by {@link WiredNode}.
|
|
||||||
*
|
|
||||||
* @see WiredNode#getNetwork()
|
|
||||||
*/
|
|
||||||
@ApiStatus.NonExtendable
|
|
||||||
public interface WiredNetwork {
|
|
||||||
/**
|
|
||||||
* Create a connection between two nodes.
|
|
||||||
* <p>
|
|
||||||
* This should only be used on the server thread.
|
|
||||||
*
|
|
||||||
* @param left The first node to connect
|
|
||||||
* @param right The second node to connect
|
|
||||||
* @return {@code true} if a connection was created or {@code false} if the connection already exists.
|
|
||||||
* @throws IllegalStateException If neither node is on the network.
|
|
||||||
* @throws IllegalArgumentException If {@code left} and {@code right} are equal.
|
|
||||||
* @see WiredNode#connectTo(WiredNode)
|
|
||||||
* @see WiredNetwork#connect(WiredNode, WiredNode)
|
|
||||||
* @deprecated Use {@link WiredNode#connectTo(WiredNode)}
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
boolean connect(WiredNode left, WiredNode right);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Destroy a connection between this node and another.
|
|
||||||
* <p>
|
|
||||||
* This should only be used on the server thread.
|
|
||||||
*
|
|
||||||
* @param left The first node in the connection.
|
|
||||||
* @param right The second node in the connection.
|
|
||||||
* @return {@code true} if a connection was destroyed or {@code false} if no connection exists.
|
|
||||||
* @throws IllegalArgumentException If either node is not on the network.
|
|
||||||
* @throws IllegalArgumentException If {@code left} and {@code right} are equal.
|
|
||||||
* @see WiredNode#disconnectFrom(WiredNode)
|
|
||||||
* @see WiredNetwork#connect(WiredNode, WiredNode)
|
|
||||||
* @deprecated Use {@link WiredNode#disconnectFrom(WiredNode)}
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
boolean disconnect(WiredNode left, WiredNode right);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sever all connections this node has, removing it from this network.
|
|
||||||
* <p>
|
|
||||||
* This should only be used on the server thread. You should only call this on nodes
|
|
||||||
* that your network element owns.
|
|
||||||
*
|
|
||||||
* @param node The node to remove
|
|
||||||
* @return Whether this node was removed from the network. One cannot remove a node from a network where it is the
|
|
||||||
* only element.
|
|
||||||
* @throws IllegalArgumentException If the node is not in the network.
|
|
||||||
* @see WiredNode#remove()
|
|
||||||
* @deprecated Use {@link WiredNode#remove()}
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
boolean remove(WiredNode node);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update the peripherals a node provides.
|
|
||||||
* <p>
|
|
||||||
* This should only be used on the server thread. You should only call this on nodes
|
|
||||||
* that your network element owns.
|
|
||||||
*
|
|
||||||
* @param node The node to attach peripherals for.
|
|
||||||
* @param peripherals The new peripherals for this node.
|
|
||||||
* @throws IllegalArgumentException If the node is not in the network.
|
|
||||||
* @see WiredNode#updatePeripherals(Map)
|
|
||||||
* @deprecated Use {@link WiredNode#updatePeripherals(Map)}
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
void updatePeripherals(WiredNode node, Map<String, IPeripheral> peripherals);
|
|
||||||
}
|
|
@@ -11,7 +11,7 @@ import org.jetbrains.annotations.ApiStatus;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wired nodes act as a layer between {@link WiredElement}s and {@link WiredNetwork}s.
|
* A single node on a wired network.
|
||||||
* <p>
|
* <p>
|
||||||
* Firstly, a node acts as a packet network, capable of sending and receiving modem messages to connected nodes. These
|
* Firstly, a node acts as a packet network, capable of sending and receiving modem messages to connected nodes. These
|
||||||
* methods may be safely used on any thread.
|
* methods may be safely used on any thread.
|
||||||
@@ -32,18 +32,6 @@ public interface WiredNode extends PacketNetwork {
|
|||||||
*/
|
*/
|
||||||
WiredElement getElement();
|
WiredElement getElement();
|
||||||
|
|
||||||
/**
|
|
||||||
* The network this node is currently connected to. Note that this may change
|
|
||||||
* after any network operation, so it should not be cached.
|
|
||||||
* <p>
|
|
||||||
* This should only be used on the server thread.
|
|
||||||
*
|
|
||||||
* @return This node's network.
|
|
||||||
* @deprecated Use the connect/disconnect/remove methods on {@link WiredNode}.
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
WiredNetwork getNetwork();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a connection from this node to another.
|
* Create a connection from this node to another.
|
||||||
* <p>
|
* <p>
|
||||||
|
@@ -8,10 +8,12 @@ import dan200.computercraft.api.network.PacketSender;
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An object on a {@link WiredNetwork} capable of sending packets.
|
* An object on a wired network capable of sending packets.
|
||||||
* <p>
|
* <p>
|
||||||
* Unlike a regular {@link PacketSender}, this must be associated with the node you are attempting to
|
* Unlike a regular {@link PacketSender}, this must be associated with the node you are attempting to
|
||||||
* to send the packet from.
|
* to send the packet from.
|
||||||
|
*
|
||||||
|
* @see WiredElement
|
||||||
*/
|
*/
|
||||||
public interface WiredSender extends PacketSender {
|
public interface WiredSender extends PacketSender {
|
||||||
/**
|
/**
|
||||||
|
@@ -4,8 +4,7 @@
|
|||||||
|
|
||||||
package dan200.computercraft.api.pocket;
|
package dan200.computercraft.api.pocket;
|
||||||
|
|
||||||
import dan200.computercraft.api.upgrades.UpgradeBase;
|
import net.minecraft.network.chat.Component;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
|
||||||
import net.minecraft.world.item.ItemStack;
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
|
||||||
|
|
||||||
@@ -15,27 +14,20 @@ import net.minecraft.world.item.ItemStack;
|
|||||||
* One does not have to use this, but it does provide a convenient template.
|
* One does not have to use this, but it does provide a convenient template.
|
||||||
*/
|
*/
|
||||||
public abstract class AbstractPocketUpgrade implements IPocketUpgrade {
|
public abstract class AbstractPocketUpgrade implements IPocketUpgrade {
|
||||||
private final ResourceLocation id;
|
private final Component adjective;
|
||||||
private final String adjective;
|
|
||||||
private final ItemStack stack;
|
private final ItemStack stack;
|
||||||
|
|
||||||
protected AbstractPocketUpgrade(ResourceLocation id, String adjective, ItemStack stack) {
|
protected AbstractPocketUpgrade(Component adjective, ItemStack stack) {
|
||||||
this.id = id;
|
|
||||||
this.adjective = adjective;
|
this.adjective = adjective;
|
||||||
this.stack = stack;
|
this.stack = stack;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected AbstractPocketUpgrade(ResourceLocation id, ItemStack stack) {
|
protected AbstractPocketUpgrade(String adjective, ItemStack stack) {
|
||||||
this(id, UpgradeBase.getDefaultAdjective(id), stack);
|
this(Component.translatable(adjective), stack);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final ResourceLocation getUpgradeID() {
|
public final Component getAdjective() {
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final String getUnlocalisedAdjective() {
|
|
||||||
return adjective;
|
return adjective;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -4,48 +4,18 @@
|
|||||||
|
|
||||||
package dan200.computercraft.api.pocket;
|
package dan200.computercraft.api.pocket;
|
||||||
|
|
||||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
|
||||||
import dan200.computercraft.api.upgrades.UpgradeBase;
|
import dan200.computercraft.api.upgrades.UpgradeBase;
|
||||||
import net.minecraft.nbt.CompoundTag;
|
import dan200.computercraft.api.upgrades.UpgradeData;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.core.component.DataComponentPatch;
|
||||||
import net.minecraft.world.entity.Entity;
|
|
||||||
import net.minecraft.world.item.ItemStack;
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
import org.jetbrains.annotations.ApiStatus;
|
||||||
import javax.annotation.Nullable;
|
import org.jspecify.annotations.Nullable;
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wrapper class for pocket computers.
|
* Access to a pocket computer for {@linkplain IPocketUpgrade pocket upgrades}.
|
||||||
*/
|
*/
|
||||||
public interface IPocketAccess {
|
@ApiStatus.NonExtendable
|
||||||
/**
|
public interface IPocketAccess extends PocketComputer {
|
||||||
* Gets the entity holding this item.
|
|
||||||
* <p>
|
|
||||||
* This must be called on the server thread.
|
|
||||||
*
|
|
||||||
* @return The holding entity, or {@code null} if none exists.
|
|
||||||
*/
|
|
||||||
@Nullable
|
|
||||||
Entity getEntity();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the colour of this pocket computer as a RGB number.
|
|
||||||
*
|
|
||||||
* @return The colour this pocket computer is. This will be a RGB colour between {@code 0x000000} and
|
|
||||||
* {@code 0xFFFFFF} or -1 if it has no colour.
|
|
||||||
* @see #setColour(int)
|
|
||||||
*/
|
|
||||||
int getColour();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the colour of the pocket computer to a RGB number.
|
|
||||||
*
|
|
||||||
* @param colour The colour this pocket computer should be changed to. This should be a RGB colour between
|
|
||||||
* {@code 0x000000} and {@code 0xFFFFFF} or -1 to reset to the default colour.
|
|
||||||
* @see #getColour()
|
|
||||||
*/
|
|
||||||
void setColour(int colour);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the colour of this pocket computer's light as a RGB number.
|
* Get the colour of this pocket computer's light as a RGB number.
|
||||||
*
|
*
|
||||||
@@ -64,24 +34,50 @@ public interface IPocketAccess {
|
|||||||
*/
|
*/
|
||||||
void setLight(int colour);
|
void setLight(int colour);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the currently equipped upgrade.
|
||||||
|
*
|
||||||
|
* @return The currently equipped upgrade.
|
||||||
|
* @see #getUpgradeData()
|
||||||
|
* @see #setUpgrade(UpgradeData)
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
UpgradeData<IPocketUpgrade> getUpgrade();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the upgrade for this pocket computer, also updating the item stack.
|
||||||
|
* <p>
|
||||||
|
* This method can only be called from the main server thread, when this computer is {@linkplain #isActive() is
|
||||||
|
* active}.
|
||||||
|
*
|
||||||
|
* @param upgrade The new upgrade to set it to, may be {@code null}.
|
||||||
|
* @see #getUpgrade()
|
||||||
|
*/
|
||||||
|
void setUpgrade(@Nullable UpgradeData<IPocketUpgrade> upgrade);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the upgrade-specific NBT.
|
* Get the upgrade-specific NBT.
|
||||||
* <p>
|
* <p>
|
||||||
* This is persisted between computer reboots and chunk loads.
|
* This is persisted between computer reboots and chunk loads.
|
||||||
*
|
*
|
||||||
* @return The upgrade's NBT.
|
* @return The upgrade's NBT.
|
||||||
* @see #updateUpgradeNBTData()
|
* @see #setUpgradeData(DataComponentPatch)
|
||||||
* @see UpgradeBase#getUpgradeItem(CompoundTag)
|
* @see UpgradeBase#getUpgradeItem(DataComponentPatch)
|
||||||
* @see UpgradeBase#getUpgradeData(ItemStack)
|
* @see UpgradeBase#getUpgradeData(ItemStack)
|
||||||
|
* @see #getUpgrade()
|
||||||
*/
|
*/
|
||||||
CompoundTag getUpgradeNBTData();
|
DataComponentPatch getUpgradeData();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mark the upgrade-specific NBT as dirty.
|
* Update the upgrade-specific data.
|
||||||
|
* <p>
|
||||||
|
* This method can only be called from the main server thread, when this computer is {@linkplain #isActive() is
|
||||||
|
* active}.
|
||||||
*
|
*
|
||||||
* @see #getUpgradeNBTData()
|
* @param data The new upgrade data.
|
||||||
|
* @see #getUpgradeData()
|
||||||
*/
|
*/
|
||||||
void updateUpgradeNBTData();
|
void setUpgradeData(DataComponentPatch data);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove the current peripheral and create a new one.
|
* Remove the current peripheral and create a new one.
|
||||||
@@ -90,13 +86,4 @@ public interface IPocketAccess {
|
|||||||
* entity} changes.
|
* entity} changes.
|
||||||
*/
|
*/
|
||||||
void invalidatePeripheral();
|
void invalidatePeripheral();
|
||||||
|
|
||||||
/**
|
|
||||||
* Get a list of all upgrades for the pocket computer.
|
|
||||||
*
|
|
||||||
* @return A collection of all upgrade names.
|
|
||||||
* @deprecated This is a relic of a previous API, which no longer makes sense with newer versions of ComputerCraft.
|
|
||||||
*/
|
|
||||||
@Deprecated(forRemoval = true)
|
|
||||||
Map<ResourceLocation, IPeripheral> getUpgrades();
|
|
||||||
}
|
}
|
||||||
|
@@ -4,25 +4,46 @@
|
|||||||
|
|
||||||
package dan200.computercraft.api.pocket;
|
package dan200.computercraft.api.pocket;
|
||||||
|
|
||||||
|
import dan200.computercraft.api.ComputerCraftAPI;
|
||||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||||
import dan200.computercraft.api.upgrades.UpgradeBase;
|
import dan200.computercraft.api.upgrades.UpgradeBase;
|
||||||
import net.minecraft.world.level.Level;
|
import dan200.computercraft.api.upgrades.UpgradeType;
|
||||||
|
import dan200.computercraft.impl.ComputerCraftAPIService;
|
||||||
import javax.annotation.Nullable;
|
import net.minecraft.core.Registry;
|
||||||
|
import net.minecraft.resources.ResourceKey;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import net.minecraft.server.level.ServerLevel;
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A peripheral which can be equipped to the back side of a pocket computer.
|
* A peripheral which can be equipped to the back side of a pocket computer.
|
||||||
* <p>
|
* <p>
|
||||||
* Pocket upgrades are defined in two stages. First, on creates a {@link IPocketUpgrade} subclass and corresponding
|
* Pocket upgrades are defined in two stages. First, one creates a {@link IPocketUpgrade} subclass and corresponding
|
||||||
* {@link PocketUpgradeSerialiser} instance, which are then registered in a Forge registry.
|
* {@link UpgradeType} instance, which are then registered in a Minecraft registry.
|
||||||
* <p>
|
* <p>
|
||||||
* You then write a JSON file in your mod's {@literal data/} folder. This is then parsed when the world is loaded, and
|
* You then write a JSON file in your mod's {@literal data/} folder. This is then parsed when the world is loaded, and
|
||||||
* the upgrade registered internally. See the documentation in {@link PocketUpgradeSerialiser} for details on this process
|
* the upgrade registered internally.
|
||||||
* and where files should be located.
|
|
||||||
*
|
|
||||||
* @see PocketUpgradeSerialiser For how to register a pocket computer upgrade.
|
|
||||||
*/
|
*/
|
||||||
public interface IPocketUpgrade extends UpgradeBase {
|
public interface IPocketUpgrade extends UpgradeBase {
|
||||||
|
ResourceKey<Registry<IPocketUpgrade>> REGISTRY = ResourceKey.createRegistryKey(ResourceLocation.fromNamespaceAndPath(ComputerCraftAPI.MOD_ID, "pocket_upgrade"));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The registry key for pocket upgrade types.
|
||||||
|
*
|
||||||
|
* @return The registry key.
|
||||||
|
*/
|
||||||
|
static ResourceKey<Registry<UpgradeType<? extends IPocketUpgrade>>> typeRegistry() {
|
||||||
|
return ComputerCraftAPIService.get().pocketUpgradeRegistryId();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the type of this upgrade.
|
||||||
|
*
|
||||||
|
* @return The type of this upgrade.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
UpgradeType<? extends IPocketUpgrade> getType();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a peripheral for the pocket computer.
|
* Creates a peripheral for the pocket computer.
|
||||||
* <p>
|
* <p>
|
||||||
@@ -50,7 +71,7 @@ public interface IPocketUpgrade extends UpgradeBase {
|
|||||||
/**
|
/**
|
||||||
* Called when the pocket computer is right clicked.
|
* Called when the pocket computer is right clicked.
|
||||||
*
|
*
|
||||||
* @param world The world the computer is in.
|
* @param level The world the computer is in.
|
||||||
* @param access The access object for the pocket item stack.
|
* @param access The access object for the pocket item stack.
|
||||||
* @param peripheral The peripheral for this upgrade.
|
* @param peripheral The peripheral for this upgrade.
|
||||||
* @return {@code true} to stop the GUI from opening, otherwise false. You should always provide some code path
|
* @return {@code true} to stop the GUI from opening, otherwise false. You should always provide some code path
|
||||||
@@ -58,7 +79,7 @@ public interface IPocketUpgrade extends UpgradeBase {
|
|||||||
* access the GUI.
|
* access the GUI.
|
||||||
* @see #createPeripheral(IPocketAccess)
|
* @see #createPeripheral(IPocketAccess)
|
||||||
*/
|
*/
|
||||||
default boolean onRightClick(Level world, IPocketAccess access, @Nullable IPeripheral peripheral) {
|
default boolean onRightClick(ServerLevel level, IPocketAccess access, @Nullable IPeripheral peripheral) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user