mirror of
https://github.com/SquidDev-CC/CC-Tweaked
synced 2025-10-18 15:37:38 +00:00
Compare commits
281 Commits
v1.19-1.10
...
v1.19.3-1.
Author | SHA1 | Date | |
---|---|---|---|
![]() |
9e6e0c8b88 | ||
![]() |
5502412181 | ||
![]() |
62e3c5f9aa | ||
![]() |
7e54a40fa9 | ||
![]() |
8ac42566ec | ||
![]() |
66b20d2bdb | ||
![]() |
81dad421d5 | ||
![]() |
3075d3cea8 | ||
![]() |
cdab8f429e | ||
![]() |
2e5cd29e12 | ||
![]() |
22cadd6730 | ||
![]() |
366052ec48 | ||
![]() |
3224e0bf8b | ||
![]() |
fb4b097a66 | ||
![]() |
67f3d91850 | ||
![]() |
1e3a930543 | ||
![]() |
b21e2f4e63 | ||
![]() |
a12b405acf | ||
![]() |
e076818b29 | ||
![]() |
1554c7b397 | ||
![]() |
6cd32a6368 | ||
![]() |
7335a892b5 | ||
![]() |
83eddc6636 | ||
![]() |
d066d175bf | ||
![]() |
9873ccfa0d | ||
![]() |
da7a50368d | ||
![]() |
8cfbfe7ceb | ||
![]() |
67244b17af | ||
![]() |
9e1de23f4a | ||
![]() |
86b60855d6 | ||
![]() |
db6b6fd173 | ||
![]() |
2014e9527e | ||
![]() |
f43b839056 | ||
![]() |
f561572509 | ||
![]() |
edb21f33be | ||
![]() |
02b68b259e | ||
![]() |
28a55349a9 | ||
![]() |
2457a31728 | ||
![]() |
cdc91a8e5d | ||
![]() |
8024017f53 | ||
![]() |
592ff84aea | ||
![]() |
4360458416 | ||
![]() |
717e096b94 | ||
![]() |
34a31abd9c | ||
![]() |
bdecb88cca | ||
![]() |
af15030fa4 | ||
![]() |
3a883db49e | ||
![]() |
8ea5b64f64 | ||
![]() |
7b6caf76e4 | ||
![]() |
230c7ee904 | ||
![]() |
aa203802c6 | ||
![]() |
1259e29f21 | ||
![]() |
77f62dac94 | ||
![]() |
7f34aff6bb | ||
![]() |
3047e3cdf4 | ||
![]() |
7a83a403f0 | ||
![]() |
a1d5c76d00 | ||
![]() |
bcdfa7c5ff | ||
![]() |
2c59b9122b | ||
![]() |
d2c7b944ab | ||
![]() |
e241575329 | ||
![]() |
86c4c7483d | ||
![]() |
9010219b9c | ||
![]() |
172d1824fc | ||
![]() |
9d394f44d3 | ||
![]() |
6e5b7243f4 | ||
![]() |
27b732f835 | ||
![]() |
4fa7f50534 | ||
![]() |
eeac86b07c | ||
![]() |
36ce490566 | ||
![]() |
e7fe22d4f8 | ||
![]() |
2b237332ce | ||
![]() |
1276478deb | ||
![]() |
551f6ba60c | ||
![]() |
99a2b26fc5 | ||
![]() |
0787e17ebe | ||
![]() |
06163e4f25 | ||
![]() |
18fbd96c10 | ||
![]() |
367773e173 | ||
![]() |
8007a30849 | ||
![]() |
df38f3e887 | ||
![]() |
c3fe9f00d4 | ||
![]() |
3b42f22a4f | ||
![]() |
9962ce1a5c | ||
![]() |
9f48395596 | ||
![]() |
020c5cd2d3 | ||
![]() |
a9c0b02e3c | ||
![]() |
fc5f296eeb | ||
![]() |
c96172e78d | ||
![]() |
fa122a56cf | ||
![]() |
87c6d3aef6 | ||
![]() |
95c57e843d | ||
![]() |
b13998dd96 | ||
![]() |
47816805fb | ||
![]() |
b8fce1eecc | ||
![]() |
ee2670d53b | ||
![]() |
3a96aea894 | ||
![]() |
0fc78acd49 | ||
![]() |
737d8a2585 | ||
![]() |
e2447bb0fd | ||
![]() |
2255d49d16 | ||
![]() |
3fa39b5f98 | ||
![]() |
08df68dcc0 | ||
![]() |
8f92417a2f | ||
![]() |
b58b9b7df3 | ||
![]() |
8d2e150f05 | ||
![]() |
8152f19b6e | ||
![]() |
b2b58892e3 | ||
![]() |
8360e8234d | ||
![]() |
77624fc6fd | ||
![]() |
1d335f7290 | ||
![]() |
f04acdc199 | ||
![]() |
bdf590fa30 | ||
![]() |
0c4fd2b29e | ||
![]() |
8a7156785d | ||
![]() |
4d50b48ea6 | ||
![]() |
48285404b9 | ||
![]() |
b36b96e0bc | ||
![]() |
34c7fcf750 | ||
![]() |
cc73fcd85d | ||
![]() |
22729f6f16 | ||
![]() |
55494b7671 | ||
![]() |
7d47b219c5 | ||
![]() |
320007dbc6 | ||
![]() |
0908acbe9b | ||
![]() |
e8f9cdd221 | ||
![]() |
53abe5e56e | ||
![]() |
564752c8dd | ||
![]() |
6d665ad841 | ||
![]() |
9cd728fea9 | ||
![]() |
1c890e5a5c | ||
![]() |
955b9c7d28 | ||
![]() |
76710eec9d | ||
![]() |
d8e2161f15 | ||
![]() |
c82f37d3bf | ||
![]() |
c8c128d335 | ||
![]() |
acc254a1ef | ||
![]() |
a17b001950 | ||
![]() |
e4e528e5bf | ||
![]() |
6cc86b0ae5 | ||
![]() |
f478c4ffc4 | ||
![]() |
7df0412c2d | ||
![]() |
998efcc950 | ||
![]() |
45c5de73bb | ||
![]() |
c919011a7e | ||
![]() |
0f1f5247ca | ||
![]() |
0db32bd0fe | ||
![]() |
1d3ecb551d | ||
![]() |
aefda6a381 | ||
![]() |
c1bf9f0b24 | ||
![]() |
629abb65e3 | ||
![]() |
11ac865877 | ||
![]() |
6b93fafc46 | ||
![]() |
1acb8441ec | ||
![]() |
4b0988768d | ||
![]() |
f528046535 | ||
![]() |
93f747fb54 | ||
![]() |
5d4c34fbac | ||
![]() |
4c5b3a6ee5 | ||
![]() |
7701b343fb | ||
![]() |
14cb97cba1 | ||
![]() |
1490ca8624 | ||
![]() |
f5b89982de | ||
![]() |
1a87175ae7 | ||
![]() |
c4184a33bc | ||
![]() |
b3702fed78 | ||
![]() |
b5056fc3b8 | ||
![]() |
38b2c944f3 | ||
![]() |
5ee5b11995 | ||
![]() |
b2d2153258 | ||
![]() |
3d6ef0cf96 | ||
![]() |
71f81e1201 | ||
![]() |
1e88d37004 | ||
![]() |
97387556fe | ||
![]() |
1f910ee2ba | ||
![]() |
a9ef874174 | ||
![]() |
a2911038c5 | ||
![]() |
158850be09 | ||
![]() |
be827a21db | ||
![]() |
562f224c01 | ||
![]() |
f45614175a | ||
![]() |
af7af615c7 | ||
![]() |
8171578e80 | ||
![]() |
f4e542b4db | ||
![]() |
3e3bc8d4b1 | ||
![]() |
b48f590b92 | ||
![]() |
6ab90dc30d | ||
![]() |
0cfdd7b5e9 | ||
![]() |
af5d816798 | ||
![]() |
57cf6084e2 | ||
![]() |
e9cde9e1bf | ||
![]() |
68da044ff2 | ||
![]() |
18d9993fa7 | ||
![]() |
0c3de1087e | ||
![]() |
ff89e5feeb | ||
![]() |
0b26ab366d | ||
![]() |
cb9731306c | ||
![]() |
5d833ac634 | ||
![]() |
9db3e6d2a0 | ||
![]() |
1e703f1b07 | ||
![]() |
b663028f42 | ||
![]() |
cee60cdb5b | ||
![]() |
695ef0542a | ||
![]() |
c0d20b72c9 | ||
![]() |
cf05ab1db1 | ||
![]() |
c49547b962 | ||
![]() |
c8e15f201c | ||
![]() |
bc79100a2f | ||
![]() |
9ed5ebb868 | ||
![]() |
a9b74dc979 | ||
![]() |
12b8a0393f | ||
![]() |
cbfd83c2ba | ||
![]() |
8564c1e54b | ||
![]() |
66dff1523b | ||
![]() |
08895cdecc | ||
![]() |
5be290a1e2 | ||
![]() |
371f931140 | ||
![]() |
da5956e943 | ||
![]() |
e7533f2353 | ||
![]() |
0b7fbcde53 | ||
![]() |
76f8dd2d14 | ||
![]() |
c3b7302108 | ||
![]() |
61ac48c99f | ||
![]() |
d22e138413 | ||
![]() |
ba64e06ca7 | ||
![]() |
db8c979a06 | ||
![]() |
9d18487dc5 | ||
![]() |
34a2e87735 | ||
![]() |
feb7681c9c | ||
![]() |
ad4a2aa68d | ||
![]() |
4228011b84 | ||
![]() |
c43d851e63 | ||
![]() |
50fe7935a3 | ||
![]() |
bd19fdf350 | ||
![]() |
c3615d9c5b | ||
![]() |
e2041f7438 | ||
![]() |
d61202e2b8 | ||
![]() |
6ce88a7dcf | ||
![]() |
5d65b3e654 | ||
![]() |
abf857f864 | ||
![]() |
ebef3117f2 | ||
![]() |
25a44bea6e | ||
![]() |
b28c1ac8e0 | ||
![]() |
69b211b4fb | ||
![]() |
48147fa61c | ||
![]() |
ba976f9a16 | ||
![]() |
969feb4a1c | ||
![]() |
4a273ae8e5 | ||
![]() |
bd5de11ad5 | ||
![]() |
5366fcb9c8 | ||
![]() |
6335e77da6 | ||
![]() |
4cfd0a2d1c | ||
![]() |
be3a960273 | ||
![]() |
f25a73b8f2 | ||
![]() |
954254e7e4 | ||
![]() |
e906f3ebc3 | ||
![]() |
56f0e0674f | ||
![]() |
4e438df9ad | ||
![]() |
51c3a9d8af | ||
![]() |
d967730085 | ||
![]() |
d6afee8deb | ||
![]() |
41bddcab9f | ||
![]() |
b8d7695392 | ||
![]() |
b7fa4102df | ||
![]() |
92c613a7a2 | ||
![]() |
d2f94f2653 | ||
![]() |
718111787c | ||
![]() |
bb0e449560 | ||
![]() |
ee495b3359 | ||
![]() |
d1e952770d | ||
![]() |
2d30208631 | ||
![]() |
03f50f9298 | ||
![]() |
8fc7820a12 | ||
![]() |
1a0e3fc2fa | ||
![]() |
6d5b13dbbc | ||
![]() |
f9f8233ef4 | ||
![]() |
a2e3d9d9bd | ||
![]() |
b7f698d6f7 | ||
![]() |
93f3cd4a53 | ||
![]() |
755f8eff93 | ||
![]() |
a879efc3d0 |
@@ -8,6 +8,17 @@ charset = utf-8
|
|||||||
trim_trailing_whitespace = true
|
trim_trailing_whitespace = true
|
||||||
insert_final_newline = true
|
insert_final_newline = true
|
||||||
|
|
||||||
|
ij_continuation_indent_size = 4
|
||||||
|
ij_any_do_while_brace_force = if_multiline
|
||||||
|
ij_any_if_brace_force = if_multiline
|
||||||
|
ij_any_for_brace_force = if_multiline
|
||||||
|
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
|
||||||
|
|
||||||
|
2
.git-blame-ignore-revs
Normal file
2
.git-blame-ignore-revs
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
# Reformat everything
|
||||||
|
f478c4ffc4fb9fc2200ec9b0bc751d047057ce81
|
4
.gitattributes
vendored
4
.gitattributes
vendored
@@ -1,6 +1,6 @@
|
|||||||
# Ignore changes in generated files
|
# Ignore changes in generated files
|
||||||
src/generated/** linguist-generated
|
projects/*/src/generated/** linguist-generated
|
||||||
src/testMod/server-files/structures linguist-generated
|
projects/common/src/testMod/resources/data/cctest/structures/* linguist-generated
|
||||||
|
|
||||||
* text=auto
|
* text=auto
|
||||||
|
|
||||||
|
2
.github/ISSUE_TEMPLATE/bug_report.yaml
vendored
2
.github/ISSUE_TEMPLATE/bug_report.yaml
vendored
@@ -9,8 +9,8 @@ body:
|
|||||||
description: What version of Minecraft are you using?
|
description: What version of Minecraft are you using?
|
||||||
options:
|
options:
|
||||||
- 1.16.x
|
- 1.16.x
|
||||||
- 1.17.x
|
|
||||||
- 1.18.x
|
- 1.18.x
|
||||||
|
- 1.19.x
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: input
|
- type: input
|
||||||
|
5
.github/ISSUE_TEMPLATE/config.yml
vendored
5
.github/ISSUE_TEMPLATE/config.yml
vendored
@@ -1,8 +1,5 @@
|
|||||||
blank_issues_enabled: false
|
blank_issues_enabled: false
|
||||||
contact_links:
|
contact_links:
|
||||||
- name: ComputerCraft Discord
|
|
||||||
url: https://discord.computercraft.cc
|
|
||||||
about: Get help on the ComputerCraft Discord.
|
|
||||||
- name: GitHub Discussions
|
- name: GitHub Discussions
|
||||||
url: https://github.com/cc-tweaked/CC-Tweaked/discussions
|
url: https://github.com/cc-tweaked/CC-Tweaked/discussions
|
||||||
about: Or ask questions on GitHub Discussions.
|
about: Ask questions on GitHub Discussions.
|
||||||
|
96
.github/workflows/main-ci.yml
vendored
96
.github/workflows/main-ci.yml
vendored
@@ -8,20 +8,19 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- name: Clone repository
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Set up Java 8
|
- name: Set up Java
|
||||||
uses: actions/setup-java@v1
|
uses: actions/setup-java@v3
|
||||||
with:
|
with:
|
||||||
java-version: 8
|
java-version: 17
|
||||||
|
distribution: 'temurin'
|
||||||
|
|
||||||
- name: Cache gradle dependencies
|
- name: Setup Gradle
|
||||||
uses: actions/cache@v2
|
uses: gradle/gradle-build-action@v2
|
||||||
with:
|
with:
|
||||||
path: ~/.gradle/caches
|
cache-read-only: ${{ !startsWith(github.ref, 'refs/heads/mc-') }}
|
||||||
key: ${{ runner.os }}-gradle-${{ hashFiles('gradle.properties') }}
|
|
||||||
restore-keys: |
|
|
||||||
${{ runner.os }}-gradle-
|
|
||||||
|
|
||||||
- name: Disable Gradle daemon
|
- name: Disable Gradle daemon
|
||||||
run: |
|
run: |
|
||||||
@@ -32,39 +31,68 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
./gradlew assemble || ./gradlew assemble
|
./gradlew assemble || ./gradlew assemble
|
||||||
./gradlew downloadAssets || ./gradlew downloadAssets
|
./gradlew downloadAssets || ./gradlew downloadAssets
|
||||||
xvfb-run ./gradlew build
|
./gradlew build
|
||||||
|
|
||||||
|
- 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: 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
|
- name: Upload Jar
|
||||||
uses: actions/upload-artifact@v2
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: CC-Tweaked
|
name: CC-Tweaked
|
||||||
path: build/libs
|
path: ./jars
|
||||||
|
|
||||||
- name: Upload Screnshots
|
- name: Upload coverage
|
||||||
uses: actions/upload-artifact@v2
|
uses: codecov/codecov-action@v3
|
||||||
with:
|
|
||||||
name: Screenshots
|
|
||||||
path: test-files/client/screenshots
|
|
||||||
if-no-files-found: ignore
|
|
||||||
retention-days: 5
|
|
||||||
if: failure()
|
|
||||||
|
|
||||||
- name: Upload Coverage
|
|
||||||
uses: codecov/codecov-action@v2
|
|
||||||
|
|
||||||
- name: Parse test reports
|
- name: Parse test reports
|
||||||
run: ./tools/parse-reports.py
|
run: ./tools/parse-reports.py
|
||||||
if: ${{ failure() }}
|
if: ${{ failure() }}
|
||||||
|
|
||||||
- name: Cache pre-commit
|
|
||||||
uses: actions/cache@v2
|
|
||||||
with:
|
|
||||||
path: ~/.cache/pre-commit
|
|
||||||
key: ${{ runner.os }}-pre-commit-${{ hashFiles('config/pre-commit/config.yml') }}
|
|
||||||
restore-keys: |
|
|
||||||
${{ runner.os }}-pre-commit-
|
|
||||||
|
|
||||||
- name: Run linters
|
- name: Run linters
|
||||||
|
uses: pre-commit/action@v3.0.0
|
||||||
|
|
||||||
|
build-core:
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- name: Windows
|
||||||
|
uses: windows-latest
|
||||||
|
|
||||||
|
- name: macOS
|
||||||
|
uses: macos-latest
|
||||||
|
|
||||||
|
name: Test on ${{ matrix.name }}
|
||||||
|
runs-on: ${{ matrix.uses }}
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Clone repository
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Set up Java
|
||||||
|
uses: actions/setup-java@v3
|
||||||
|
with:
|
||||||
|
java-version: 17
|
||||||
|
distribution: 'temurin'
|
||||||
|
|
||||||
|
- name: Setup Gradle
|
||||||
|
uses: gradle/gradle-build-action@v2
|
||||||
|
with:
|
||||||
|
cache-read-only: ${{ !startsWith(github.ref, 'refs/heads/mc-') }}
|
||||||
|
|
||||||
|
- name: Run tests
|
||||||
run: |
|
run: |
|
||||||
pip install pre-commit
|
./gradlew --configure-on-demand :core:test
|
||||||
pre-commit run --config config/pre-commit/config.yml --show-diff-on-failure --all --color=always
|
|
||||||
|
- name: Parse test reports
|
||||||
|
run: python3 ./tools/parse-reports.py
|
||||||
|
if: ${{ failure() }}
|
||||||
|
4
.github/workflows/make-doc.sh
vendored
4
.github/workflows/make-doc.sh
vendored
@@ -12,8 +12,8 @@ chmod 600 "$HOME/.ssh/key"
|
|||||||
|
|
||||||
# And upload
|
# And upload
|
||||||
rsync -avc -e "ssh -i $HOME/.ssh/key -o StrictHostKeyChecking=no -p $SSH_PORT" \
|
rsync -avc -e "ssh -i $HOME/.ssh/key -o StrictHostKeyChecking=no -p $SSH_PORT" \
|
||||||
"$GITHUB_WORKSPACE/build/docs/site/" \
|
"$GITHUB_WORKSPACE/projects/web/build/site/" \
|
||||||
"$SSH_USER@$SSH_HOST:/$DEST"
|
"$SSH_USER@$SSH_HOST:/$DEST"
|
||||||
rsync -avc -e "ssh -i $HOME/.ssh/key -o StrictHostKeyChecking=no -p $SSH_PORT" \
|
rsync -avc -e "ssh -i $HOME/.ssh/key -o StrictHostKeyChecking=no -p $SSH_PORT" \
|
||||||
"$GITHUB_WORKSPACE/build/docs/javadoc/" \
|
"$GITHUB_WORKSPACE/projects/common-api/build/docs/javadoc/" \
|
||||||
"$SSH_USER@$SSH_HOST:/$DEST/javadoc"
|
"$SSH_USER@$SSH_HOST:/$DEST/javadoc"
|
||||||
|
30
.github/workflows/make-doc.yml
vendored
30
.github/workflows/make-doc.yml
vendored
@@ -3,7 +3,7 @@ name: Build documentation
|
|||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- mc-1.16.x
|
- mc-1.19.x
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
make_doc:
|
make_doc:
|
||||||
@@ -11,35 +11,25 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- name: Clone repository
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Set up Java 8
|
- name: Set up Java
|
||||||
uses: actions/setup-java@v1
|
uses: actions/setup-java@v1
|
||||||
with:
|
with:
|
||||||
java-version: 8
|
java-version: 17
|
||||||
|
distribution: 'temurin'
|
||||||
|
|
||||||
- name: Cache gradle dependencies
|
- name: Setup Gradle
|
||||||
uses: actions/cache@v2
|
uses: gradle/gradle-build-action@v2
|
||||||
with:
|
with:
|
||||||
path: ~/.gradle/caches
|
cache-read-only: ${{ !startsWith(github.ref, 'refs/heads/mc-') }}
|
||||||
key: ${{ runner.os }}-gradle-${{ hashFiles('gradle.properties') }}
|
|
||||||
restore-keys: |
|
|
||||||
${{ runner.os }}-gradle-
|
|
||||||
|
|
||||||
- name: Setup illuaminate
|
|
||||||
run: |
|
|
||||||
test -d bin || mkdir bin
|
|
||||||
test -f bin/illuaminate || wget -q -Obin/illuaminate https://squiddev.cc/illuaminate/linux-x86-64/illuaminate
|
|
||||||
chmod +x bin/illuaminate
|
|
||||||
|
|
||||||
- name: Setup node
|
|
||||||
run: npm ci
|
|
||||||
|
|
||||||
- name: Build with Gradle
|
- name: Build with Gradle
|
||||||
run: ./gradlew compileJava --no-daemon || ./gradlew compileJava --no-daemon
|
run: ./gradlew compileJava --no-daemon || ./gradlew compileJava --no-daemon
|
||||||
|
|
||||||
- name: Generate documentation
|
- name: Generate documentation
|
||||||
run: ./gradlew docWebsite javadoc --no-daemon
|
run: ./gradlew docWebsite :common-api:javadoc --no-daemon
|
||||||
|
|
||||||
- name: Upload documentation
|
- name: Upload documentation
|
||||||
run: .github/workflows/make-doc.sh 2> /dev/null
|
run: .github/workflows/make-doc.sh 2> /dev/null
|
||||||
|
11
.gitignore
vendored
11
.gitignore
vendored
@@ -2,14 +2,17 @@
|
|||||||
/classes
|
/classes
|
||||||
/logs
|
/logs
|
||||||
/build
|
/build
|
||||||
|
/projects/*/logs
|
||||||
|
/projects/*/build
|
||||||
|
/buildSrc/build
|
||||||
/out
|
/out
|
||||||
/doc/out/
|
/doc/out/
|
||||||
/node_modules
|
/node_modules
|
||||||
|
.jqwik-database
|
||||||
|
|
||||||
# Runtime directories
|
# Runtime directories
|
||||||
/run
|
/run
|
||||||
/run-*
|
/projects/*/run
|
||||||
/test-files
|
|
||||||
|
|
||||||
*.ipr
|
*.ipr
|
||||||
*.iws
|
*.iws
|
||||||
@@ -22,8 +25,6 @@
|
|||||||
/.project
|
/.project
|
||||||
/.settings
|
/.settings
|
||||||
/.vscode
|
/.vscode
|
||||||
bin/
|
|
||||||
*.launch
|
*.launch
|
||||||
|
|
||||||
/src/generated/resources/.cache
|
/projects/*/src/generated/resources/.cache
|
||||||
/src/web/mount/*.d.ts
|
|
||||||
|
@@ -17,6 +17,6 @@ vscode:
|
|||||||
|
|
||||||
tasks:
|
tasks:
|
||||||
- name: Setup pre-commit hool
|
- name: Setup pre-commit hool
|
||||||
init: pre-commit install --config config/pre-commit/config.yml --allow-missing-config
|
init: pre-commit install --allow-missing-config
|
||||||
- name: Install npm packages
|
- name: Install npm packages
|
||||||
init: npm ci
|
init: npm ci
|
||||||
|
54
.pre-commit-config.yaml
Normal file
54
.pre-commit-config.yaml
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
# See https://pre-commit.com for more information
|
||||||
|
# See https://pre-commit.com/hooks.html for more hooks
|
||||||
|
repos:
|
||||||
|
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||||
|
rev: v4.0.1
|
||||||
|
hooks:
|
||||||
|
- id: trailing-whitespace
|
||||||
|
- id: end-of-file-fixer
|
||||||
|
- id: check-merge-conflict
|
||||||
|
|
||||||
|
# Quick syntax checkers
|
||||||
|
- id: check-xml
|
||||||
|
- id: check-yaml
|
||||||
|
- id: check-toml
|
||||||
|
- id: check-json
|
||||||
|
exclude: "tsconfig\\.json$"
|
||||||
|
|
||||||
|
- repo: https://github.com/editorconfig-checker/editorconfig-checker.python
|
||||||
|
rev: 2.3.54
|
||||||
|
hooks:
|
||||||
|
- id: editorconfig-checker
|
||||||
|
args: ['-disable-indentation']
|
||||||
|
exclude: "^(.*\\.(bat)|LICENSE)$"
|
||||||
|
|
||||||
|
- repo: local
|
||||||
|
hooks:
|
||||||
|
- id: license
|
||||||
|
name: Spotless
|
||||||
|
files: ".*\\.(java|kt|kts)$"
|
||||||
|
language: system
|
||||||
|
entry: ./gradlew spotlessApply
|
||||||
|
pass_filenames: false
|
||||||
|
require_serial: true
|
||||||
|
- id: checkstyle
|
||||||
|
name: Check Java codestyle
|
||||||
|
files: ".*\\.java$"
|
||||||
|
language: system
|
||||||
|
entry: ./gradlew checkstyleMain checkstyleTest
|
||||||
|
pass_filenames: false
|
||||||
|
require_serial: true
|
||||||
|
- id: illuaminate
|
||||||
|
name: Check Lua code
|
||||||
|
files: ".*\\.(lua|java|md)"
|
||||||
|
language: system
|
||||||
|
entry: ./gradlew lintLua
|
||||||
|
pass_filenames: false
|
||||||
|
require_serial: true
|
||||||
|
|
||||||
|
exclude: |
|
||||||
|
(?x)^(
|
||||||
|
projects/[a-z]+/src/generated|
|
||||||
|
projects/core/src/test/resources/test-rom/data/json-parsing/|
|
||||||
|
.*\.dfpwm
|
||||||
|
)
|
133
CODE_OF_CONDUCT.md
Normal file
133
CODE_OF_CONDUCT.md
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
|
||||||
|
# Contributor Covenant Code of Conduct
|
||||||
|
|
||||||
|
## Our Pledge
|
||||||
|
|
||||||
|
We as members, contributors, and leaders pledge to make participation in our
|
||||||
|
community a harassment-free experience for everyone, regardless of age, body
|
||||||
|
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
||||||
|
identity and expression, level of experience, education, socio-economic status,
|
||||||
|
nationality, personal appearance, race, caste, color, religion, or sexual
|
||||||
|
identity and orientation.
|
||||||
|
|
||||||
|
We pledge to act and interact in ways that contribute to an open, welcoming,
|
||||||
|
diverse, inclusive, and healthy community.
|
||||||
|
|
||||||
|
## Our Standards
|
||||||
|
|
||||||
|
Examples of behavior that contributes to a positive environment for our
|
||||||
|
community include:
|
||||||
|
|
||||||
|
* Demonstrating empathy and kindness toward other people
|
||||||
|
* Being respectful of differing opinions, viewpoints, and experiences
|
||||||
|
* Giving and gracefully accepting constructive feedback
|
||||||
|
* Accepting responsibility and apologizing to those affected by our mistakes,
|
||||||
|
and learning from the experience
|
||||||
|
* Focusing on what is best not just for us as individuals, but for the overall
|
||||||
|
community
|
||||||
|
|
||||||
|
Examples of unacceptable behavior include:
|
||||||
|
|
||||||
|
* The use of sexualized language or imagery, and sexual attention or advances of
|
||||||
|
any kind
|
||||||
|
* Trolling, insulting or derogatory comments, and personal or political attacks
|
||||||
|
* Public or private harassment
|
||||||
|
* Publishing others' private information, such as a physical or email address,
|
||||||
|
without their explicit permission
|
||||||
|
* Other conduct which could reasonably be considered inappropriate in a
|
||||||
|
professional setting
|
||||||
|
|
||||||
|
## Enforcement Responsibilities
|
||||||
|
|
||||||
|
Community leaders are responsible for clarifying and enforcing our standards of
|
||||||
|
acceptable behavior and will take appropriate and fair corrective action in
|
||||||
|
response to any behavior that they deem inappropriate, threatening, offensive,
|
||||||
|
or harmful.
|
||||||
|
|
||||||
|
Community leaders have the right and responsibility to remove, edit, or reject
|
||||||
|
comments, commits, code, wiki edits, issues, and other contributions that are
|
||||||
|
not aligned to this Code of Conduct, and will communicate reasons for moderation
|
||||||
|
decisions when appropriate.
|
||||||
|
|
||||||
|
## Scope
|
||||||
|
|
||||||
|
This Code of Conduct applies within all community spaces, and also applies when
|
||||||
|
an individual is officially representing the community in public spaces.
|
||||||
|
Examples of representing our community include using an official e-mail address,
|
||||||
|
posting via an official social media account, or acting as an appointed
|
||||||
|
representative at an online or offline event.
|
||||||
|
|
||||||
|
## Enforcement
|
||||||
|
|
||||||
|
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||||
|
reported to the community leaders responsible for enforcement at
|
||||||
|
"conduct AT squiddev DOT cc". All complaints will be reviewed and investigated
|
||||||
|
promptly and fairly.
|
||||||
|
|
||||||
|
All community leaders are obligated to respect the privacy and security of the
|
||||||
|
reporter of any incident.
|
||||||
|
|
||||||
|
## Enforcement Guidelines
|
||||||
|
|
||||||
|
Community leaders will follow these Community Impact Guidelines in determining
|
||||||
|
the consequences for any action they deem in violation of this Code of Conduct:
|
||||||
|
|
||||||
|
### 1. Correction
|
||||||
|
|
||||||
|
**Community Impact**: Use of inappropriate language or other behavior deemed
|
||||||
|
unprofessional or unwelcome in the community.
|
||||||
|
|
||||||
|
**Consequence**: A private, written warning from community leaders, providing
|
||||||
|
clarity around the nature of the violation and an explanation of why the
|
||||||
|
behavior was inappropriate. A public apology may be requested.
|
||||||
|
|
||||||
|
### 2. Warning
|
||||||
|
|
||||||
|
**Community Impact**: A violation through a single incident or series of
|
||||||
|
actions.
|
||||||
|
|
||||||
|
**Consequence**: A warning with consequences for continued behavior. No
|
||||||
|
interaction with the people involved, including unsolicited interaction with
|
||||||
|
those enforcing the Code of Conduct, for a specified period of time. This
|
||||||
|
includes avoiding interactions in community spaces as well as external channels
|
||||||
|
like social media. Violating these terms may lead to a temporary or permanent
|
||||||
|
ban.
|
||||||
|
|
||||||
|
### 3. Temporary Ban
|
||||||
|
|
||||||
|
**Community Impact**: A serious violation of community standards, including
|
||||||
|
sustained inappropriate behavior.
|
||||||
|
|
||||||
|
**Consequence**: A temporary ban from any sort of interaction or public
|
||||||
|
communication with the community for a specified period of time. No public or
|
||||||
|
private interaction with the people involved, including unsolicited interaction
|
||||||
|
with those enforcing the Code of Conduct, is allowed during this period.
|
||||||
|
Violating these terms may lead to a permanent ban.
|
||||||
|
|
||||||
|
### 4. Permanent Ban
|
||||||
|
|
||||||
|
**Community Impact**: Demonstrating a pattern of violation of community
|
||||||
|
standards, including sustained inappropriate behavior, harassment of an
|
||||||
|
individual, or aggression toward or disparagement of classes of individuals.
|
||||||
|
|
||||||
|
**Consequence**: A permanent ban from any sort of public interaction within the
|
||||||
|
community.
|
||||||
|
|
||||||
|
## Attribution
|
||||||
|
|
||||||
|
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
||||||
|
version 2.1, available at
|
||||||
|
[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].
|
||||||
|
|
||||||
|
Community Impact Guidelines were inspired by
|
||||||
|
[Mozilla's code of conduct enforcement ladder][Mozilla CoC].
|
||||||
|
|
||||||
|
For answers to common questions about this code of conduct, see the FAQ at
|
||||||
|
[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at
|
||||||
|
[https://www.contributor-covenant.org/translations][translations].
|
||||||
|
|
||||||
|
[homepage]: https://www.contributor-covenant.org
|
||||||
|
[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
|
||||||
|
[Mozilla CoC]: https://github.com/mozilla/diversity
|
||||||
|
[FAQ]: https://www.contributor-covenant.org/faq
|
||||||
|
[translations]: https://www.contributor-covenant.org/translations
|
152
CONTRIBUTING.md
152
CONTRIBUTING.md
@@ -4,112 +4,102 @@ provides an introduction as to how to get started in helping out.
|
|||||||
|
|
||||||
If you've any other questions, [just ask the community][community] or [open an issue][new-issue].
|
If you've any other questions, [just ask the community][community] or [open an issue][new-issue].
|
||||||
|
|
||||||
|
## Table of Contents
|
||||||
|
- [Reporting issues](#reporting-issues)
|
||||||
|
- [Translations](#translations)
|
||||||
|
- [Setting up a development environment](#setting-up-a-development-environment)
|
||||||
|
- [Developing CC: Tweaked](#developing-cc-tweaked)
|
||||||
|
- [Writing documentation](#writing-documentation)
|
||||||
|
|
||||||
## Reporting issues
|
## Reporting issues
|
||||||
If you have a bug, suggestion, or other feedback, the best thing to do is [file an issue][new-issue]. When doing so,
|
If you have a bug, suggestion, or other feedback, the best thing to do is [file an issue][new-issue]. When doing so, do
|
||||||
do 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 [Weblate], an online interface for managing language strings. This is synced
|
||||||
automatically with GitHub, so please don't submit PRs adding/changing translations!
|
automatically with GitHub, so please don't submit PRs adding/changing translations!
|
||||||
|
|
||||||
## Developing
|
## Setting up a development environment
|
||||||
In order to develop CC: Tweaked, you'll need to download the source code and then run it. This is a pretty simple
|
In order to develop CC: Tweaked, you'll need to download the source code and then run it.
|
||||||
process. When building on Windows, Use `gradlew.bat` instead of `./gradlew`.
|
|
||||||
|
|
||||||
- **Clone the repository:** `git clone https://github.com/cc-tweaked/CC-Tweaked.git && cd CC-Tweaked`
|
- Make sure you've got the following software instealled:
|
||||||
- **Setup Forge:** `./gradlew build`
|
- Java Development Kit (JDK) installed. This can be downloaded from [Adoptium].
|
||||||
- **Run Minecraft:** `./gradlew runClient` (or run the `GradleStart` class from your IDE).
|
- [Git](https://git-scm.com/).
|
||||||
- **Optionally:** For small PRs (especially those only touching Lua code), it may be easier to use GitPod, which
|
- If you want to work on documentation, [NodeJS][node].
|
||||||
provides a pre-configured environment: [](https://gitpod.io/#https://github.com/cc-tweaked/CC-Tweaked/)
|
|
||||||
|
|
||||||
Do note you will need to download the mod after compiling to test.
|
- Download CC: Tweaked's source code:
|
||||||
|
```
|
||||||
|
git clone https://github.com/cc-tweaked/CC-Tweaked.git
|
||||||
|
cd CC-Tweaked
|
||||||
|
```
|
||||||
|
|
||||||
If you want to run CC:T in a normal Minecraft instance, run `./gradlew build` and copy the `.jar` from `build/libs`.
|
- Build CC: Tweaked with `./gradlew build`. This will be very slow the first time it runs, as it needs to download a
|
||||||
These commands may take a few minutes to run the first time, as the environment is set up, but should be much faster
|
lot of dependencies (and decompile Minecraft several times). Subsequent runs should be much faster!
|
||||||
afterwards.
|
|
||||||
|
|
||||||
The following sections describe the more niche sections of CC: Tweaked's build system. Some bits of these are
|
- You're now ready to start developing CC: Tweaked. Running `./gradlew :forge:runClient` or
|
||||||
quite-complex, and (dare I say) over-engineered, so you may wish to ignore them. Well tested/documented PRs are always
|
`./gradle :fabric:runClient` will start Minecraft under Forge and Fabric respectively.
|
||||||
preferred (and I'd definitely recommend setting up the tooling if you're doing serious development work), but for
|
|
||||||
small changes it can be a lot.
|
|
||||||
|
|
||||||
### Code linters
|
If you want to run CC:T in a normal Minecraft instance, run `./gradlew assemble` and copy the `.jar` from
|
||||||
CC: Tweaked uses a couple of "linters" on its source code, to enforce a consistent style across the project. While these
|
`projects/forge/build/libs` (for Forge) or `projects/fabric/build/libs` (for Fabric).
|
||||||
are run whenever you submit a PR, it's often useful to run this before committing.
|
|
||||||
|
|
||||||
- **[Checkstyle]:** Checks Java code to ensure it is consistently formatted. This can be run with `./gradlew build` or
|
## Developing CC: Tweaked
|
||||||
`./gradle check`.
|
Before making any major changes to CC: Tweaked, I'd recommend you have a read of the [the architecture
|
||||||
- **[illuaminate]:** Checks Lua code for semantic and styleistic issues. See [the usage section][illuaminate-usage] for
|
document][architecture] first. While it's not a comprehensive document, it gives a good hint of where you should start
|
||||||
how to download and run it. You may need to generate the Java documentation stubs (see "Documentation" below) for all
|
looking to make your changes. As always, if you're not sure [do ask the community][community]!
|
||||||
lints to pass.
|
|
||||||
|
|
||||||
### Documentation
|
### Testing
|
||||||
|
When making larger changes, it's may be useful to write a test to make sure your code works as expected.
|
||||||
|
|
||||||
|
CC: Tweaked has several test suites, each designed to test something different:
|
||||||
|
|
||||||
|
- In order to test CraftOS and its builtin APIs, we have a test suite written in Lua located at
|
||||||
|
`projects/core/src/test/resources/test-rom/`. These don't rely on any Minecraft code, which means they can run on
|
||||||
|
emulators, acting as a sort of compliance test.
|
||||||
|
|
||||||
|
These tests are written using a test system called "mcfly", heavily inspired by [busted]. Groups of tests go inside
|
||||||
|
`describe` blocks, and a single test goes inside `it`. Assertions are generally written using `expect` (inspired by
|
||||||
|
Hamcrest and the like). For instance, `expect(foo):eq("bar")` asserts that your variable `foo` is equal to the
|
||||||
|
expected value `"bar"`.
|
||||||
|
|
||||||
|
These tests can be run with `./gradlew :core:test`.
|
||||||
|
|
||||||
|
- In-game functionality, such as the behaviour of blocks and items, is tested using [Minecraft's gametest
|
||||||
|
system][mc-test] (`projects/common/src/testMod`). These tests spin up a server, spawn a structure for each test, and
|
||||||
|
then run some code on the blocks defined in that structure.
|
||||||
|
|
||||||
|
These tests can be run with `./gradlew runGametest` (or `./gradle :forge:runGametest`/`./gradlew :fabric:runGametest`
|
||||||
|
for a single loader).
|
||||||
|
|
||||||
|
For more information, [see the architecture document][architecture].
|
||||||
|
|
||||||
|
## Writing documentation
|
||||||
When writing documentation for [CC: Tweaked's documentation website][docs], it may be useful to build the documentation
|
When writing documentation for [CC: Tweaked's documentation website][docs], it may be useful to build the documentation
|
||||||
and preview it yourself before submitting a PR.
|
and preview it yourself before submitting a PR.
|
||||||
|
|
||||||
Building all documentation is, sadly, a multi-stage process (though this is largely hidden by Gradle). First we need to
|
You'll first need to [set up a development environment as above](#setting-up-a-development-environment).
|
||||||
convert Java doc-comments into Lua ones, we also generate some Javascript to embed. All of this is then finally fed into
|
|
||||||
illuaminate, which spits out our HTML.
|
|
||||||
|
|
||||||
#### Setting up the tooling
|
Once this is set up, you can now run `./gradlew docWebsite`. This generates documentation from our Lua and Java code,
|
||||||
For various reasons, getting the environment set up to build documentation can be pretty complex. I'd quite like to
|
writing the resulting HTML into `./projects/web/build/site`, which can then be opened in a browser. When iterating on
|
||||||
automate this via Docker and/or nix in the future, but this needs to be done manually for now.
|
documentation, you can instead run `./gradlew docWebsite -t`, which will rebuild documentation every time you change a
|
||||||
|
file.
|
||||||
|
|
||||||
This tooling is only needed if you need to build the whole website. If you just want to generate the Lua stubs, you can
|
Documentation is built using [illuaminate] which, while not currently documented (somewhat ironic), is largely the same
|
||||||
skp this section.
|
as [ldoc][ldoc]. Documentation comments are written in Markdown, though note that we do not support many GitHub-specific
|
||||||
- Install Node/npm and install our Node packages with `npm ci`.
|
markdown features - if you can, do check what the documentation looks like locally!
|
||||||
- Install [illuaminate][illuaminate-usage] as described above.
|
|
||||||
|
|
||||||
#### Building documentation
|
When writing long-form documentation (such as the guides in [doc/guides](doc/guides)), I find it useful to tell a
|
||||||
Gradle should be your entrypoint to building most documentation. There's two tasks which are of interest:
|
narrative. Think of what you want the user to learn or achieve, then start introducing a simple concept and then talk
|
||||||
|
about how you can build on that, until you've covered everything!
|
||||||
- `./gradlew luaJavadoc` - Generate documentation stubs for Java methods.
|
|
||||||
- `./gradlew docWebsite` - Generate the whole website (including Javascript pages). The resulting HTML is stored at
|
|
||||||
`./build/docs/site/`.
|
|
||||||
|
|
||||||
#### Writing documentation
|
|
||||||
illuaminate's documentation system is not currently documented (somewhat ironic), but is _largely_ the same as
|
|
||||||
[ldoc][ldoc]. Documentation comments are written in Markdown,
|
|
||||||
|
|
||||||
Our markdown engine does _not_ support GitHub flavoured markdown, and so does not support all the features one might
|
|
||||||
expect (such as tables). It is very much recommended that you build and preview the docs locally first.
|
|
||||||
|
|
||||||
### Testing
|
|
||||||
Thankfully running tests is much simpler than running the documentation generator! `./gradlew check` will run the
|
|
||||||
entire test suite (and some additional bits of verification).
|
|
||||||
|
|
||||||
Before we get into writing tests, it's worth mentioning the various test suites that CC: Tweaked has:
|
|
||||||
- "Core" Java (`./src/test/java`): These test core bits of the mod which don't require any Minecraft interaction.
|
|
||||||
This includes the `@LuaFunction` system, file system code, etc...
|
|
||||||
|
|
||||||
These tests are run by `./gradlew test`.
|
|
||||||
|
|
||||||
- CraftOS (`./src/test/resources/test-rom/`): These tests are written in Lua, and ensure the Lua environment, libraries
|
|
||||||
and programs work as expected. These are (generally) written to be able to be run on emulators too, to provide some
|
|
||||||
sort of compliance test.
|
|
||||||
|
|
||||||
These tests are run by the '"Core" Java' test suite, and so are also run with `./gradlew test`.
|
|
||||||
|
|
||||||
- In-game (`./src/testMod/java/dan200/computercraft/ingame/`): These tests are run on an actual Minecraft server and client,
|
|
||||||
using [the same system Mojang do][mc-test]. The aim of these is to test in-game behaviour of blocks and peripherals.
|
|
||||||
|
|
||||||
These are run by `./gradlew testClient` and `./gradlew testServer`. You may want to run the client under `xvfb-run`
|
|
||||||
or similar when running in a headless environment.
|
|
||||||
|
|
||||||
## CraftOS tests
|
|
||||||
CraftOS's tests are written using a test system called "mcfly", heavily inspired by [busted] (and thus RSpec). Groups of
|
|
||||||
tests go inside `describe` blocks, and a single test goes inside `it`.
|
|
||||||
|
|
||||||
Assertions are generally written using `expect` (inspired by Hamcrest and the like). For instance, `expect(foo):eq("bar")`
|
|
||||||
asserts that your variable `foo` is equal to the expected value `"bar"`.
|
|
||||||
|
|
||||||
[new-issue]: https://github.com/cc-tweaked/CC-Tweaked/issues/new/choose "Create a new issue"
|
[new-issue]: https://github.com/cc-tweaked/CC-Tweaked/issues/new/choose "Create a new issue"
|
||||||
[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"
|
||||||
[checkstyle]: https://checkstyle.org/
|
[checkstyle]: https://checkstyle.org/
|
||||||
[illuaminate]: https://github.com/SquidDev/illuaminate/ "Illuaminate on GitHub"
|
[illuaminate]: https://github.com/SquidDev/illuaminate/ "Illuaminate on GitHub"
|
||||||
[illuaminate-usage]: https://github.com/SquidDev/illuaminate/blob/master/README.md#usage "Installing Illuaminate"
|
|
||||||
[weblate]: https://i18n.tweaked.cc/projects/cc-tweaked/minecraft/ "CC: Tweaked weblate instance"
|
[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"
|
||||||
|
[architecture]: projects/ARCHITECTURE.md
|
||||||
|
25
README.md
25
README.md
@@ -13,9 +13,8 @@ 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 we have a [forum](https://forums.computercraft.cc/) and [Discord guild](https://discord.computercraft.cc)!
|
ComputerCraft, do check out our [forum] and [GitHub discussions page][GitHub discussions]! There's also a fairly
|
||||||
There's also a fairly populated, albeit quiet [IRC channel](http://webchat.esper.net/?channels=computercraft), if that's
|
populated, albeit quiet [IRC channel][irc], if that's more your cup of tea.
|
||||||
more your cup of tea.
|
|
||||||
|
|
||||||
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").
|
||||||
|
|
||||||
@@ -27,15 +26,26 @@ on is present.
|
|||||||
```groovy
|
```groovy
|
||||||
repositories {
|
repositories {
|
||||||
maven {
|
maven {
|
||||||
url 'https://squiddev.cc/maven/'
|
url "https://squiddev.cc/maven/"
|
||||||
content {
|
content {
|
||||||
includeGroup 'org.squiddev'
|
includeGroup("cc.tweaked")
|
||||||
|
includeModule("org.squiddev", "Cobalt")
|
||||||
|
includeModule("fuzs.forgeconfigapiport", "forgeconfigapiport-fabric")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation fg.deobf("org.squiddev:cc-tweaked-${mc_version}:${cct_version}")
|
// Vanilla (i.e. for multi-loader systems)
|
||||||
|
compileOnly("cc.tweaked:cc-tweaked-$mcVersion-common-api")
|
||||||
|
|
||||||
|
// Forge Gradle
|
||||||
|
compileOnly(fg.deobf("cc.tweaked:cc-tweaked-$mcVersion-forge-api:$cctVersion"))
|
||||||
|
runtimeOnly(fg.deobf("cc.tweaked:cc-tweaked-$mcVersion-forge:$cctVersion"))
|
||||||
|
|
||||||
|
// Fabric Loom
|
||||||
|
modCompileOnly("cc.tweaked:cc-tweaked-$mcVersion-fabric-api:$cctVersion")
|
||||||
|
modRuntimeOnly("cc.tweaked:cc-tweaked-$mcVersion-fabric:$cctVersion")
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -51,3 +61,6 @@ the generated documentation [can be browsed online](https://tweaked.cc/javadoc/)
|
|||||||
[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."
|
||||||
[ccrestitched]: https://www.curseforge.com/minecraft/mc-mods/cc-restitched "Download CC: Restitched from CurseForge"
|
[ccrestitched]: https://www.curseforge.com/minecraft/mc-mods/cc-restitched "Download CC: Restitched from CurseForge"
|
||||||
|
[forum]: https://forums.computercraft.cc/
|
||||||
|
[GitHub Discussions]: https://github.com/cc-tweaked/CC-Tweaked/discussions
|
||||||
|
[IRC]: https://webchat.esper.net/?channels=computercraft "#computercraft on EsperNet"
|
||||||
|
623
build.gradle
623
build.gradle
@@ -1,623 +0,0 @@
|
|||||||
buildscript {
|
|
||||||
repositories {
|
|
||||||
mavenCentral()
|
|
||||||
maven { url = "https://maven.minecraftforge.net" }
|
|
||||||
maven { url = 'https://maven.parchmentmc.org' }
|
|
||||||
}
|
|
||||||
dependencies {
|
|
||||||
classpath 'net.minecraftforge.gradle:ForgeGradle:5.1.+'
|
|
||||||
classpath "org.spongepowered:mixingradle:0.7.+"
|
|
||||||
classpath 'org.parchmentmc:librarian:1.+'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
plugins {
|
|
||||||
id "checkstyle"
|
|
||||||
id "jacoco"
|
|
||||||
id "maven-publish"
|
|
||||||
id "com.github.hierynomus.license" version "0.16.1"
|
|
||||||
id "com.matthewprenger.cursegradle" version "1.4.0"
|
|
||||||
id "com.github.breadmoirai.github-release" version "2.2.12"
|
|
||||||
id "org.jetbrains.kotlin.jvm" version "1.6.0"
|
|
||||||
id "com.modrinth.minotaur" version "1.2.1"
|
|
||||||
}
|
|
||||||
|
|
||||||
apply plugin: 'net.minecraftforge.gradle'
|
|
||||||
apply plugin: "org.spongepowered.mixin"
|
|
||||||
apply plugin: 'org.parchmentmc.librarian.forgegradle'
|
|
||||||
|
|
||||||
version = mod_version
|
|
||||||
|
|
||||||
group = "org.squiddev"
|
|
||||||
archivesBaseName = "cc-tweaked-${mc_version}"
|
|
||||||
|
|
||||||
def javaVersion = JavaLanguageVersion.of(17)
|
|
||||||
java {
|
|
||||||
toolchain {
|
|
||||||
languageVersion = javaVersion
|
|
||||||
}
|
|
||||||
|
|
||||||
withSourcesJar()
|
|
||||||
withJavadocJar()
|
|
||||||
}
|
|
||||||
|
|
||||||
tasks.withType(JavaExec).configureEach {
|
|
||||||
javaLauncher = javaToolchains.launcherFor {
|
|
||||||
languageVersion = javaVersion
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sourceSets {
|
|
||||||
main.resources {
|
|
||||||
srcDir 'src/generated/resources'
|
|
||||||
}
|
|
||||||
|
|
||||||
testMod {}
|
|
||||||
}
|
|
||||||
|
|
||||||
minecraft {
|
|
||||||
runs {
|
|
||||||
all {
|
|
||||||
lazyToken('minecraft_classpath') {
|
|
||||||
configurations.shade.copyRecursive().resolve().collect { it.absolutePath }.join(File.pathSeparator)
|
|
||||||
}
|
|
||||||
|
|
||||||
property 'forge.logging.markers', 'REGISTRIES'
|
|
||||||
property 'forge.logging.console.level', 'debug'
|
|
||||||
|
|
||||||
mods {
|
|
||||||
computercraft {
|
|
||||||
source sourceSets.main
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
arg "-mixin.config=computercraft.mixins.json"
|
|
||||||
}
|
|
||||||
|
|
||||||
client {
|
|
||||||
workingDirectory project.file('run')
|
|
||||||
}
|
|
||||||
|
|
||||||
server {
|
|
||||||
workingDirectory project.file("run/server")
|
|
||||||
arg "--nogui"
|
|
||||||
}
|
|
||||||
|
|
||||||
data {
|
|
||||||
workingDirectory project.file('run')
|
|
||||||
args '--mod', 'computercraft', '--all', '--output', file('src/generated/resources/'), '--existing', file('src/main/resources/')
|
|
||||||
}
|
|
||||||
|
|
||||||
testClient {
|
|
||||||
workingDirectory project.file('test-files/client')
|
|
||||||
parent runs.client
|
|
||||||
|
|
||||||
mods {
|
|
||||||
cctest {
|
|
||||||
source sourceSets.testMod
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
lazyToken('minecraft_classpath') {
|
|
||||||
(configurations.shade.copyRecursive().resolve() + configurations.testModExtra.copyRecursive().resolve())
|
|
||||||
.collect { it.absolutePath }
|
|
||||||
.join(File.pathSeparator)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
gameTestServer {
|
|
||||||
workingDirectory project.file('test-files/server')
|
|
||||||
|
|
||||||
mods {
|
|
||||||
cctest {
|
|
||||||
source sourceSets.testMod
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
lazyToken('minecraft_classpath') {
|
|
||||||
(configurations.shade.copyRecursive().resolve() + configurations.testModExtra.copyRecursive().resolve())
|
|
||||||
.collect { it.absolutePath }
|
|
||||||
.join(File.pathSeparator)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// mappings channel: 'parchment', version: "${mapping_version}-${mc_version}"
|
|
||||||
mappings channel: 'official', version: mc_version
|
|
||||||
|
|
||||||
accessTransformer file('src/main/resources/META-INF/accesstransformer.cfg')
|
|
||||||
accessTransformer file('src/testMod/resources/META-INF/accesstransformer.cfg')
|
|
||||||
}
|
|
||||||
|
|
||||||
mixin {
|
|
||||||
add sourceSets.main, 'computercraft.mixins.refmap.json'
|
|
||||||
}
|
|
||||||
|
|
||||||
repositories {
|
|
||||||
mavenCentral()
|
|
||||||
maven {
|
|
||||||
name "SquidDev"
|
|
||||||
url "https://squiddev.cc/maven"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
configurations {
|
|
||||||
shade
|
|
||||||
implementation.extendsFrom shade
|
|
||||||
|
|
||||||
cctJavadoc
|
|
||||||
|
|
||||||
testModExtra
|
|
||||||
testModImplementation.extendsFrom(testModExtra)
|
|
||||||
testModImplementation.extendsFrom(implementation)
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
checkstyle "com.puppycrawl.tools:checkstyle:8.45"
|
|
||||||
|
|
||||||
minecraft "net.minecraftforge:forge:${mc_version}-${forge_version}"
|
|
||||||
annotationProcessor 'org.spongepowered:mixin:0.8.4:processor'
|
|
||||||
|
|
||||||
compileOnly fg.deobf("mezz.jei:jei-1.18.2:9.4.1.116:api")
|
|
||||||
// runtimeOnly fg.deobf("mezz.jei:jei-1.18.2:9.4.1.116")
|
|
||||||
|
|
||||||
shade 'org.squiddev:Cobalt:0.5.5'
|
|
||||||
shade 'io.netty:netty-codec-http:4.1.76.Final'
|
|
||||||
|
|
||||||
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.7.0'
|
|
||||||
testImplementation 'org.junit.jupiter:junit-jupiter-params:5.7.0'
|
|
||||||
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.7.0'
|
|
||||||
testImplementation 'org.hamcrest:hamcrest:2.2'
|
|
||||||
testImplementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.6.0'
|
|
||||||
testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.2'
|
|
||||||
|
|
||||||
testModImplementation sourceSets.main.output
|
|
||||||
testModExtra('org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.6.0') {
|
|
||||||
exclude group: "org.jetbrains", module: "annotations"
|
|
||||||
}
|
|
||||||
|
|
||||||
cctJavadoc 'cc.tweaked:cct-javadoc:1.4.6'
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compile tasks
|
|
||||||
|
|
||||||
compileTestModJava {
|
|
||||||
dependsOn(compileJava)
|
|
||||||
}
|
|
||||||
|
|
||||||
javadoc {
|
|
||||||
include "dan200/computercraft/api/**/*.java"
|
|
||||||
}
|
|
||||||
|
|
||||||
task luaJavadoc(type: Javadoc) {
|
|
||||||
description "Generates documentation for Java-side Lua functions."
|
|
||||||
group "documentation"
|
|
||||||
|
|
||||||
source = sourceSets.main.allJava
|
|
||||||
destinationDir = file("${project.docsDir}/luaJavadoc")
|
|
||||||
classpath = sourceSets.main.compileClasspath
|
|
||||||
|
|
||||||
options.docletpath = configurations.cctJavadoc.files as List
|
|
||||||
options.doclet = "cc.tweaked.javadoc.LuaDoclet"
|
|
||||||
options.noTimestamp = false
|
|
||||||
|
|
||||||
javadocTool = javaToolchains.javadocToolFor {
|
|
||||||
languageVersion = javaVersion
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
jar {
|
|
||||||
manifest {
|
|
||||||
attributes([
|
|
||||||
"Specification-Title" : "computercraft",
|
|
||||||
"Specification-Vendor" : "SquidDev",
|
|
||||||
"Specification-Version" : "1",
|
|
||||||
"Implementation-Title" : "CC: Tweaked",
|
|
||||||
"Implementation-Version" : "${mod_version}",
|
|
||||||
"Implementation-Vendor" : "SquidDev",
|
|
||||||
"Implementation-Timestamp": new Date().format("yyyy-MM-dd'T'HH:mm:ssZ")
|
|
||||||
,
|
|
||||||
"MixinConfigs" : "computercraft.mixins.json",
|
|
||||||
])
|
|
||||||
}
|
|
||||||
|
|
||||||
duplicatesStrategy(DuplicatesStrategy.WARN)
|
|
||||||
|
|
||||||
from configurations.shade.collect { it.isDirectory() ? it : zipTree(it) }
|
|
||||||
}
|
|
||||||
|
|
||||||
jar.finalizedBy('reobfJar')
|
|
||||||
|
|
||||||
[compileJava, compileTestJava, compileTestModJava].forEach {
|
|
||||||
it.configure {
|
|
||||||
options.compilerArgs << "-Xlint" << "-Xlint:-processing"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
processResources {
|
|
||||||
def hash = 'none'
|
|
||||||
Set<String> contributors = []
|
|
||||||
try {
|
|
||||||
hash = ["git", "-C", projectDir, "rev-parse", "HEAD"].execute().text.trim()
|
|
||||||
|
|
||||||
def blacklist = ['GitHub', 'Daniel Ratcliffe', 'Weblate']
|
|
||||||
|
|
||||||
// Extract all authors, commiters and co-authors from the git log.
|
|
||||||
def authors = ["git", "-C", projectDir, "log", "--format=tformat:%an <%ae>%n%cn <%ce>%n%(trailers:key=Co-authored-by,valueonly)"]
|
|
||||||
.execute().text.readLines().unique()
|
|
||||||
|
|
||||||
// We now pass this through git's mailmap to de-duplicate some authors.
|
|
||||||
def remapAuthors = ["git", "check-mailmap", "--stdin"].execute()
|
|
||||||
remapAuthors.withWriter { stdin ->
|
|
||||||
if (stdin !instanceof BufferedWriter) stdin = new BufferedWriter(stdin)
|
|
||||||
|
|
||||||
authors.forEach {
|
|
||||||
if (it == "") return
|
|
||||||
if (!it.endsWith(">")) it += ">" // Some commits have broken Co-Authored-By lines!
|
|
||||||
stdin.writeLine(it)
|
|
||||||
}
|
|
||||||
stdin.close()
|
|
||||||
}
|
|
||||||
|
|
||||||
// And finally extract out the actual name.
|
|
||||||
def emailRegex = ~/^([^<]+) <.+>$/
|
|
||||||
remapAuthors.text.readLines().forEach {
|
|
||||||
def matcher = it =~ emailRegex
|
|
||||||
matcher.find()
|
|
||||||
def name = matcher.group(1)
|
|
||||||
if (!blacklist.contains(name)) contributors.add(name)
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace()
|
|
||||||
}
|
|
||||||
inputs.property "commithash", hash
|
|
||||||
duplicatesStrategy = DuplicatesStrategy.INCLUDE
|
|
||||||
|
|
||||||
from(sourceSets.main.resources.srcDirs) {
|
|
||||||
include 'data/computercraft/lua/rom/help/credits.txt'
|
|
||||||
|
|
||||||
expand(
|
|
||||||
'gitcontributors': contributors.sort(false, String.CASE_INSENSITIVE_ORDER).join('\n')
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
from(sourceSets.main.resources.srcDirs) {
|
|
||||||
exclude 'data/computercraft/lua/rom/help/credits.txt'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sourcesJar {
|
|
||||||
duplicatesStrategy = DuplicatesStrategy.INCLUDE
|
|
||||||
}
|
|
||||||
|
|
||||||
// Web tasks
|
|
||||||
|
|
||||||
|
|
||||||
import com.hierynomus.gradle.license.tasks.LicenseCheck
|
|
||||||
import com.hierynomus.gradle.license.tasks.LicenseFormat
|
|
||||||
import com.modrinth.minotaur.TaskModrinthUpload
|
|
||||||
import org.apache.tools.ant.taskdefs.condition.Os
|
|
||||||
|
|
||||||
List<String> mkCommand(String command) {
|
|
||||||
return Os.isFamily(Os.FAMILY_WINDOWS) ? ["cmd", "/c", command] : ["sh", "-c", command]
|
|
||||||
}
|
|
||||||
|
|
||||||
task rollup(type: Exec) {
|
|
||||||
group = "build"
|
|
||||||
description = "Bundles JS into rollup"
|
|
||||||
|
|
||||||
inputs.files(fileTree("src/web")).withPropertyName("sources")
|
|
||||||
inputs.file("package-lock.json").withPropertyName("package-lock.json")
|
|
||||||
inputs.file("tsconfig.json").withPropertyName("Typescript config")
|
|
||||||
inputs.file("rollup.config.js").withPropertyName("Rollup config")
|
|
||||||
outputs.file("$buildDir/rollup/index.js").withPropertyName("output")
|
|
||||||
|
|
||||||
commandLine mkCommand('"node_modules/.bin/rollup" --config rollup.config.js')
|
|
||||||
}
|
|
||||||
|
|
||||||
task illuaminateDocs(type: Exec, dependsOn: [rollup, luaJavadoc]) {
|
|
||||||
group = "build"
|
|
||||||
description = "Generates docs using Illuaminate"
|
|
||||||
|
|
||||||
inputs.files(fileTree("doc")).withPropertyName("docs")
|
|
||||||
inputs.files(fileTree("src/main/resources/data/computercraft/lua/rom")).withPropertyName("lua rom")
|
|
||||||
inputs.file("illuaminate.sexp").withPropertyName("illuaminate.sexp")
|
|
||||||
inputs.dir("$buildDir/docs/luaJavadoc")
|
|
||||||
inputs.file("$buildDir/rollup/index.js").withPropertyName("scripts")
|
|
||||||
inputs.file("src/web/styles.css").withPropertyName("styles")
|
|
||||||
outputs.dir("$buildDir/docs/lua")
|
|
||||||
|
|
||||||
commandLine mkCommand('"bin/illuaminate" doc-gen')
|
|
||||||
}
|
|
||||||
|
|
||||||
task jsxDocs(type: Exec, dependsOn: [illuaminateDocs]) {
|
|
||||||
group = "build"
|
|
||||||
description = "Post-processes documentation to statically render some dynamic content."
|
|
||||||
|
|
||||||
inputs.files(fileTree("src/web")).withPropertyName("sources")
|
|
||||||
inputs.file("src/generated/export/index.json").withPropertyName("export")
|
|
||||||
inputs.file("package-lock.json").withPropertyName("package-lock.json")
|
|
||||||
inputs.file("tsconfig.json").withPropertyName("Typescript config")
|
|
||||||
inputs.files(fileTree("$buildDir/docs/lua"))
|
|
||||||
outputs.dir("$buildDir/docs/site")
|
|
||||||
|
|
||||||
commandLine mkCommand('"node_modules/.bin/ts-node" --esm src/web/transform.tsx')
|
|
||||||
}
|
|
||||||
|
|
||||||
task docWebsite(type: Copy, dependsOn: [jsxDocs]) {
|
|
||||||
from('doc') {
|
|
||||||
include 'logo.png'
|
|
||||||
include 'images/**'
|
|
||||||
}
|
|
||||||
from("$buildDir/rollup") {
|
|
||||||
exclude 'index.js'
|
|
||||||
}
|
|
||||||
from("$buildDir/docs/lua") {
|
|
||||||
exclude '**/*.html'
|
|
||||||
}
|
|
||||||
from("src/generated/export/items") {
|
|
||||||
into("images/items")
|
|
||||||
}
|
|
||||||
|
|
||||||
into "${project.docsDir}/site"
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check tasks
|
|
||||||
|
|
||||||
test {
|
|
||||||
useJUnitPlatform()
|
|
||||||
testLogging {
|
|
||||||
events "skipped", "failed"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
jacocoTestReport {
|
|
||||||
dependsOn('test')
|
|
||||||
reports {
|
|
||||||
xml.required = true
|
|
||||||
html.required = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
check.dependsOn jacocoTestReport
|
|
||||||
|
|
||||||
license {
|
|
||||||
mapping("java", "SLASHSTAR_STYLE")
|
|
||||||
strictCheck true
|
|
||||||
|
|
||||||
ext.year = Calendar.getInstance().get(Calendar.YEAR)
|
|
||||||
}
|
|
||||||
|
|
||||||
[licenseMain, licenseFormatMain].forEach {
|
|
||||||
it.configure {
|
|
||||||
include("**/*.java")
|
|
||||||
exclude("dan200/computercraft/api/**")
|
|
||||||
header file('config/license/main.txt')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[licenseTest, licenseFormatTest, licenseTestMod, licenseFormatTestMod].forEach {
|
|
||||||
it.configure {
|
|
||||||
include("**/*.java")
|
|
||||||
header file('config/license/main.txt')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
gradle.projectsEvaluated {
|
|
||||||
tasks.withType(LicenseFormat) {
|
|
||||||
outputs.upToDateWhen { false }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
task licenseAPI(type: LicenseCheck)
|
|
||||||
task licenseFormatAPI(type: LicenseFormat)
|
|
||||||
[licenseAPI, licenseFormatAPI].forEach {
|
|
||||||
it.configure {
|
|
||||||
source = sourceSets.main.java
|
|
||||||
include("dan200/computercraft/api/**")
|
|
||||||
header file('config/license/api.txt')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
tasks.register("testServer", JavaExec.class).configure {
|
|
||||||
it.group('In-game tests')
|
|
||||||
it.description("Runs tests on a temporary Minecraft instance.")
|
|
||||||
it.dependsOn("prepareRunGameTestServer", "cleanTestServer", 'compileTestModJava')
|
|
||||||
|
|
||||||
// Copy from runTestServer. We do it in this slightly odd way as runTestServer
|
|
||||||
// isn't created until the task is configured (which is no good for us).
|
|
||||||
JavaExec exec = tasks.getByName("runGameTestServer")
|
|
||||||
exec.copyTo(it)
|
|
||||||
it.setClasspath(exec.getClasspath())
|
|
||||||
it.mainClass = exec.mainClass
|
|
||||||
it.setArgs(exec.getArgs())
|
|
||||||
|
|
||||||
// Jacoco and modlauncher don't play well together as the classes loaded in-game don't
|
|
||||||
// match up with those written to disk. We get Jacoco to dump all classes to disk, and
|
|
||||||
// use that when generating the report.
|
|
||||||
def coverageOut = new File(buildDir, "jacocoClassDump/testServer")
|
|
||||||
jacoco.applyTo(it)
|
|
||||||
it.jacoco.setIncludes(["dan200.computercraft.*"])
|
|
||||||
it.jacoco.setClassDumpDir(coverageOut)
|
|
||||||
it.outputs.dir(coverageOut)
|
|
||||||
// Older versions of modlauncher don't include a protection domain (and thus no code
|
|
||||||
// source). Jacoco skips such classes by default, so we need to explicitly include them.
|
|
||||||
it.jacoco.setIncludeNoLocationClasses(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
tasks.register("jacocoTestServerReport", JacocoReport.class).configure {
|
|
||||||
it.group('In-game')
|
|
||||||
it.description("Generate coverage reports for testServer")
|
|
||||||
it.dependsOn("testServer")
|
|
||||||
|
|
||||||
it.executionData(new File(buildDir, "jacoco/testServer.exec"))
|
|
||||||
it.sourceDirectories.from(sourceSets.main.allJava.srcDirs)
|
|
||||||
it.classDirectories.from(new File(buildDir, "jacocoClassDump/testServer"))
|
|
||||||
|
|
||||||
it.reports {
|
|
||||||
xml.enabled true
|
|
||||||
html.enabled true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
check.dependsOn("jacocoTestServerReport")
|
|
||||||
|
|
||||||
|
|
||||||
// Upload tasks
|
|
||||||
|
|
||||||
task checkRelease {
|
|
||||||
group "upload"
|
|
||||||
description "Verifies that everything is ready for a release"
|
|
||||||
|
|
||||||
inputs.property "version", mod_version
|
|
||||||
inputs.file("src/main/resources/data/computercraft/lua/rom/help/changelog.md")
|
|
||||||
inputs.file("src/main/resources/data/computercraft/lua/rom/help/whatsnew.md")
|
|
||||||
|
|
||||||
doLast {
|
|
||||||
def ok = true
|
|
||||||
|
|
||||||
// Check we're targetting the current version
|
|
||||||
def whatsnew = new File(projectDir, "src/main/resources/data/computercraft/lua/rom/help/whatsnew.md").readLines()
|
|
||||||
if (whatsnew[0] != "New features in CC: Tweaked $mod_version") {
|
|
||||||
ok = false
|
|
||||||
project.logger.error("Expected `whatsnew.md' to target $mod_version.")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check "read more" exists and trim it
|
|
||||||
def idx = whatsnew.findIndexOf { it == 'Type "help changelog" to see the full version history.' }
|
|
||||||
if (idx == -1) {
|
|
||||||
ok = false
|
|
||||||
project.logger.error("Must mention the changelog in whatsnew.md")
|
|
||||||
} else {
|
|
||||||
whatsnew = whatsnew.getAt(0..<idx)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check whatsnew and changelog match.
|
|
||||||
def versionChangelog = "# " + whatsnew.join("\n")
|
|
||||||
def changelog = new File(projectDir, "src/main/resources/data/computercraft/lua/rom/help/changelog.md").getText()
|
|
||||||
if (!changelog.startsWith(versionChangelog)) {
|
|
||||||
ok = false
|
|
||||||
project.logger.error("whatsnew and changelog are not in sync")
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!ok) throw new IllegalStateException("Could not check release")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
check.dependsOn checkRelease
|
|
||||||
|
|
||||||
def isStable = true
|
|
||||||
|
|
||||||
curseforge {
|
|
||||||
apiKey = project.hasProperty('curseForgeApiKey') ? project.curseForgeApiKey : ''
|
|
||||||
project {
|
|
||||||
id = '282001'
|
|
||||||
releaseType = isStable ? 'release' : 'alpha'
|
|
||||||
changelog = "Release notes can be found on the GitHub repository (https://github.com/cc-tweaked/CC-Tweaked/releases/tag/v${mc_version}-${mod_version})."
|
|
||||||
|
|
||||||
addGameVersion "${mc_version}"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
tasks.register('publishModrinth', TaskModrinthUpload.class).configure {
|
|
||||||
dependsOn('assemble', 'reobfJar')
|
|
||||||
onlyIf {
|
|
||||||
project.hasProperty('modrinthApiKey')
|
|
||||||
}
|
|
||||||
|
|
||||||
token = project.hasProperty('modrinthApiKey') ? project.getProperty('modrinthApiKey') : ''
|
|
||||||
projectId = 'gu7yAYhd'
|
|
||||||
versionNumber = "${project.mc_version}-${project.mod_version}"
|
|
||||||
uploadFile = jar
|
|
||||||
versionType = isStable ? 'RELEASE' : 'ALPHA'
|
|
||||||
addGameVersion(project.mc_version)
|
|
||||||
changelog = "Release notes can be found on the [GitHub repository](https://github.com/cc-tweaked/CC-Tweaked/releases/tag/v${mc_version}-${mod_version})."
|
|
||||||
addLoader('forge')
|
|
||||||
}
|
|
||||||
|
|
||||||
tasks.withType(GenerateModuleMetadata) {
|
|
||||||
// We can't generate metadata as that includes Forge as a dependency.
|
|
||||||
enabled = false
|
|
||||||
}
|
|
||||||
|
|
||||||
publishing {
|
|
||||||
publications {
|
|
||||||
maven(MavenPublication) {
|
|
||||||
from components.java
|
|
||||||
|
|
||||||
pom {
|
|
||||||
name = 'CC: Tweaked'
|
|
||||||
description = 'CC: Tweaked is a fork of ComputerCraft, adding programmable computers, turtles and more to Minecraft.'
|
|
||||||
url = 'https://github.com/cc-tweaked/CC-Tweaked'
|
|
||||||
|
|
||||||
scm {
|
|
||||||
url = 'https://github.com/cc-tweaked/CC-Tweaked.git'
|
|
||||||
}
|
|
||||||
|
|
||||||
issueManagement {
|
|
||||||
system = 'github'
|
|
||||||
url = 'https://github.com/cc-tweaked/CC-Tweaked/issues'
|
|
||||||
}
|
|
||||||
|
|
||||||
licenses {
|
|
||||||
license {
|
|
||||||
name = 'ComputerCraft Public License, Version 1.0'
|
|
||||||
url = 'https://github.com/cc-tweaked/CC-Tweaked/blob/mc-1.15.x/LICENSE'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
withXml { asNode().remove(asNode().get("dependencies")) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
repositories {
|
|
||||||
if (project.hasProperty("mavenUser")) {
|
|
||||||
maven {
|
|
||||||
name = "SquidDev"
|
|
||||||
url = "https://squiddev.cc/maven"
|
|
||||||
credentials {
|
|
||||||
username = project.property("mavenUser") as String
|
|
||||||
password = project.property("mavenPass") as String
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
githubRelease {
|
|
||||||
token project.hasProperty('githubApiKey') ? project.githubApiKey : ''
|
|
||||||
owner 'cc-tweaked'
|
|
||||||
repo 'CC-Tweaked'
|
|
||||||
targetCommitish.set(project.provider({
|
|
||||||
def cmd = ["git", "rev-parse", "--abbrev-ref", "HEAD"]
|
|
||||||
println(cmd)
|
|
||||||
def proc = cmd.execute([], projectDir)
|
|
||||||
if (proc.waitFor() != 0) {
|
|
||||||
println(proc.err.text.trim())
|
|
||||||
throw new IllegalStateException("Executed with a non-0 exit code (${proc.exitValue()}).")
|
|
||||||
}
|
|
||||||
|
|
||||||
def branch = proc.text.trim()
|
|
||||||
if (branch == "") throw new IllegalStateException("Cannot determine branch")
|
|
||||||
return branch
|
|
||||||
}))
|
|
||||||
|
|
||||||
tagName "v${mc_version}-${mod_version}"
|
|
||||||
releaseName "[${mc_version}] ${mod_version}"
|
|
||||||
body.set(project.provider({
|
|
||||||
"## " + new File(projectDir, "src/main/resources/data/computercraft/lua/rom/help/whatsnew.md")
|
|
||||||
.readLines()
|
|
||||||
.takeWhile { it != 'Type "help changelog" to see the full version history.' }
|
|
||||||
.join("\n").trim()
|
|
||||||
}))
|
|
||||||
prerelease !isStable
|
|
||||||
}
|
|
||||||
|
|
||||||
def uploadTasks = ["publish", "curseforge", "publishModrinth", "githubRelease"]
|
|
||||||
uploadTasks.forEach { tasks.getByName(it).dependsOn checkRelease }
|
|
||||||
|
|
||||||
task uploadAll(dependsOn: uploadTasks) {
|
|
||||||
group "upload"
|
|
||||||
description "Uploads to all repositories (Maven, Curse, Modrinth, GitHub release)"
|
|
||||||
}
|
|
52
build.gradle.kts
Normal file
52
build.gradle.kts
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
import org.jetbrains.gradle.ext.compiler
|
||||||
|
import org.jetbrains.gradle.ext.settings
|
||||||
|
|
||||||
|
plugins {
|
||||||
|
publishing
|
||||||
|
alias(libs.plugins.taskTree)
|
||||||
|
alias(libs.plugins.githubRelease)
|
||||||
|
id("org.jetbrains.gradle.plugin.idea-ext")
|
||||||
|
id("cc-tweaked")
|
||||||
|
}
|
||||||
|
|
||||||
|
val isUnstable = project.properties["isUnstable"] == "true"
|
||||||
|
val modVersion: String by extra
|
||||||
|
val mcVersion: String by extra
|
||||||
|
|
||||||
|
githubRelease {
|
||||||
|
token(findProperty("githubApiKey") as String? ?: "")
|
||||||
|
owner.set("cc-tweaked")
|
||||||
|
repo.set("CC-Tweaked")
|
||||||
|
targetCommitish.set(cct.gitBranch)
|
||||||
|
|
||||||
|
tagName.set("v$mcVersion-$modVersion")
|
||||||
|
releaseName.set("[$mcVersion] $modVersion")
|
||||||
|
body.set(
|
||||||
|
provider {
|
||||||
|
"## " + project(":core").file("src/main/resources/data/computercraft/lua/rom/help/whatsnew.md")
|
||||||
|
.readLines()
|
||||||
|
.takeWhile { it != "Type \"help changelog\" to see the full version history." }
|
||||||
|
.joinToString("\n").trim()
|
||||||
|
},
|
||||||
|
)
|
||||||
|
prerelease.set(isUnstable)
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.publish { dependsOn(tasks.githubRelease) }
|
||||||
|
|
||||||
|
idea.project.settings.compiler.javac {
|
||||||
|
// We want ErrorProne to be present when compiling via IntelliJ, as it offers some helpful warnings
|
||||||
|
// and errors. Loop through our source sets and find the appropriate flags.
|
||||||
|
moduleJavacAdditionalOptions = subprojects
|
||||||
|
.asSequence()
|
||||||
|
.map { evaluationDependsOn(it.path) }
|
||||||
|
.flatMap { project ->
|
||||||
|
val sourceSets = project.extensions.findByType(SourceSetContainer::class) ?: return@flatMap sequenceOf()
|
||||||
|
sourceSets.asSequence().map { sourceSet ->
|
||||||
|
val name = "${idea.project.name}.${project.name}.${sourceSet.name}"
|
||||||
|
val compile = project.tasks.named(sourceSet.compileJavaTaskName, JavaCompile::class).get()
|
||||||
|
name to compile.options.allCompilerArgs.joinToString(" ") { if (it.contains(" ")) "\"$it\"" else it }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.toMap()
|
||||||
|
}
|
70
buildSrc/build.gradle.kts
Normal file
70
buildSrc/build.gradle.kts
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
plugins {
|
||||||
|
`java-gradle-plugin`
|
||||||
|
`kotlin-dsl`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Duplicated in settings.gradle.kts
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
gradlePluginPortal()
|
||||||
|
|
||||||
|
maven("https://maven.minecraftforge.net") {
|
||||||
|
name = "Forge"
|
||||||
|
content {
|
||||||
|
includeGroup("net.minecraftforge")
|
||||||
|
includeGroup("net.minecraftforge.gradle")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
maven("https://maven.parchmentmc.org") {
|
||||||
|
name = "Librarian"
|
||||||
|
content {
|
||||||
|
includeGroupByRegex("^org\\.parchmentmc.*")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
maven("https://repo.spongepowered.org/repository/maven-public/") {
|
||||||
|
name = "Sponge"
|
||||||
|
content {
|
||||||
|
includeGroup("org.spongepowered")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
maven("https://maven.fabricmc.net/") {
|
||||||
|
name = "Fabric"
|
||||||
|
content {
|
||||||
|
includeGroup("net.fabricmc")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation(libs.errorProne.plugin)
|
||||||
|
implementation(libs.kotlin.plugin)
|
||||||
|
implementation(libs.spotless)
|
||||||
|
|
||||||
|
implementation(libs.fabric.loom)
|
||||||
|
implementation(libs.forgeGradle)
|
||||||
|
implementation(libs.librarian)
|
||||||
|
implementation(libs.quiltflower)
|
||||||
|
implementation(libs.vanillaGradle)
|
||||||
|
}
|
||||||
|
|
||||||
|
gradlePlugin {
|
||||||
|
plugins {
|
||||||
|
register("cc-tweaked") {
|
||||||
|
id = "cc-tweaked"
|
||||||
|
implementationClass = "cc.tweaked.gradle.CCTweakedPlugin"
|
||||||
|
}
|
||||||
|
|
||||||
|
register("cc-tweaked.illuaminate") {
|
||||||
|
id = "cc-tweaked.illuaminate"
|
||||||
|
implementationClass = "cc.tweaked.gradle.IlluaminatePlugin"
|
||||||
|
}
|
||||||
|
|
||||||
|
register("cc-tweaked.node") {
|
||||||
|
id = "cc-tweaked.node"
|
||||||
|
implementationClass = "cc.tweaked.gradle.NodePlugin"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
7
buildSrc/settings.gradle.kts
Normal file
7
buildSrc/settings.gradle.kts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
dependencyResolutionManagement {
|
||||||
|
versionCatalogs {
|
||||||
|
create("libs") {
|
||||||
|
from(files("../gradle/libs.versions.toml"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
66
buildSrc/src/main/kotlin/cc-tweaked.fabric.gradle.kts
Normal file
66
buildSrc/src/main/kotlin/cc-tweaked.fabric.gradle.kts
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
/** Default configuration for Fabric projects. */
|
||||||
|
|
||||||
|
import cc.tweaked.gradle.CCTweakedExtension
|
||||||
|
import cc.tweaked.gradle.CCTweakedPlugin
|
||||||
|
import cc.tweaked.gradle.IdeaRunConfigurations
|
||||||
|
import cc.tweaked.gradle.MinecraftConfigurations
|
||||||
|
|
||||||
|
plugins {
|
||||||
|
`java-library`
|
||||||
|
id("fabric-loom")
|
||||||
|
id("io.github.juuxel.loom-quiltflower")
|
||||||
|
id("cc-tweaked.java-convention")
|
||||||
|
}
|
||||||
|
|
||||||
|
plugins.apply(CCTweakedPlugin::class.java)
|
||||||
|
|
||||||
|
val mcVersion: String by extra
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
maven("https://maven.parchmentmc.org/") {
|
||||||
|
name = "Parchment"
|
||||||
|
content {
|
||||||
|
includeGroup("org.parchmentmc.data")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
loom {
|
||||||
|
splitEnvironmentSourceSets()
|
||||||
|
splitModDependencies.set(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
MinecraftConfigurations.setup(project)
|
||||||
|
|
||||||
|
extensions.configure(CCTweakedExtension::class.java) {
|
||||||
|
linters(minecraft = true, loader = "fabric")
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
val libs = project.extensions.getByType<VersionCatalogsExtension>().named("libs")
|
||||||
|
|
||||||
|
minecraft("com.mojang:minecraft:$mcVersion")
|
||||||
|
mappings(
|
||||||
|
loom.layered {
|
||||||
|
officialMojangMappings()
|
||||||
|
parchment(
|
||||||
|
project.dependencies.create(
|
||||||
|
group = "org.parchmentmc.data",
|
||||||
|
name = "parchment-${libs.findVersion("parchmentMc").get()}",
|
||||||
|
version = libs.findVersion("parchment").get().toString(),
|
||||||
|
ext = "zip",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
modImplementation(libs.findLibrary("fabric-loader").get())
|
||||||
|
modImplementation(libs.findLibrary("fabric-api").get())
|
||||||
|
|
||||||
|
// Depend on error prone annotations to silence a lot of compile warnings.
|
||||||
|
compileOnlyApi(libs.findLibrary("errorProne.annotations").get())
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.ideaSyncTask {
|
||||||
|
doLast { IdeaRunConfigurations(project).patch() }
|
||||||
|
}
|
39
buildSrc/src/main/kotlin/cc-tweaked.forge.gradle.kts
Normal file
39
buildSrc/src/main/kotlin/cc-tweaked.forge.gradle.kts
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
/** Default configuration for Forge projects. */
|
||||||
|
|
||||||
|
import cc.tweaked.gradle.CCTweakedExtension
|
||||||
|
import cc.tweaked.gradle.CCTweakedPlugin
|
||||||
|
import cc.tweaked.gradle.IdeaRunConfigurations
|
||||||
|
import cc.tweaked.gradle.MinecraftConfigurations
|
||||||
|
|
||||||
|
plugins {
|
||||||
|
id("cc-tweaked.java-convention")
|
||||||
|
id("net.minecraftforge.gradle")
|
||||||
|
id("org.parchmentmc.librarian.forgegradle")
|
||||||
|
}
|
||||||
|
|
||||||
|
plugins.apply(CCTweakedPlugin::class.java)
|
||||||
|
|
||||||
|
val mcVersion: String by extra
|
||||||
|
|
||||||
|
minecraft {
|
||||||
|
val libs = project.extensions.getByType<VersionCatalogsExtension>().named("libs")
|
||||||
|
mappings("parchment", "${libs.findVersion("parchmentMc").get()}-${libs.findVersion("parchment").get()}-$mcVersion")
|
||||||
|
|
||||||
|
accessTransformer(project(":forge").file("src/main/resources/META-INF/accesstransformer.cfg"))
|
||||||
|
}
|
||||||
|
|
||||||
|
MinecraftConfigurations.setup(project)
|
||||||
|
|
||||||
|
extensions.configure(CCTweakedExtension::class.java) {
|
||||||
|
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() }
|
||||||
|
}
|
60
buildSrc/src/main/kotlin/cc-tweaked.gametest.gradle.kts
Normal file
60
buildSrc/src/main/kotlin/cc-tweaked.gametest.gradle.kts
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
import cc.tweaked.gradle.clientClasses
|
||||||
|
import cc.tweaked.gradle.commonClasses
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets up the configurations for writing game tests.
|
||||||
|
*
|
||||||
|
* See notes in [cc.tweaked.gradle.MinecraftConfigurations] for the general design behind these cursed ideas.
|
||||||
|
*/
|
||||||
|
|
||||||
|
plugins {
|
||||||
|
id("cc-tweaked.kotlin-convention")
|
||||||
|
id("cc-tweaked.java-convention")
|
||||||
|
}
|
||||||
|
|
||||||
|
val main = sourceSets["main"]
|
||||||
|
val client = sourceSets["client"]
|
||||||
|
|
||||||
|
// Both testMod and testFixtures inherit from the main and client classpath, just so we have access to Minecraft classes.
|
||||||
|
val testMod by sourceSets.creating {
|
||||||
|
compileClasspath += main.compileClasspath + client.compileClasspath
|
||||||
|
runtimeClasspath += main.runtimeClasspath + client.runtimeClasspath
|
||||||
|
}
|
||||||
|
|
||||||
|
configurations {
|
||||||
|
named(testMod.compileClasspathConfigurationName) {
|
||||||
|
shouldResolveConsistentlyWith(compileClasspath.get())
|
||||||
|
}
|
||||||
|
|
||||||
|
named(testMod.runtimeClasspathConfigurationName) {
|
||||||
|
shouldResolveConsistentlyWith(runtimeClasspath.get())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
val testFixtures by sourceSets.creating {
|
||||||
|
compileClasspath += main.compileClasspath + client.compileClasspath
|
||||||
|
}
|
||||||
|
|
||||||
|
java.registerFeature("testFixtures") {
|
||||||
|
usingSourceSet(testFixtures)
|
||||||
|
disablePublication()
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
val libs = project.extensions.getByType<VersionCatalogsExtension>().named("libs")
|
||||||
|
add(testFixtures.apiConfigurationName, libs.findBundle("test").get())
|
||||||
|
// Consumers of this project already have the common and client classes on the classpath, so it's fine for these
|
||||||
|
// to be compile-only.
|
||||||
|
add(testFixtures.compileOnlyApiConfigurationName, commonClasses(project))
|
||||||
|
add(testFixtures.compileOnlyApiConfigurationName, clientClasses(project))
|
||||||
|
|
||||||
|
testImplementation(testFixtures(project))
|
||||||
|
}
|
198
buildSrc/src/main/kotlin/cc-tweaked.java-convention.gradle.kts
Normal file
198
buildSrc/src/main/kotlin/cc-tweaked.java-convention.gradle.kts
Normal file
@@ -0,0 +1,198 @@
|
|||||||
|
import cc.tweaked.gradle.CCTweakedExtension
|
||||||
|
import cc.tweaked.gradle.CCTweakedPlugin
|
||||||
|
import cc.tweaked.gradle.LicenseHeader
|
||||||
|
import com.diffplug.gradle.spotless.FormatExtension
|
||||||
|
import com.diffplug.spotless.LineEnding
|
||||||
|
import net.ltgt.gradle.errorprone.CheckSeverity
|
||||||
|
import net.ltgt.gradle.errorprone.errorprone
|
||||||
|
import java.nio.charset.StandardCharsets
|
||||||
|
|
||||||
|
plugins {
|
||||||
|
`java-library`
|
||||||
|
idea
|
||||||
|
jacoco
|
||||||
|
checkstyle
|
||||||
|
id("com.diffplug.spotless")
|
||||||
|
id("net.ltgt.errorprone")
|
||||||
|
}
|
||||||
|
|
||||||
|
val modVersion: String by extra
|
||||||
|
val mcVersion: String by extra
|
||||||
|
|
||||||
|
group = "cc.tweaked"
|
||||||
|
version = modVersion
|
||||||
|
|
||||||
|
base.archivesName.convention("cc-tweaked-$mcVersion-${project.name}")
|
||||||
|
|
||||||
|
java {
|
||||||
|
toolchain {
|
||||||
|
languageVersion.set(CCTweakedPlugin.JAVA_VERSION)
|
||||||
|
}
|
||||||
|
|
||||||
|
withSourcesJar()
|
||||||
|
}
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
maven("https://squiddev.cc/maven") {
|
||||||
|
name = "SquidDev"
|
||||||
|
content {
|
||||||
|
includeGroup("org.squiddev")
|
||||||
|
includeGroup("cc.tweaked")
|
||||||
|
// Things we mirror
|
||||||
|
includeGroup("dev.architectury")
|
||||||
|
includeGroup("maven.modrinth")
|
||||||
|
includeGroup("me.shedaniel")
|
||||||
|
includeGroup("me.shedaniel.cloth")
|
||||||
|
includeGroup("mezz.jei")
|
||||||
|
includeModule("com.terraformersmc", "modmenu")
|
||||||
|
includeModule("fuzs.forgeconfigapiport", "forgeconfigapiport-fabric")
|
||||||
|
// Until https://github.com/SpongePowered/Mixin/pull/593 is merged
|
||||||
|
includeModule("org.spongepowered", "mixin")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
val libs = project.extensions.getByType<VersionCatalogsExtension>().named("libs")
|
||||||
|
checkstyle(libs.findLibrary("checkstyle").get())
|
||||||
|
|
||||||
|
errorprone(libs.findLibrary("errorProne-core").get())
|
||||||
|
errorprone(libs.findLibrary("nullAway").get())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Configure default JavaCompile tasks with our arguments.
|
||||||
|
sourceSets.all {
|
||||||
|
tasks.named(compileJavaTaskName, JavaCompile::class.java) {
|
||||||
|
// Processing just gives us "No processor claimed any of these annotations", so skip that!
|
||||||
|
options.compilerArgs.addAll(listOf("-Xlint", "-Xlint:-processing"))
|
||||||
|
|
||||||
|
options.errorprone {
|
||||||
|
check("InvalidBlockTag", CheckSeverity.OFF) // Broken by @cc.xyz
|
||||||
|
check("InvalidParam", CheckSeverity.OFF) // Broken by records.
|
||||||
|
check("InlineMeSuggester", CheckSeverity.OFF) // Minecraft uses @Deprecated liberally
|
||||||
|
// Too many false positives right now. Maybe we need an indirection for it later on.
|
||||||
|
check("ReferenceEquality", CheckSeverity.OFF)
|
||||||
|
check("UnusedVariable", CheckSeverity.OFF) // Too many false positives with records.
|
||||||
|
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("FutureReturnValueIgnored", CheckSeverity.OFF) // Too many false positives with Netty
|
||||||
|
|
||||||
|
check("NullAway", CheckSeverity.ERROR)
|
||||||
|
option("NullAway:AnnotatedPackages", listOf("dan200.computercraft", "net.fabricmc.fabric.api").joinToString(","))
|
||||||
|
option("NullAway:ExcludedFieldAnnotations", listOf("org.spongepowered.asm.mixin.Shadow").joinToString(","))
|
||||||
|
option("NullAway:CastToNonNullMethod", "dan200.computercraft.core.util.Nullability.assertNonNull")
|
||||||
|
option("NullAway:CheckOptionalEmptiness")
|
||||||
|
option("NullAway:AcknowledgeRestrictiveAnnotations")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.compileTestJava {
|
||||||
|
options.errorprone {
|
||||||
|
check("NullAway", CheckSeverity.OFF)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
tasks.withType(JavaCompile::class.java).configureEach {
|
||||||
|
options.encoding = "UTF-8"
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.withType(AbstractArchiveTask::class.java).configureEach {
|
||||||
|
isPreserveFileTimestamps = false
|
||||||
|
isReproducibleFileOrder = true
|
||||||
|
dirMode = Integer.valueOf("755", 8)
|
||||||
|
fileMode = Integer.valueOf("664", 8)
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.jar {
|
||||||
|
manifest {
|
||||||
|
attributes(
|
||||||
|
"Specification-Title" to "computercraft",
|
||||||
|
"Specification-Vendor" to "SquidDev",
|
||||||
|
"Specification-Version" to "1",
|
||||||
|
"Implementation-Title" to "cctweaked-${project.name}",
|
||||||
|
"Implementation-Version" to modVersion,
|
||||||
|
"Implementation-Vendor" to "SquidDev",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.javadoc {
|
||||||
|
options {
|
||||||
|
val stdOptions = this as StandardJavadocDocletOptions
|
||||||
|
stdOptions.addBooleanOption("Xdoclint:all,-missing", true)
|
||||||
|
stdOptions.links("https://docs.oracle.com/en/java/javase/17/docs/api/")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.test {
|
||||||
|
finalizedBy("jacocoTestReport")
|
||||||
|
|
||||||
|
useJUnitPlatform()
|
||||||
|
testLogging {
|
||||||
|
events("skipped", "failed")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.withType(JacocoReport::class.java).configureEach {
|
||||||
|
reports.xml.required.set(true)
|
||||||
|
reports.html.required.set(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
project.plugins.withType(CCTweakedPlugin::class.java) {
|
||||||
|
// Set up jacoco to read from /all/ our source directories.
|
||||||
|
val cct = project.extensions.getByType<CCTweakedExtension>()
|
||||||
|
project.tasks.named("jacocoTestReport", JacocoReport::class.java) {
|
||||||
|
for (ref in cct.sourceSets.get()) sourceDirectories.from(ref.allSource.sourceDirectories)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
spotless {
|
||||||
|
encoding = StandardCharsets.UTF_8
|
||||||
|
lineEndings = LineEnding.UNIX
|
||||||
|
|
||||||
|
fun FormatExtension.defaults() {
|
||||||
|
endWithNewline()
|
||||||
|
trimTrailingWhitespace()
|
||||||
|
indentWithSpaces(4)
|
||||||
|
}
|
||||||
|
|
||||||
|
val licenser = LicenseHeader.create(
|
||||||
|
api = rootProject.file("config/license/api.txt"),
|
||||||
|
main = rootProject.file("config/license/main.txt"),
|
||||||
|
)
|
||||||
|
|
||||||
|
java {
|
||||||
|
defaults()
|
||||||
|
addStep(licenser)
|
||||||
|
removeUnusedImports()
|
||||||
|
}
|
||||||
|
|
||||||
|
val ktlintConfig = mapOf(
|
||||||
|
"disabled_rules" to "no-wildcard-imports",
|
||||||
|
"ij_kotlin_allow_trailing_comma" to "true",
|
||||||
|
"ij_kotlin_allow_trailing_comma_on_call_site" to "true",
|
||||||
|
)
|
||||||
|
|
||||||
|
kotlinGradle {
|
||||||
|
defaults()
|
||||||
|
ktlint().editorConfigOverride(ktlintConfig)
|
||||||
|
}
|
||||||
|
|
||||||
|
kotlin {
|
||||||
|
defaults()
|
||||||
|
ktlint().editorConfigOverride(ktlintConfig)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
idea.module {
|
||||||
|
excludeDirs.addAll(project.files("run", "out", "logs").files)
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
// TODO: Submit a patch to Forge to support ProjectRootManager.
|
||||||
|
inheritOutputDirs = true
|
||||||
|
}
|
@@ -0,0 +1,21 @@
|
|||||||
|
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()}"
|
||||||
|
}
|
||||||
|
}
|
45
buildSrc/src/main/kotlin/cc-tweaked.publishing.gradle.kts
Normal file
45
buildSrc/src/main/kotlin/cc-tweaked.publishing.gradle.kts
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
import org.gradle.kotlin.dsl.`maven-publish`
|
||||||
|
|
||||||
|
plugins {
|
||||||
|
`java-library`
|
||||||
|
`maven-publish`
|
||||||
|
}
|
||||||
|
|
||||||
|
publishing {
|
||||||
|
publications {
|
||||||
|
register<MavenPublication>("maven") {
|
||||||
|
artifactId = base.archivesName.get()
|
||||||
|
from(components["java"])
|
||||||
|
|
||||||
|
pom {
|
||||||
|
name.set("CC: Tweaked")
|
||||||
|
description.set("CC: Tweaked is a fork of ComputerCraft, adding programmable computers, turtles and more to Minecraft.")
|
||||||
|
url.set("https://github.com/cc-tweaked/CC-Tweaked")
|
||||||
|
|
||||||
|
scm {
|
||||||
|
url.set("https://github.com/cc-tweaked/CC-Tweaked.git")
|
||||||
|
}
|
||||||
|
|
||||||
|
issueManagement {
|
||||||
|
system.set("github")
|
||||||
|
url.set("https://github.com/cc-tweaked/CC-Tweaked/issues")
|
||||||
|
}
|
||||||
|
|
||||||
|
licenses {
|
||||||
|
license {
|
||||||
|
name.set("ComputerCraft Public License, Version 1.0")
|
||||||
|
url.set("https://github.com/cc-tweaked/CC-Tweaked/blob/HEAD/LICENSE")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
maven("https://squiddev.cc/maven") {
|
||||||
|
name = "SquidDev"
|
||||||
|
|
||||||
|
credentials(PasswordCredentials::class)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
31
buildSrc/src/main/kotlin/cc-tweaked.vanilla.gradle.kts
Normal file
31
buildSrc/src/main/kotlin/cc-tweaked.vanilla.gradle.kts
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
/** Default configuration for non-modloader-specific Minecraft projects. */
|
||||||
|
|
||||||
|
import cc.tweaked.gradle.CCTweakedExtension
|
||||||
|
import cc.tweaked.gradle.CCTweakedPlugin
|
||||||
|
import cc.tweaked.gradle.MinecraftConfigurations
|
||||||
|
|
||||||
|
plugins {
|
||||||
|
id("cc-tweaked.java-convention")
|
||||||
|
id("org.spongepowered.gradle.vanilla")
|
||||||
|
}
|
||||||
|
|
||||||
|
plugins.apply(CCTweakedPlugin::class.java)
|
||||||
|
|
||||||
|
val mcVersion: String by extra
|
||||||
|
|
||||||
|
minecraft {
|
||||||
|
version(mcVersion)
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
val libs = project.extensions.getByType<VersionCatalogsExtension>().named("libs")
|
||||||
|
|
||||||
|
// Depend on error prone annotations to silence a lot of compile warnings.
|
||||||
|
compileOnlyApi(libs.findLibrary("errorProne.annotations").get())
|
||||||
|
}
|
||||||
|
|
||||||
|
MinecraftConfigurations.setup(project)
|
||||||
|
|
||||||
|
extensions.configure(CCTweakedExtension::class.java) {
|
||||||
|
linters(minecraft = true, loader = null)
|
||||||
|
}
|
268
buildSrc/src/main/kotlin/cc/tweaked/gradle/CCTweakedExtension.kt
Normal file
268
buildSrc/src/main/kotlin/cc/tweaked/gradle/CCTweakedExtension.kt
Normal file
@@ -0,0 +1,268 @@
|
|||||||
|
package cc.tweaked.gradle
|
||||||
|
|
||||||
|
import net.ltgt.gradle.errorprone.CheckSeverity
|
||||||
|
import net.ltgt.gradle.errorprone.errorprone
|
||||||
|
import org.gradle.api.GradleException
|
||||||
|
import org.gradle.api.NamedDomainObjectProvider
|
||||||
|
import org.gradle.api.Project
|
||||||
|
import org.gradle.api.Task
|
||||||
|
import org.gradle.api.attributes.TestSuiteType
|
||||||
|
import org.gradle.api.file.FileSystemOperations
|
||||||
|
import org.gradle.api.plugins.JavaPluginExtension
|
||||||
|
import org.gradle.api.provider.Provider
|
||||||
|
import org.gradle.api.provider.SetProperty
|
||||||
|
import org.gradle.api.reporting.ReportingExtension
|
||||||
|
import org.gradle.api.tasks.SourceSet
|
||||||
|
import org.gradle.api.tasks.bundling.Jar
|
||||||
|
import org.gradle.api.tasks.compile.JavaCompile
|
||||||
|
import org.gradle.api.tasks.javadoc.Javadoc
|
||||||
|
import org.gradle.configurationcache.extensions.capitalized
|
||||||
|
import org.gradle.language.base.plugins.LifecycleBasePlugin
|
||||||
|
import org.gradle.language.jvm.tasks.ProcessResources
|
||||||
|
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.JacocoTaskExtension
|
||||||
|
import org.gradle.testing.jacoco.tasks.JacocoReport
|
||||||
|
import org.jetbrains.kotlin.gradle.dsl.KotlinProjectExtension
|
||||||
|
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||||
|
import java.io.File
|
||||||
|
import java.io.IOException
|
||||||
|
import java.net.URI
|
||||||
|
import java.net.URL
|
||||||
|
import java.util.regex.Pattern
|
||||||
|
|
||||||
|
abstract class CCTweakedExtension(
|
||||||
|
private val project: Project,
|
||||||
|
private val fs: FileSystemOperations,
|
||||||
|
) {
|
||||||
|
/** Get the hash of the latest git commit. */
|
||||||
|
val gitHash: Provider<String> = gitProvider(project, "<no git hash>") {
|
||||||
|
ProcessHelpers.captureOut("git", "-C", project.rootProject.projectDir.absolutePath, "rev-parse", "HEAD").trim()
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get the current git branch. */
|
||||||
|
val gitBranch: Provider<String> = gitProvider(project, "<no git branch>") {
|
||||||
|
ProcessHelpers.captureOut("git", "-C", project.rootProject.projectDir.absolutePath, "rev-parse", "--abbrev-ref", "HEAD")
|
||||||
|
.trim()
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get a list of all contributors to the project. */
|
||||||
|
val gitContributors: Provider<List<String>> = gitProvider(project, listOf()) {
|
||||||
|
ProcessHelpers.captureLines(
|
||||||
|
"git", "-C", project.rootProject.projectDir.absolutePath, "shortlog", "-ns",
|
||||||
|
"--group=author", "--group=trailer:co-authored-by", "HEAD",
|
||||||
|
)
|
||||||
|
.asSequence()
|
||||||
|
.map {
|
||||||
|
val matcher = COMMIT_COUNTS.matcher(it)
|
||||||
|
matcher.find()
|
||||||
|
matcher.group(1)
|
||||||
|
}
|
||||||
|
.filter { !IGNORED_USERS.contains(it) }
|
||||||
|
.toList()
|
||||||
|
.sortedWith(String.CASE_INSENSITIVE_ORDER)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* References to other sources
|
||||||
|
*/
|
||||||
|
val sourceDirectories: SetProperty<SourceSetReference> = project.objects.setProperty(SourceSetReference::class.java)
|
||||||
|
|
||||||
|
/** All source sets referenced by this project. */
|
||||||
|
val sourceSets = sourceDirectories.map { x -> x.map { it.sourceSet } }
|
||||||
|
|
||||||
|
init {
|
||||||
|
sourceDirectories.finalizeValueOnRead()
|
||||||
|
project.afterEvaluate { sourceDirectories.disallowChanges() }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mark this project as consuming another project. Its [sourceDirectories] are added, allowing easier configuration
|
||||||
|
* of run configurations and other tasks which consume sources/classes.
|
||||||
|
*/
|
||||||
|
fun externalSources(project: Project) {
|
||||||
|
val otherCct = project.extensions.getByType(CCTweakedExtension::class.java)
|
||||||
|
for (sourceSet in otherCct.sourceDirectories.get()) {
|
||||||
|
sourceDirectories.add(SourceSetReference(sourceSet.sourceSet, classes = sourceSet.classes, external = true))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a dependency on another project such that its sources and compiles are processed with this one.
|
||||||
|
*
|
||||||
|
* This is used when importing a common library into a loader-specific one, as we want to compile sources using
|
||||||
|
* the loader-specific sources.
|
||||||
|
*/
|
||||||
|
fun inlineProject(path: String) {
|
||||||
|
val otherProject = project.evaluationDependsOn(path)
|
||||||
|
val otherJava = otherProject.extensions.getByType(JavaPluginExtension::class.java)
|
||||||
|
val main = otherJava.sourceSets.getByName("main")
|
||||||
|
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.
|
||||||
|
extendSourceSet(otherProject, main)
|
||||||
|
extendSourceSet(otherProject, client)
|
||||||
|
if (testMod != null) extendSourceSet(otherProject, testMod)
|
||||||
|
if (testFixtures != null) extendSourceSet(otherProject, testFixtures)
|
||||||
|
|
||||||
|
// 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.sourcesJarTaskName, Jar::class.java) { from(main.allSource, client.allSource) }
|
||||||
|
sourceDirectories.addAll(SourceSetReference.inline(main), SourceSetReference.inline(client))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extend a source set with files from another project.
|
||||||
|
*
|
||||||
|
* This actually extends the original compile tasks, as extending the source sets does not play well with IDEs.
|
||||||
|
*/
|
||||||
|
private fun extendSourceSet(otherProject: Project, sourceSet: SourceSet) {
|
||||||
|
project.tasks.named(sourceSet.compileJavaTaskName, JavaCompile::class.java) {
|
||||||
|
dependsOn(otherProject.tasks.named(sourceSet.compileJavaTaskName)) // Avoid duplicate compile errors
|
||||||
|
source(sourceSet.allJava)
|
||||||
|
}
|
||||||
|
|
||||||
|
project.tasks.named(sourceSet.processResourcesTaskName, ProcessResources::class.java) {
|
||||||
|
from(sourceSet.resources)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Also try to depend on Kotlin if it exists
|
||||||
|
val kotlin = otherProject.extensions.findByType(KotlinProjectExtension::class.java)
|
||||||
|
if (kotlin != null) {
|
||||||
|
val compileKotlin = sourceSet.getCompileTaskName("kotlin")
|
||||||
|
project.tasks.named(compileKotlin, KotlinCompile::class.java) {
|
||||||
|
dependsOn(otherProject.tasks.named(compileKotlin))
|
||||||
|
source(kotlin.sourceSets.getByName(sourceSet.name).kotlin)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we're doing an IDE sync, add a fake dependency to ensure it's on the classpath.
|
||||||
|
if (isIdeSync) project.dependencies.add(sourceSet.apiConfigurationName, sourceSet.output)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun linters(@Suppress("UNUSED_PARAMETER") vararg unused: UseNamedArgs, minecraft: Boolean, loader: String?) {
|
||||||
|
val java = project.extensions.getByType(JavaPluginExtension::class.java)
|
||||||
|
val sourceSets = java.sourceSets
|
||||||
|
|
||||||
|
project.dependencies.run { add("errorprone", project(mapOf("path" to ":lints"))) }
|
||||||
|
sourceSets.all {
|
||||||
|
val name = name
|
||||||
|
project.tasks.named(compileJavaTaskName, JavaCompile::class.java) {
|
||||||
|
options.errorprone {
|
||||||
|
// Only the main source set should run the side checker
|
||||||
|
check("SideChecker", if (minecraft && name == "main") CheckSeverity.DEFAULT else CheckSeverity.OFF)
|
||||||
|
|
||||||
|
// The MissingLoaderOverride check superseeds the MissingOverride one, so disable that.
|
||||||
|
if (loader != null) {
|
||||||
|
check("MissingOverride", CheckSeverity.OFF)
|
||||||
|
option("ModLoader", loader)
|
||||||
|
} else {
|
||||||
|
check("LoaderOverride", CheckSeverity.OFF)
|
||||||
|
check("MissingLoaderOverride", CheckSeverity.OFF)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun <T> jacoco(task: NamedDomainObjectProvider<T>) where T : Task, T : JavaForkOptions {
|
||||||
|
val classDump = project.buildDir.resolve("jacocoClassDump/${task.name}")
|
||||||
|
val reportTaskName = "jacoco${task.name.capitalized()}Report"
|
||||||
|
|
||||||
|
val jacoco = project.extensions.getByType(JacocoPluginExtension::class.java)
|
||||||
|
task.configure {
|
||||||
|
finalizedBy(reportTaskName)
|
||||||
|
|
||||||
|
doFirst("Clean class dump directory") { fs.delete { delete(classDump) } }
|
||||||
|
|
||||||
|
jacoco.applyTo(this)
|
||||||
|
extensions.configure(JacocoTaskExtension::class.java) {
|
||||||
|
includes = listOf("dan200.computercraft.*")
|
||||||
|
classDumpDir = classDump
|
||||||
|
|
||||||
|
// Older versions of modlauncher don't include a protection domain (and thus no code
|
||||||
|
// source). Jacoco skips such classes by default, so we need to explicitly include them.
|
||||||
|
isIncludeNoLocationClasses = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
project.tasks.register(reportTaskName, JacocoReport::class.java) {
|
||||||
|
group = LifecycleBasePlugin.VERIFICATION_GROUP
|
||||||
|
description = "Generates code coverage report for the ${task.name} task."
|
||||||
|
|
||||||
|
executionData(task.get())
|
||||||
|
classDirectories.from(classDump)
|
||||||
|
|
||||||
|
// Don't want to use sourceSets(...) here as we have a custom class directory.
|
||||||
|
for (ref in sourceSets.get()) sourceDirectories.from(ref.allSource.sourceDirectories)
|
||||||
|
}
|
||||||
|
|
||||||
|
project.extensions.configure(ReportingExtension::class.java) {
|
||||||
|
reports.register("${task.name}CodeCoverageReport", JacocoCoverageReport::class.java) {
|
||||||
|
testType.set(TestSuiteType.INTEGRATION_TEST)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Download a file by creating a dummy Ivy repository.
|
||||||
|
*
|
||||||
|
* This should only be used for one-off downloads. Using a more conventional Ivy or Maven repository is preferred
|
||||||
|
* where possible.
|
||||||
|
*/
|
||||||
|
fun downloadFile(label: String, url: String): File {
|
||||||
|
val url = URL(url)
|
||||||
|
val path = File(url.path)
|
||||||
|
|
||||||
|
project.repositories.ivy {
|
||||||
|
name = label
|
||||||
|
setUrl(URI(url.protocol, url.userInfo, url.host, url.port, path.parent, null, null))
|
||||||
|
patternLayout {
|
||||||
|
artifact("[artifact].[ext]")
|
||||||
|
}
|
||||||
|
metadataSources {
|
||||||
|
artifact()
|
||||||
|
}
|
||||||
|
content {
|
||||||
|
includeModule("cc.tweaked.internal", path.nameWithoutExtension)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return project.configurations.detachedConfiguration(
|
||||||
|
project.dependencies.create(
|
||||||
|
mapOf(
|
||||||
|
"group" to "cc.tweaked.internal",
|
||||||
|
"name" to path.nameWithoutExtension,
|
||||||
|
"ext" to path.extension,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
).resolve().single()
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val COMMIT_COUNTS = Pattern.compile("""^\s*[0-9]+\s+(.*)$""")
|
||||||
|
private val IGNORED_USERS = setOf(
|
||||||
|
"GitHub", "Daniel Ratcliffe", "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
|
||||||
|
get() = java.lang.Boolean.parseBoolean(System.getProperty("idea.sync.active", "false"))
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,25 @@
|
|||||||
|
package cc.tweaked.gradle
|
||||||
|
|
||||||
|
import org.gradle.api.Plugin
|
||||||
|
import org.gradle.api.Project
|
||||||
|
import org.gradle.api.plugins.JavaPlugin
|
||||||
|
import org.gradle.api.plugins.JavaPluginExtension
|
||||||
|
import org.gradle.jvm.toolchain.JavaLanguageVersion
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configures projects to match a shared configuration.
|
||||||
|
*/
|
||||||
|
class CCTweakedPlugin : Plugin<Project> {
|
||||||
|
override fun apply(project: Project) {
|
||||||
|
val cct = project.extensions.create("cct", CCTweakedExtension::class.java)
|
||||||
|
|
||||||
|
project.plugins.withType(JavaPlugin::class.java) {
|
||||||
|
val sourceSets = project.extensions.getByType(JavaPluginExtension::class.java).sourceSets
|
||||||
|
cct.sourceDirectories.add(SourceSetReference.internal(sourceSets.getByName("main")))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
val JAVA_VERSION = JavaLanguageVersion.of(17)
|
||||||
|
}
|
||||||
|
}
|
64
buildSrc/src/main/kotlin/cc/tweaked/gradle/CheckChangelog.kt
Normal file
64
buildSrc/src/main/kotlin/cc/tweaked/gradle/CheckChangelog.kt
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
package cc.tweaked.gradle
|
||||||
|
|
||||||
|
import org.gradle.api.DefaultTask
|
||||||
|
import org.gradle.api.GradleException
|
||||||
|
import org.gradle.api.file.RegularFileProperty
|
||||||
|
import org.gradle.api.provider.Property
|
||||||
|
import org.gradle.api.tasks.*
|
||||||
|
import org.gradle.language.base.plugins.LifecycleBasePlugin
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks the `changelog.md` and `whatsnew.md` files are well-formed.
|
||||||
|
*/
|
||||||
|
@CacheableTask
|
||||||
|
abstract class CheckChangelog : DefaultTask() {
|
||||||
|
init {
|
||||||
|
group = LifecycleBasePlugin.VERIFICATION_GROUP
|
||||||
|
description = "Verifies the changelog and whatsnew file are consistent."
|
||||||
|
}
|
||||||
|
|
||||||
|
@get:Input
|
||||||
|
abstract val version: Property<String>
|
||||||
|
|
||||||
|
@get:InputFile
|
||||||
|
@get:PathSensitive(PathSensitivity.NONE)
|
||||||
|
abstract val changelog: RegularFileProperty
|
||||||
|
|
||||||
|
@get:InputFile
|
||||||
|
@get:PathSensitive(PathSensitivity.NONE)
|
||||||
|
abstract val whatsNew: RegularFileProperty
|
||||||
|
|
||||||
|
@TaskAction
|
||||||
|
fun check() {
|
||||||
|
val version = version.get()
|
||||||
|
|
||||||
|
var ok = true
|
||||||
|
|
||||||
|
// Check we're targeting the current version
|
||||||
|
var whatsNew = whatsNew.get().asFile.readLines()
|
||||||
|
if (whatsNew[0] != "New features in CC: Tweaked $version") {
|
||||||
|
ok = false
|
||||||
|
logger.error("Expected `whatsnew.md' to target $version.")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check "read more" exists and trim it
|
||||||
|
val idx = whatsNew.indexOfFirst { it == "Type \"help changelog\" to see the full version history." }
|
||||||
|
if (idx == -1) {
|
||||||
|
ok = false
|
||||||
|
logger.error("Must mention the changelog in whatsnew.md")
|
||||||
|
} else {
|
||||||
|
whatsNew = whatsNew.slice(0 until idx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check whatsnew and changelog match.
|
||||||
|
val expectedChangelog = sequenceOf("# ${whatsNew[0]}") + whatsNew.slice(1 until whatsNew.size).asSequence()
|
||||||
|
val changelog = changelog.get().asFile.readLines()
|
||||||
|
val mismatch = expectedChangelog.zip(changelog.asSequence()).filter { (a, b) -> a != b }.firstOrNull()
|
||||||
|
if (mismatch != null) {
|
||||||
|
ok = false
|
||||||
|
logger.error("whatsnew and changelog are not in sync")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ok) throw GradleException("Could not check release")
|
||||||
|
}
|
||||||
|
}
|
72
buildSrc/src/main/kotlin/cc/tweaked/gradle/CheckLicense.kt
Normal file
72
buildSrc/src/main/kotlin/cc/tweaked/gradle/CheckLicense.kt
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
package cc.tweaked.gradle
|
||||||
|
|
||||||
|
import com.diffplug.spotless.FormatterFunc
|
||||||
|
import com.diffplug.spotless.FormatterStep
|
||||||
|
import com.diffplug.spotless.generic.LicenseHeaderStep
|
||||||
|
import java.io.File
|
||||||
|
import java.io.Serializable
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Similar to [LicenseHeaderStep], but supports multiple licenses.
|
||||||
|
*/
|
||||||
|
object LicenseHeader {
|
||||||
|
/**
|
||||||
|
* The current year to use in templates. Intentionally not dynamic to avoid failing the build.
|
||||||
|
*/
|
||||||
|
private const val YEAR = 2022
|
||||||
|
|
||||||
|
private val COMMENT = Regex("""^/\*(.*?)\*/\n?""", RegexOption.DOT_MATCHES_ALL)
|
||||||
|
|
||||||
|
fun create(api: File, main: File): FormatterStep = FormatterStep.createLazy(
|
||||||
|
"license",
|
||||||
|
{ Licenses(getTemplateText(api), getTemplateText(main)) },
|
||||||
|
{ state -> FormatterFunc.NeedsFile { contents, file -> formatFile(state, contents, file) } },
|
||||||
|
)
|
||||||
|
|
||||||
|
private fun getTemplateText(file: File): String =
|
||||||
|
file.readText().trim().replace("\${year}", "$YEAR")
|
||||||
|
|
||||||
|
private fun formatFile(licenses: Licenses, contents: String, file: File): String {
|
||||||
|
val license = getLicense(contents)
|
||||||
|
val expectedLicense = getExpectedLicense(licenses, file.parentFile)
|
||||||
|
|
||||||
|
return when {
|
||||||
|
license == null -> setLicense(expectedLicense, contents)
|
||||||
|
license.second != expectedLicense -> setLicense(expectedLicense, contents, license.first)
|
||||||
|
else -> contents
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getExpectedLicense(licenses: Licenses, root: File): String {
|
||||||
|
var file: File? = root
|
||||||
|
while (file != null) {
|
||||||
|
if (file.name == "api" && file.parentFile?.name == "computercraft") return licenses.api
|
||||||
|
file = file.parentFile
|
||||||
|
}
|
||||||
|
return licenses.main
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getLicense(contents: String): Pair<Int, String>? {
|
||||||
|
val match = COMMENT.find(contents) ?: return null
|
||||||
|
val license = match.groups[1]!!.value
|
||||||
|
.trim().lineSequence()
|
||||||
|
.map { it.trimStart(' ', '*') }
|
||||||
|
.joinToString("\n")
|
||||||
|
return Pair(match.range.last + 1, license)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setLicense(license: String, contents: String, start: Int = 0): String {
|
||||||
|
val out = StringBuilder()
|
||||||
|
out.append("/*\n")
|
||||||
|
for (line in license.lineSequence()) out.append(" * ").append(line).append("\n")
|
||||||
|
out.append(" */\n")
|
||||||
|
out.append(contents, start, contents.length)
|
||||||
|
return out.toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
private data class Licenses(val api: String, val main: String) : Serializable {
|
||||||
|
companion object {
|
||||||
|
private const val serialVersionUID: Long = 7741106448372435662L
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
11
buildSrc/src/main/kotlin/cc/tweaked/gradle/ExecTasks.kt
Normal file
11
buildSrc/src/main/kotlin/cc/tweaked/gradle/ExecTasks.kt
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
package cc.tweaked.gradle
|
||||||
|
|
||||||
|
import org.gradle.api.provider.Property
|
||||||
|
import org.gradle.api.tasks.AbstractExecTask
|
||||||
|
import org.gradle.api.tasks.OutputDirectory
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
abstract class ExecToDir : AbstractExecTask<ExecToDir>(ExecToDir::class.java) {
|
||||||
|
@get:OutputDirectory
|
||||||
|
abstract val output: Property<File>
|
||||||
|
}
|
117
buildSrc/src/main/kotlin/cc/tweaked/gradle/Extensions.kt
Normal file
117
buildSrc/src/main/kotlin/cc/tweaked/gradle/Extensions.kt
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
package cc.tweaked.gradle
|
||||||
|
|
||||||
|
import org.gradle.api.artifacts.dsl.DependencyHandler
|
||||||
|
import org.gradle.api.tasks.JavaExec
|
||||||
|
import org.gradle.process.BaseExecSpec
|
||||||
|
import org.gradle.process.JavaExecSpec
|
||||||
|
import org.gradle.process.ProcessForkOptions
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add an annotation processor to all source sets.
|
||||||
|
*/
|
||||||
|
fun DependencyHandler.annotationProcessorEverywhere(dep: Any) {
|
||||||
|
add("compileOnly", dep)
|
||||||
|
add("annotationProcessor", dep)
|
||||||
|
|
||||||
|
add("clientCompileOnly", dep)
|
||||||
|
add("clientAnnotationProcessor", dep)
|
||||||
|
|
||||||
|
add("testCompileOnly", dep)
|
||||||
|
add("testAnnotationProcessor", dep)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A version of [JavaExecSpec.copyTo] which copies *all* properties.
|
||||||
|
*/
|
||||||
|
fun JavaExec.copyToFull(spec: JavaExec) {
|
||||||
|
copyTo(spec)
|
||||||
|
|
||||||
|
// Additional Java options
|
||||||
|
spec.jvmArgs = jvmArgs // Fabric overrides getJvmArgs so copyTo doesn't do the right thing.
|
||||||
|
spec.args = args
|
||||||
|
spec.argumentProviders.addAll(argumentProviders)
|
||||||
|
spec.mainClass.set(mainClass)
|
||||||
|
spec.classpath = classpath
|
||||||
|
spec.javaLauncher.set(javaLauncher)
|
||||||
|
if (executable != null) spec.setExecutable(executable!!)
|
||||||
|
|
||||||
|
// Additional ExecSpec options
|
||||||
|
copyToExec(spec)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copy additional [BaseExecSpec] options which aren't handled by [ProcessForkOptions.copyTo].
|
||||||
|
*/
|
||||||
|
fun BaseExecSpec.copyToExec(spec: BaseExecSpec) {
|
||||||
|
spec.isIgnoreExitValue = isIgnoreExitValue
|
||||||
|
if (standardInput != null) spec.standardInput = standardInput
|
||||||
|
if (standardOutput != null) spec.standardOutput = standardOutput
|
||||||
|
if (errorOutput != null) spec.errorOutput = errorOutput
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An alternative to [Nothing] with a more descriptive name. Use to enforce calling a function with named arguments:
|
||||||
|
*
|
||||||
|
* ```kotlin
|
||||||
|
* fun f(vararg unused: UseNamedArgs, arg1: Int, arg2: Int) {
|
||||||
|
* // ...
|
||||||
|
* }
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
class UseNamedArgs private constructor()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An [AutoCloseable] implementation which can be used to combine other [AutoCloseable] instances.
|
||||||
|
*
|
||||||
|
* Values which implement [AutoCloseable] can be dynamically registered with [CloseScope.add]. When the scope is closed,
|
||||||
|
* each value is closed in the opposite order.
|
||||||
|
*
|
||||||
|
* This is largely intended for cases where it's not appropriate to nest [AutoCloseable.use], for instance when nested
|
||||||
|
* would be too deep.
|
||||||
|
*/
|
||||||
|
class CloseScope : AutoCloseable {
|
||||||
|
private val toClose = ArrayDeque<AutoCloseable>()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a value to be closed when this scope is closed.
|
||||||
|
*/
|
||||||
|
public fun add(value: AutoCloseable) {
|
||||||
|
toClose.addLast(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun close() {
|
||||||
|
close(null)
|
||||||
|
}
|
||||||
|
|
||||||
|
@PublishedApi
|
||||||
|
internal fun close(baseException: Throwable?) {
|
||||||
|
var exception = baseException
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
var toClose = toClose.removeLastOrNull() ?: break
|
||||||
|
try {
|
||||||
|
toClose.close()
|
||||||
|
} catch (e: Throwable) {
|
||||||
|
if (exception == null) {
|
||||||
|
exception = e
|
||||||
|
} else {
|
||||||
|
exception.addSuppressed(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (exception != null) throw exception
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun <R> use(block: (CloseScope) -> R): R {
|
||||||
|
var exception: Throwable? = null
|
||||||
|
try {
|
||||||
|
return block(this)
|
||||||
|
} catch (e: Throwable) {
|
||||||
|
exception = e
|
||||||
|
throw e
|
||||||
|
} finally {
|
||||||
|
close(exception)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,170 @@
|
|||||||
|
package cc.tweaked.gradle
|
||||||
|
|
||||||
|
import org.gradle.api.Project
|
||||||
|
import org.gradle.api.logging.Logging
|
||||||
|
import org.w3c.dom.Attr
|
||||||
|
import org.w3c.dom.Document
|
||||||
|
import org.w3c.dom.Node
|
||||||
|
import org.xml.sax.InputSource
|
||||||
|
import java.nio.file.Files
|
||||||
|
import java.nio.file.Path
|
||||||
|
import javax.xml.parsers.DocumentBuilderFactory
|
||||||
|
import javax.xml.transform.TransformerFactory
|
||||||
|
import javax.xml.transform.dom.DOMSource
|
||||||
|
import javax.xml.transform.stream.StreamResult
|
||||||
|
import javax.xml.xpath.XPathConstants
|
||||||
|
import javax.xml.xpath.XPathFactory
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Patches up run configurations from ForgeGradle and Loom.
|
||||||
|
*
|
||||||
|
* 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.minecraftforge.gradle.common.util.runs.IntellijRunGenerator
|
||||||
|
*/
|
||||||
|
internal class IdeaRunConfigurations(project: Project) {
|
||||||
|
private val rootProject = project.rootProject
|
||||||
|
|
||||||
|
private val documentBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder()
|
||||||
|
private val xpath = XPathFactory.newInstance().newXPath()
|
||||||
|
private val writer = TransformerFactory.newInstance().newTransformer()
|
||||||
|
|
||||||
|
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) {
|
||||||
|
val runConfigDir = ideaDir.resolve("runConfigurations")
|
||||||
|
if (!runConfigDir.isDirectory) return
|
||||||
|
|
||||||
|
Files.list(runConfigDir.toPath()).use {
|
||||||
|
for (configuration in it) {
|
||||||
|
val filename = configuration.fileName.toString();
|
||||||
|
when {
|
||||||
|
filename.endsWith("_fabric.xml") -> patchFabric(configuration)
|
||||||
|
filename.startsWith("forge_") && filename.endsWith(".xml") -> patchForge(configuration)
|
||||||
|
else -> {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun patchFabric(path: Path) = withXml(path) {
|
||||||
|
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) {
|
||||||
|
val node = this@IdeaRunConfigurations.xpath.evaluate(xpath, document, XPathConstants.NODE) as Node?
|
||||||
|
if (node == null) {
|
||||||
|
LOGGER.error("[{}] Cannot find {}", path.fileName, xpath)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val attr = node.attributes.getNamedItem(attribute) as Attr? ?: document.createAttribute(attribute)
|
||||||
|
val oldValue = attr.value
|
||||||
|
attr.value = value(attr.value)
|
||||||
|
node.attributes.setNamedItem(attr)
|
||||||
|
|
||||||
|
if (oldValue != attr.value) {
|
||||||
|
LOGGER.info("[{}] Setting {}@{}:\n Old: {}\n New: {}", path.fileName, xpath, attribute, oldValue, attr.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun withXml(path: Path, run: LocatedDocument.() -> Unit) {
|
||||||
|
val doc = Files.newBufferedReader(path).use { documentBuilder.parse(InputSource(it)) }
|
||||||
|
run(LocatedDocument(path, doc))
|
||||||
|
Files.newBufferedWriter(path).use { writer.transform(DOMSource(doc), StreamResult(it)) }
|
||||||
|
}
|
||||||
|
|
||||||
|
private class LocatedDocument(val path: Path, val document: Document)
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val LOGGER = Logging.getLogger(IdeaRunConfigurations::class.java)
|
||||||
|
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",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
123
buildSrc/src/main/kotlin/cc/tweaked/gradle/Illuaminate.kt
Normal file
123
buildSrc/src/main/kotlin/cc/tweaked/gradle/Illuaminate.kt
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
package cc.tweaked.gradle
|
||||||
|
|
||||||
|
import org.gradle.api.DefaultTask
|
||||||
|
import org.gradle.api.Plugin
|
||||||
|
import org.gradle.api.Project
|
||||||
|
import org.gradle.api.Task
|
||||||
|
import org.gradle.api.artifacts.Dependency
|
||||||
|
import org.gradle.api.provider.Property
|
||||||
|
import org.gradle.api.provider.Provider
|
||||||
|
import org.gradle.api.tasks.AbstractExecTask
|
||||||
|
import org.gradle.api.tasks.Input
|
||||||
|
import org.gradle.api.tasks.TaskAction
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
abstract class IlluaminateExtension {
|
||||||
|
/** The version of illuaminate to use. */
|
||||||
|
abstract val version: Property<String>
|
||||||
|
|
||||||
|
/** The path to illuaminate. If not given, illuaminate will be downloaded automatically. */
|
||||||
|
abstract val file: Property<File>
|
||||||
|
}
|
||||||
|
|
||||||
|
class IlluaminatePlugin : Plugin<Project> {
|
||||||
|
override fun apply(project: Project) {
|
||||||
|
val extension = project.extensions.create("illuaminate", IlluaminateExtension::class.java)
|
||||||
|
extension.file.convention(setupDependency(project, extension.version))
|
||||||
|
|
||||||
|
project.tasks.register(SetupIlluaminate.NAME, SetupIlluaminate::class.java) {
|
||||||
|
file.set(extension.file.map { it.absolutePath })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Set up a repository for illuaminate and download our binary from it. */
|
||||||
|
private fun setupDependency(project: Project, version: Provider<String>): Provider<File> {
|
||||||
|
project.repositories.ivy {
|
||||||
|
name = "Illuaminate"
|
||||||
|
setUrl("https://squiddev.cc/illuaminate/bin/")
|
||||||
|
patternLayout {
|
||||||
|
artifact("[revision]/[artifact]-[ext]")
|
||||||
|
}
|
||||||
|
metadataSources {
|
||||||
|
artifact()
|
||||||
|
}
|
||||||
|
content {
|
||||||
|
includeModule("cc.squiddev", "illuaminate")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return version.map {
|
||||||
|
val dep = illuaminateArtifact(project, it)
|
||||||
|
val configuration = project.configurations.detachedConfiguration(dep)
|
||||||
|
configuration.isTransitive = false
|
||||||
|
configuration.resolve().single()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Define a dependency for illuaminate from a version number and the current operating system. */
|
||||||
|
private fun illuaminateArtifact(project: Project, version: String): Dependency {
|
||||||
|
val osName = System.getProperty("os.name").toLowerCase()
|
||||||
|
val (os, suffix) = when {
|
||||||
|
osName.contains("windows") -> Pair("windows", ".exe")
|
||||||
|
osName.contains("mac os") || osName.contains("darwin") -> Pair("macos", "")
|
||||||
|
osName.contains("linux") -> Pair("linux", "")
|
||||||
|
else -> error("Unsupported OS $osName for illuaminate")
|
||||||
|
}
|
||||||
|
|
||||||
|
val osArch = System.getProperty("os.arch").toLowerCase()
|
||||||
|
val arch = when {
|
||||||
|
// On macOS the x86_64 binary will work for both ARM and Intel Macs through Rosetta.
|
||||||
|
os == "macos" -> "x86_64"
|
||||||
|
osArch == "arm" || osArch.startsWith("aarch") -> error("Unsupported architecture '$osArch' for illuaminate")
|
||||||
|
osArch.contains("64") -> "x86_64"
|
||||||
|
else -> error("Unsupported architecture '$osArch' for illuaminate")
|
||||||
|
}
|
||||||
|
|
||||||
|
return project.dependencies.create(
|
||||||
|
mapOf(
|
||||||
|
"group" to "cc.squiddev",
|
||||||
|
"name" to "illuaminate",
|
||||||
|
"version" to version,
|
||||||
|
"ext" to "$os-$arch$suffix",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val Task.illuaminatePath: String? // "?" needed to avoid overload ambiguity in setExecutable below.
|
||||||
|
get() = project.extensions.getByType(IlluaminateExtension::class.java).file.get().absolutePath
|
||||||
|
|
||||||
|
/** Prepares illuaminate for being run. This simply requests the dependency and then marks it as executable. */
|
||||||
|
abstract class SetupIlluaminate : DefaultTask() {
|
||||||
|
@get:Input
|
||||||
|
abstract val file: Property<String>
|
||||||
|
|
||||||
|
@TaskAction
|
||||||
|
fun setExecutable() {
|
||||||
|
val file = File(this.file.get())
|
||||||
|
if (file.canExecute()) {
|
||||||
|
didWork = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
file.setExecutable(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val NAME: String = "setupIlluaminate"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class IlluaminateExec : AbstractExecTask<IlluaminateExec>(IlluaminateExec::class.java) {
|
||||||
|
init {
|
||||||
|
dependsOn(SetupIlluaminate.NAME)
|
||||||
|
executable = illuaminatePath
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class IlluaminateExecToDir : ExecToDir() {
|
||||||
|
init {
|
||||||
|
dependsOn(SetupIlluaminate.NAME)
|
||||||
|
executable = illuaminatePath
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,62 @@
|
|||||||
|
package cc.tweaked.gradle
|
||||||
|
|
||||||
|
import org.gradle.api.artifacts.Dependency
|
||||||
|
import org.gradle.api.artifacts.MinimalExternalModuleDependency
|
||||||
|
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 {
|
||||||
|
(dep.group.isNullOrEmpty() || dep.group == it.groupId) &&
|
||||||
|
(dep.name.isNullOrEmpty() || dep.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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,189 @@
|
|||||||
|
package cc.tweaked.gradle
|
||||||
|
|
||||||
|
import org.gradle.api.Project
|
||||||
|
import org.gradle.api.artifacts.Configuration
|
||||||
|
import org.gradle.api.artifacts.ModuleDependency
|
||||||
|
import org.gradle.api.artifacts.dsl.DependencyHandler
|
||||||
|
import org.gradle.api.attributes.Bundling
|
||||||
|
import org.gradle.api.attributes.Category
|
||||||
|
import org.gradle.api.attributes.LibraryElements
|
||||||
|
import org.gradle.api.attributes.Usage
|
||||||
|
import org.gradle.api.attributes.java.TargetJvmVersion
|
||||||
|
import org.gradle.api.capabilities.Capability
|
||||||
|
import org.gradle.api.plugins.BasePlugin
|
||||||
|
import org.gradle.api.plugins.JavaPluginExtension
|
||||||
|
import org.gradle.api.tasks.SourceSet
|
||||||
|
import org.gradle.api.tasks.bundling.Jar
|
||||||
|
import org.gradle.api.tasks.javadoc.Javadoc
|
||||||
|
import org.gradle.kotlin.dsl.get
|
||||||
|
import org.gradle.kotlin.dsl.named
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This sets up a separate client-only source set, and extends that and the main/common source set with additional
|
||||||
|
* metadata, to make it easier to consume jars downstream.
|
||||||
|
*/
|
||||||
|
class MinecraftConfigurations private constructor(private val project: Project) {
|
||||||
|
private val java = project.extensions.getByType(JavaPluginExtension::class.java)
|
||||||
|
private val sourceSets = java.sourceSets
|
||||||
|
private val configurations = project.configurations
|
||||||
|
private val objects = project.objects
|
||||||
|
|
||||||
|
private val main = sourceSets[SourceSet.MAIN_SOURCE_SET_NAME]
|
||||||
|
private val test = sourceSets[SourceSet.TEST_SOURCE_SET_NAME]
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs the initial setup of our configurations.
|
||||||
|
*/
|
||||||
|
private fun setup() {
|
||||||
|
// Define a client source set.
|
||||||
|
val client = sourceSets.maybeCreate("client")
|
||||||
|
|
||||||
|
// Ensure the client classpaths behave the same as the main ones.
|
||||||
|
configurations.named(client.compileClasspathConfigurationName) {
|
||||||
|
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).
|
||||||
|
val clientApi = configurations.maybeCreate(client.apiConfigurationName).apply {
|
||||||
|
isVisible = false
|
||||||
|
isCanBeConsumed = false
|
||||||
|
isCanBeResolved = false
|
||||||
|
}
|
||||||
|
configurations.named(client.implementationConfigurationName) { extendsFrom(clientApi) }
|
||||||
|
|
||||||
|
/*
|
||||||
|
Now add outgoing variants for the main and common source sets that we can consume downstream. This is possibly
|
||||||
|
the worst way to do things, but unfortunately the alternatives don't actually work very well:
|
||||||
|
|
||||||
|
- Just using source set outputs: This means dependencies don't propagate, which means when :fabric depends
|
||||||
|
on :fabric-api, we don't inherit the fake :common-api in IDEA.
|
||||||
|
|
||||||
|
- Having separate common/main jars: Nice in principle, but unfortunately Forge needs a separate deobf jar
|
||||||
|
task (as the original jar is obfuscated), and IDEA is not able to map its output back to a source set.
|
||||||
|
|
||||||
|
This works for now, but is incredibly brittle. It's part of the reason we can't use testFixtures inside our
|
||||||
|
MC projects, as that adds a project(self) -> test dependency, which would pull in the jar instead.
|
||||||
|
|
||||||
|
Note we register a fake client jar here. It's not actually needed, but is there to make sure IDEA has
|
||||||
|
a way to tell that client classes are needed at runtime.
|
||||||
|
|
||||||
|
I'm so sorry, deeply aware how cursed this is.
|
||||||
|
*/
|
||||||
|
setupOutgoing(main, "CommonOnly")
|
||||||
|
project.tasks.register(client.jarTaskName, Jar::class.java) {
|
||||||
|
description = "An empty jar standing in for the client classes."
|
||||||
|
group = BasePlugin.BUILD_GROUP
|
||||||
|
archiveClassifier.set("client")
|
||||||
|
}
|
||||||
|
setupOutgoing(client)
|
||||||
|
|
||||||
|
// Reset the client classpath (Loom configures it slightly differently to this) and add a main -> client
|
||||||
|
// dependency. Here we /can/ use source set outputs as we add transitive deps by patching the classpath. Nasty,
|
||||||
|
// but avoids accidentally pulling in Forge's obfuscated jar.
|
||||||
|
client.compileClasspath = client.compileClasspath + main.compileClasspath
|
||||||
|
client.runtimeClasspath = client.runtimeClasspath + main.runtimeClasspath
|
||||||
|
project.dependencies.add(client.apiConfigurationName, main.output)
|
||||||
|
|
||||||
|
// Also add client classes to the test classpath. We do the same nasty tricks as needed for main -> client.
|
||||||
|
test.compileClasspath += client.compileClasspath
|
||||||
|
test.runtimeClasspath += client.runtimeClasspath
|
||||||
|
project.dependencies.add(test.implementationConfigurationName, client.output)
|
||||||
|
|
||||||
|
// Configure some tasks to include our additional files.
|
||||||
|
project.tasks.named("javadoc", Javadoc::class.java) {
|
||||||
|
source(client.allJava)
|
||||||
|
classpath = main.compileClasspath + main.output + client.compileClasspath + client.output
|
||||||
|
}
|
||||||
|
// This are already done by Fabric, but we need it for Forge and vanilla. It shouldn't conflict at all.
|
||||||
|
project.tasks.named("jar", Jar::class.java) { from(client.output) }
|
||||||
|
project.tasks.named("sourcesJar", Jar::class.java) { from(client.allSource) }
|
||||||
|
|
||||||
|
project.extensions.configure(CCTweakedExtension::class.java) {
|
||||||
|
sourceDirectories.add(SourceSetReference.internal(client))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setupOutgoing(sourceSet: SourceSet, suffix: String = "") {
|
||||||
|
setupOutgoing("${sourceSet.apiElementsConfigurationName}$suffix", sourceSet, objects.named(Usage.JAVA_API)) {
|
||||||
|
description = "API elements for ${sourceSet.name}"
|
||||||
|
extendsFrom(configurations[sourceSet.apiConfigurationName])
|
||||||
|
}
|
||||||
|
|
||||||
|
setupOutgoing("${sourceSet.runtimeElementsConfigurationName}$suffix", sourceSet, objects.named(Usage.JAVA_RUNTIME)) {
|
||||||
|
description = "Runtime elements for ${sourceSet.name}"
|
||||||
|
extendsFrom(configurations[sourceSet.implementationConfigurationName], configurations[sourceSet.runtimeOnlyConfigurationName])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set up an outgoing configuration for a specific source set. We set an additional "main" or "client" capability
|
||||||
|
* (depending on the source set name) which allows downstream projects to consume them separately (see
|
||||||
|
* [DependencyHandler.commonClasses] and [DependencyHandler.clientClasses]).
|
||||||
|
*/
|
||||||
|
private fun setupOutgoing(name: String, sourceSet: SourceSet, usage: Usage, configure: Configuration.() -> Unit) {
|
||||||
|
configurations.register(name) {
|
||||||
|
isVisible = false
|
||||||
|
isCanBeConsumed = true
|
||||||
|
isCanBeResolved = false
|
||||||
|
|
||||||
|
configure(this)
|
||||||
|
|
||||||
|
attributes {
|
||||||
|
attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category.LIBRARY))
|
||||||
|
attribute(Usage.USAGE_ATTRIBUTE, usage)
|
||||||
|
attribute(Bundling.BUNDLING_ATTRIBUTE, objects.named(Bundling.EXTERNAL))
|
||||||
|
attributeProvider(
|
||||||
|
TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE,
|
||||||
|
java.toolchain.languageVersion.map { it.asInt() },
|
||||||
|
)
|
||||||
|
attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, objects.named(LibraryElements.JAR))
|
||||||
|
}
|
||||||
|
|
||||||
|
outgoing {
|
||||||
|
capability(BasicOutgoingCapability(project, sourceSet.name))
|
||||||
|
|
||||||
|
// We have two outgoing variants here: the original jar and the classes.
|
||||||
|
artifact(project.tasks.named(sourceSet.jarTaskName))
|
||||||
|
|
||||||
|
variants.create("classes") {
|
||||||
|
attributes.attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, objects.named(LibraryElements.CLASSES))
|
||||||
|
sourceSet.output.classesDirs.forEach { artifact(it) { builtBy(sourceSet.output) } }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun setup(project: Project) {
|
||||||
|
MinecraftConfigurations(project).setup()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class BasicIncomingCapability(private val module: ModuleDependency, private val name: String) : Capability {
|
||||||
|
override fun getGroup(): String = module.group!!
|
||||||
|
override fun getName(): String = "${module.name}-$name"
|
||||||
|
override fun getVersion(): String? = null
|
||||||
|
}
|
||||||
|
|
||||||
|
private class BasicOutgoingCapability(private val project: Project, private val name: String) : Capability {
|
||||||
|
override fun getGroup(): String = project.group.toString()
|
||||||
|
override fun getName(): String = "${project.name}-$name"
|
||||||
|
override fun getVersion(): String = project.version.toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun DependencyHandler.clientClasses(notation: Any): ModuleDependency {
|
||||||
|
val dep = create(notation) as ModuleDependency
|
||||||
|
dep.capabilities { requireCapability(BasicIncomingCapability(dep, "client")) }
|
||||||
|
return dep
|
||||||
|
}
|
||||||
|
|
||||||
|
fun DependencyHandler.commonClasses(notation: Any): ModuleDependency {
|
||||||
|
val dep = create(notation) as ModuleDependency
|
||||||
|
dep.capabilities { requireCapability(BasicIncomingCapability(dep, "main")) }
|
||||||
|
return dep
|
||||||
|
}
|
191
buildSrc/src/main/kotlin/cc/tweaked/gradle/MinecraftExec.kt
Normal file
191
buildSrc/src/main/kotlin/cc/tweaked/gradle/MinecraftExec.kt
Normal file
@@ -0,0 +1,191 @@
|
|||||||
|
package cc.tweaked.gradle
|
||||||
|
|
||||||
|
import org.gradle.api.GradleException
|
||||||
|
import org.gradle.api.file.FileSystemOperations
|
||||||
|
import org.gradle.api.invocation.Gradle
|
||||||
|
import org.gradle.api.provider.Provider
|
||||||
|
import org.gradle.api.services.BuildService
|
||||||
|
import org.gradle.api.services.BuildServiceParameters
|
||||||
|
import org.gradle.api.tasks.*
|
||||||
|
import org.gradle.kotlin.dsl.getByName
|
||||||
|
import org.gradle.language.base.plugins.LifecycleBasePlugin
|
||||||
|
import java.io.File
|
||||||
|
import java.nio.file.Files
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
|
import java.util.function.Supplier
|
||||||
|
import javax.inject.Inject
|
||||||
|
import kotlin.random.Random
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A [JavaExec] task for client-tests. This sets some common setup, and uses [MinecraftRunnerService] to ensure only one
|
||||||
|
* test runs at once.
|
||||||
|
*/
|
||||||
|
abstract class ClientJavaExec : JavaExec() {
|
||||||
|
private val clientRunner: Provider<MinecraftRunnerService> = MinecraftRunnerService.get(project.gradle)
|
||||||
|
|
||||||
|
init {
|
||||||
|
group = LifecycleBasePlugin.VERIFICATION_GROUP
|
||||||
|
usesService(clientRunner)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When [false], tests will not be run automatically, allowing the user to debug rendering.
|
||||||
|
*/
|
||||||
|
@get:Input
|
||||||
|
val clientDebug get() = project.hasProperty("clientDebug")
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When [false], tests will not run under a framebuffer.
|
||||||
|
*/
|
||||||
|
@get:Input
|
||||||
|
val useFramebuffer get() = !clientDebug && !project.hasProperty("clientNoFramebuffer")
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The path test results are written to.
|
||||||
|
*/
|
||||||
|
@get:OutputFile
|
||||||
|
val testResults = project.layout.buildDirectory.file("test-results/$name.xml")
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copy configuration from a task with the given name.
|
||||||
|
*/
|
||||||
|
fun copyFrom(path: String) = copyFrom(project.tasks.getByName(path, JavaExec::class))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copy configuration from an existing [JavaExec] task.
|
||||||
|
*/
|
||||||
|
fun copyFrom(task: JavaExec) {
|
||||||
|
for (dep in task.dependsOn) dependsOn(dep)
|
||||||
|
task.copyToFull(this)
|
||||||
|
|
||||||
|
if (!clientDebug) systemProperty("cctest.client", "")
|
||||||
|
systemProperty("cctest.gametest-report", testResults.get().asFile.absoluteFile)
|
||||||
|
workingDir(project.buildDir.resolve("gametest").resolve(name))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Only run tests with the given tags.
|
||||||
|
*/
|
||||||
|
fun tags(vararg tags: String) {
|
||||||
|
systemProperty("cctest.tags", tags.joinToString(","))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write a file with the given contents before starting Minecraft. This may be useful for writing config files.
|
||||||
|
*/
|
||||||
|
fun withFileContents(path: Any, contents: Supplier<String>) {
|
||||||
|
val file = project.file(path).toPath()
|
||||||
|
doFirst {
|
||||||
|
Files.createDirectories(file.parent)
|
||||||
|
Files.writeString(file, contents.get())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copy a file to the provided path before starting Minecraft. This copy only occurs if the file does not already
|
||||||
|
* exist.
|
||||||
|
*/
|
||||||
|
fun withFileFrom(path: Any, source: Supplier<File>) {
|
||||||
|
val file = project.file(path).toPath()
|
||||||
|
doFirst {
|
||||||
|
Files.createDirectories(file.parent)
|
||||||
|
if (!Files.exists(file)) Files.copy(source.get().toPath(), file)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@TaskAction
|
||||||
|
override fun exec() {
|
||||||
|
Files.createDirectories(workingDir.toPath())
|
||||||
|
fsOperations.delete { delete(workingDir.resolve("screenshots")) }
|
||||||
|
|
||||||
|
if (useFramebuffer) {
|
||||||
|
clientRunner.get().wrapClient(this) { super.exec() }
|
||||||
|
} else {
|
||||||
|
super.exec()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@get:Inject
|
||||||
|
protected abstract val fsOperations: FileSystemOperations
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A service for [JavaExec] tasks which start Minecraft.
|
||||||
|
*
|
||||||
|
* Tasks may run `usesService(MinecraftRunnerService.get(gradle))` to ensure that only one Minecraft-related task runs
|
||||||
|
* at once.
|
||||||
|
*/
|
||||||
|
abstract class MinecraftRunnerService : BuildService<BuildServiceParameters.None> {
|
||||||
|
private val hasXvfb = lazy {
|
||||||
|
System.getProperty("os.name", "").equals("linux", ignoreCase = true) && ProcessHelpers.onPath("xvfb-run")
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun wrapClient(exec: JavaExec, run: () -> Unit) = when {
|
||||||
|
hasXvfb.value -> runXvfb(exec, run)
|
||||||
|
else -> run()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run a program under Xvfb, preventing it spawning a window.
|
||||||
|
*/
|
||||||
|
private fun runXvfb(exec: JavaExec, run: () -> Unit) {
|
||||||
|
fun ProcessBuilder.startVerbose(): Process {
|
||||||
|
exec.logger.info("Running ${this.command()}")
|
||||||
|
return start()
|
||||||
|
}
|
||||||
|
|
||||||
|
CloseScope().use { scope ->
|
||||||
|
val dir = Files.createTempDirectory("cctweaked").toAbsolutePath()
|
||||||
|
scope.add { fsOperations.delete { delete(dir) } }
|
||||||
|
|
||||||
|
val authFile = Files.createTempFile(dir, "Xauthority", "").toAbsolutePath()
|
||||||
|
|
||||||
|
val cookie = StringBuilder().also {
|
||||||
|
for (i in 0..31) it.append("0123456789abcdef"[Random.nextInt(16)])
|
||||||
|
}.toString()
|
||||||
|
|
||||||
|
val xvfb =
|
||||||
|
ProcessBuilder("Xvfb", "-displayfd", "1", "-screen", "0", "640x480x24", "-nolisten", "tcp").also {
|
||||||
|
it.inheritIO()
|
||||||
|
it.environment()["XAUTHORITY"] = authFile.toString()
|
||||||
|
it.redirectOutput(ProcessBuilder.Redirect.PIPE)
|
||||||
|
}.startVerbose()
|
||||||
|
scope.add { xvfb.destroyForcibly().waitFor() }
|
||||||
|
|
||||||
|
val server = xvfb.inputReader().use { it.readLine().trim() }
|
||||||
|
exec.logger.info("Running at :$server (XAUTHORITY=$authFile.toA")
|
||||||
|
|
||||||
|
ProcessBuilder("xauth", "add", ":$server", ".", cookie).also {
|
||||||
|
it.inheritIO()
|
||||||
|
it.environment()["XAUTHORITY"] = authFile.toString()
|
||||||
|
}.startVerbose().waitForOrThrow("Failed to setup XAuthority file")
|
||||||
|
|
||||||
|
scope.add {
|
||||||
|
ProcessBuilder("xauth", "remove", ":$server").also {
|
||||||
|
it.inheritIO()
|
||||||
|
it.environment()["XAUTHORITY"] = authFile.toString()
|
||||||
|
}.startVerbose().waitFor()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait a few seconds for Xvfb to start. Ugly, but identical to xvfb-run.
|
||||||
|
if (xvfb.waitFor(3, TimeUnit.SECONDS)) {
|
||||||
|
throw GradleException("Xvfb unexpectedly exited (with status code ${xvfb.exitValue()})")
|
||||||
|
}
|
||||||
|
|
||||||
|
exec.environment("XAUTHORITY", authFile.toString())
|
||||||
|
exec.environment("DISPLAY", ":$server")
|
||||||
|
|
||||||
|
run()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@get:Inject
|
||||||
|
protected abstract val fsOperations: FileSystemOperations
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun get(gradle: Gradle): Provider<MinecraftRunnerService> =
|
||||||
|
gradle.sharedServices.registerIfAbsent("cc.tweaked.gradle.ClientJavaExec", MinecraftRunnerService::class.java) {
|
||||||
|
maxParallelUsages.set(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
60
buildSrc/src/main/kotlin/cc/tweaked/gradle/Node.kt
Normal file
60
buildSrc/src/main/kotlin/cc/tweaked/gradle/Node.kt
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
package cc.tweaked.gradle
|
||||||
|
|
||||||
|
import org.gradle.api.DefaultTask
|
||||||
|
import org.gradle.api.Plugin
|
||||||
|
import org.gradle.api.Project
|
||||||
|
import org.gradle.api.file.Directory
|
||||||
|
import org.gradle.api.file.DirectoryProperty
|
||||||
|
import org.gradle.api.provider.Provider
|
||||||
|
import org.gradle.api.tasks.*
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
class NodePlugin : Plugin<Project> {
|
||||||
|
override fun apply(project: Project) {
|
||||||
|
val extension = project.extensions.create("node", NodeExtension::class.java)
|
||||||
|
project.tasks.register(NpmInstall.TASK_NAME, NpmInstall::class.java) {
|
||||||
|
projectRoot.convention(extension.projectRoot)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class NodeExtension(project: Project) {
|
||||||
|
/** The directory containing `package-lock.json` and `node_modules/`. */
|
||||||
|
abstract val projectRoot: DirectoryProperty
|
||||||
|
|
||||||
|
init {
|
||||||
|
projectRoot.convention(project.layout.projectDirectory)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Installs node modules as dependencies. */
|
||||||
|
abstract class NpmInstall : DefaultTask() {
|
||||||
|
@get:Internal
|
||||||
|
abstract val projectRoot: DirectoryProperty
|
||||||
|
|
||||||
|
@get:InputFile
|
||||||
|
@get:PathSensitive(PathSensitivity.NONE)
|
||||||
|
val packageLock: Provider<File> = projectRoot.file("package-lock.json").map { it.asFile }
|
||||||
|
|
||||||
|
@get:OutputDirectory
|
||||||
|
val nodeModules: Provider<Directory> = projectRoot.dir("node_modules")
|
||||||
|
|
||||||
|
@TaskAction
|
||||||
|
fun install() {
|
||||||
|
project.exec {
|
||||||
|
commandLine("npm", "ci")
|
||||||
|
workingDir = projectRoot.get().asFile
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
internal const val TASK_NAME: String = "npmInstall"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class NpxExecToDir : ExecToDir() {
|
||||||
|
init {
|
||||||
|
dependsOn(NpmInstall.TASK_NAME)
|
||||||
|
executable = "npx"
|
||||||
|
}
|
||||||
|
}
|
50
buildSrc/src/main/kotlin/cc/tweaked/gradle/ProcessHelpers.kt
Normal file
50
buildSrc/src/main/kotlin/cc/tweaked/gradle/ProcessHelpers.kt
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
package cc.tweaked.gradle
|
||||||
|
|
||||||
|
import org.codehaus.groovy.runtime.ProcessGroovyMethods
|
||||||
|
import org.gradle.api.GradleException
|
||||||
|
import java.io.BufferedReader
|
||||||
|
import java.io.File
|
||||||
|
import java.io.InputStreamReader
|
||||||
|
|
||||||
|
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 {
|
||||||
|
val path = System.getenv("PATH") ?: return false
|
||||||
|
return path.splitToSequence(File.pathSeparator).any { File(it, name).exists() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun Process.waitForOrThrow(message: String) {
|
||||||
|
val ret = waitFor()
|
||||||
|
if (ret != 0) throw GradleException("$message (exited with $ret)")
|
||||||
|
}
|
@@ -0,0 +1,20 @@
|
|||||||
|
package cc.tweaked.gradle
|
||||||
|
|
||||||
|
import org.gradle.api.tasks.SourceSet
|
||||||
|
|
||||||
|
data class SourceSetReference(
|
||||||
|
val sourceSet: SourceSet,
|
||||||
|
val classes: Boolean,
|
||||||
|
val external: Boolean,
|
||||||
|
) {
|
||||||
|
companion object {
|
||||||
|
/** A source set in the current project. */
|
||||||
|
fun internal(sourceSet: SourceSet) = SourceSetReference(sourceSet, classes = true, external = false)
|
||||||
|
|
||||||
|
/** A source set from another project. */
|
||||||
|
fun external(sourceSet: SourceSet) = SourceSetReference(sourceSet, classes = true, external = true)
|
||||||
|
|
||||||
|
/** A source set which is inlined into the current project. */
|
||||||
|
fun inline(sourceSet: SourceSet) = SourceSetReference(sourceSet, classes = false, external = false)
|
||||||
|
}
|
||||||
|
}
|
12
buildSrc/src/main/kotlin/cc/tweaked/gradle/XmlUtil.kt
Normal file
12
buildSrc/src/main/kotlin/cc/tweaked/gradle/XmlUtil.kt
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
@@ -17,7 +17,10 @@
|
|||||||
<module name="TreeWalker">
|
<module name="TreeWalker">
|
||||||
<!-- Annotations -->
|
<!-- Annotations -->
|
||||||
<module name="AnnotationLocation" />
|
<module name="AnnotationLocation" />
|
||||||
<module name="AnnotationUseStyle" />
|
<module name="AnnotationUseStyle">
|
||||||
|
<!-- We want trailing commas on multiline arrays. -->
|
||||||
|
<property name="trailingArrayComma" value="ignore" />
|
||||||
|
</module>
|
||||||
<module name="MissingDeprecated" />
|
<module name="MissingDeprecated" />
|
||||||
<module name="MissingOverride" />
|
<module name="MissingOverride" />
|
||||||
|
|
||||||
@@ -26,17 +29,11 @@
|
|||||||
<module name="EmptyCatchBlock">
|
<module name="EmptyCatchBlock">
|
||||||
<property name="exceptionVariableName" value="ignored" />
|
<property name="exceptionVariableName" value="ignored" />
|
||||||
</module>
|
</module>
|
||||||
<module name="LeftCurly">
|
<module name="LeftCurly" />
|
||||||
<property name="option" value="nl" />
|
|
||||||
<!-- The defaults, minus lambdas. -->
|
|
||||||
<property name="tokens" value="ANNOTATION_DEF,CLASS_DEF,CTOR_DEF,ENUM_CONSTANT_DEF,ENUM_DEF,INTERFACE_DEF,LITERAL_CASE,LITERAL_CATCH,LITERAL_DEFAULT,LITERAL_DO,LITERAL_ELSE,LITERAL_FINALLY,LITERAL_FOR,LITERAL_IF,LITERAL_SWITCH,LITERAL_SYNCHRONIZED,LITERAL_TRY,LITERAL_WHILE,METHOD_DEF,OBJBLOCK,STATIC_INIT" />
|
|
||||||
</module>
|
|
||||||
<module name="NeedBraces">
|
<module name="NeedBraces">
|
||||||
<property name="allowSingleLineStatement" value="true"/>
|
<property name="allowSingleLineStatement" value="true"/>
|
||||||
</module>
|
</module>
|
||||||
<module name="RightCurly">
|
<module name="RightCurly" />
|
||||||
<property name="option" value="alone" />
|
|
||||||
</module>
|
|
||||||
|
|
||||||
<!-- Class design. As if we've ever followed good practice here. -->
|
<!-- Class design. As if we've ever followed good practice here. -->
|
||||||
<module name="FinalClass" />
|
<module name="FinalClass" />
|
||||||
@@ -78,7 +75,9 @@
|
|||||||
|
|
||||||
<!-- Javadoc -->
|
<!-- Javadoc -->
|
||||||
<!-- TODO: Missing* checks for the dan200.computercraft.api package? -->
|
<!-- TODO: Missing* checks for the dan200.computercraft.api package? -->
|
||||||
<module name="AtclauseOrder" />
|
<module name="AtclauseOrder">
|
||||||
|
<property name="tagOrder" value="@param, @return, @throws, @deprecated"/>
|
||||||
|
</module>
|
||||||
<module name="InvalidJavadocPosition" />
|
<module name="InvalidJavadocPosition" />
|
||||||
<module name="JavadocBlockTagLocation" />
|
<module name="JavadocBlockTagLocation" />
|
||||||
<module name="JavadocMethod"/>
|
<module name="JavadocMethod"/>
|
||||||
@@ -107,19 +106,16 @@
|
|||||||
<module name="LocalFinalVariableName" />
|
<module name="LocalFinalVariableName" />
|
||||||
<module name="LocalVariableName" />
|
<module name="LocalVariableName" />
|
||||||
<module name="MemberName" />
|
<module name="MemberName" />
|
||||||
<module name="MethodName" />
|
<module name="MethodName">
|
||||||
|
<property name="format" value="^(computercraft\$)?[a-z][a-zA-Z0-9]*$" />
|
||||||
|
</module>
|
||||||
<module name="MethodTypeParameterName" />
|
<module name="MethodTypeParameterName" />
|
||||||
<module name="PackageName">
|
<module name="PackageName">
|
||||||
<property name="format" value="^dan200\.computercraft(\.[a-z][a-z0-9]*)*" />
|
<property name="format" value="^(dan200\.computercraft|cc\.tweaked)(\.[a-z][a-z0-9]*)*" />
|
||||||
</module>
|
</module>
|
||||||
<module name="ParameterName" />
|
<module name="ParameterName" />
|
||||||
<module name="StaticVariableName">
|
<module name="StaticVariableName">
|
||||||
<property name="format" value="^[a-z][a-zA-Z0-9]*|CAPABILITY(_[A-Z_]+)?$" />
|
<property name="format" value="^[a-z][a-zA-Z0-9]*|CAPABILITY(_[A-Z_]+)?$" />
|
||||||
<property name="applyToPrivate" value="false" />
|
|
||||||
</module>
|
|
||||||
<module name="StaticVariableName">
|
|
||||||
<property name="format" value="^(s_)?[a-z][a-zA-Z0-9]*|CAPABILITY(_[A-Z_]+)?$" />
|
|
||||||
<property name="applyToPrivate" value="true" />
|
|
||||||
</module>
|
</module>
|
||||||
<module name="TypeName" />
|
<module name="TypeName" />
|
||||||
|
|
||||||
@@ -132,18 +128,11 @@
|
|||||||
<module name="MethodParamPad" />
|
<module name="MethodParamPad" />
|
||||||
<module name="NoLineWrap" />
|
<module name="NoLineWrap" />
|
||||||
<module name="NoWhitespaceAfter">
|
<module name="NoWhitespaceAfter">
|
||||||
<property name="tokens" value="AT,INC,DEC,UNARY_MINUS,UNARY_PLUS,BNOT,LNOT,DOT,ARRAY_DECLARATOR,INDEX_OP" />
|
<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" />
|
||||||
<!-- TODO: Decide on an OperatorWrap style. -->
|
<!-- TODO: Decide on an OperatorWrap style. -->
|
||||||
<module name="ParenPad">
|
<module name="ParenPad" />
|
||||||
<property name="option" value="space" />
|
|
||||||
<property name="tokens" value="ANNOTATION,ANNOTATION_FIELD_DEF,CTOR_CALL,CTOR_DEF,ENUM_CONSTANT_DEF,LITERAL_CATCH,LITERAL_DO,LITERAL_FOR,LITERAL_IF,LITERAL_NEW,LITERAL_SWITCH,LITERAL_SYNCHRONIZED,LITERAL_WHILE,METHOD_CALL,METHOD_DEF,RESOURCE_SPECIFICATION,SUPER_CTOR_CALL,LAMBDA" />
|
|
||||||
</module>
|
|
||||||
<module name="ParenPad">
|
|
||||||
<property name="option" value="nospace" />
|
|
||||||
<property name="tokens" value="DOT,EXPR,QUESTION" />
|
|
||||||
</module>
|
|
||||||
<module name="SeparatorWrap">
|
<module name="SeparatorWrap">
|
||||||
<property name="option" value="eol" />
|
<property name="option" value="eol" />
|
||||||
<property name="tokens" value="COMMA,SEMI,ELLIPSIS,ARRAY_DECLARATOR,RBRACK,METHOD_REF" />
|
<property name="tokens" value="COMMA,SEMI,ELLIPSIS,ARRAY_DECLARATOR,RBRACK,METHOD_REF" />
|
||||||
|
File diff suppressed because it is too large
Load Diff
@@ -1,61 +0,0 @@
|
|||||||
<code_scheme name="Project" version="173">
|
|
||||||
<JSON>
|
|
||||||
<option name="OBJECT_WRAPPING" value="1" />
|
|
||||||
<option name="ARRAY_WRAPPING" value="1" />
|
|
||||||
</JSON>
|
|
||||||
<JavaCodeStyleSettings>
|
|
||||||
<option name="PACKAGES_TO_USE_IMPORT_ON_DEMAND">
|
|
||||||
<value />
|
|
||||||
</option>
|
|
||||||
<option name="JD_P_AT_EMPTY_LINES" value="false" />
|
|
||||||
<option name="JD_PRESERVE_LINE_FEEDS" value="true" />
|
|
||||||
</JavaCodeStyleSettings>
|
|
||||||
<codeStyleSettings language="JAVA">
|
|
||||||
<option name="KEEP_FIRST_COLUMN_COMMENT" value="false" />
|
|
||||||
<option name="BRACE_STYLE" value="2" />
|
|
||||||
<option name="CLASS_BRACE_STYLE" value="2" />
|
|
||||||
<option name="METHOD_BRACE_STYLE" value="2" />
|
|
||||||
<option name="LAMBDA_BRACE_STYLE" value="5" />
|
|
||||||
<option name="ELSE_ON_NEW_LINE" value="true" />
|
|
||||||
<option name="CATCH_ON_NEW_LINE" value="true" />
|
|
||||||
<option name="FINALLY_ON_NEW_LINE" value="true" />
|
|
||||||
<option name="SPACE_WITHIN_METHOD_CALL_PARENTHESES" value="true" />
|
|
||||||
<option name="SPACE_WITHIN_METHOD_PARENTHESES" value="true" />
|
|
||||||
<option name="SPACE_WITHIN_IF_PARENTHESES" value="true" />
|
|
||||||
<option name="SPACE_WITHIN_WHILE_PARENTHESES" value="true" />
|
|
||||||
<option name="SPACE_WITHIN_FOR_PARENTHESES" value="true" />
|
|
||||||
<option name="SPACE_WITHIN_TRY_PARENTHESES" value="true" />
|
|
||||||
<option name="SPACE_WITHIN_CATCH_PARENTHESES" value="true" />
|
|
||||||
<option name="SPACE_WITHIN_SWITCH_PARENTHESES" value="true" />
|
|
||||||
<option name="SPACE_WITHIN_SYNCHRONIZED_PARENTHESES" value="true" />
|
|
||||||
<option name="SPACE_WITHIN_ARRAY_INITIALIZER_BRACES" value="true" />
|
|
||||||
<option name="SPACE_BEFORE_IF_PARENTHESES" value="false" />
|
|
||||||
<option name="SPACE_BEFORE_WHILE_PARENTHESES" value="false" />
|
|
||||||
<option name="SPACE_BEFORE_FOR_PARENTHESES" value="false" />
|
|
||||||
<option name="SPACE_BEFORE_TRY_PARENTHESES" value="false" />
|
|
||||||
<option name="SPACE_BEFORE_CATCH_PARENTHESES" value="false" />
|
|
||||||
<option name="SPACE_BEFORE_SWITCH_PARENTHESES" value="false" />
|
|
||||||
<option name="SPACE_BEFORE_SYNCHRONIZED_PARENTHESES" value="false" />
|
|
||||||
<option name="SPACE_BEFORE_ARRAY_INITIALIZER_LBRACE" value="true" />
|
|
||||||
<option name="KEEP_SIMPLE_METHODS_IN_ONE_LINE" value="true" />
|
|
||||||
<option name="KEEP_SIMPLE_LAMBDAS_IN_ONE_LINE" value="true" />
|
|
||||||
<option name="KEEP_SIMPLE_CLASSES_IN_ONE_LINE" value="true" />
|
|
||||||
<option name="IF_BRACE_FORCE" value="1" />
|
|
||||||
<option name="DOWHILE_BRACE_FORCE" value="1" />
|
|
||||||
<option name="WHILE_BRACE_FORCE" value="1" />
|
|
||||||
<option name="FOR_BRACE_FORCE" value="1" />
|
|
||||||
<option name="SPACE_WITHIN_ANNOTATION_PARENTHESES" value="true" />
|
|
||||||
<indentOptions>
|
|
||||||
<option name="CONTINUATION_INDENT_SIZE" value="4" />
|
|
||||||
</indentOptions>
|
|
||||||
</codeStyleSettings>
|
|
||||||
<codeStyleSettings language="JSON">
|
|
||||||
<option name="KEEP_BLANK_LINES_IN_CODE" value="1" />
|
|
||||||
<option name="SPACE_WITHIN_BRACKETS" value="true" />
|
|
||||||
<option name="SPACE_WITHIN_BRACES" value="true" />
|
|
||||||
<indentOptions>
|
|
||||||
<option name="INDENT_SIZE" value="4" />
|
|
||||||
<option name="CONTINUATION_INDENT_SIZE" value="4" />
|
|
||||||
</indentOptions>
|
|
||||||
</codeStyleSettings>
|
|
||||||
</code_scheme>
|
|
@@ -1,56 +0,0 @@
|
|||||||
# See https://pre-commit.com for more information
|
|
||||||
# See https://pre-commit.com/hooks.html for more hooks
|
|
||||||
repos:
|
|
||||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
|
||||||
rev: v4.0.1
|
|
||||||
hooks:
|
|
||||||
- id: trailing-whitespace
|
|
||||||
- id: end-of-file-fixer
|
|
||||||
- id: check-merge-conflict
|
|
||||||
|
|
||||||
# Quick syntax checkers
|
|
||||||
- id: check-xml
|
|
||||||
- id: check-yaml
|
|
||||||
- id: check-toml
|
|
||||||
- id: check-json
|
|
||||||
exclude: "tsconfig\\.json$"
|
|
||||||
|
|
||||||
- repo: https://github.com/editorconfig-checker/editorconfig-checker.python
|
|
||||||
rev: 2.3.54
|
|
||||||
hooks:
|
|
||||||
- id: editorconfig-checker
|
|
||||||
args: ['-disable-indentation']
|
|
||||||
exclude: "^(.*\\.(bat)|LICENSE)$"
|
|
||||||
|
|
||||||
- repo: local
|
|
||||||
hooks:
|
|
||||||
- id: checkstyle
|
|
||||||
name: Check Java codestyle
|
|
||||||
files: ".*\\.java$"
|
|
||||||
language: system
|
|
||||||
entry: ./gradlew checkstyleMain checkstyleTest
|
|
||||||
pass_filenames: false
|
|
||||||
require_serial: true
|
|
||||||
- id: license
|
|
||||||
name: Check Java license headers
|
|
||||||
files: ".*\\.java$"
|
|
||||||
language: system
|
|
||||||
entry: ./gradlew licenseFormat
|
|
||||||
pass_filenames: false
|
|
||||||
require_serial: true
|
|
||||||
- id: illuaminate
|
|
||||||
name: Check Lua code
|
|
||||||
files: ".*\\.(lua|java|md)"
|
|
||||||
language: script
|
|
||||||
entry: config/pre-commit/illuaminate-lint.sh
|
|
||||||
pass_filenames: false
|
|
||||||
require_serial: true
|
|
||||||
|
|
||||||
exclude: |
|
|
||||||
(?x)^(
|
|
||||||
src/generated|
|
|
||||||
src/test/resources/test-rom/data/json-parsing/|
|
|
||||||
src/testMod/server-files/|
|
|
||||||
config/idea/|
|
|
||||||
.*\.dfpwm
|
|
||||||
)
|
|
@@ -1,16 +0,0 @@
|
|||||||
#!/usr/bin/env sh
|
|
||||||
set -e
|
|
||||||
|
|
||||||
test -d bin || mkdir bin
|
|
||||||
test -f bin/illuaminate || curl -s -obin/illuaminate https://squiddev.cc/illuaminate/linux-x86-64/illuaminate
|
|
||||||
chmod +x bin/illuaminate
|
|
||||||
|
|
||||||
if [ -n ${GITHUB_ACTIONS+x} ]; then
|
|
||||||
# Register a problem matcher (see https://github.com/actions/toolkit/blob/master/docs/problem-matchers.md)
|
|
||||||
# for illuaminate.
|
|
||||||
echo "::add-matcher::.github/matchers/illuaminate.json"
|
|
||||||
trap 'echo "::remove-matcher owner=illuaminate::"' EXIT
|
|
||||||
fi
|
|
||||||
|
|
||||||
./gradlew luaJavadoc
|
|
||||||
bin/illuaminate lint
|
|
@@ -3,19 +3,20 @@ module: [kind=event] alarm
|
|||||||
see: os.setAlarm To start an alarm.
|
see: os.setAlarm To start an alarm.
|
||||||
---
|
---
|
||||||
|
|
||||||
The @{timer} event is fired when an alarm started with @{os.setAlarm} completes.
|
The @{alarm} event is fired when an alarm started with @{os.setAlarm} completes.
|
||||||
|
|
||||||
## Return Values
|
## Return Values
|
||||||
1. @{string}: The event name.
|
1. @{string}: The event name.
|
||||||
2. @{number}: The ID of the alarm that finished.
|
2. @{number}: The ID of the alarm that finished.
|
||||||
|
|
||||||
## Example
|
## Example
|
||||||
Starts a timer and then prints its ID:
|
Starts a timer and then waits for it to complete.
|
||||||
|
|
||||||
```lua
|
```lua
|
||||||
local alarmID = os.setAlarm(os.time() + 0.05)
|
local alarm_id = os.setAlarm(os.time() + 0.05)
|
||||||
local event, id
|
local event, id
|
||||||
repeat
|
repeat
|
||||||
event, id = os.pullEvent("alarm")
|
event, id = os.pullEvent("alarm")
|
||||||
until id == alarmID
|
until id == alarm_id
|
||||||
print("Alarm with ID " .. id .. " was fired")
|
print("Alarm with ID " .. id .. " was fired")
|
||||||
```
|
```
|
||||||
|
@@ -3,7 +3,7 @@ module: [kind=event] char
|
|||||||
see: key To listen to any key press.
|
see: key To listen to any key press.
|
||||||
---
|
---
|
||||||
|
|
||||||
The @{char} event is fired when a character is _typed_ on the keyboard.
|
The @{char} event is fired when a character is typed on the keyboard.
|
||||||
|
|
||||||
The @{char} event is different to a key press. Sometimes multiple key presses may result in one character being
|
The @{char} event is different to a key press. Sometimes multiple key presses may result in one character being
|
||||||
typed (for instance, on some European keyboards). Similarly, some keys (e.g. <kbd>Ctrl</kbd>) do not have any
|
typed (for instance, on some European keyboards). Similarly, some keys (e.g. <kbd>Ctrl</kbd>) do not have any
|
||||||
@@ -16,9 +16,10 @@ corresponding character. The @{key} should be used if you want to listen to key
|
|||||||
|
|
||||||
## Example
|
## Example
|
||||||
Prints each character the user presses:
|
Prints each character the user presses:
|
||||||
|
|
||||||
```lua
|
```lua
|
||||||
while true do
|
while true do
|
||||||
local event, character = os.pullEvent("char")
|
local event, character = os.pullEvent("char")
|
||||||
print(character .. " was pressed.")
|
print(character .. " was pressed.")
|
||||||
end
|
end
|
||||||
```
|
```
|
||||||
|
@@ -6,7 +6,7 @@ The @{computer_command} event is fired when the `/computercraft queue` command i
|
|||||||
|
|
||||||
## Return Values
|
## Return Values
|
||||||
1. @{string}: The event name.
|
1. @{string}: The event name.
|
||||||
... @{string}: The arguments passed to the command.
|
2. @{string}<abbr title="Variable number of arguments">…</abbr>: The arguments passed to the command.
|
||||||
|
|
||||||
## Example
|
## Example
|
||||||
Prints the contents of messages sent:
|
Prints the contents of messages sent:
|
||||||
|
42
doc/events/file_transfer.md
Normal file
42
doc/events/file_transfer.md
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
---
|
||||||
|
module: [kind=event] file_transfer
|
||||||
|
since: 1.101.0
|
||||||
|
---
|
||||||
|
|
||||||
|
The @{file_transfer} event is queued when a user drags-and-drops a file on an open computer.
|
||||||
|
|
||||||
|
This event contains a single argument of type @{TransferredFiles}, which can be used to @{TransferredFiles.getFiles|get
|
||||||
|
the files to be transferred}. Each file returned is a @{fs.BinaryReadHandle|binary file handle} with an additional
|
||||||
|
@{TransferredFile.getName|getName} method.
|
||||||
|
|
||||||
|
## Return values
|
||||||
|
1. @{string}: The event name
|
||||||
|
2. @{TransferredFiles}: The list of transferred files.
|
||||||
|
|
||||||
|
## Example
|
||||||
|
Waits for a user to drop files on top of the computer, then prints the list of files and the size of each file.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local _, files = os.pullEvent("file_transfer")
|
||||||
|
for _, file in ipairs(files.getFiles()) do
|
||||||
|
-- Seek to the end of the file to get its size, then go back to the beginning.
|
||||||
|
local size = file.seek("end")
|
||||||
|
file.seek("set", 0)
|
||||||
|
|
||||||
|
print(file.getName() .. " " .. file.getSize())
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
## Example
|
||||||
|
Save each transferred file to the computer's storage.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local _, files = os.pullEvent("file_transfer")
|
||||||
|
for _, file in ipairs(files.getFiles()) do
|
||||||
|
local handle = fs.open(file.getName(), "wb")
|
||||||
|
handle.write(file.readAll())
|
||||||
|
|
||||||
|
handle.close()
|
||||||
|
file.close()
|
||||||
|
end
|
||||||
|
```
|
@@ -11,4 +11,4 @@ This event is normally handled inside @{http.checkURL}, but it can still be seen
|
|||||||
1. @{string}: The event name.
|
1. @{string}: The event name.
|
||||||
2. @{string}: The URL requested to be checked.
|
2. @{string}: The URL requested to be checked.
|
||||||
3. @{boolean}: Whether the check succeeded.
|
3. @{boolean}: Whether the check succeeded.
|
||||||
4. @{string|nil}: If the check failed, a reason explaining why the check failed.
|
4. <span class="type">@{string}|@{nil}</span>: If the check failed, a reason explaining why the check failed.
|
||||||
|
@@ -11,7 +11,8 @@ This event is normally handled inside @{http.get} and @{http.post}, but it can s
|
|||||||
1. @{string}: The event name.
|
1. @{string}: The event name.
|
||||||
2. @{string}: The URL of the site requested.
|
2. @{string}: The URL of the site requested.
|
||||||
3. @{string}: An error describing the failure.
|
3. @{string}: An error describing the failure.
|
||||||
4. @{http.Response|nil}: A response handle if the connection succeeded, but the server's response indicated failure.
|
4. <span class="type">@{http.Response}|@{nil}</span>: A response handle if the connection succeeded, but the server's
|
||||||
|
response indicated failure.
|
||||||
|
|
||||||
## Example
|
## Example
|
||||||
Prints an error why the website cannot be contacted:
|
Prints an error why the website cannot be contacted:
|
||||||
|
@@ -10,7 +10,7 @@ This event is normally handled inside @{http.get} and @{http.post}, but it can s
|
|||||||
## Return Values
|
## Return Values
|
||||||
1. @{string}: The event name.
|
1. @{string}: The event name.
|
||||||
2. @{string}: The URL of the site requested.
|
2. @{string}: The URL of the site requested.
|
||||||
3. @{http.Response}: The handle for the response text.
|
3. @{http.Response}: The successful HTTP response.
|
||||||
|
|
||||||
## Example
|
## Example
|
||||||
Prints the content of a website (this may fail if the request fails):
|
Prints the content of a website (this may fail if the request fails):
|
||||||
|
@@ -10,7 +10,7 @@ The @{modem_message} event is fired when a message is received on an open channe
|
|||||||
3. @{number}: The channel that the message was sent on.
|
3. @{number}: The channel that the message was sent on.
|
||||||
4. @{number}: The reply channel set by the sender.
|
4. @{number}: The reply channel set by the sender.
|
||||||
5. @{any}: The message as sent by the sender.
|
5. @{any}: The message as sent by the sender.
|
||||||
6. @{number}: The distance between the sender and the receiver, in blocks.
|
6. <span class="type">@{number}|@{nil}</span>: The distance between the sender and the receiver in blocks, or @{nil} if the message was sent between dimensions.
|
||||||
|
|
||||||
## Example
|
## Example
|
||||||
Wraps a @{modem} peripheral, opens channel 0 for listening, and prints all received messages.
|
Wraps a @{modem} peripheral, opens channel 0 for listening, and prints all received messages.
|
||||||
@@ -20,7 +20,9 @@ local modem = peripheral.find("modem") or error("No modem attached", 0)
|
|||||||
modem.open(0)
|
modem.open(0)
|
||||||
|
|
||||||
while true do
|
while true do
|
||||||
local event, side, channel, replyChannel, message, distance = os.pullEvent("modem_message")
|
local event, side, channel, replyChannel, message, distance = os.pullEvent("modem_message")
|
||||||
print(("Message received on side %s on channel %d (reply to %d) from %f blocks away with message %s"):format(side, channel, replyChannel, distance, tostring(message)))
|
print(("Message received on side %s on channel %d (reply to %d) from %f blocks away with message %s"):format(
|
||||||
|
side, channel, replyChannel, distance, tostring(message)
|
||||||
|
))
|
||||||
end
|
end
|
||||||
```
|
```
|
||||||
|
@@ -6,10 +6,11 @@ The @{monitor_resize} event is fired when an adjacent or networked monitor's siz
|
|||||||
|
|
||||||
## Return Values
|
## Return Values
|
||||||
1. @{string}: The event name.
|
1. @{string}: The event name.
|
||||||
2. @{string}: The side or network ID of the monitor that resized.
|
2. @{string}: The side or network ID of the monitor that was resized.
|
||||||
|
|
||||||
## Example
|
## Example
|
||||||
Prints a message when a monitor is resized:
|
Prints a message when a monitor is resized:
|
||||||
|
|
||||||
```lua
|
```lua
|
||||||
while true do
|
while true do
|
||||||
local event, side = os.pullEvent("monitor_resize")
|
local event, side = os.pullEvent("monitor_resize")
|
||||||
|
@@ -14,7 +14,7 @@ This event is usually handled by @{rednet.receive}, but it can also be pulled ma
|
|||||||
1. @{string}: The event name.
|
1. @{string}: The event name.
|
||||||
2. @{number}: The ID of the sending computer.
|
2. @{number}: The ID of the sending computer.
|
||||||
3. @{any}: The message sent.
|
3. @{any}: The message sent.
|
||||||
4. @{string|nil}: The protocol of the message, if provided.
|
4. <span class="type">@{string}|@{nil}</span>: The protocol of the message, if provided.
|
||||||
|
|
||||||
## Example
|
## Example
|
||||||
Prints a message when one is sent:
|
Prints a message when one is sent:
|
||||||
|
@@ -4,6 +4,9 @@ module: [kind=event] redstone
|
|||||||
|
|
||||||
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 change.
|
||||||
|
|
||||||
|
## Return values
|
||||||
|
1. @{string}: The event name.
|
||||||
|
|
||||||
## Example
|
## Example
|
||||||
Prints a message when a redstone input changes:
|
Prints a message when a redstone input changes:
|
||||||
```lua
|
```lua
|
||||||
|
@@ -10,7 +10,7 @@ The @{task_complete} event is fired when an asynchronous task completes. This is
|
|||||||
2. @{number}: The ID of the task that completed.
|
2. @{number}: The ID of the task that completed.
|
||||||
3. @{boolean}: Whether the command succeeded.
|
3. @{boolean}: Whether the command succeeded.
|
||||||
4. @{string}: If the command failed, an error message explaining the failure. (This is not present if the command succeeded.)
|
4. @{string}: If the command failed, an error message explaining the failure. (This is not present if the command succeeded.)
|
||||||
...: Any parameters returned from the command.
|
5. <abbr title="Variable number of arguments">…</abbr>: Any parameters returned from the command.
|
||||||
|
|
||||||
## Example
|
## Example
|
||||||
Prints the results of an asynchronous command:
|
Prints the results of an asynchronous command:
|
||||||
|
@@ -9,8 +9,12 @@ The @{term_resize} event is fired when the main terminal is resized. For instanc
|
|||||||
When this event fires, some parts of the terminal may have been moved or deleted. Simple terminal programs (those
|
When this event fires, some parts of the terminal may have been moved or deleted. Simple terminal programs (those
|
||||||
not using @{term.setCursorPos}) can ignore this event, but more complex GUI programs should redraw the entire screen.
|
not using @{term.setCursorPos}) can ignore this event, but more complex GUI programs should redraw the entire screen.
|
||||||
|
|
||||||
|
## Return values
|
||||||
|
1. @{string}: The event name.
|
||||||
|
|
||||||
## Example
|
## Example
|
||||||
Prints :
|
Print a message each time the terminal is resized.
|
||||||
|
|
||||||
```lua
|
```lua
|
||||||
while true do
|
while true do
|
||||||
os.pullEvent("term_resize")
|
os.pullEvent("term_resize")
|
||||||
|
@@ -8,6 +8,9 @@ This event is normally handled by @{os.pullEvent}, and will not be returned. How
|
|||||||
|
|
||||||
@{terminate} will be sent even when a filter is provided to @{os.pullEventRaw}. When using @{os.pullEventRaw} with a filter, make sure to check that the event is not @{terminate}.
|
@{terminate} will be sent even when a filter is provided to @{os.pullEventRaw}. When using @{os.pullEventRaw} with a filter, make sure to check that the event is not @{terminate}.
|
||||||
|
|
||||||
|
## Return values
|
||||||
|
1. @{string}: The event name.
|
||||||
|
|
||||||
## Example
|
## Example
|
||||||
Prints a message when Ctrl-T is held:
|
Prints a message when Ctrl-T is held:
|
||||||
```lua
|
```lua
|
||||||
|
@@ -10,12 +10,12 @@ The @{timer} event is fired when a timer started with @{os.startTimer} completes
|
|||||||
2. @{number}: The ID of the timer that finished.
|
2. @{number}: The ID of the timer that finished.
|
||||||
|
|
||||||
## Example
|
## Example
|
||||||
Starts a timer and then prints its ID:
|
Start and wait for a timer to finish.
|
||||||
```lua
|
```lua
|
||||||
local timerID = os.startTimer(2)
|
local timer_id = os.startTimer(2)
|
||||||
local event, id
|
local event, id
|
||||||
repeat
|
repeat
|
||||||
event, id = os.pullEvent("timer")
|
event, id = os.pullEvent("timer")
|
||||||
until id == timerID
|
until id == timer_id
|
||||||
print("Timer with ID " .. id .. " was fired")
|
print("Timer with ID " .. id .. " was fired")
|
||||||
```
|
```
|
||||||
|
@@ -4,6 +4,9 @@ module: [kind=event] turtle_inventory
|
|||||||
|
|
||||||
The @{turtle_inventory} event is fired when a turtle's inventory is changed.
|
The @{turtle_inventory} event is fired when a turtle's inventory is changed.
|
||||||
|
|
||||||
|
## Return values
|
||||||
|
1. @{string}: The event name.
|
||||||
|
|
||||||
## Example
|
## Example
|
||||||
Prints a message when the inventory is changed:
|
Prints a message when the inventory is changed:
|
||||||
```lua
|
```lua
|
||||||
|
90
doc/guides/gps_setup.md
Normal file
90
doc/guides/gps_setup.md
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
---
|
||||||
|
module: [kind=guide] gps_setup
|
||||||
|
---
|
||||||
|
|
||||||
|
# Setting up GPS
|
||||||
|
The @{gps} API allows computers and turtles to find their current position using wireless modems.
|
||||||
|
|
||||||
|
In order to use GPS, you'll need to set up multiple *GPS hosts*. These are computers running the special `gps host`
|
||||||
|
program, which tell other computers the host's position. Several hosts running together are known as a *GPS
|
||||||
|
constellation*.
|
||||||
|
|
||||||
|
In order to give the best results, a GPS constellation needs at least four computers. More than four GPS hosts per
|
||||||
|
constellation is redundant, but it does not cause problems.
|
||||||
|
|
||||||
|
## Building a GPS constellation
|
||||||
|
{.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
|
||||||
|
modems or 4 ender modems. Try not to mix ender and wireless modems together as you might get some odd behavior when your
|
||||||
|
requesting computers are out of range.
|
||||||
|
|
||||||
|
:::tip Ender modems vs wireless modems
|
||||||
|
Ender modems have a very large range, which makes them very useful for setting up GPS hosts. If you do this then you
|
||||||
|
will likely only need one GPS constellation for the whole dimension (such as the Overworld or Nether).
|
||||||
|
|
||||||
|
If you do use wireless modems then you may find that you need multiple GPS constellations to cover your needs.
|
||||||
|
|
||||||
|
A computer needs a wireless or ender modem and to be in range of a GPS constellation that is in the same dimension as it
|
||||||
|
to use the GPS API. The reason for this is that ComputerCraft mimics real-life GPS by making use of the distance
|
||||||
|
parameter of @{modem_message|modem messages} and some maths.
|
||||||
|
:::
|
||||||
|
|
||||||
|
Locate where you want to place your GPS constellation. You will need an area at least 6 blocks high, 6 blocks wide, and
|
||||||
|
6 blocks deep (6x6x6). If you are using wireless modems then you may want to build your constellation as high as you can
|
||||||
|
because high altitude boosts modem message range and thus the radius that your constellation covers.
|
||||||
|
|
||||||
|
The GPS constellation will only work when it is in a loaded chunk. If you want your constellation to always be
|
||||||
|
accessible, you may want to permanently load the chunk using a vanilla or modded chunk loader. Make sure that your 6x6x6
|
||||||
|
area fits in a single chunk to reduce the number of chunks that need to be kept loaded.
|
||||||
|
|
||||||
|
Let's get started building the constellation! Place your first computer in one of the corners of your 6x6x6. Remember
|
||||||
|
which computer this is as other computers need to be placed relative to it. Place the second computer 4 blocks above the
|
||||||
|
first. Go back to your first computer and place your third computer 5 blocks in front of your first computer, leaving 4
|
||||||
|
blocks of air between them. Finally for the fourth computer, go back to your first computer and place it 5 blocks right
|
||||||
|
of your first computer, leaving 4 blocks of air between them.
|
||||||
|
|
||||||
|
With all four computers placed within the 6x6x6, place one modem on top of each computer. You should have 4 modems and 4
|
||||||
|
computers all within your 6x6x6 where each modem is attached to a computer and each computer has a modem.
|
||||||
|
|
||||||
|
Currently your GPS constellation will not work, that's because each host is not aware that it's a GPS host. We will fix
|
||||||
|
this in the next section.
|
||||||
|
|
||||||
|
## Configuring the constellation
|
||||||
|
Now that the structure of your constellation is built, we need to configure each host in it.
|
||||||
|
|
||||||
|
Go back to the first computer that you placed and create a startup file, by running `edit startup`.
|
||||||
|
|
||||||
|
Type the following code into the file:
|
||||||
|
```lua
|
||||||
|
shell.run("gps", "host", x, y, z)
|
||||||
|
```
|
||||||
|
|
||||||
|
Escape from the computer GUI and then press <kbd>F3</kbd> to open Minecraft's debug screen and then look at the computer
|
||||||
|
(without opening the GUI). On the right of the screen about halfway down you should see an entry labeled `Targeted
|
||||||
|
Block`, the numbers correspond to the position of the block that you are looking at. Replace `x` with the first number,
|
||||||
|
`y` with the second number, and `z` with the third number.
|
||||||
|
|
||||||
|
For example, if I had a computer at x = 59, y = 5, z = -150, then my <kbd>F3</kbd> debug screen entry would be `Target
|
||||||
|
Block: 59, 5, -150` and I would change my startup file to this `shell.run("gps", "host", 59, 5, -150)`.
|
||||||
|
|
||||||
|
To hide Minecraft's debug screen, press <kbd>F3</kbd> again.
|
||||||
|
|
||||||
|
Create similar startup files for the other computers in your constellation, making sure to input the each computer's own
|
||||||
|
coordinates.
|
||||||
|
|
||||||
|
:::caution Modem messages come from the computer's position, not the modem's
|
||||||
|
Wireless modems transmit from the block that they are attached to *not* the block space that they occupy, the
|
||||||
|
coordinates that you input into your GPS host should be the position of the computer and not the position of the modem.
|
||||||
|
:::
|
||||||
|
|
||||||
|
Congratulations, your constellation is now fully set up! You can test it by placing another computer close by, placing a
|
||||||
|
wireless modem on it, and running the `gps locate` program (or calling the @{gps.locate} function).
|
||||||
|
|
||||||
|
:::info Why use Minecraft's coordinates?
|
||||||
|
CC doesn't care if you use Minecraft's coordinate system, so long as all of the GPS hosts with overlapping ranges use
|
||||||
|
the same reference point (requesting computers will get confused if hosts have different reference points). However,
|
||||||
|
using MC's coordinate system does provide a nice standard to adopt server-wide. It also is consistent with how command
|
||||||
|
computers get their location, they use MC's command system to get their block which returns that in MC's coordinate
|
||||||
|
system.
|
||||||
|
:::
|
@@ -25,7 +25,7 @@ single-player or multiplayer. Look for lines that look like this:
|
|||||||
```
|
```
|
||||||
|
|
||||||
On 1.95.0 and later, this will be a single entry with `host = "$private"`. On earlier versions, this will be a number of
|
On 1.95.0 and later, this will be a single entry with `host = "$private"`. On earlier versions, this will be a number of
|
||||||
`[[http.rules]]` with various IP addresses. You will want to remove all of the `[[http.rules]]` entires that have
|
`[[http.rules]]` with various IP addresses. You will want to remove all of the `[[http.rules]]` entries that have
|
||||||
`action = "deny"`. Then save the file and relaunch Minecraft (Server).
|
`action = "deny"`. Then save the file and relaunch Minecraft (Server).
|
||||||
|
|
||||||
Here's what it should look like after removing:
|
Here's what it should look like after removing:
|
||||||
@@ -54,7 +54,7 @@ like this:
|
|||||||
```toml
|
```toml
|
||||||
#A list of wildcards for domains or IP ranges that cannot be accessed through the "http" API on Computers.
|
#A list of wildcards for domains or IP ranges that cannot be accessed through the "http" API on Computers.
|
||||||
#If this is empty then all whitelisted domains will be accessible. Example: "*.github.com" will block access to all subdomains of github.com.
|
#If this is empty then all whitelisted domains will be accessible. Example: "*.github.com" will block access to all subdomains of github.com.
|
||||||
#You can use domain names ("pastebin.com"), wilcards ("*.pastebin.com") or CIDR notation ("127.0.0.0/8").
|
#You can use domain names ("pastebin.com"), wildcards ("*.pastebin.com") or CIDR notation ("127.0.0.0/8").
|
||||||
blacklist = ["127.0.0.0/8", "10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16", "fd00::/8"]
|
blacklist = ["127.0.0.0/8", "10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16", "fd00::/8"]
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -65,7 +65,7 @@ Here's what it should look like after removing:
|
|||||||
```toml
|
```toml
|
||||||
#A list of wildcards for domains or IP ranges that cannot be accessed through the "http" API on Computers.
|
#A list of wildcards for domains or IP ranges that cannot be accessed through the "http" API on Computers.
|
||||||
#If this is empty then all whitelisted domains will be accessible. Example: "*.github.com" will block access to all subdomains of github.com.
|
#If this is empty then all whitelisted domains will be accessible. Example: "*.github.com" will block access to all subdomains of github.com.
|
||||||
#You can use domain names ("pastebin.com"), wilcards ("*.pastebin.com") or CIDR notation ("127.0.0.0/8").
|
#You can use domain names ("pastebin.com"), wildcards ("*.pastebin.com") or CIDR notation ("127.0.0.0/8").
|
||||||
blacklist = []
|
blacklist = []
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@@ -185,7 +185,7 @@ end
|
|||||||
|
|
||||||
:::note Confused?
|
:::note Confused?
|
||||||
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 [Discord] or [IRC] either!
|
cover. That said, don't be afraid to ask on [GitHub Discussions] or [IRC] either!
|
||||||
:::
|
:::
|
||||||
|
|
||||||
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
|
||||||
@@ -200,6 +200,5 @@ 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
|
||||||
[Discord]: https://discord.computercraft.cc "The Minecraft Computer Mods Discord"
|
[IRC]: https://webchat.esper.net/?channels=computercraft "#computercraft on EsperNet"
|
||||||
[IRC]: http://webchat.esper.net/?channels=computercraft "IRC webchat on EsperNet"
|
|
||||||
|
BIN
doc/images/gps-constellation-example.png
Normal file
BIN
doc/images/gps-constellation-example.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 331 KiB |
@@ -37,8 +37,7 @@ little daunting getting started. Thankfully, there's several fantastic tutorials
|
|||||||
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 pop in to the [Minecraft Computer Mod Discord guild][discord] or ComputerCraft's
|
If you get stuck, do [ask a question on GitHub][GitHub Discussions] or pop in to the ComputerCraft's [IRC channel][IRC].
|
||||||
[IRC channel][irc].
|
|
||||||
|
|
||||||
## 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,5 +50,5 @@ CC: Tweaked lives on [GitHub]. If you've got any ideas, feedback or bugs please
|
|||||||
[forge]: https://files.minecraftforge.net/ "Download Minecraft Forge."
|
[forge]: https://files.minecraftforge.net/ "Download Minecraft Forge."
|
||||||
[ccrestitched]: https://www.curseforge.com/minecraft/mc-mods/cc-restitched "Download CC: Restitched from CurseForge"
|
[ccrestitched]: https://www.curseforge.com/minecraft/mc-mods/cc-restitched "Download CC: Restitched from CurseForge"
|
||||||
[lua]: https://www.lua.org/ "Lua's main website"
|
[lua]: https://www.lua.org/ "Lua's main website"
|
||||||
[discord]: https://discord.computercraft.cc "The Minecraft Computer Mods Discord"
|
[GitHub Discussions]: https://github.com/cc-tweaked/CC-Tweaked/discussions
|
||||||
[irc]: http://webchat.esper.net/?channels=computercraft "IRC webchat on EsperNet"
|
[IRC]: https://webchat.esper.net/?channels=computercraft "#computercraft on EsperNet"
|
||||||
|
61
doc/mod-page.md
Normal file
61
doc/mod-page.md
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
# 
|
||||||
|
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 better performance, stability, and a wealth of new features.
|
||||||
|
|
||||||
|
**Fabric support is added by the [CC: Restitched][ccrestitched] project**
|
||||||
|
|
||||||
|
## Testimonials
|
||||||
|
|
||||||
|
> I'm not sure what that is [...] I don't know where that came from.
|
||||||
|
>
|
||||||
|
> \- [direwolf20, December 2020](https://youtu.be/D8Ue9I-SKeM?t=980)
|
||||||
|
|
||||||
|
> It is basically ComputerCraft. It has the turtles, and the computers, and writing Lua programs and all that stuff.
|
||||||
|
>
|
||||||
|
> \- [direwolf20, May 2022](https://youtu.be/7Ruq33XmQIQ?t=537)
|
||||||
|
|
||||||
|
|
||||||
|
## Features
|
||||||
|
Controlled using the [Lua programming language][lua], CC: Tweaked's computers provides all the tools you need to start
|
||||||
|
writing code and automating your Minecraft world.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
While computers are incredibly powerful, they're rather limited by their inability to move about. *Turtles* are the
|
||||||
|
solution here. They can move about the world, placing and breaking blocks, swinging a sword to protect you from zombies,
|
||||||
|
or whatever else you program them to!
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Not all problems can be solved with a pickaxe though, and so CC: Tweaked also provides a bunch of additional peripherals
|
||||||
|
for your computers. You can play a tune with speakers, display text or images on a monitor, connect all your
|
||||||
|
computers together with modems, and much more.
|
||||||
|
|
||||||
|
Computers can now also interact with inventories such as chests, allowing you to build complex inventory and item
|
||||||
|
management systems.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Getting Started
|
||||||
|
While ComputerCraft is lovely for both experienced programmers and for people who have never coded before, it can be a
|
||||||
|
little daunting getting started. Thankfully, there's several fantastic tutorials out there:
|
||||||
|
|
||||||
|
- [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")
|
||||||
|
- [Lyqyd's Computer Basics 1](http://www.computercraft.info/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
|
||||||
|
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].
|
||||||
|
|
||||||
|
## Get Involved
|
||||||
|
CC: Tweaked lives on [GitHub]. If you've got any ideas, feedback or bugs please do [create an issue][bug].
|
||||||
|
|
||||||
|
[github]: https://github.com/cc-tweaked/CC-Tweaked/ "CC: Tweaked on GitHub"
|
||||||
|
[bug]: https://github.com/cc-tweaked/CC-Tweaked/issues/new/choose
|
||||||
|
[computercraft]: https://github.com/dan200/ComputerCraft "ComputerCraft on GitHub"
|
||||||
|
[forge]: https://files.minecraftforge.net/ "Download Minecraft Forge."
|
||||||
|
[ccrestitched]: https://modrinth.com/mod/cc-restitched "Download CC: Restitched from Modrinth"
|
||||||
|
[lua]: https://www.lua.org/ "Lua's main website"
|
||||||
|
[GitHub Discussions]: https://github.com/cc-tweaked/CC-Tweaked/discussions
|
||||||
|
[IRC]: http://webchat.esper.net/?channels=computercraft "#computercraft on EsperNet"
|
@@ -1,34 +0,0 @@
|
|||||||
--- @module fs
|
|
||||||
|
|
||||||
--- Returns true if a path is mounted to the parent filesystem.
|
|
||||||
--
|
|
||||||
-- The root filesystem "/" is considered a mount, along with disk folders and
|
|
||||||
-- the rom folder. Other programs (such as network shares) can exstend this to
|
|
||||||
-- make other mount types by correctly assigning their return value for getDrive.
|
|
||||||
--
|
|
||||||
-- @tparam string path The path to check.
|
|
||||||
-- @treturn boolean If the path is mounted, rather than a normal file/folder.
|
|
||||||
-- @throws If the path does not exist.
|
|
||||||
-- @see getDrive
|
|
||||||
-- @since 1.87.0
|
|
||||||
function isDriveRoot(path) end
|
|
||||||
|
|
||||||
--[[- Provides completion for a file or directory name, suitable for use with
|
|
||||||
@{_G.read}.
|
|
||||||
|
|
||||||
When a directory is a possible candidate for completion, two entries are
|
|
||||||
included - one with a trailing slash (indicating that entries within this
|
|
||||||
directory exist) and one without it (meaning this entry is an immediate
|
|
||||||
completion candidate). `include_dirs` can be set to @{false} to only include
|
|
||||||
those with a trailing slash.
|
|
||||||
|
|
||||||
@tparam string path The path to complete.
|
|
||||||
@tparam string location The location where paths are resolved from.
|
|
||||||
@tparam[opt] boolean include_files When @{false}, only directories will be
|
|
||||||
included in the returned list.
|
|
||||||
@tparam[opt] boolean include_dirs When @{false}, "raw" directories will not be
|
|
||||||
included in the returned list.
|
|
||||||
@treturn { string... } A list of possible completion candidates.
|
|
||||||
@since 1.74
|
|
||||||
]]
|
|
||||||
function complete(path, location, include_files, include_dirs) end
|
|
@@ -14,7 +14,7 @@ thread, not the whole program.
|
|||||||
|
|
||||||
:::tip
|
:::tip
|
||||||
Because sleep internally uses timers, it is a function that yields. This means
|
Because sleep internally uses timers, it is a function that yields. This means
|
||||||
that you can use it to prevent "Too long without yielding" errors, however, as
|
that you can use it to prevent "Too long without yielding" errors. However, as
|
||||||
the minimum sleep time is 0.05 seconds, it will slow your program down.
|
the minimum sleep time is 0.05 seconds, it will slow your program down.
|
||||||
:::
|
:::
|
||||||
|
|
||||||
|
@@ -1,177 +0,0 @@
|
|||||||
--- Make HTTP requests, sending and receiving data to a remote web server.
|
|
||||||
--
|
|
||||||
-- @module http
|
|
||||||
-- @since 1.1
|
|
||||||
-- @see local_ips To allow accessing servers running on your local network.
|
|
||||||
|
|
||||||
--- Asynchronously make a HTTP request to the given url.
|
|
||||||
--
|
|
||||||
-- This returns immediately, a @{http_success} or @{http_failure} will be queued
|
|
||||||
-- once the request has completed.
|
|
||||||
--
|
|
||||||
-- @tparam string url The url to request
|
|
||||||
-- @tparam[opt] string body An optional string containing the body of the
|
|
||||||
-- request. If specified, a `POST` request will be made instead.
|
|
||||||
-- @tparam[opt] { [string] = string } headers Additional headers to send as part
|
|
||||||
-- of this request.
|
|
||||||
-- @tparam[opt] boolean binary Whether to make a binary HTTP request. If true,
|
|
||||||
-- the body will not be UTF-8 encoded, and the received response will not be
|
|
||||||
-- decoded.
|
|
||||||
--
|
|
||||||
-- @tparam[2] {
|
|
||||||
-- url = string, body? = string, headers? = { [string] = string },
|
|
||||||
-- binary? = boolean, method? = string, redirect? = boolean,
|
|
||||||
-- } request Options for the request.
|
|
||||||
--
|
|
||||||
-- This table form is an expanded version of the previous syntax. All arguments
|
|
||||||
-- from above are passed in as fields instead (for instance,
|
|
||||||
-- `http.request("https://example.com")` becomes `http.request { url =
|
|
||||||
-- "https://example.com" }`).
|
|
||||||
--
|
|
||||||
-- This table also accepts several additional options:
|
|
||||||
--
|
|
||||||
-- - `method`: Which HTTP method to use, for instance `"PATCH"` or `"DELETE"`.
|
|
||||||
-- - `redirect`: Whether to follow HTTP redirects. Defaults to true.
|
|
||||||
--
|
|
||||||
-- @see http.get For a synchronous way to make GET requests.
|
|
||||||
-- @see http.post For a synchronous way to make POST requests.
|
|
||||||
--
|
|
||||||
-- @changed 1.63 Added argument for headers.
|
|
||||||
-- @changed 1.80pr1 Added argument for binary handles.
|
|
||||||
-- @changed 1.80pr1.6 Added support for table argument.
|
|
||||||
-- @changed 1.86.0 Added PATCH and TRACE methods.
|
|
||||||
function request(...) end
|
|
||||||
|
|
||||||
--- Make a HTTP GET request to the given url.
|
|
||||||
--
|
|
||||||
-- @tparam string url The url to request
|
|
||||||
-- @tparam[opt] { [string] = string } headers Additional headers to send as part
|
|
||||||
-- of this request.
|
|
||||||
-- @tparam[opt] boolean binary Whether to make a binary HTTP request. If true,
|
|
||||||
-- the body will not be UTF-8 encoded, and the received response will not be
|
|
||||||
-- decoded.
|
|
||||||
--
|
|
||||||
-- @tparam[2] {
|
|
||||||
-- url = string, headers? = { [string] = string },
|
|
||||||
-- binary? = boolean, method? = string, redirect? = boolean,
|
|
||||||
-- } request Options for the request. See @{http.request} for details on how
|
|
||||||
-- these options behave.
|
|
||||||
--
|
|
||||||
-- @treturn Response The resulting http response, which can be read from.
|
|
||||||
-- @treturn[2] nil When the http request failed, such as in the event of a 404
|
|
||||||
-- error or connection timeout.
|
|
||||||
-- @treturn string A message detailing why the request failed.
|
|
||||||
-- @treturn Response|nil The failing http response, if available.
|
|
||||||
--
|
|
||||||
-- @changed 1.63 Added argument for headers.
|
|
||||||
-- @changed 1.80pr1 Response handles are now returned on error if available.
|
|
||||||
-- @changed 1.80pr1 Added argument for binary handles.
|
|
||||||
-- @changed 1.80pr1.6 Added support for table argument.
|
|
||||||
-- @changed 1.86.0 Added PATCH and TRACE methods.
|
|
||||||
--
|
|
||||||
-- @usage Make a request to [example.tweaked.cc](https://example.tweaked.cc),
|
|
||||||
-- and print the returned page.
|
|
||||||
-- ```lua
|
|
||||||
-- local request = http.get("https://example.tweaked.cc")
|
|
||||||
-- print(request.readAll())
|
|
||||||
-- -- => HTTP is working!
|
|
||||||
-- request.close()
|
|
||||||
-- ```
|
|
||||||
function get(...) end
|
|
||||||
|
|
||||||
--- Make a HTTP POST request to the given url.
|
|
||||||
--
|
|
||||||
-- @tparam string url The url to request
|
|
||||||
-- @tparam string body The body of the POST request.
|
|
||||||
-- @tparam[opt] { [string] = string } headers Additional headers to send as part
|
|
||||||
-- of this request.
|
|
||||||
-- @tparam[opt] boolean binary Whether to make a binary HTTP request. If true,
|
|
||||||
-- the body will not be UTF-8 encoded, and the received response will not be
|
|
||||||
-- decoded.
|
|
||||||
--
|
|
||||||
-- @tparam[2] {
|
|
||||||
-- url = string, body? = string, headers? = { [string] = string },
|
|
||||||
-- binary? = boolean, method? = string, redirect? = boolean,
|
|
||||||
-- } request Options for the request. See @{http.request} for details on how
|
|
||||||
-- these options behave.
|
|
||||||
--
|
|
||||||
-- @treturn Response The resulting http response, which can be read from.
|
|
||||||
-- @treturn[2] nil When the http request failed, such as in the event of a 404
|
|
||||||
-- error or connection timeout.
|
|
||||||
-- @treturn string A message detailing why the request failed.
|
|
||||||
-- @treturn Response|nil The failing http response, if available.
|
|
||||||
--
|
|
||||||
-- @since 1.31
|
|
||||||
-- @changed 1.63 Added argument for headers.
|
|
||||||
-- @changed 1.80pr1 Response handles are now returned on error if available.
|
|
||||||
-- @changed 1.80pr1 Added argument for binary handles.
|
|
||||||
-- @changed 1.80pr1.6 Added support for table argument.
|
|
||||||
-- @changed 1.86.0 Added PATCH and TRACE methods.
|
|
||||||
function post(...) end
|
|
||||||
|
|
||||||
--- Asynchronously determine whether a URL can be requested.
|
|
||||||
--
|
|
||||||
-- If this returns `true`, one should also listen for @{http_check} which will
|
|
||||||
-- container further information about whether the URL is allowed or not.
|
|
||||||
--
|
|
||||||
-- @tparam string url The URL to check.
|
|
||||||
-- @treturn true When this url is not invalid. This does not imply that it is
|
|
||||||
-- allowed - see the comment above.
|
|
||||||
-- @treturn[2] false When this url is invalid.
|
|
||||||
-- @treturn string A reason why this URL is not valid (for instance, if it is
|
|
||||||
-- malformed, or blocked).
|
|
||||||
--
|
|
||||||
-- @see http.checkURL For a synchronous version.
|
|
||||||
function checkURLAsync(url) end
|
|
||||||
|
|
||||||
--- Determine whether a URL can be requested.
|
|
||||||
--
|
|
||||||
-- If this returns `true`, one should also listen for @{http_check} which will
|
|
||||||
-- container further information about whether the URL is allowed or not.
|
|
||||||
--
|
|
||||||
-- @tparam string url The URL to check.
|
|
||||||
-- @treturn true When this url is valid and can be requested via @{http.request}.
|
|
||||||
-- @treturn[2] false When this url is invalid.
|
|
||||||
-- @treturn string A reason why this URL is not valid (for instance, if it is
|
|
||||||
-- malformed, or blocked).
|
|
||||||
--
|
|
||||||
-- @see http.checkURLAsync For an asynchronous version.
|
|
||||||
--
|
|
||||||
-- @usage
|
|
||||||
-- ```lua
|
|
||||||
-- print(http.checkURL("https://example.tweaked.cc/"))
|
|
||||||
-- -- => true
|
|
||||||
-- print(http.checkURL("http://localhost/"))
|
|
||||||
-- -- => false Domain not permitted
|
|
||||||
-- print(http.checkURL("not a url"))
|
|
||||||
-- -- => false URL malformed
|
|
||||||
-- ```
|
|
||||||
function checkURL(url) end
|
|
||||||
|
|
||||||
--- Open a websocket.
|
|
||||||
--
|
|
||||||
-- @tparam string url The websocket url to connect to. This should have the
|
|
||||||
-- `ws://` or `wss://` protocol.
|
|
||||||
-- @tparam[opt] { [string] = string } headers Additional headers to send as part
|
|
||||||
-- of the initial websocket connection.
|
|
||||||
--
|
|
||||||
-- @treturn Websocket The websocket connection.
|
|
||||||
-- @treturn[2] false If the websocket connection failed.
|
|
||||||
-- @treturn string An error message describing why the connection failed.
|
|
||||||
-- @since 1.80pr1.1
|
|
||||||
-- @changed 1.80pr1.3 No longer asynchronous.
|
|
||||||
-- @changed 1.95.3 Added User-Agent to default headers.
|
|
||||||
function websocket(url, headers) end
|
|
||||||
|
|
||||||
--- Asynchronously open a websocket.
|
|
||||||
--
|
|
||||||
-- This returns immediately, a @{websocket_success} or @{websocket_failure}
|
|
||||||
-- will be queued once the request has completed.
|
|
||||||
--
|
|
||||||
-- @tparam string url The websocket url to connect to. This should have the
|
|
||||||
-- `ws://` or `wss://` protocol.
|
|
||||||
-- @tparam[opt] { [string] = string } headers Additional headers to send as part
|
|
||||||
-- of the initial websocket connection.
|
|
||||||
-- @since 1.80pr1.3
|
|
||||||
-- @changed 1.95.3 Added User-Agent to default headers.
|
|
||||||
function websocketAsync(url, headers) end
|
|
@@ -1,10 +1,12 @@
|
|||||||
org.gradle.jvmargs=-Xmx3G
|
org.gradle.jvmargs=-Xmx3G
|
||||||
|
org.gradle.parallel=true
|
||||||
|
|
||||||
|
kotlin.stdlib.default.dependency=false
|
||||||
|
kotlin.jvm.target.validation.mode=error
|
||||||
|
|
||||||
# Mod properties
|
# Mod properties
|
||||||
mod_version=1.100.6
|
isUnstable=false
|
||||||
|
modVersion=1.103.0
|
||||||
|
|
||||||
# Minecraft properties (update mods.toml when changing)
|
# Minecraft properties: We want to configure this here so we can read it in settings.gradle
|
||||||
mc_version=1.19
|
mcVersion=1.19.3
|
||||||
mapping_version=2022.03.13
|
|
||||||
forge_version=41.0.16
|
|
||||||
# NO SERIOUSLY, UPDATE mods.toml WHEN CHANGING
|
|
||||||
|
154
gradle/libs.versions.toml
Normal file
154
gradle/libs.versions.toml
Normal file
@@ -0,0 +1,154 @@
|
|||||||
|
[versions]
|
||||||
|
|
||||||
|
# Minecraft
|
||||||
|
# MC version is specified in gradle.properties, as we need that in settings.gradle.
|
||||||
|
fabric-api = "0.68.1+1.19.3"
|
||||||
|
fabric-loader = "0.14.11"
|
||||||
|
forge = "44.1.0"
|
||||||
|
forgeSpi = "6.0.0"
|
||||||
|
mixin = "0.8.5"
|
||||||
|
parchment = "2022.11.27"
|
||||||
|
parchmentMc = "1.19.2"
|
||||||
|
|
||||||
|
# Normal dependencies
|
||||||
|
asm = "9.3"
|
||||||
|
autoService = "1.0.1"
|
||||||
|
checkerFramework = "3.12.0"
|
||||||
|
cobalt = "0.6.0"
|
||||||
|
fastutil = "8.5.9"
|
||||||
|
guava = "31.1-jre"
|
||||||
|
jetbrainsAnnotations = "23.0.0"
|
||||||
|
jsr305 = "3.0.2"
|
||||||
|
kotlin = "1.8.0"
|
||||||
|
kotlin-coroutines = "1.6.4"
|
||||||
|
netty = "4.1.82.Final"
|
||||||
|
nightConfig = "3.6.5"
|
||||||
|
slf4j = "1.7.36"
|
||||||
|
|
||||||
|
# Minecraft mods
|
||||||
|
forgeConfig = "5.0.4"
|
||||||
|
iris = "1.19.3-v1.4.6"
|
||||||
|
jei = "11.3.0.262"
|
||||||
|
modmenu = "5.0.1"
|
||||||
|
oculus = "1.2.5"
|
||||||
|
rei = "10.0.578"
|
||||||
|
rubidium = "0.6.1"
|
||||||
|
sodium = "mc1.19.3-0.4.6"
|
||||||
|
|
||||||
|
# Testing
|
||||||
|
byteBuddy = "1.12.19"
|
||||||
|
hamcrest = "2.2"
|
||||||
|
jqwik = "1.7.0"
|
||||||
|
junit = "5.9.1"
|
||||||
|
|
||||||
|
# Build tools
|
||||||
|
cctJavadoc = "1.6.0"
|
||||||
|
checkstyle = "10.3.4"
|
||||||
|
curseForgeGradle = "1.0.11"
|
||||||
|
errorProne-core = "2.14.0"
|
||||||
|
errorProne-plugin = "2.0.2"
|
||||||
|
fabric-loom = "1.0-SNAPSHOT"
|
||||||
|
forgeGradle = "5.1.+"
|
||||||
|
githubRelease = "2.2.12"
|
||||||
|
ideaExt = "1.1.6"
|
||||||
|
illuaminate = "0.1.0-13-g689d73d"
|
||||||
|
librarian = "1.+"
|
||||||
|
minotaur = "2.+"
|
||||||
|
mixinGradle = "0.7.+"
|
||||||
|
nullAway = "0.9.9"
|
||||||
|
quiltflower = "1.7.3"
|
||||||
|
shadow = "7.1.2"
|
||||||
|
spotless = "6.8.0"
|
||||||
|
taskTree = "2.1.0"
|
||||||
|
vanillaGradle = "0.2.1-SNAPSHOT"
|
||||||
|
|
||||||
|
[libraries]
|
||||||
|
# Normal dependencies
|
||||||
|
asm = { module = "org.ow2.asm:asm", version.ref = "asm" }
|
||||||
|
autoService = { module = "com.google.auto.service:auto-service", version.ref = "autoService" }
|
||||||
|
checkerFramework = { module = "org.checkerframework:checker-qual", version.ref = "checkerFramework" }
|
||||||
|
cobalt = { module = "org.squiddev:Cobalt", version.ref = "cobalt" }
|
||||||
|
fastutil = { module = "it.unimi.dsi:fastutil", version.ref = "fastutil" }
|
||||||
|
forgeSpi = { module = "net.minecraftforge:forgespi", version.ref = "forgeSpi" }
|
||||||
|
guava = { module = "com.google.guava:guava", version.ref = "guava" }
|
||||||
|
jetbrainsAnnotations = { module = "org.jetbrains:annotations", version.ref = "jetbrainsAnnotations" }
|
||||||
|
jsr305 = { module = "com.google.code.findbugs:jsr305", version.ref = "jsr305" }
|
||||||
|
kotlin-platform = { module = "org.jetbrains.kotlin:kotlin-bom", version.ref = "kotlin" }
|
||||||
|
kotlin-coroutines = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kotlin-coroutines" }
|
||||||
|
kotlin-stdlib = { module = "org.jetbrains.kotlin:kotlin-stdlib", version.ref = "kotlin" }
|
||||||
|
netty-http = { module = "io.netty:netty-codec-http", version.ref = "netty" }
|
||||||
|
nightConfig-core = { module = "com.electronwill.night-config:core", version.ref = "nightConfig" }
|
||||||
|
nightConfig-toml = { module = "com.electronwill.night-config:toml", version.ref = "nightConfig" }
|
||||||
|
slf4j = { module = "org.slf4j:slf4j-api", version.ref = "slf4j" }
|
||||||
|
|
||||||
|
# Minecraft mods
|
||||||
|
fabric-api = { module = "net.fabricmc.fabric-api:fabric-api", version.ref = "fabric-api" }
|
||||||
|
fabric-loader = { module = "net.fabricmc:fabric-loader", version.ref = "fabric-loader" }
|
||||||
|
forgeConfig = { module = "fuzs.forgeconfigapiport:forgeconfigapiport-fabric", version.ref = "forgeConfig" }
|
||||||
|
iris = { module = "maven.modrinth:iris", version.ref = "iris" }
|
||||||
|
jei-api = { module = "mezz.jei:jei-1.19.2-common-api", version.ref = "jei" }
|
||||||
|
jei-fabric = { module = "mezz.jei:jei-1.19.2-fabric", version.ref = "jei" }
|
||||||
|
jei-forge = { module = "mezz.jei:jei-1.19.2-forge", version.ref = "jei" }
|
||||||
|
mixin = { module = "org.spongepowered:mixin", version.ref = "mixin" }
|
||||||
|
modmenu = { module = "com.terraformersmc:modmenu", version.ref = "modmenu" }
|
||||||
|
oculus = { module = "maven.modrinth:oculus", version.ref = "oculus" }
|
||||||
|
rei-api = { module = "me.shedaniel:RoughlyEnoughItems-api", version.ref = "rei" }
|
||||||
|
rei-builtin = { module = "me.shedaniel:RoughlyEnoughItems-default-plugin", version.ref = "rei" }
|
||||||
|
rei-fabric = { module = "me.shedaniel:RoughlyEnoughItems-fabric", version.ref = "rei" }
|
||||||
|
rubidium = { module = "maven.modrinth:rubidium", version.ref = "rubidium" }
|
||||||
|
sodium = { module = "maven.modrinth:sodium", version.ref = "sodium" }
|
||||||
|
|
||||||
|
# Testing
|
||||||
|
byteBuddyAgent = { module ="net.bytebuddy:byte-buddy-agent", version.ref = "byteBuddy" }
|
||||||
|
byteBuddy = { module ="net.bytebuddy:byte-buddy", version.ref = "byteBuddy" }
|
||||||
|
hamcrest = { module = "org.hamcrest:hamcrest", version.ref = "hamcrest" }
|
||||||
|
jqwik-api = { module = "net.jqwik:jqwik-api", version.ref = "jqwik" }
|
||||||
|
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-engine = { module = "org.junit.jupiter:junit-jupiter-engine", version.ref = "junit" }
|
||||||
|
junit-jupiter-params = { module = "org.junit.jupiter:junit-jupiter-params", version.ref = "junit" }
|
||||||
|
slf4j-simple = { module = "org.slf4j:slf4j-simple", version.ref = "slf4j" }
|
||||||
|
|
||||||
|
# Build tools
|
||||||
|
cctJavadoc = { module = "cc.tweaked:cct-javadoc", version.ref = "cctJavadoc" }
|
||||||
|
checkstyle = { module = "com.puppycrawl.tools:checkstyle", version.ref = "checkstyle" }
|
||||||
|
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-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-testHelpers = { module = "com.google.errorprone:error_prone_test_helpers", version.ref = "errorProne-core" }
|
||||||
|
fabric-loom = { module = "net.fabricmc:fabric-loom", version.ref = "fabric-loom" }
|
||||||
|
forgeGradle = { module = "net.minecraftforge.gradle:ForgeGradle", version.ref = "forgeGradle" }
|
||||||
|
kotlin-plugin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" }
|
||||||
|
librarian = { module = "org.parchmentmc:librarian", version.ref = "librarian" }
|
||||||
|
nullAway = { module = "com.uber.nullaway:nullaway", version.ref = "nullAway" }
|
||||||
|
quiltflower = { module = "io.github.juuxel:loom-quiltflower", version.ref = "quiltflower" }
|
||||||
|
spotless = { module = "com.diffplug.spotless:spotless-plugin-gradle", version.ref = "spotless" }
|
||||||
|
vanillaGradle = { module = "org.spongepowered:vanillagradle", version.ref = "vanillaGradle" }
|
||||||
|
|
||||||
|
[plugins]
|
||||||
|
curseForgeGradle = { id = "net.darkhax.curseforgegradle", version.ref = "curseForgeGradle" }
|
||||||
|
forgeGradle = { id = "net.minecraftforge.gradle", version.ref = "forgeGradle" }
|
||||||
|
githubRelease = { id = "com.github.breadmoirai.github-release", version.ref = "githubRelease" }
|
||||||
|
ideaExt = { id = "org.jetbrains.gradle.plugin.idea-ext", version.ref = "ideaExt" }
|
||||||
|
kotlin = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
|
||||||
|
librarian = { id = "org.parchmentmc.librarian.forgegradle", version.ref = "librarian" }
|
||||||
|
minotaur = { id = "com.modrinth.minotaur", version.ref = "minotaur" }
|
||||||
|
mixinGradle = { id = "org.spongepowered.mixin", version.ref = "mixinGradle" }
|
||||||
|
shadow = { id = "com.github.johnrengelman.shadow", version.ref = "shadow" }
|
||||||
|
taskTree = { id = "com.dorongold.task-tree", version.ref = "taskTree" }
|
||||||
|
|
||||||
|
[bundles]
|
||||||
|
kotlin = ["kotlin-stdlib", "kotlin-coroutines"]
|
||||||
|
|
||||||
|
# Minecraft
|
||||||
|
externalMods-common = ["jei-api", "forgeConfig", "nightConfig-core", "nightConfig-toml"]
|
||||||
|
externalMods-forge-compile = ["oculus", "jei-api"]
|
||||||
|
externalMods-forge-runtime = []
|
||||||
|
externalMods-fabric = ["fabric-loader", "fabric-api", "forgeConfig", "nightConfig-core", "nightConfig-toml"]
|
||||||
|
externalMods-fabric-compile = ["iris", "jei-api", "rei-api", "rei-builtin"]
|
||||||
|
externalMods-fabric-runtime = ["modmenu"]
|
||||||
|
|
||||||
|
# Testing
|
||||||
|
test = ["junit-jupiter-api", "junit-jupiter-params", "hamcrest", "jqwik-api"]
|
||||||
|
testRuntime = ["junit-jupiter-engine", "jqwik-engine"]
|
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,5 +1,5 @@
|
|||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3-bin.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
|
@@ -2,25 +2,26 @@
|
|||||||
|
|
||||||
(sources
|
(sources
|
||||||
/doc/
|
/doc/
|
||||||
/build/docs/luaJavadoc/
|
/projects/forge/build/docs/luaJavadoc/
|
||||||
/src/main/resources/*/computercraft/lua/bios.lua
|
/projects/core/src/main/resources/data/computercraft/lua/bios.lua
|
||||||
/src/main/resources/*/computercraft/lua/rom/
|
/projects/core/src/main/resources/data/computercraft/lua/rom/
|
||||||
/src/test/resources/test-rom
|
/projects/core/src/test/resources/test-rom
|
||||||
/src/web/mount)
|
/projects/web/src/mount)
|
||||||
|
|
||||||
|
|
||||||
(doc
|
(doc
|
||||||
(destination build/docs/lua)
|
; Also defined in projects/web/build.gradle.kts
|
||||||
|
(destination /projects/web/build/illuaminate)
|
||||||
(index doc/index.md)
|
(index doc/index.md)
|
||||||
|
|
||||||
(site
|
(site
|
||||||
(title "CC: Tweaked")
|
(title "CC: Tweaked")
|
||||||
(logo src/main/resources/pack.png)
|
(logo projects/common/src/main/resources/pack.png)
|
||||||
(url https://tweaked.cc/)
|
(url https://tweaked.cc/)
|
||||||
(source-link https://github.com/cc-tweaked/CC-Tweaked/blob/${commit}/${path}#L${line})
|
(source-link https://github.com/cc-tweaked/CC-Tweaked/blob/${commit}/${path}#L${line})
|
||||||
|
|
||||||
(styles src/web/styles.css)
|
(styles /projects/web/src/styles.css)
|
||||||
(scripts build/rollup/index.js)
|
(scripts /projects/web/build/rollup/index.js)
|
||||||
(head doc/head.html))
|
(head doc/head.html))
|
||||||
|
|
||||||
(module-kinds
|
(module-kinds
|
||||||
@@ -32,15 +33,15 @@
|
|||||||
|
|
||||||
(library-path
|
(library-path
|
||||||
/doc/stub/
|
/doc/stub/
|
||||||
/build/docs/luaJavadoc/
|
/projects/forge/build/docs/luaJavadoc/
|
||||||
|
|
||||||
/src/main/resources/*/computercraft/lua/rom/apis/
|
/projects/core/src/main/resources/data/computercraft/lua/rom/apis/
|
||||||
/src/main/resources/*/computercraft/lua/rom/apis/command/
|
/projects/core/src/main/resources/data/computercraft/lua/rom/apis/command/
|
||||||
/src/main/resources/*/computercraft/lua/rom/apis/turtle/
|
/projects/core/src/main/resources/data/computercraft/lua/rom/apis/turtle/
|
||||||
|
|
||||||
/src/main/resources/*/computercraft/lua/rom/modules/main/
|
/projects/core/src/main/resources/data/computercraft/lua/rom/modules/main/
|
||||||
/src/main/resources/*/computercraft/lua/rom/modules/command/
|
/projects/core/src/main/resources/data/computercraft/lua/rom/modules/command/
|
||||||
/src/main/resources/*/computercraft/lua/rom/modules/turtle/))
|
/projects/core/src/main/resources/data/computercraft/lua/rom/modules/turtle/))
|
||||||
|
|
||||||
(at /
|
(at /
|
||||||
(linters
|
(linters
|
||||||
@@ -80,37 +81,37 @@
|
|||||||
;; We disable the unused global linter in bios.lua and the APIs. In the future
|
;; We disable the unused global linter in bios.lua and the APIs. In the future
|
||||||
;; hopefully we'll get illuaminate to handle this.
|
;; hopefully we'll get illuaminate to handle this.
|
||||||
(at
|
(at
|
||||||
(/src/main/resources/*/computercraft/lua/bios.lua
|
(/projects/core/src/main/resources/data/computercraft/lua/bios.lua
|
||||||
/src/main/resources/*/computercraft/lua/rom/apis/)
|
/projects/core/src/main/resources/data/computercraft/lua/rom/apis/)
|
||||||
(linters -var:unused-global)
|
(linters -var:unused-global)
|
||||||
(lint (allow-toplevel-global true)))
|
(lint (allow-toplevel-global true)))
|
||||||
|
|
||||||
;; Silence some variable warnings in documentation stubs.
|
;; Silence some variable warnings in documentation stubs.
|
||||||
(at (/doc/stub/ /build/docs/luaJavadoc/)
|
(at (/doc/stub/ /projects/forge/build/docs/luaJavadoc/)
|
||||||
(linters -var:unused-global)
|
(linters -var:unused-global)
|
||||||
(lint (allow-toplevel-global true)))
|
(lint (allow-toplevel-global true)))
|
||||||
|
|
||||||
;; Suppress warnings for currently undocumented modules.
|
;; Suppress warnings for currently undocumented modules.
|
||||||
(at
|
(at
|
||||||
(; Lua APIs
|
(; Lua APIs
|
||||||
/src/main/resources/*/computercraft/lua/rom/apis/io.lua
|
/projects/core/src/main/resources/data/computercraft/lua/rom/apis/io.lua
|
||||||
/src/main/resources/*/computercraft/lua/rom/apis/window.lua)
|
/projects/core/src/main/resources/data/computercraft/lua/rom/apis/window.lua)
|
||||||
|
|
||||||
(linters -doc:undocumented -doc:undocumented-arg -doc:undocumented-return))
|
(linters -doc:undocumented -doc:undocumented-arg -doc:undocumented-return))
|
||||||
|
|
||||||
;; Suppress warnings for various APIs using its own deprecated members.
|
;; Suppress warnings for various APIs using its own deprecated members.
|
||||||
(at
|
(at
|
||||||
(/src/main/resources/*/computercraft/lua/bios.lua
|
(/projects/core/src/main/resources/data/computercraft/lua/bios.lua
|
||||||
/src/main/resources/*/computercraft/lua/rom/apis/turtle/turtle.lua)
|
/projects/core/src/main/resources/data/computercraft/lua/rom/apis/turtle/turtle.lua)
|
||||||
(linters -var:deprecated))
|
(linters -var:deprecated))
|
||||||
|
|
||||||
(at /src/test/resources/test-rom
|
(at /projects/core/src/test/resources/test-rom
|
||||||
; We should still be able to test deprecated members.
|
; We should still be able to test deprecated members.
|
||||||
(linters -var:deprecated)
|
(linters -var:deprecated)
|
||||||
|
|
||||||
(lint
|
(lint
|
||||||
(globals
|
(globals
|
||||||
:max sleep write
|
:max sleep write
|
||||||
cct_test describe expect howlci fail it pending stub)))
|
cct_test describe expect howlci fail it pending stub before_each)))
|
||||||
|
|
||||||
(at /src/web/mount/expr_template.lua (lint (globals :max __expr__)))
|
(at /projects/web/src/mount/expr_template.lua (lint (globals :max __expr__)))
|
||||||
|
640
package-lock.json
generated
640
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -11,7 +11,7 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@rollup/plugin-typescript": "^8.2.5",
|
"@rollup/plugin-typescript": "^8.2.5",
|
||||||
"@rollup/plugin-url": "^6.1.0",
|
"@rollup/plugin-url": "^7.0.0",
|
||||||
"@types/glob": "^7.2.0",
|
"@types/glob": "^7.2.0",
|
||||||
"@types/react-dom": "^18.0.5",
|
"@types/react-dom": "^18.0.5",
|
||||||
"glob": "^8.0.3",
|
"glob": "^8.0.3",
|
||||||
|
159
projects/ARCHITECTURE.md
Normal file
159
projects/ARCHITECTURE.md
Normal file
@@ -0,0 +1,159 @@
|
|||||||
|
# Architecture
|
||||||
|
CC: Tweaked has a rather complex project layout, as there's several use-cases we want to support (multiple mod loaders,
|
||||||
|
usable outside of Minecraft). As such, it can be tricky to understand how the code is structured and how the various
|
||||||
|
sub-projects interact. This document provides a high-level overview of the entire mod.
|
||||||
|
|
||||||
|
## Project Outline
|
||||||
|
CC: Tweaked is split into 4 primary modules (`core`, `common`, `fabric`, `forge`). These themselves are then split into
|
||||||
|
a public API (i.e `core-api`) and the actual implementation (i.e. `core`).
|
||||||
|
|
||||||
|
- `core`: This contains the core "computer" part of ComputerCraft, such as the Lua VM, filesystem and builtin APIs.
|
||||||
|
This is also where the Lua ROM is located (`projects/core/src/main/resources/data/computercraft/lua`). Notably this
|
||||||
|
project does _not_ depend on Minecraft, making it possible to use it in emulators and other tooling.
|
||||||
|
|
||||||
|
- `common`: This contains all non mod-loader-specific Minecraft code. This is where computers, turtles and peripherals
|
||||||
|
are defined (and everything else Minecraft-related!).
|
||||||
|
|
||||||
|
This project is separates client code into its own separate source set (suitably named `client`). This helps us
|
||||||
|
ensure that server code can never reference client-only code (such as LWJGL).
|
||||||
|
|
||||||
|
- `forge` and `fabric`: These contain any mod-loader specific code.
|
||||||
|
|
||||||
|
When we need to call loader-specific code from our own code (for instance, sending network messages or firing
|
||||||
|
loader-specific events), we use a `PlatformHelper` interface (defined in
|
||||||
|
`projects/common/src/main/java/dan200/computercraft/shared/platform/PlatformHelper.java`). This abstracts over most
|
||||||
|
loader-specific code we need to use, and is then implemented by each mod-loader-specific project. The concrete
|
||||||
|
implementation is then loaded with Java's [`ServiceLoader`][ServiceLoader], in a design based on [jaredlll08's
|
||||||
|
multi-loader template][MultiLoader-Template]. We use a similar system for communicating between the API and its
|
||||||
|
implementation.
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart LR
|
||||||
|
subgraph Common
|
||||||
|
platform(PlatformHelper)
|
||||||
|
impl[AbstractComputerCraftAPI]
|
||||||
|
end
|
||||||
|
subgraph API
|
||||||
|
api(ComputerCraft API) --> impl
|
||||||
|
end
|
||||||
|
subgraph Forge[Forge]
|
||||||
|
platform --> forgePlatform[PlatformHelperImpl]
|
||||||
|
impl -.-> forgeImpl[ComputerCraftAPIImpl]
|
||||||
|
end
|
||||||
|
subgraph Fabric
|
||||||
|
platform --> fabricPlatform[PlatformHelperImpl]
|
||||||
|
impl -.-> fabricImpl[ComputerCraftAPIImpl]
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
Note the `PlatformHelper` is only used when calling from our code into loader-specific code. While we use this to _fire_
|
||||||
|
events, we do not use it to _subscribe_ to events. For that we just subscribe to the events in the loader-specific
|
||||||
|
project, and then dispatch to the common `CommonHooks` (for shared code) and `ClientHooks` (for client-specific code).
|
||||||
|
|
||||||
|
You may notice there's a couple of other, smaller modules in the codebase. These you can probably ignore, but are worth
|
||||||
|
mentioning:
|
||||||
|
|
||||||
|
- `lints`: This defines an [ErrorProne] plugin which adds a couple of compile-time checks to our code. This is what
|
||||||
|
enforces that no client-specific code is used inside the `main` source set (and a couple of other things!).
|
||||||
|
|
||||||
|
- `web`: This contains the additional tooling for building [the documentation website][tweaked.cc], such as support for
|
||||||
|
rendering recipes
|
||||||
|
|
||||||
|
- `buildSrc` (in the base directory, not in `projects/`): This contains any build logic shared between modules. For
|
||||||
|
instance, `cc-tweaked.java-convention.gradle.kts` sets up the defaults for Java that we use across the whole project.
|
||||||
|
|
||||||
|
> **Note**
|
||||||
|
> The Forge and Fabric modules (and their API counterparts) depend on the common modules. However, in order to correctly
|
||||||
|
> process mixins we need to compile the common code along with the Forge/Fabric code. This leads to a slightly strange
|
||||||
|
> build process:
|
||||||
|
>
|
||||||
|
> - In your IDE, Forge/Fabric depend on the common as normal.
|
||||||
|
> - When building via Gradle, the common code is compiled alongside Forge/Fabric.
|
||||||
|
>
|
||||||
|
> You shouldn't need to worry about this - it should all be set up automatically - but hopefully explains a little bit
|
||||||
|
> why our Gradle scripts are slightly odd!
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
CC: Tweaked has a small (though growing!) test suite to ensure various features behave correctly. Most tests are written
|
||||||
|
in Java using [JUnit], though we also make use of [jqwik] for property testing.
|
||||||
|
|
||||||
|
### Test Fixtures
|
||||||
|
Some projects define an additional `testFixtures` folder alongside their main `test` code (i.e.
|
||||||
|
`projects/core/src/testFixtures`). This source set contains test-related code which might be consumed in dependent
|
||||||
|
projects. For instance, core's test fixtures defines additional [Hamcrest] matchers, which are used in both `core` and
|
||||||
|
`common`'s test suite.
|
||||||
|
|
||||||
|
Test fixtures may also define [Test Interfaces]. This is a pattern for writing tests to ensure that an implementation
|
||||||
|
obeys its interface's contract. For instance, we might have a `ListContract` test, which asserts an abstract list
|
||||||
|
behaves as expected:
|
||||||
|
|
||||||
|
```java
|
||||||
|
interface ListContract<T extends List<Integer>> {
|
||||||
|
T newList();
|
||||||
|
|
||||||
|
@Test
|
||||||
|
default void testAddInsert() {
|
||||||
|
var list = newList();
|
||||||
|
assertTrue(list.add(123));
|
||||||
|
assertTrue(list.contains(123));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
We can then use this interface to create tests for a specific implementation:
|
||||||
|
|
||||||
|
```java
|
||||||
|
class ArrayListTest implements ListContract<ArrayList<Integer>> {
|
||||||
|
@Override public ArrayList<Integer> newList() { return new ArrayList<>(); }
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This is especially useful when testing `PlatformHelper` and other mod loader abstractions.
|
||||||
|
|
||||||
|
### Lua tests
|
||||||
|
While the majority of CC: Tweaked is written in Java, a significant portion of the code is written in Lua. As such, it's
|
||||||
|
also useful to test that.
|
||||||
|
|
||||||
|
This is done by starting a Lua VM with all of ComputerCraft's APIs loaded, then starting a custom test framework
|
||||||
|
(`mcfly.lua`). This test framework discovers tests and sends them back to the Java side. These are turned into JUnit
|
||||||
|
tests which are then in turn run on the computer again. This allows the tests to integrate with existing Java testing
|
||||||
|
tooling (for instance, XML test reports and IDE integration).
|
||||||
|
|
||||||
|
There's a slightly more detailed description of the process at `ComputerTestDelegate.java`.
|
||||||
|
|
||||||
|
### Game tests
|
||||||
|
CC: Tweaked also runs several tests in-game using Minecraft's [gametest framework][mc-test]. These work by starting
|
||||||
|
a Minecraft server and then, for each test, spawning a structure and then interacting with the blocks inside the
|
||||||
|
structure, asserting they behave as expected.
|
||||||
|
|
||||||
|
Unlike most of our other tests, these are written in Kotlin. We make extensive use of [extension methods] to augment
|
||||||
|
vanilla's own test classes, which helps give a more consistent feel to the API.
|
||||||
|
|
||||||
|
Each test works by defining a sequence of steps. Each step can either run an action (`thenExecute`), sleep for a period
|
||||||
|
(`thenIdle`) or sleep until a condition is met (`thenWaitUntil`).
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
fun Some_test(context: GameTestHelper) = context.sequence {
|
||||||
|
thenExecute { context.setBlock(BlockPos(2, 2, 2), Blocks.AIR) }
|
||||||
|
thenIdle(4)
|
||||||
|
thenExecute { context.assertBlockHas(lamp, RedstoneLampBlock.LIT, false, "Lamp should not be lit") }
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Some tests need to use Lua APIs from a computer, such as when testing `turtle.dig`. In order to do this, we install
|
||||||
|
a custom "Lua" runtime (see `ManagedComputers.kt`) which actually runs Java functions. Tests can then enqueue a function
|
||||||
|
to run on a particular computer and then wait for it to finish.
|
||||||
|
|
||||||
|
While the internals of this is quite complex, it ends up being a much nicer workflow than writing parts of the test in
|
||||||
|
Lua. It also ends up being much more efficient, which is important when running a dozen tests at once!
|
||||||
|
|
||||||
|
[MultiLoader-Template]: https://github.com/jaredlll08/MultiLoader-Template/ "MultiLoader-Template - A template for a Forge + Fabric project setup using a Common source set."
|
||||||
|
[ServiceLoader]: https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/ServiceLoader.html "ServiceLoader (Java SE 17 and JDK 17)"
|
||||||
|
[ErrorProne]: https://errorprone.info/ "ErrorProne"
|
||||||
|
[tweaked.cc]: https://tweaked.cc "CC: Tweaked"
|
||||||
|
[JUnit]: https://junit.org/junit5/ "JUnit 5"
|
||||||
|
[jqwik]: https://jqwik.net/
|
||||||
|
[Hamcrest]: https://hamcrest.org/JavaHamcrest/ "Java Hamcrest"
|
||||||
|
[Test Interfaces]: https://junit.org/junit5/docs/current/user-guide/#writing-tests-test-interfaces-and-default-methods
|
||||||
|
[mc-test]: https://www.youtube.com/watch?v=vXaWOJTCYNg "Testing Minecraft in Minecraft on YouTube"
|
||||||
|
[extension methods]: https://kotlinlang.org/docs/extensions.html "Extensions | Kotlin"
|
20
projects/common-api/build.gradle.kts
Normal file
20
projects/common-api/build.gradle.kts
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
plugins {
|
||||||
|
id("cc-tweaked.java-convention")
|
||||||
|
id("cc-tweaked.publishing")
|
||||||
|
id("cc-tweaked.vanilla")
|
||||||
|
}
|
||||||
|
|
||||||
|
java {
|
||||||
|
withJavadocJar()
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
api(project(":core-api"))
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.javadoc {
|
||||||
|
include("dan200/computercraft/api/**/*.java")
|
||||||
|
|
||||||
|
// 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 })
|
||||||
|
}
|
@@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the public ComputerCraft API - http://www.computercraft.info
|
||||||
|
* Copyright Daniel Ratcliffe, 2011-2022. This API may be redistributed unmodified and in full only.
|
||||||
|
* For help using the API, and posting your mods, visit the forums at computercraft.info.
|
||||||
|
*/
|
||||||
|
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;
|
||||||
|
|
||||||
|
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
public static <T extends ITurtleUpgrade> void registerTurtleUpgradeModeller(TurtleUpgradeSerialiser<T> serialiser, TurtleUpgradeModeller<T> modeller) {
|
||||||
|
getInstance().registerTurtleUpgradeModeller(serialiser, modeller);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ComputerCraftAPIClientService getInstance() {
|
||||||
|
return ComputerCraftAPIClientService.get();
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,57 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the public ComputerCraft API - http://www.computercraft.info
|
||||||
|
* Copyright Daniel Ratcliffe, 2011-2022. This API may be redistributed unmodified and in full only.
|
||||||
|
* For help using the API, and posting your mods, visit the forums at computercraft.info.
|
||||||
|
*/
|
||||||
|
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,76 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the public ComputerCraft API - http://www.computercraft.info
|
||||||
|
* Copyright Daniel Ratcliffe, 2011-2022. This API may be redistributed unmodified and in full only.
|
||||||
|
* For help using the API, and posting your mods, visit the forums at computercraft.info.
|
||||||
|
*/
|
||||||
|
package dan200.computercraft.api.client.turtle;
|
||||||
|
|
||||||
|
import dan200.computercraft.api.client.ComputerCraftAPIClient;
|
||||||
|
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.api.turtle.TurtleUpgradeSerialiser;
|
||||||
|
import net.minecraft.client.resources.model.ModelResourceLocation;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides models for a {@link ITurtleUpgrade}.
|
||||||
|
*
|
||||||
|
* @param <T> The type of turtle upgrade this modeller applies to.
|
||||||
|
* @see ComputerCraftAPIClient#registerTurtleUpgradeModeller(TurtleUpgradeSerialiser, TurtleUpgradeModeller) To register a modeller.
|
||||||
|
*/
|
||||||
|
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!
|
||||||
|
* @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);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A basic {@link TurtleUpgradeModeller} which renders using the upgrade's {@linkplain ITurtleUpgrade#getCraftingItem()
|
||||||
|
* crafting 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.FLAT_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) {
|
||||||
|
return (upgrade, turtle, side) -> TransformedModel.of(side == TurtleSide.LEFT ? 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 (upgrade, turtle, side) -> TransformedModel.of(side == TurtleSide.LEFT ? left : right);
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,32 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the public ComputerCraft API - http://www.computercraft.info
|
||||||
|
* Copyright Daniel Ratcliffe, 2011-2022. This API may be redistributed unmodified and in full only.
|
||||||
|
* For help using the API, and posting your mods, visit the forums at computercraft.info.
|
||||||
|
*/
|
||||||
|
package dan200.computercraft.api.client.turtle;
|
||||||
|
|
||||||
|
import com.mojang.math.Transformation;
|
||||||
|
import dan200.computercraft.api.client.TransformedModel;
|
||||||
|
import dan200.computercraft.api.turtle.ITurtleUpgrade;
|
||||||
|
import dan200.computercraft.api.turtle.TurtleSide;
|
||||||
|
import org.joml.Matrix4f;
|
||||||
|
|
||||||
|
class TurtleUpgradeModellers {
|
||||||
|
private static final Transformation leftTransform = getMatrixFor(-0.40625f);
|
||||||
|
private static final Transformation rightTransform = getMatrixFor(0.40625f);
|
||||||
|
|
||||||
|
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> FLAT_ITEM = (upgrade, turtle, side) ->
|
||||||
|
TransformedModel.of(upgrade.getCraftingItem(), side == TurtleSide.LEFT ? leftTransform : rightTransform);
|
||||||
|
}
|
@@ -0,0 +1,46 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of ComputerCraft - http://www.computercraft.info
|
||||||
|
* Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
|
||||||
|
* Send enquiries to dratcliffe@gmail.com
|
||||||
|
*/
|
||||||
|
package dan200.computercraft.impl.client;
|
||||||
|
|
||||||
|
import dan200.computercraft.impl.Services;
|
||||||
|
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);
|
||||||
|
|
||||||
|
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() {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,44 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of ComputerCraft - http://www.computercraft.info
|
||||||
|
* Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
|
||||||
|
* Send enquiries to dratcliffe@gmail.com
|
||||||
|
*/
|
||||||
|
package dan200.computercraft.impl.client;
|
||||||
|
|
||||||
|
import dan200.computercraft.api.client.ComputerCraftAPIClient;
|
||||||
|
import dan200.computercraft.api.client.turtle.TurtleUpgradeModeller;
|
||||||
|
import dan200.computercraft.api.turtle.ITurtleUpgrade;
|
||||||
|
import dan200.computercraft.api.turtle.TurtleUpgradeSerialiser;
|
||||||
|
import dan200.computercraft.impl.Services;
|
||||||
|
import org.jetbrains.annotations.ApiStatus;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Backing interface for {@link ComputerCraftAPIClient}
|
||||||
|
* <p>
|
||||||
|
* Do <strong>NOT</strong> directly reference this class. It exists for internal use by the API.
|
||||||
|
*/
|
||||||
|
@ApiStatus.Internal
|
||||||
|
public interface ComputerCraftAPIClientService {
|
||||||
|
static ComputerCraftAPIClientService get() {
|
||||||
|
var instance = Instance.INSTANCE;
|
||||||
|
return instance == null ? Services.raise(ComputerCraftAPIClientService.class, Instance.ERROR) : instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
<T extends ITurtleUpgrade> void registerTurtleUpgradeModeller(TurtleUpgradeSerialiser<T> serialiser, TurtleUpgradeModeller<T> modeller);
|
||||||
|
|
||||||
|
final class Instance {
|
||||||
|
static final @Nullable ComputerCraftAPIClientService INSTANCE;
|
||||||
|
static final @Nullable Throwable ERROR;
|
||||||
|
|
||||||
|
static {
|
||||||
|
var helper = Services.tryLoad(ComputerCraftAPIClientService.class);
|
||||||
|
INSTANCE = helper.instance();
|
||||||
|
ERROR = helper.error();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Instance() {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,203 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the public ComputerCraft API - http://www.computercraft.info
|
||||||
|
* Copyright Daniel Ratcliffe, 2011-2022. This API may be redistributed unmodified and in full only.
|
||||||
|
* For help using the API, and posting your mods, visit the forums at computercraft.info.
|
||||||
|
*/
|
||||||
|
package dan200.computercraft.api;
|
||||||
|
|
||||||
|
import dan200.computercraft.api.filesystem.Mount;
|
||||||
|
import dan200.computercraft.api.filesystem.WritableMount;
|
||||||
|
import dan200.computercraft.api.lua.GenericSource;
|
||||||
|
import dan200.computercraft.api.lua.ILuaAPI;
|
||||||
|
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.wired.WiredElement;
|
||||||
|
import dan200.computercraft.api.network.wired.WiredNode;
|
||||||
|
import dan200.computercraft.api.peripheral.IComputerAccess;
|
||||||
|
import dan200.computercraft.api.redstone.BundledRedstoneProvider;
|
||||||
|
import dan200.computercraft.api.turtle.ITurtleAccess;
|
||||||
|
import dan200.computercraft.api.turtle.TurtleRefuelHandler;
|
||||||
|
import dan200.computercraft.impl.ComputerCraftAPIService;
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.core.Direction;
|
||||||
|
import net.minecraft.server.MinecraftServer;
|
||||||
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
import net.minecraft.world.level.Level;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The static entry point to the ComputerCraft API.
|
||||||
|
* <p>
|
||||||
|
* Members in this class must be called after ComputerCraft has been initialised, but may be called before it is
|
||||||
|
* fully loaded.
|
||||||
|
*/
|
||||||
|
public final class ComputerCraftAPI {
|
||||||
|
public static final String MOD_ID = "computercraft";
|
||||||
|
|
||||||
|
public static String getInstalledVersion() {
|
||||||
|
return getInstance().getInstalledVersion();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a numbered directory in a subfolder of the save directory for a given world, and returns that number.
|
||||||
|
* <p>
|
||||||
|
* Use in conjunction with createSaveDirMount() to create a unique place for your peripherals or media items to store files.
|
||||||
|
*
|
||||||
|
* @param server The server for which the save dir should be created.
|
||||||
|
* @param parentSubPath The folder path within the save directory where the new directory should be created. eg: "computercraft/disk"
|
||||||
|
* @return The numerical value of the name of the new folder, or -1 if the folder could not be created for some reason.
|
||||||
|
* <p>
|
||||||
|
* eg: if createUniqueNumberedSaveDir( world, "computer/disk" ) was called returns 42, then "computer/disk/42" is now
|
||||||
|
* available for writing.
|
||||||
|
* @see #createSaveDirMount(MinecraftServer, String, long)
|
||||||
|
*/
|
||||||
|
public static int createUniqueNumberedSaveDir(MinecraftServer server, String parentSubPath) {
|
||||||
|
return getInstance().createUniqueNumberedSaveDir(server, parentSubPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a file system mount that maps to a subfolder of the save directory for a given world, and returns it.
|
||||||
|
* <p>
|
||||||
|
* Use in conjunction with Use {@link IComputerAccess#mount(String, Mount)} or {@link IComputerAccess#mountWritable(String, WritableMount)}
|
||||||
|
* to mount this on a computer's file system.
|
||||||
|
* <p>
|
||||||
|
* If the same folder may be mounted on multiple computers at once (for instance, if you provide a network file share),
|
||||||
|
* the same mount instance should be used for all computers. You should NOT have multiple mount instances for the
|
||||||
|
* same folder.
|
||||||
|
*
|
||||||
|
* @param server The server which the save dir can be found.
|
||||||
|
* @param subPath The folder path within the save directory that the mount should map to. eg: "disk/42".
|
||||||
|
* Use {@link #createUniqueNumberedSaveDir(MinecraftServer, String)} to create a new numbered folder
|
||||||
|
* to use.
|
||||||
|
* @param capacity The amount of data that can be stored in the directory before it fills up, in bytes.
|
||||||
|
* @return The newly created mount.
|
||||||
|
* @see #createUniqueNumberedSaveDir(MinecraftServer, String)
|
||||||
|
* @see IComputerAccess#mount(String, Mount)
|
||||||
|
* @see IComputerAccess#mountWritable(String, WritableMount)
|
||||||
|
* @see Mount
|
||||||
|
* @see WritableMount
|
||||||
|
*/
|
||||||
|
public static WritableMount createSaveDirMount(MinecraftServer server, String subPath, long capacity) {
|
||||||
|
return getInstance().createSaveDirMount(server, subPath, capacity);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a file system mount to a resource folder, and returns it.
|
||||||
|
* <p>
|
||||||
|
* Use in conjunction with {@link IComputerAccess#mount} or {@link IComputerAccess#mountWritable} to mount a
|
||||||
|
* resource folder onto a computer's file system.
|
||||||
|
* <p>
|
||||||
|
* The files in this mount will be a combination of files in all mod jar, and data packs that contain
|
||||||
|
* resources with the same domain and path. For instance, ComputerCraft's resources are stored in
|
||||||
|
* "/data/computercraft/lua/rom". We construct a mount for that with
|
||||||
|
* {@code createResourceMount("computercraft", "lua/rom")}.
|
||||||
|
*
|
||||||
|
* @param server The current Minecraft server, from which to read resources from.
|
||||||
|
* @param domain The domain under which to look for resources. eg: "mymod".
|
||||||
|
* @param subPath The subPath under which to look for resources. eg: "lua/myfiles".
|
||||||
|
* @return The mount, or {@code null} if it could be created for some reason.
|
||||||
|
* @see IComputerAccess#mount(String, Mount)
|
||||||
|
* @see IComputerAccess#mountWritable(String, WritableMount)
|
||||||
|
* @see Mount
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
public static Mount createResourceMount(MinecraftServer server, String domain, String subPath) {
|
||||||
|
return getInstance().createResourceMount(server, domain, subPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a method source for generic peripherals.
|
||||||
|
*
|
||||||
|
* @param source The method source to register.
|
||||||
|
* @see GenericSource
|
||||||
|
*/
|
||||||
|
public static void registerGenericSource(GenericSource source) {
|
||||||
|
getInstance().registerGenericSource(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a bundled redstone provider to provide bundled redstone output for blocks.
|
||||||
|
*
|
||||||
|
* @param provider The bundled redstone provider to register.
|
||||||
|
* @see BundledRedstoneProvider
|
||||||
|
*/
|
||||||
|
public static void registerBundledRedstoneProvider(BundledRedstoneProvider provider) {
|
||||||
|
getInstance().registerBundledRedstoneProvider(provider);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If there is a Computer or Turtle at a certain position in the world, get it's bundled redstone output.
|
||||||
|
*
|
||||||
|
* @param world The world this block is in.
|
||||||
|
* @param pos The position this block is at.
|
||||||
|
* @param side The side to extract the bundled redstone output from.
|
||||||
|
* @return If there is a block capable of emitting bundled redstone at the location, it's signal (0-65535) will be returned.
|
||||||
|
* If there is no block capable of emitting bundled redstone at the location, -1 will be returned.
|
||||||
|
* @see BundledRedstoneProvider
|
||||||
|
*/
|
||||||
|
public static int getBundledRedstoneOutput(Level world, BlockPos pos, Direction 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.
|
||||||
|
*
|
||||||
|
* @param server The current Minecraft server.
|
||||||
|
* @return The global wireless network, or {@code null} if it could not be fetched.
|
||||||
|
*/
|
||||||
|
public static PacketNetwork getWirelessNetwork(MinecraftServer server) {
|
||||||
|
return getInstance().getWirelessNetwork(server);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a custom {@link ILuaAPI}, which may be added onto all computers without requiring a peripheral.
|
||||||
|
* <p>
|
||||||
|
* Before implementing this interface, consider alternative methods of providing methods. It is generally preferred
|
||||||
|
* to use peripherals to provide functionality to users.
|
||||||
|
*
|
||||||
|
* @param factory The factory for your API subclass.
|
||||||
|
* @see ILuaAPIFactory
|
||||||
|
*/
|
||||||
|
public static void registerAPIFactory(ILuaAPIFactory factory) {
|
||||||
|
getInstance().registerAPIFactory(factory);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a new wired node for a given wired element.
|
||||||
|
*
|
||||||
|
* @param element The element to construct it for
|
||||||
|
* @return The element's node
|
||||||
|
* @see WiredElement#getNode()
|
||||||
|
*/
|
||||||
|
public static WiredNode createWiredNodeForElement(WiredElement element) {
|
||||||
|
return getInstance().createWiredNodeForElement(element);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a refuel handler for turtles. This may be used to provide alternative fuel sources, such as consuming RF
|
||||||
|
* batteries.
|
||||||
|
*
|
||||||
|
* @param handler The turtle refuel handler.
|
||||||
|
* @see TurtleRefuelHandler#refuel(ITurtleAccess, ItemStack, int, int)
|
||||||
|
*/
|
||||||
|
public static void registerRefuelHandler(TurtleRefuelHandler handler) {
|
||||||
|
getInstance().registerRefuelHandler(handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ComputerCraftAPIService getInstance() {
|
||||||
|
return ComputerCraftAPIService.get();
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,72 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the public ComputerCraft API - http://www.computercraft.info
|
||||||
|
* Copyright Daniel Ratcliffe, 2011-2022. This API may be redistributed unmodified and in full only.
|
||||||
|
* For help using the API, and posting your mods, visit the forums at computercraft.info.
|
||||||
|
*/
|
||||||
|
package dan200.computercraft.api;
|
||||||
|
|
||||||
|
import net.minecraft.core.registries.Registries;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import net.minecraft.tags.TagKey;
|
||||||
|
import net.minecraft.world.InteractionHand;
|
||||||
|
import net.minecraft.world.entity.player.Player;
|
||||||
|
import net.minecraft.world.item.Item;
|
||||||
|
import net.minecraft.world.item.context.UseOnContext;
|
||||||
|
import net.minecraft.world.level.Level;
|
||||||
|
import net.minecraft.world.level.block.Block;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tags provided by ComputerCraft.
|
||||||
|
*/
|
||||||
|
public class ComputerCraftTags {
|
||||||
|
public static class Items {
|
||||||
|
public static final TagKey<Item> COMPUTER = make("computer");
|
||||||
|
public static final TagKey<Item> TURTLE = make("turtle");
|
||||||
|
public static final TagKey<Item> WIRED_MODEM = make("wired_modem");
|
||||||
|
public static final TagKey<Item> MONITOR = make("monitor");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Items which can be {@linkplain Item#use(Level, Player, InteractionHand) used} when calling
|
||||||
|
* {@code turtle.place()}.
|
||||||
|
* <p>
|
||||||
|
* This does not cover items who handle placing inside {@link Item#useOn(UseOnContext)}, as that is always
|
||||||
|
* called.
|
||||||
|
*/
|
||||||
|
public static final TagKey<Item> TURTLE_CAN_PLACE = make("turtle_can_place");
|
||||||
|
|
||||||
|
private static TagKey<Item> make(String name) {
|
||||||
|
return TagKey.create(Registries.ITEM, new ResourceLocation(ComputerCraftAPI.MOD_ID, name));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Blocks {
|
||||||
|
public static final TagKey<Block> COMPUTER = make("computer");
|
||||||
|
public static final TagKey<Block> TURTLE = make("turtle");
|
||||||
|
public static final TagKey<Block> WIRED_MODEM = make("wired_modem");
|
||||||
|
public static final TagKey<Block> MONITOR = make("monitor");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Blocks which can be broken by any turtle tool.
|
||||||
|
*/
|
||||||
|
public static final TagKey<Block> TURTLE_ALWAYS_BREAKABLE = make("turtle_always_breakable");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Blocks which can be broken by the default shovel tool.
|
||||||
|
*/
|
||||||
|
public static final TagKey<Block> TURTLE_SHOVEL_BREAKABLE = make("turtle_shovel_harvestable");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Blocks which can be broken with the default sword tool.
|
||||||
|
*/
|
||||||
|
public static final TagKey<Block> TURTLE_SWORD_BREAKABLE = make("turtle_sword_harvestable");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Blocks which can be broken with the default hoe tool.
|
||||||
|
*/
|
||||||
|
public static final TagKey<Block> TURTLE_HOE_BREAKABLE = make("turtle_hoe_harvestable");
|
||||||
|
|
||||||
|
private static TagKey<Block> make(String name) {
|
||||||
|
return TagKey.create(Registries.BLOCK, new ResourceLocation(ComputerCraftAPI.MOD_ID, name));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,75 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the public ComputerCraft API - http://www.computercraft.info
|
||||||
|
* Copyright Daniel Ratcliffe, 2011-2022. This API may be redistributed unmodified and in full only.
|
||||||
|
* For help using the API, and posting your mods, visit the forums at computercraft.info.
|
||||||
|
*/
|
||||||
|
package dan200.computercraft.api.detail;
|
||||||
|
|
||||||
|
import net.minecraft.world.item.Item;
|
||||||
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
public abstract class BasicItemDetailProvider<T> implements DetailProvider<ItemStack> {
|
||||||
|
private final Class<T> itemType;
|
||||||
|
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}.
|
||||||
|
*
|
||||||
|
* @param itemType The type the stack's item must have.
|
||||||
|
* @param namespace The namespace to use for this provider.
|
||||||
|
*/
|
||||||
|
public BasicItemDetailProvider(@Nullable String namespace, Class<T> itemType) {
|
||||||
|
Objects.requireNonNull(itemType);
|
||||||
|
this.itemType = itemType;
|
||||||
|
this.namespace = namespace;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new item detail provider. Meta will be inserted directly into the results.
|
||||||
|
*
|
||||||
|
* @param itemType The type the stack's item must have.
|
||||||
|
*/
|
||||||
|
public BasicItemDetailProvider(Class<T> itemType) {
|
||||||
|
this(null, itemType);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provide additional details for the given {@link Item} and {@link ItemStack}. 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 stack The item stack to provide details for.
|
||||||
|
* @param item The item to provide details for.
|
||||||
|
*/
|
||||||
|
public abstract void provideDetails(
|
||||||
|
Map<? super String, Object> data, ItemStack stack, T item
|
||||||
|
);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void provideDetails(Map<? super String, Object> data, ItemStack stack) {
|
||||||
|
var item = stack.getItem();
|
||||||
|
if (!itemType.isInstance(item)) return;
|
||||||
|
|
||||||
|
// If `namespace` is specified, insert into a new data map instead of the existing one.
|
||||||
|
Map<? super String, Object> child = namespace == null ? data : new HashMap<>();
|
||||||
|
|
||||||
|
provideDetails(child, stack, itemType.cast(item));
|
||||||
|
|
||||||
|
if (namespace != null) {
|
||||||
|
data.put(namespace, child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -10,7 +10,6 @@ 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 javax.annotation.Nonnull;
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -22,14 +21,12 @@ import javax.annotation.Nullable;
|
|||||||
* @param blockEntity The block entity at this position, if it exists.
|
* @param blockEntity The block entity at this position, if it exists.
|
||||||
*/
|
*/
|
||||||
public record BlockReference(
|
public record BlockReference(
|
||||||
@Nonnull Level level,
|
Level level,
|
||||||
@Nonnull BlockPos pos,
|
BlockPos pos,
|
||||||
@Nonnull BlockState state,
|
BlockState state,
|
||||||
@Nullable BlockEntity blockEntity
|
@Nullable BlockEntity blockEntity
|
||||||
)
|
) {
|
||||||
{
|
public BlockReference(Level level, BlockPos pos) {
|
||||||
public BlockReference( Level level, BlockPos pos )
|
this(level, pos, level.getBlockState(pos), level.getBlockEntity(pos));
|
||||||
{
|
|
||||||
this( level, pos, level.getBlockState( pos ), level.getBlockEntity( pos ) );
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -0,0 +1,33 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the public ComputerCraft API - http://www.computercraft.info
|
||||||
|
* Copyright Daniel Ratcliffe, 2011-2022. This API may be redistributed unmodified and in full only.
|
||||||
|
* For help using the API, and posting your mods, visit the forums at computercraft.info.
|
||||||
|
*/
|
||||||
|
package dan200.computercraft.api.detail;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provide details about a block, fluid, or item.
|
||||||
|
* <p>
|
||||||
|
* When implementing this interface, be careful to only expose information the player can see through normal gameplay.
|
||||||
|
* Computers shouldn't break progression or mechanics of other mods.
|
||||||
|
*
|
||||||
|
* @param <T> The type of object that this provider can provide details for.
|
||||||
|
* @see DetailRegistry
|
||||||
|
*/
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface DetailProvider<T> {
|
||||||
|
/**
|
||||||
|
* Provide additional details for the given object. This method is called by functions such as
|
||||||
|
* {@code turtle.getItemDetail()} and {@code turtle.inspect()}. 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. New properties should be added to this map.
|
||||||
|
* @param object The object to provide details for.
|
||||||
|
*/
|
||||||
|
void provideDetails(Map<? super String, Object> data, T object);
|
||||||
|
}
|
@@ -0,0 +1,52 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the public ComputerCraft API - http://www.computercraft.info
|
||||||
|
* Copyright Daniel Ratcliffe, 2011-2022. This API may be redistributed unmodified and in full only.
|
||||||
|
* For help using the API, and posting your mods, visit the forums at computercraft.info.
|
||||||
|
*/
|
||||||
|
package dan200.computercraft.api.detail;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.ApiStatus;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A registry which provides computer-visible detail about in-game objects such as blocks, items or fluids.
|
||||||
|
* <p>
|
||||||
|
* These are used by computer methods such as {@code turtle.getItemDetail()} or {@code turtle.inspect()}.
|
||||||
|
* <p>
|
||||||
|
* Specific instances of this registry are available from {@link VanillaDetailRegistries} and loader-specific versions
|
||||||
|
* also in this package.
|
||||||
|
*
|
||||||
|
* @param <T> The type of object that this registry provides details for.
|
||||||
|
*/
|
||||||
|
@ApiStatus.NonExtendable
|
||||||
|
public interface DetailRegistry<T> {
|
||||||
|
/**
|
||||||
|
* Registers a detail provider.
|
||||||
|
*
|
||||||
|
* @param provider The detail provider to register.
|
||||||
|
* @see DetailProvider
|
||||||
|
*/
|
||||||
|
void addProvider(DetailProvider<T> provider);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compute basic details about an object. This is cheaper than computing all details operation, and so is suitable
|
||||||
|
* for when you need to compute the details for a large number of values.
|
||||||
|
* <p>
|
||||||
|
* This method <em>MAY</em> be thread safe: consult the instance's documentation for details.
|
||||||
|
*
|
||||||
|
* @param object The object to get details for.
|
||||||
|
* @return The basic details.
|
||||||
|
*/
|
||||||
|
Map<String, Object> getBasicDetails(T object);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compute all details about an object, using {@link #getBasicDetails(Object)} and any registered providers.
|
||||||
|
* <p>
|
||||||
|
* This method is <em>NOT</em> thread safe. It should only be called from the computer thread.
|
||||||
|
*
|
||||||
|
* @param object The object to get details for.
|
||||||
|
* @return The computed details.
|
||||||
|
*/
|
||||||
|
Map<String, Object> getDetails(T object);
|
||||||
|
}
|
@@ -0,0 +1,31 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the public ComputerCraft API - http://www.computercraft.info
|
||||||
|
* Copyright Daniel Ratcliffe, 2011-2022. This API may be redistributed unmodified and in full only.
|
||||||
|
* For help using the API, and posting your mods, visit the forums at computercraft.info.
|
||||||
|
*/
|
||||||
|
package dan200.computercraft.api.detail;
|
||||||
|
|
||||||
|
import dan200.computercraft.impl.ComputerCraftAPIService;
|
||||||
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
import net.minecraft.world.level.block.Block;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link DetailRegistry}s for built-in Minecraft types.
|
||||||
|
*/
|
||||||
|
public class VanillaDetailRegistries {
|
||||||
|
/**
|
||||||
|
* Provides details for {@link ItemStack}s.
|
||||||
|
* <p>
|
||||||
|
* This instance's {@link DetailRegistry#getBasicDetails(Object)} is thread safe (assuming the stack is immutable)
|
||||||
|
* and may be called from the computer thread.
|
||||||
|
*/
|
||||||
|
public static final DetailRegistry<ItemStack> ITEM_STACK = ComputerCraftAPIService.get().getItemStackDetailRegistry();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides details for {@link BlockReference}, a reference to a {@link Block} in the world.
|
||||||
|
* <p>
|
||||||
|
* This instance's {@link DetailRegistry#getBasicDetails(Object)} is thread safe and may be called from the computer
|
||||||
|
* thread.
|
||||||
|
*/
|
||||||
|
public static final DetailRegistry<BlockReference> BLOCK_IN_WORLD = ComputerCraftAPIService.get().getBlockInWorldDetailRegistry();
|
||||||
|
}
|
@@ -0,0 +1,86 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the public ComputerCraft API - http://www.computercraft.info
|
||||||
|
* Copyright Daniel Ratcliffe, 2011-2022. This API may be redistributed unmodified and in full only.
|
||||||
|
* For help using the API, and posting your mods, visit the forums at computercraft.info.
|
||||||
|
*/
|
||||||
|
package dan200.computercraft.api.media;
|
||||||
|
|
||||||
|
import dan200.computercraft.api.ComputerCraftAPI;
|
||||||
|
import dan200.computercraft.api.filesystem.Mount;
|
||||||
|
import dan200.computercraft.api.filesystem.WritableMount;
|
||||||
|
import net.minecraft.server.MinecraftServer;
|
||||||
|
import net.minecraft.server.level.ServerLevel;
|
||||||
|
import net.minecraft.sounds.SoundEvent;
|
||||||
|
import net.minecraft.world.item.Item;
|
||||||
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents an item that can be placed in a disk drive and used by a Computer.
|
||||||
|
* <p>
|
||||||
|
* Implement this interface on your {@link Item} class to allow it to be used in the drive. Alternatively, register
|
||||||
|
* a {@link MediaProvider}.
|
||||||
|
*/
|
||||||
|
public interface IMedia {
|
||||||
|
/**
|
||||||
|
* Get a string representing the label of this item. Will be called via {@code disk.getLabel()} in lua.
|
||||||
|
*
|
||||||
|
* @param stack The {@link ItemStack} to inspect.
|
||||||
|
* @return The label. ie: "Dan's Programs".
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
String getLabel(ItemStack stack);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set a string representing the label of this item. Will be called vi {@code disk.setLabel()} in lua.
|
||||||
|
*
|
||||||
|
* @param stack The {@link ItemStack} to modify.
|
||||||
|
* @param label The string to set the label to.
|
||||||
|
* @return true if the label was updated, false if the label may not be modified.
|
||||||
|
*/
|
||||||
|
default boolean setLabel(ItemStack stack, @Nullable String label) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If this disk represents an item with audio (like a record), get the readable name of the audio track. ie:
|
||||||
|
* "Jonathan Coulton - Still Alive"
|
||||||
|
*
|
||||||
|
* @param stack The {@link ItemStack} to modify.
|
||||||
|
* @return The name, or null if this item does not represent an item with audio.
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
default String getAudioTitle(ItemStack stack) {
|
||||||
|
return 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If this disk represents an item with data (like a floppy disk), get a mount representing it's contents. This will
|
||||||
|
* be mounted onto the filesystem of the computer while the media is in the disk drive.
|
||||||
|
*
|
||||||
|
* @param stack The {@link ItemStack} to modify.
|
||||||
|
* @param level The world in which the item and disk drive reside.
|
||||||
|
* @return The mount, or null if this item does not represent an item with data. If the mount returned also
|
||||||
|
* implements {@link WritableMount}, it will mounted using mountWritable()
|
||||||
|
* @see Mount
|
||||||
|
* @see WritableMount
|
||||||
|
* @see ComputerCraftAPI#createSaveDirMount(MinecraftServer, String, long)
|
||||||
|
* @see ComputerCraftAPI#createResourceMount(MinecraftServer, String, String)
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
default Mount createDataMount(ItemStack stack, ServerLevel level) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,28 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the public ComputerCraft API - http://www.computercraft.info
|
||||||
|
* Copyright Daniel Ratcliffe, 2011-2022. This API may be redistributed unmodified and in full only.
|
||||||
|
* For help using the API, and posting your mods, visit the forums at computercraft.info.
|
||||||
|
*/
|
||||||
|
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,29 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the public ComputerCraft API - http://www.computercraft.info
|
||||||
|
* Copyright Daniel Ratcliffe, 2011-2022. This API may be redistributed unmodified and in full only.
|
||||||
|
* For help using the API, and posting your mods, visit the forums at computercraft.info.
|
||||||
|
*/
|
||||||
|
package dan200.computercraft.api.network;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a packet which may be sent across a {@link PacketNetwork}.
|
||||||
|
*
|
||||||
|
* @param channel The channel to send the packet along. Receiving devices should only process packets from on
|
||||||
|
* channels they are listening to.
|
||||||
|
* @param replyChannel The channel to reply on.
|
||||||
|
* @param payload The contents of this packet. This should be a "valid" Lua object, safe for queuing as an
|
||||||
|
* event or returning from a peripheral call.
|
||||||
|
* @param sender The object which sent this packet.
|
||||||
|
* @see PacketSender
|
||||||
|
* @see PacketNetwork#transmitSameDimension(Packet, double)
|
||||||
|
* @see PacketNetwork#transmitInterdimensional(Packet)
|
||||||
|
* @see PacketReceiver#receiveDifferentDimension(Packet)
|
||||||
|
* @see PacketReceiver#receiveSameDimension(Packet, double)
|
||||||
|
*/
|
||||||
|
public record Packet(
|
||||||
|
int channel,
|
||||||
|
int replyChannel,
|
||||||
|
Object payload,
|
||||||
|
PacketSender sender
|
||||||
|
) {
|
||||||
|
}
|
@@ -0,0 +1,57 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the public ComputerCraft API - http://www.computercraft.info
|
||||||
|
* Copyright Daniel Ratcliffe, 2011-2022. This API may be redistributed unmodified and in full only.
|
||||||
|
* For help using the API, and posting your mods, visit the forums at computercraft.info.
|
||||||
|
*/
|
||||||
|
package dan200.computercraft.api.network;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A packet network represents a collection of devices which can send and receive packets.
|
||||||
|
*
|
||||||
|
* @see Packet
|
||||||
|
* @see PacketReceiver
|
||||||
|
*/
|
||||||
|
public interface PacketNetwork {
|
||||||
|
/**
|
||||||
|
* Add a receiver to the network.
|
||||||
|
*
|
||||||
|
* @param receiver The receiver to register to the network.
|
||||||
|
*/
|
||||||
|
void addReceiver(PacketReceiver receiver);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove a receiver from the network.
|
||||||
|
*
|
||||||
|
* @param receiver The device to remove from the network.
|
||||||
|
*/
|
||||||
|
void removeReceiver(PacketReceiver receiver);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine whether this network is wireless.
|
||||||
|
*
|
||||||
|
* @return Whether this network is wireless.
|
||||||
|
*/
|
||||||
|
boolean isWireless();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Submit a packet for transmitting across the network. This will route the packet through the network, sending it
|
||||||
|
* to all receivers within range (or any interdimensional ones).
|
||||||
|
*
|
||||||
|
* @param packet The packet to send.
|
||||||
|
* @param range The maximum distance this packet will be sent.
|
||||||
|
* @see #transmitInterdimensional(Packet)
|
||||||
|
* @see PacketReceiver#receiveSameDimension(Packet, double)
|
||||||
|
*/
|
||||||
|
void transmitSameDimension(Packet packet, double range);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Submit a packet for transmitting across the network. This will route the packet through the network, sending it
|
||||||
|
* to all receivers across all dimensions.
|
||||||
|
*
|
||||||
|
* @param packet The packet to send.
|
||||||
|
* @see #transmitSameDimension(Packet, double)
|
||||||
|
* @see PacketReceiver#receiveDifferentDimension(Packet)
|
||||||
|
*/
|
||||||
|
void transmitInterdimensional(Packet packet);
|
||||||
|
}
|
@@ -0,0 +1,80 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the public ComputerCraft API - http://www.computercraft.info
|
||||||
|
* Copyright Daniel Ratcliffe, 2011-2022. This API may be redistributed unmodified and in full only.
|
||||||
|
* For help using the API, and posting your mods, visit the forums at computercraft.info.
|
||||||
|
*/
|
||||||
|
package dan200.computercraft.api.network;
|
||||||
|
|
||||||
|
import net.minecraft.world.level.Level;
|
||||||
|
import net.minecraft.world.phys.Vec3;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An object on an {@link PacketNetwork}, capable of receiving packets.
|
||||||
|
*/
|
||||||
|
public interface PacketReceiver {
|
||||||
|
/**
|
||||||
|
* Get the world in which this packet receiver exists.
|
||||||
|
*
|
||||||
|
* @return The receivers's world.
|
||||||
|
*/
|
||||||
|
Level getLevel();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the position in the world at which this receiver exists.
|
||||||
|
*
|
||||||
|
* @return The receiver's position.
|
||||||
|
*/
|
||||||
|
Vec3 getPosition();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the maximum distance this receiver can send and receive messages.
|
||||||
|
* <p>
|
||||||
|
* When determining whether a receiver can receive a message, the largest distance of the packet and receiver is
|
||||||
|
* used - ensuring it is within range. If the packet or receiver is inter-dimensional, then the packet will always
|
||||||
|
* be received.
|
||||||
|
*
|
||||||
|
* @return The maximum distance this device can send and receive messages.
|
||||||
|
* @see #isInterdimensional()
|
||||||
|
* @see #receiveSameDimension(Packet packet, double)
|
||||||
|
* @see PacketNetwork#transmitInterdimensional(Packet)
|
||||||
|
*/
|
||||||
|
double getRange();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine whether this receiver can receive packets from other dimensions.
|
||||||
|
* <p>
|
||||||
|
* A device will receive an inter-dimensional packet if either it or the sending device is inter-dimensional.
|
||||||
|
*
|
||||||
|
* @return Whether this receiver receives packets from other dimensions.
|
||||||
|
* @see #getRange()
|
||||||
|
* @see #receiveDifferentDimension(Packet)
|
||||||
|
* @see PacketNetwork#transmitInterdimensional(Packet)
|
||||||
|
*/
|
||||||
|
boolean isInterdimensional();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Receive a network packet from the same dimension.
|
||||||
|
*
|
||||||
|
* @param packet The packet to receive. Generally you should check that you are listening on the given channel and,
|
||||||
|
* if so, queue the appropriate modem event.
|
||||||
|
* @param distance The distance this packet has travelled from the source.
|
||||||
|
* @see Packet
|
||||||
|
* @see #getRange()
|
||||||
|
* @see PacketNetwork#transmitSameDimension(Packet, double)
|
||||||
|
* @see PacketNetwork#transmitInterdimensional(Packet)
|
||||||
|
*/
|
||||||
|
void receiveSameDimension(Packet packet, double distance);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Receive a network packet from a different dimension.
|
||||||
|
*
|
||||||
|
* @param packet The packet to receive. Generally you should check that you are listening on the given channel and,
|
||||||
|
* if so, queue the appropriate modem event.
|
||||||
|
* @see Packet
|
||||||
|
* @see PacketNetwork#transmitInterdimensional(Packet)
|
||||||
|
* @see PacketNetwork#transmitSameDimension(Packet, double)
|
||||||
|
* @see #isInterdimensional()
|
||||||
|
*/
|
||||||
|
void receiveDifferentDimension(Packet packet);
|
||||||
|
}
|
@@ -0,0 +1,37 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the public ComputerCraft API - http://www.computercraft.info
|
||||||
|
* Copyright Daniel Ratcliffe, 2011-2022. This API may be redistributed unmodified and in full only.
|
||||||
|
* For help using the API, and posting your mods, visit the forums at computercraft.info.
|
||||||
|
*/
|
||||||
|
package dan200.computercraft.api.network;
|
||||||
|
|
||||||
|
import net.minecraft.world.level.Level;
|
||||||
|
import net.minecraft.world.phys.Vec3;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An object on a {@link PacketNetwork}, capable of sending packets.
|
||||||
|
*/
|
||||||
|
public interface PacketSender {
|
||||||
|
/**
|
||||||
|
* Get the world in which this packet sender exists.
|
||||||
|
*
|
||||||
|
* @return The sender's world.
|
||||||
|
*/
|
||||||
|
Level getLevel();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the position in the world at which this sender exists.
|
||||||
|
*
|
||||||
|
* @return The sender's position.
|
||||||
|
*/
|
||||||
|
Vec3 getPosition();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get some sort of identification string for this sender. This does not strictly need to be unique, but you
|
||||||
|
* should be able to extract some identifiable information from it.
|
||||||
|
*
|
||||||
|
* @return This device's id.
|
||||||
|
*/
|
||||||
|
String getSenderID();
|
||||||
|
}
|
@@ -0,0 +1,31 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the public ComputerCraft API - http://www.computercraft.info
|
||||||
|
* Copyright Daniel Ratcliffe, 2011-2022. This API may be redistributed unmodified and in full only.
|
||||||
|
* For help using the API, and posting your mods, visit the forums at computercraft.info.
|
||||||
|
*/
|
||||||
|
package dan200.computercraft.api.network.wired;
|
||||||
|
|
||||||
|
import dan200.computercraft.api.ComputerCraftAPI;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An object which may be part of a wired network.
|
||||||
|
* <p>
|
||||||
|
* Elements should construct a node using {@link ComputerCraftAPI#createWiredNodeForElement(WiredElement)}. This acts
|
||||||
|
* as a proxy for all network objects. Whilst the node may change networks, an element's node should remain constant
|
||||||
|
* for its lifespan.
|
||||||
|
* <p>
|
||||||
|
* Elements are generally tied to a block or tile entity in world. In such as case, one should provide the
|
||||||
|
* {@link WiredElement} capability for the appropriate sides.
|
||||||
|
*/
|
||||||
|
public interface WiredElement extends WiredSender {
|
||||||
|
/**
|
||||||
|
* Called when objects on the network change. This may occur when network nodes are added or removed, or when
|
||||||
|
* peripherals change.
|
||||||
|
*
|
||||||
|
* @param change The change which occurred.
|
||||||
|
* @see WiredNetworkChange
|
||||||
|
*/
|
||||||
|
default void networkChanged(WiredNetworkChange change) {
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,83 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the public ComputerCraft API - http://www.computercraft.info
|
||||||
|
* Copyright Daniel Ratcliffe, 2011-2022. This API may be redistributed unmodified and in full only.
|
||||||
|
* For help using the API, and posting your mods, visit the forums at computercraft.info.
|
||||||
|
*/
|
||||||
|
package dan200.computercraft.api.network.wired;
|
||||||
|
|
||||||
|
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||||
|
|
||||||
|
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()
|
||||||
|
*/
|
||||||
|
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)
|
||||||
|
*/
|
||||||
|
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)
|
||||||
|
*/
|
||||||
|
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()
|
||||||
|
*/
|
||||||
|
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)
|
||||||
|
*/
|
||||||
|
void updatePeripherals(WiredNode node, Map<String, IPeripheral> peripherals);
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user