Compare commits
293 Commits
v1.18-1.99
...
v1.19.2-1.
Author | SHA1 | Date | |
---|---|---|---|
![]() |
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 | ||
![]() |
a913232e62 | ||
![]() |
557765d8f0 | ||
![]() |
1e044aed68 | ||
![]() |
5382d34d29 | ||
![]() |
cbbb34cdd4 | ||
![]() |
8f7719a8dc | ||
![]() |
ca58e39707 | ||
![]() |
0aac966239 | ||
![]() |
0e1e8dfa8c | ||
![]() |
a1cbc1d803 | ||
![]() |
0b6dc25607 | ||
![]() |
b91809bfc7 | ||
![]() |
178126725e | ||
![]() |
cd76425877 | ||
![]() |
4411756b06 | ||
![]() |
1fd57a874f | ||
![]() |
3f0704624e | ||
![]() |
3b6cd783cb | ||
![]() |
a07bba4ece | ||
![]() |
ab22726883 | ||
![]() |
2efad38f53 | ||
![]() |
83a1af6526 | ||
![]() |
8b89d88d04 | ||
![]() |
36635662f1 | ||
![]() |
bbc0afa111 | ||
![]() |
34dc915d57 | ||
![]() |
24ed0ca723 | ||
![]() |
5052718428 | ||
![]() |
431e4c9419 | ||
![]() |
2639b84eb2 | ||
![]() |
d631111610 | ||
![]() |
c981c75b7c | ||
![]() |
f05a539443 | ||
![]() |
d8a7ab540a | ||
![]() |
a7536ea4fa | ||
![]() |
d9e75d7c47 | ||
![]() |
78334c4cb1 | ||
![]() |
f5f0c7990a | ||
![]() |
87a1c1a525 | ||
![]() |
be45b718b3 | ||
![]() |
ad2d1d6a05 | ||
![]() |
65a7370db1 | ||
![]() |
03b0244084 | ||
![]() |
6322e72110 | ||
![]() |
7ad6132494 | ||
![]() |
e2189535b8 | ||
![]() |
79467499e6 | ||
![]() |
074793090d | ||
![]() |
cbbab26bf3 | ||
![]() |
9cb7091ce7 | ||
![]() |
e909e11e05 | ||
![]() |
6239dbe9ca | ||
![]() |
49601f0b7c | ||
![]() |
caa412b7d2 | ||
![]() |
9cb7a5bec7 | ||
![]() |
118b89ea41 | ||
![]() |
f2474bbfa2 | ||
![]() |
159f90896e | ||
![]() |
f108ba93af | ||
![]() |
2a4f75ba15 | ||
![]() |
ad228e94a3 | ||
![]() |
42b98bce28 | ||
![]() |
59e3608d2a | ||
![]() |
fccca22d3f | ||
![]() |
4bfdb65989 | ||
![]() |
22e8b9b587 | ||
![]() |
77a00b14ae | ||
![]() |
78aa757549 | ||
![]() |
1196568a7c | ||
![]() |
48c4f397f9 | ||
![]() |
8871f40ced | ||
![]() |
aa62c1f206 | ||
![]() |
fd32b06d6f | ||
![]() |
739d6813c0 | ||
![]() |
daf81b897a | ||
![]() |
e865d96f7b | ||
![]() |
79b1872cab | ||
![]() |
41fa95bce4 | ||
![]() |
2a92794da3 | ||
![]() |
b3e009cca5 | ||
![]() |
ba7598c689 | ||
![]() |
70c5cbafec | ||
![]() |
2c64186965 | ||
![]() |
7731759c77 | ||
![]() |
e6339b2847 | ||
![]() |
31ba17d085 | ||
![]() |
6353e8d930 | ||
![]() |
78cce4981a | ||
![]() |
97c953a9be | ||
![]() |
52df7cb8a4 | ||
![]() |
6735cfd12e | ||
![]() |
bcc7dd6991 | ||
![]() |
f994696161 | ||
![]() |
4a4e8bb4b6 | ||
![]() |
bd36185662 | ||
![]() |
045c4fc88c | ||
![]() |
e0fcc425c6 | ||
![]() |
e01895d719 | ||
![]() |
87b38f4249 | ||
![]() |
60d1d1bb18 | ||
![]() |
cdf8b77ffd | ||
![]() |
e2ce52fe81 | ||
![]() |
9edce36efd | ||
![]() |
e05588c662 | ||
![]() |
9cf70b10ef | ||
![]() |
9ac8f3aeea | ||
![]() |
e191b08eb5 | ||
![]() |
a1221b99e1 | ||
![]() |
85bced6b1d | ||
![]() |
fc4569e0cc | ||
![]() |
e7f08313d9 | ||
![]() |
79366bf2f5 | ||
![]() |
413fa5bcc8 | ||
![]() |
79fc8237b6 | ||
![]() |
9d50d6414c | ||
![]() |
16df86224b | ||
![]() |
a9519f68a1 | ||
![]() |
f1a08a3362 | ||
![]() |
802949d888 | ||
![]() |
2b901f2d5e | ||
![]() |
62f2cd5cb2 | ||
![]() |
e558b31b2b | ||
![]() |
afd82fbf1f | ||
![]() |
901d8d4c3b | ||
![]() |
f794ce42ab | ||
![]() |
f470478a0f | ||
![]() |
aa009df740 | ||
![]() |
0c6c0badde | ||
![]() |
bed2e0b658 | ||
![]() |
0f9ddac83c | ||
![]() |
932b77d7ee | ||
![]() |
5eedea1bbb | ||
![]() |
114261944a | ||
![]() |
4d10639efb | ||
![]() |
aa36b49c50 | ||
![]() |
8a1067940d | ||
![]() |
2562642664 | ||
![]() |
632db1cfa5 | ||
![]() |
aa0d544bba | ||
![]() |
2f6ad00764 | ||
![]() |
05da4dd362 | ||
![]() |
0477b2742c | ||
![]() |
fe3c42ce22 | ||
![]() |
f6fcba7a39 | ||
![]() |
82a7edee12 | ||
![]() |
b048b6666d | ||
![]() |
e16f66e128 | ||
![]() |
1cfad31a0d | ||
![]() |
7c373c6e06 | ||
![]() |
6196aae488 | ||
![]() |
92a0ef2b75 | ||
![]() |
57c5d19f95 | ||
![]() |
1f6e0f287d | ||
![]() |
0e4b7a5a75 | ||
![]() |
47ad7a35dc | ||
![]() |
3eab2a9b57 | ||
![]() |
c4024a4c4c | ||
![]() |
f5fb82cd7d | ||
![]() |
e18ba8a2c2 | ||
![]() |
422bfdb60d | ||
![]() |
1851ed31cd |
3
.gitattributes
vendored
@@ -1,5 +1,5 @@
|
|||||||
# Ignore changes in generated files
|
# Ignore changes in generated files
|
||||||
src/generated/resources/data/** linguist-generated
|
src/generated/** linguist-generated
|
||||||
src/testMod/server-files/structures linguist-generated
|
src/testMod/server-files/structures linguist-generated
|
||||||
|
|
||||||
* text=auto
|
* text=auto
|
||||||
@@ -13,3 +13,4 @@ src/testMod/server-files/structures linguist-generated
|
|||||||
|
|
||||||
*.png binary
|
*.png binary
|
||||||
*.jar binary
|
*.jar binary
|
||||||
|
*.dfpwm binary
|
||||||
|
4
.github/ISSUE_TEMPLATE/bug_report.yaml
vendored
@@ -8,9 +8,9 @@ body:
|
|||||||
label: Minecraft Version
|
label: Minecraft Version
|
||||||
description: What version of Minecraft are you using?
|
description: What version of Minecraft are you using?
|
||||||
options:
|
options:
|
||||||
- 1.15.x
|
|
||||||
- 1.16.x
|
- 1.16.x
|
||||||
- 1.17.x
|
- 1.18.x
|
||||||
|
- 1.19.x
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: input
|
- type: input
|
||||||
|
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.
|
||||||
|
44
.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,7 +31,7 @@ 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: Upload Jar
|
- name: Upload Jar
|
||||||
uses: actions/upload-artifact@v2
|
uses: actions/upload-artifact@v2
|
||||||
@@ -40,31 +39,12 @@ jobs:
|
|||||||
name: CC-Tweaked
|
name: CC-Tweaked
|
||||||
path: build/libs
|
path: build/libs
|
||||||
|
|
||||||
- name: Upload Screnshots
|
- name: Upload coverage
|
||||||
uses: actions/upload-artifact@v2
|
|
||||||
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
|
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
|
||||||
run: |
|
uses: pre-commit/action@v3.0.0
|
||||||
pip install pre-commit
|
|
||||||
pre-commit run --config config/pre-commit/config.yml --show-diff-on-failure --all --color=always
|
|
||||||
|
2
.github/workflows/make-doc.sh
vendored
@@ -12,7 +12,7 @@ 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/lua/" \
|
"$GITHUB_WORKSPACE/build/docs/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/build/docs/javadoc/" \
|
||||||
|
26
.github/workflows/make-doc.yml
vendored
@@ -11,29 +11,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@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
|
||||||
|
3
.gitignore
vendored
@@ -2,14 +2,15 @@
|
|||||||
/classes
|
/classes
|
||||||
/logs
|
/logs
|
||||||
/build
|
/build
|
||||||
|
/buildSrc/build
|
||||||
/out
|
/out
|
||||||
/doc/out/
|
/doc/out/
|
||||||
/node_modules
|
/node_modules
|
||||||
|
/.jqwik-database
|
||||||
|
|
||||||
# Runtime directories
|
# Runtime directories
|
||||||
/run
|
/run
|
||||||
/run-*
|
/run-*
|
||||||
/test-files
|
|
||||||
|
|
||||||
*.ipr
|
*.ipr
|
||||||
*.iws
|
*.iws
|
||||||
|
@@ -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
|
||||||
|
@@ -24,6 +24,13 @@ repos:
|
|||||||
|
|
||||||
- repo: local
|
- repo: local
|
||||||
hooks:
|
hooks:
|
||||||
|
- id: license
|
||||||
|
name: Spotless
|
||||||
|
files: ".*\\.(java|kt|kts)$"
|
||||||
|
language: system
|
||||||
|
entry: ./gradlew spotlessApply
|
||||||
|
pass_filenames: false
|
||||||
|
require_serial: true
|
||||||
- id: checkstyle
|
- id: checkstyle
|
||||||
name: Check Java codestyle
|
name: Check Java codestyle
|
||||||
files: ".*\\.java$"
|
files: ".*\\.java$"
|
||||||
@@ -31,18 +38,11 @@ repos:
|
|||||||
entry: ./gradlew checkstyleMain checkstyleTest
|
entry: ./gradlew checkstyleMain checkstyleTest
|
||||||
pass_filenames: false
|
pass_filenames: false
|
||||||
require_serial: true
|
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
|
- id: illuaminate
|
||||||
name: Check Lua code
|
name: Check Lua code
|
||||||
files: ".*\\.(lua|java|md)"
|
files: ".*\\.(lua|java|md)"
|
||||||
language: script
|
language: system
|
||||||
entry: config/pre-commit/illuaminate-lint.sh
|
entry: ./gradlew lintLua
|
||||||
pass_filenames: false
|
pass_filenames: false
|
||||||
require_serial: true
|
require_serial: true
|
||||||
|
|
||||||
@@ -51,5 +51,6 @@ exclude: |
|
|||||||
src/generated|
|
src/generated|
|
||||||
src/test/resources/test-rom/data/json-parsing/|
|
src/test/resources/test-rom/data/json-parsing/|
|
||||||
src/testMod/server-files/|
|
src/testMod/server-files/|
|
||||||
config/idea/
|
config/idea/|
|
||||||
|
.*\.dfpwm
|
||||||
)
|
)
|
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
|
@@ -39,40 +39,30 @@ are run whenever you submit a PR, it's often useful to run this before committin
|
|||||||
|
|
||||||
- **[Checkstyle]:** Checks Java code to ensure it is consistently formatted. This can be run with `./gradlew build` or
|
- **[Checkstyle]:** Checks Java code to ensure it is consistently formatted. This can be run with `./gradlew build` or
|
||||||
`./gradle check`.
|
`./gradle check`.
|
||||||
- **[illuaminate]:** Checks Lua code for semantic and styleistic issues. See [the usage section][illuaminate-usage] for
|
- **[illuaminate]:** Checks Lua code for semantic and styleistic issues. This can be run with `./gradlew lintLua`.
|
||||||
how to download and run it. You may need to generate the Java documentation stubs (see "Documentation" below) for all
|
|
||||||
lints to pass.
|
|
||||||
|
|
||||||
### Documentation
|
### 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
|
Our documentation generation pipeline is rather complex, and involves invoking several external tools. Most of this
|
||||||
convert Java doc-comments into Lua ones, we also generate some Javascript to embed. All of this is then finally fed into
|
complexity is hidden by Gradle, but you will need to perform some initial setup:
|
||||||
illuaminate, which spits out our HTML.
|
|
||||||
|
|
||||||
#### Setting up the tooling
|
- Install [Node/npm][node].
|
||||||
For various reasons, getting the environment set up to build documentation can be pretty complex. I'd quite like to
|
- Run `npm ci` to install our Node dependencies.
|
||||||
automate this via Docker and/or nix in the future, but this needs to be done manually for now.
|
|
||||||
|
|
||||||
This tooling is only needed if you need to build the whole website. If you just want to generate the Lua stubs, you can
|
You can now run `./gradlew docWebsite`. This generates documentation from our Lua and Java code, writing the resulting
|
||||||
skp this section.
|
HTML into `./build/docs/site`.
|
||||||
- Install Node/npm and install our Node packages with `npm ci`.
|
|
||||||
- Install [illuaminate][illuaminate-usage] as described above.
|
|
||||||
|
|
||||||
#### Building documentation
|
|
||||||
Gradle should be your entrypoint to building most documentation. There's two tasks which are of interest:
|
|
||||||
|
|
||||||
- `./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/lua/`.
|
|
||||||
|
|
||||||
#### Writing documentation
|
#### Writing documentation
|
||||||
illuaminate's documentation system is not currently documented (somewhat ironic), but is _largely_ the same as
|
illuaminate's documentation system is not currently documented (somewhat ironic), but is _largely_ the same as
|
||||||
[ldoc][ldoc]. Documentation comments are written in Markdown,
|
[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
|
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.
|
expect. It is recommended that you build and preview the docs locally first.
|
||||||
|
|
||||||
|
When iterating on documentation, you can get Gradle to rebuild the website every time a file changes by running
|
||||||
|
`./gradlew docWebsite -t`. This will take a couple of seconds to run, but definitely beats running it manually!
|
||||||
|
|
||||||
### Testing
|
### Testing
|
||||||
Thankfully running tests is much simpler than running the documentation generator! `./gradlew check` will run the
|
Thankfully running tests is much simpler than running the documentation generator! `./gradlew check` will run the
|
||||||
@@ -90,11 +80,10 @@ Before we get into writing tests, it's worth mentioning the various test suites
|
|||||||
|
|
||||||
These tests are run by the '"Core" Java' test suite, and so are also run with `./gradlew 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,
|
- In-game (`./src/testMod/java/dan200/computercraft/ingame/`): These tests are run on an actual Minecraft server, using
|
||||||
using [the same system Mojang do][mc-test]. The aim of these is to test in-game behaviour of blocks and peripherals.
|
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`
|
These tests are run with `./gradlew runGametest`.
|
||||||
or similar when running in a headless environment.
|
|
||||||
|
|
||||||
## CraftOS tests
|
## CraftOS tests
|
||||||
CraftOS's tests are written using a test system called "mcfly", heavily inspired by [busted] (and thus RSpec). Groups of
|
CraftOS's tests are written using a test system called "mcfly", heavily inspired by [busted] (and thus RSpec). Groups of
|
||||||
@@ -107,9 +96,9 @@ asserts that your variable `foo` is equal to the expected value `"bar"`.
|
|||||||
[community]: README.md#Community "Get in touch with the community."
|
[community]: README.md#Community "Get in touch with the community."
|
||||||
[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"
|
||||||
|
11
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").
|
||||||
|
|
||||||
@@ -35,7 +34,8 @@ repositories {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation fg.deobf("org.squiddev:cc-tweaked-${mc_version}:${cct_version}")
|
compileOnly fg.deobf("org.squiddev:cc-tweaked-${mc_version}:${cct_version}:api")
|
||||||
|
runtimeOnly fg.deobf("org.squiddev:cc-tweaked-${mc_version}:${cct_version}")
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -51,3 +51,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"
|
||||||
|
596
build.gradle
@@ -1,596 +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.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.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.java {
|
|
||||||
exclude 'dan200/computercraft/shared/integration/jei/**'
|
|
||||||
exclude 'dan200/computercraft/shared/integration/morered/**'
|
|
||||||
}
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
testServer {
|
|
||||||
workingDirectory project.file('test-files/server')
|
|
||||||
parent runs.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')
|
|
||||||
}
|
|
||||||
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}"
|
|
||||||
|
|
||||||
// compileOnly fg.deobf("mezz.jei:jei-1.17.1:8.0.0.14:api")
|
|
||||||
// runtimeOnly fg.deobf("mezz.jei:jei-1.17.1:8.0.0.14")
|
|
||||||
|
|
||||||
shade 'org.squiddev:Cobalt:0.5.2-SNAPSHOT'
|
|
||||||
|
|
||||||
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.2'
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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")
|
|
||||||
])
|
|
||||||
}
|
|
||||||
|
|
||||||
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', 'dan200', 'Daniel Ratcliffe']
|
|
||||||
["git", "-C", projectDir, "log", "--format=tformat:%an%n%cn"].execute().text.split('\n').each {
|
|
||||||
if (!blacklist.contains(it)) contributors.add(it)
|
|
||||||
}
|
|
||||||
} 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 minifyWeb(type: Exec, dependsOn: rollup) {
|
|
||||||
group = "build"
|
|
||||||
description = "Bundles JS into rollup"
|
|
||||||
|
|
||||||
inputs.file("$buildDir/rollup/index.js").withPropertyName("sources")
|
|
||||||
inputs.file("package-lock.json").withPropertyName("package-lock.json")
|
|
||||||
outputs.file("$buildDir/rollup/index.min.js").withPropertyName("output")
|
|
||||||
|
|
||||||
commandLine mkCommand('"node_modules/.bin/terser"' + " -o '$buildDir/rollup/index.min.js' '$buildDir/rollup/index.js'")
|
|
||||||
}
|
|
||||||
|
|
||||||
task illuaminateDocs(type: Exec, dependsOn: [minifyWeb, luaJavadoc]) {
|
|
||||||
group = "build"
|
|
||||||
description = "Bundles JS into rollup"
|
|
||||||
|
|
||||||
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.min.js").withPropertyName("scripts")
|
|
||||||
inputs.file("src/web/styles.css").withPropertyName("styles")
|
|
||||||
outputs.dir("$buildDir/docs/lua")
|
|
||||||
|
|
||||||
commandLine mkCommand('"bin/illuaminate" doc-gen')
|
|
||||||
}
|
|
||||||
|
|
||||||
task docWebsite(type: Copy, dependsOn: [illuaminateDocs]) {
|
|
||||||
from 'doc'
|
|
||||||
include 'logo.png'
|
|
||||||
include 'images/**'
|
|
||||||
into "${project.docsDir}/lua"
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
task setupServer(type: Copy) {
|
|
||||||
group "test server"
|
|
||||||
description "Sets up the environment for the test server."
|
|
||||||
|
|
||||||
from("src/testMod/server-files") {
|
|
||||||
include "eula.txt"
|
|
||||||
include "server.properties"
|
|
||||||
}
|
|
||||||
into "test-files/server"
|
|
||||||
}
|
|
||||||
|
|
||||||
["Client", "Server"].forEach { name ->
|
|
||||||
tasks.register("test$name", JavaExec.class).configure {
|
|
||||||
it.group('In-game tests')
|
|
||||||
it.description("Runs tests on a temporary Minecraft instance.")
|
|
||||||
it.dependsOn(setupServer, "prepareRunTest$name", "cleanTest$name", '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("runTest$name")
|
|
||||||
exec.copyTo(it)
|
|
||||||
it.setClasspath(exec.getClasspath())
|
|
||||||
it.mainClass = exec.mainClass
|
|
||||||
it.setArgs(exec.getArgs())
|
|
||||||
|
|
||||||
it.systemProperty('forge.logging.console.level', 'info')
|
|
||||||
it.systemProperty('cctest.run', 'true')
|
|
||||||
|
|
||||||
// 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/test$name")
|
|
||||||
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("jacocoTest${name}Report", JacocoReport.class).configure {
|
|
||||||
it.group('In-game')
|
|
||||||
it.description("Generate coverage reports for test$name")
|
|
||||||
it.dependsOn("test$name")
|
|
||||||
|
|
||||||
it.executionData(new File(buildDir, "jacoco/test${name}.exec"))
|
|
||||||
it.sourceDirectories.from(sourceSets.main.allJava.srcDirs)
|
|
||||||
it.classDirectories.from(new File(buildDir, "jacocoClassDump/test$name"))
|
|
||||||
|
|
||||||
it.reports {
|
|
||||||
xml.enabled true
|
|
||||||
html.enabled true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (name != "Client" || project.findProperty('cc.tweaked.clientTests') == 'true') {
|
|
||||||
// Don't run client tests unless explicitly opted into them. They're a bit of a faff
|
|
||||||
// to run and pretty flakey.
|
|
||||||
check.dependsOn("jacocoTest${name}Report")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// 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 = false
|
|
||||||
|
|
||||||
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)"
|
|
||||||
}
|
|
458
build.gradle.kts
Normal file
@@ -0,0 +1,458 @@
|
|||||||
|
import cc.tweaked.gradle.*
|
||||||
|
import net.darkhax.curseforgegradle.TaskPublishCurseForge
|
||||||
|
import net.minecraftforge.gradle.common.util.RunConfig
|
||||||
|
|
||||||
|
plugins {
|
||||||
|
// Build
|
||||||
|
alias(libs.plugins.forgeGradle)
|
||||||
|
alias(libs.plugins.mixinGradle)
|
||||||
|
alias(libs.plugins.librarian)
|
||||||
|
alias(libs.plugins.shadow)
|
||||||
|
// Publishing
|
||||||
|
`maven-publish`
|
||||||
|
alias(libs.plugins.curseForgeGradle)
|
||||||
|
alias(libs.plugins.githubRelease)
|
||||||
|
alias(libs.plugins.minotaur)
|
||||||
|
// Utility
|
||||||
|
alias(libs.plugins.taskTree)
|
||||||
|
|
||||||
|
id("cc-tweaked.illuaminate")
|
||||||
|
id("cc-tweaked.node")
|
||||||
|
id("cc-tweaked.gametest")
|
||||||
|
id("cc-tweaked")
|
||||||
|
}
|
||||||
|
|
||||||
|
val isStable = true
|
||||||
|
val modVersion: String by extra
|
||||||
|
val mcVersion: String by extra
|
||||||
|
|
||||||
|
group = "org.squiddev"
|
||||||
|
version = modVersion
|
||||||
|
base.archivesName.set("cc-tweaked-$mcVersion")
|
||||||
|
|
||||||
|
java.registerFeature("extraMods") { usingSourceSet(sourceSets.main.get()) }
|
||||||
|
|
||||||
|
sourceSets {
|
||||||
|
main {
|
||||||
|
resources.srcDir("src/generated/resources")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
minecraft {
|
||||||
|
runs {
|
||||||
|
// configureEach would be better, but we need to eagerly configure configs or otherwise the run task doesn't
|
||||||
|
// get set up properly.
|
||||||
|
all {
|
||||||
|
lazyToken("minecraft_classpath") {
|
||||||
|
configurations["shade"].copyRecursive().resolve().joinToString(File.pathSeparator) { it.absolutePath }
|
||||||
|
}
|
||||||
|
|
||||||
|
property("forge.logging.markers", "REGISTRIES")
|
||||||
|
property("forge.logging.console.level", "debug")
|
||||||
|
|
||||||
|
forceExit = false
|
||||||
|
|
||||||
|
mods.register("computercraft") { source(sourceSets.main.get()) }
|
||||||
|
}
|
||||||
|
|
||||||
|
val client by registering {
|
||||||
|
workingDirectory(file("run"))
|
||||||
|
}
|
||||||
|
|
||||||
|
val server by registering {
|
||||||
|
workingDirectory(file("run/server"))
|
||||||
|
arg("--nogui")
|
||||||
|
}
|
||||||
|
|
||||||
|
val data by registering {
|
||||||
|
workingDirectory(file("run"))
|
||||||
|
args(
|
||||||
|
"--mod",
|
||||||
|
"computercraft",
|
||||||
|
"--all",
|
||||||
|
"--output",
|
||||||
|
file("src/generated/resources/"),
|
||||||
|
"--existing",
|
||||||
|
file("src/main/resources/"),
|
||||||
|
)
|
||||||
|
property("cct.pretty-json", "true")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun RunConfig.configureForGameTest() {
|
||||||
|
val old = lazyTokens.get("minecraft_classpath")
|
||||||
|
lazyToken("minecraft_classpath") {
|
||||||
|
// We do some terrible hacks here to basically find all things not already on the runtime classpath
|
||||||
|
// and add them. /Except/ for our source sets, as those need to load inside the Minecraft classpath.
|
||||||
|
val testMod = configurations["testModRuntimeClasspath"].resolve()
|
||||||
|
val implementation = configurations.runtimeClasspath.get().resolve()
|
||||||
|
val new = (testMod - implementation)
|
||||||
|
.asSequence()
|
||||||
|
.filter { it.isFile && !it.name.endsWith("-test-fixtures.jar") }
|
||||||
|
.map { it.absolutePath }
|
||||||
|
.joinToString(File.pathSeparator)
|
||||||
|
if (old == null) new else old.get() + File.pathSeparator + new
|
||||||
|
}
|
||||||
|
|
||||||
|
property("cctest.sources", file("src/testMod/resources/data/cctest").absolutePath)
|
||||||
|
|
||||||
|
arg("--mixin.config=computercraft-gametest.mixins.json")
|
||||||
|
|
||||||
|
mods.register("cctest") {
|
||||||
|
source(sourceSets["testMod"])
|
||||||
|
source(sourceSets["testFixtures"])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val testClient by registering {
|
||||||
|
workingDirectory(file("run/testClient"))
|
||||||
|
parent(client.get())
|
||||||
|
configureForGameTest()
|
||||||
|
}
|
||||||
|
|
||||||
|
val gameTestServer by registering {
|
||||||
|
workingDirectory(file("run/testServer"))
|
||||||
|
configureForGameTest()
|
||||||
|
|
||||||
|
property("forge.logging.console.level", "info")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mappings("parchment", "${libs.versions.parchmentMc.get()}-${libs.versions.parchment.get()}-$mcVersion")
|
||||||
|
|
||||||
|
accessTransformer(file("src/main/resources/META-INF/accesstransformer.cfg"))
|
||||||
|
}
|
||||||
|
|
||||||
|
mixin {
|
||||||
|
add(sourceSets.main.get(), "computercraft.mixins.refmap.json")
|
||||||
|
config("computercraft.mixins.json")
|
||||||
|
}
|
||||||
|
|
||||||
|
reobf {
|
||||||
|
register("shadowJar")
|
||||||
|
}
|
||||||
|
|
||||||
|
configurations {
|
||||||
|
val shade by registering { isTransitive = false }
|
||||||
|
implementation { extendsFrom(shade.get()) }
|
||||||
|
register("cctJavadoc")
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
minecraft("net.minecraftforge:forge:$mcVersion-${libs.versions.forge.get()}")
|
||||||
|
annotationProcessor("org.spongepowered:mixin:0.8.5:processor")
|
||||||
|
|
||||||
|
compileOnly(libs.jetbrainsAnnotations)
|
||||||
|
annotationProcessorEverywhere(libs.autoService)
|
||||||
|
|
||||||
|
"extraModsCompileOnly"(fg.deobf("mezz.jei:jei-1.19.2-forge-api:11.3.0.262"))
|
||||||
|
"extraModsCompileOnly"(fg.deobf("mezz.jei:jei-1.19.2-common-api:11.3.0.262"))
|
||||||
|
"extraModsRuntimeOnly"(fg.deobf("mezz.jei:jei-1.19.2-forge:11.3.0.262"))
|
||||||
|
"extraModsCompileOnly"(fg.deobf("maven.modrinth:oculus:1.2.5"))
|
||||||
|
|
||||||
|
"shade"(libs.cobalt)
|
||||||
|
"shade"("io.netty:netty-codec-http:4.1.76.Final")
|
||||||
|
|
||||||
|
testFixturesApi(libs.bundles.test)
|
||||||
|
testFixturesApi(libs.bundles.kotlin)
|
||||||
|
|
||||||
|
testImplementation(libs.bundles.test)
|
||||||
|
testImplementation(libs.bundles.kotlin)
|
||||||
|
testRuntimeOnly(libs.bundles.testRuntime)
|
||||||
|
|
||||||
|
"cctJavadoc"(libs.cctJavadoc)
|
||||||
|
}
|
||||||
|
|
||||||
|
illuaminate {
|
||||||
|
version.set(libs.versions.illuaminate)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compile tasks
|
||||||
|
|
||||||
|
tasks.javadoc {
|
||||||
|
include("dan200/computercraft/api/**/*.java")
|
||||||
|
(options as StandardJavadocDocletOptions).links("https://docs.oracle.com/en/java/javase/17/docs/api/")
|
||||||
|
}
|
||||||
|
|
||||||
|
val apiJar by tasks.registering(Jar::class) {
|
||||||
|
archiveClassifier.set("api")
|
||||||
|
from(sourceSets.main.get().output) {
|
||||||
|
include("dan200/computercraft/api/**/*")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.assemble { dependsOn(apiJar) }
|
||||||
|
|
||||||
|
val luaJavadoc by tasks.registering(Javadoc::class) {
|
||||||
|
description = "Generates documentation for Java-side Lua functions."
|
||||||
|
group = JavaBasePlugin.DOCUMENTATION_GROUP
|
||||||
|
|
||||||
|
source(sourceSets.main.get().java)
|
||||||
|
setDestinationDir(buildDir.resolve("docs/luaJavadoc"))
|
||||||
|
classpath = sourceSets.main.get().compileClasspath
|
||||||
|
|
||||||
|
options.docletpath = configurations["cctJavadoc"].files.toList()
|
||||||
|
options.doclet = "cc.tweaked.javadoc.LuaDoclet"
|
||||||
|
(options as StandardJavadocDocletOptions).noTimestamp(false)
|
||||||
|
|
||||||
|
javadocTool.set(
|
||||||
|
javaToolchains.javadocToolFor {
|
||||||
|
languageVersion.set(cc.tweaked.gradle.CCTweakedPlugin.JAVA_VERSION)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.processResources {
|
||||||
|
inputs.property("modVersion", modVersion)
|
||||||
|
inputs.property("forgeVersion", libs.versions.forge.get())
|
||||||
|
inputs.property("gitHash", cct.gitHash)
|
||||||
|
|
||||||
|
filesMatching("data/computercraft/lua/rom/help/credits.txt") {
|
||||||
|
expand(mapOf("gitContributors" to cct.gitContributors.get().joinToString("\n")))
|
||||||
|
}
|
||||||
|
|
||||||
|
filesMatching("META-INF/mods.toml") {
|
||||||
|
expand(mapOf("forgeVersion" to libs.versions.forge.get(), "file" to mapOf("jarVersion" to modVersion)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.jar {
|
||||||
|
isReproducibleFileOrder = true
|
||||||
|
isPreserveFileTimestamps = false
|
||||||
|
finalizedBy("reobfJar")
|
||||||
|
archiveClassifier.set("slim")
|
||||||
|
|
||||||
|
manifest {
|
||||||
|
attributes(
|
||||||
|
"Specification-Title" to "computercraft",
|
||||||
|
"Specification-Vendor" to "SquidDev",
|
||||||
|
"Specification-Version" to "1",
|
||||||
|
"Implementation-Title" to "cctweaked",
|
||||||
|
"Implementation-Version" to modVersion,
|
||||||
|
"Implementation-Vendor" to "SquidDev",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.shadowJar {
|
||||||
|
finalizedBy("reobfShadowJar")
|
||||||
|
|
||||||
|
archiveClassifier.set("")
|
||||||
|
configurations = listOf(project.configurations["shade"])
|
||||||
|
relocate("org.squiddev.cobalt", "cc.tweaked.internal.cobalt")
|
||||||
|
minimize()
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.assemble { dependsOn("shadowJar") }
|
||||||
|
|
||||||
|
// Web tasks
|
||||||
|
|
||||||
|
val rollup by tasks.registering(NpxExecToDir::class) {
|
||||||
|
group = LifecycleBasePlugin.BUILD_GROUP
|
||||||
|
description = "Bundles JS into rollup"
|
||||||
|
|
||||||
|
// Sources
|
||||||
|
inputs.files(fileTree("src/web")).withPropertyName("sources")
|
||||||
|
// Config files
|
||||||
|
inputs.file("tsconfig.json").withPropertyName("Typescript config")
|
||||||
|
inputs.file("rollup.config.js").withPropertyName("Rollup config")
|
||||||
|
|
||||||
|
// Output directory. Also defined in illuaminate.sexp and rollup.config.js
|
||||||
|
output.set(buildDir.resolve("rollup"))
|
||||||
|
|
||||||
|
args = listOf("rollup", "--config", "rollup.config.js")
|
||||||
|
}
|
||||||
|
|
||||||
|
val illuaminateDocs by tasks.registering(IlluaminateExecToDir::class) {
|
||||||
|
group = JavaBasePlugin.DOCUMENTATION_GROUP
|
||||||
|
description = "Generates docs using Illuaminate"
|
||||||
|
|
||||||
|
// Config files
|
||||||
|
inputs.file("illuaminate.sexp").withPropertyName("illuaminate.sexp")
|
||||||
|
// Sources
|
||||||
|
inputs.files(fileTree("doc")).withPropertyName("docs")
|
||||||
|
inputs.files(fileTree("src/main/resources/data/computercraft/lua")).withPropertyName("lua rom")
|
||||||
|
inputs.files(luaJavadoc)
|
||||||
|
// Additional assets
|
||||||
|
inputs.files(rollup)
|
||||||
|
inputs.file("src/web/styles.css").withPropertyName("styles")
|
||||||
|
|
||||||
|
// Output directory. Also defined in illuaminate.sexp and transform.tsx
|
||||||
|
output.set(buildDir.resolve("illuaminate"))
|
||||||
|
|
||||||
|
args = listOf("doc-gen")
|
||||||
|
}
|
||||||
|
|
||||||
|
val jsxDocs by tasks.registering(NpxExecToDir::class) {
|
||||||
|
group = JavaBasePlugin.DOCUMENTATION_GROUP
|
||||||
|
description = "Post-processes documentation to statically render some dynamic content."
|
||||||
|
|
||||||
|
// Config files
|
||||||
|
inputs.file("tsconfig.json").withPropertyName("Typescript config")
|
||||||
|
// Sources
|
||||||
|
inputs.files(fileTree("src/web")).withPropertyName("sources")
|
||||||
|
inputs.file("src/generated/export/index.json").withPropertyName("export")
|
||||||
|
inputs.files(illuaminateDocs)
|
||||||
|
|
||||||
|
// Output directory. Also defined in src/web/transform.tsx
|
||||||
|
output.set(buildDir.resolve("jsxDocs"))
|
||||||
|
|
||||||
|
args = listOf("ts-node", "-T", "--esm", "src/web/transform.tsx")
|
||||||
|
}
|
||||||
|
|
||||||
|
val docWebsite by tasks.registering(Copy::class) {
|
||||||
|
group = JavaBasePlugin.DOCUMENTATION_GROUP
|
||||||
|
description = "Assemble docs and assets together into the documentation website."
|
||||||
|
|
||||||
|
from(jsxDocs)
|
||||||
|
|
||||||
|
from("doc") {
|
||||||
|
include("logo.png")
|
||||||
|
include("images/**")
|
||||||
|
}
|
||||||
|
from(rollup) { exclude("index.js") }
|
||||||
|
from(illuaminateDocs) { exclude("**/*.html") }
|
||||||
|
from("src/generated/export/items") { into("images/items") }
|
||||||
|
|
||||||
|
into(buildDir.resolve("docs/site"))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check tasks
|
||||||
|
|
||||||
|
tasks.test {
|
||||||
|
systemProperty("cct.test-files", buildDir.resolve("tmp/testFiles").absolutePath)
|
||||||
|
}
|
||||||
|
|
||||||
|
val lintLua by tasks.registering(IlluaminateExec::class) {
|
||||||
|
group = JavaBasePlugin.VERIFICATION_GROUP
|
||||||
|
description = "Lint Lua (and Lua docs) with illuaminate"
|
||||||
|
|
||||||
|
// Config files
|
||||||
|
inputs.file("illuaminate.sexp").withPropertyName("illuaminate.sexp")
|
||||||
|
// Sources
|
||||||
|
inputs.files(fileTree("doc")).withPropertyName("docs")
|
||||||
|
inputs.files(fileTree("src/main/resources/data/computercraft/lua")).withPropertyName("lua rom")
|
||||||
|
inputs.files(luaJavadoc)
|
||||||
|
|
||||||
|
args = listOf("lint")
|
||||||
|
|
||||||
|
doFirst { if (System.getenv("GITHUB_ACTIONS") != null) println("::add-matcher::.github/matchers/illuaminate.json") }
|
||||||
|
doLast { if (System.getenv("GITHUB_ACTIONS") != null) println("::remove-matcher owner=illuaminate::") }
|
||||||
|
}
|
||||||
|
|
||||||
|
val runGametest by tasks.registering(JavaExec::class) {
|
||||||
|
group = LifecycleBasePlugin.VERIFICATION_GROUP
|
||||||
|
description = "Runs tests on a temporary Minecraft instance."
|
||||||
|
dependsOn("cleanRunGametest")
|
||||||
|
|
||||||
|
// Copy from runGameTestServer. We do it in this slightly odd way as runGameTestServer
|
||||||
|
// isn't created until the task is configured (which is no good for us).
|
||||||
|
val exec = tasks.getByName<JavaExec>("runGameTestServer")
|
||||||
|
dependsOn(exec.dependsOn)
|
||||||
|
exec.copyToFull(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
cct.jacoco(runGametest)
|
||||||
|
|
||||||
|
tasks.check { dependsOn(runGametest) }
|
||||||
|
|
||||||
|
// Upload tasks
|
||||||
|
|
||||||
|
val checkChangelog by tasks.registering(CheckChangelog::class) {
|
||||||
|
version.set(modVersion)
|
||||||
|
whatsNew.set(file("src/main/resources/data/computercraft/lua/rom/help/whatsnew.md"))
|
||||||
|
changelog.set(file("src/main/resources/data/computercraft/lua/rom/help/changelog.md"))
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.check { dependsOn(checkChangelog) }
|
||||||
|
|
||||||
|
val publishCurseForge by tasks.registering(TaskPublishCurseForge::class) {
|
||||||
|
group = PublishingPlugin.PUBLISH_TASK_GROUP
|
||||||
|
description = "Upload artifacts to CurseForge"
|
||||||
|
|
||||||
|
apiToken = findProperty("curseForgeApiKey") ?: ""
|
||||||
|
enabled = apiToken != ""
|
||||||
|
|
||||||
|
val mainFile = upload("282001", tasks.shadowJar)
|
||||||
|
dependsOn(tasks.shadowJar) // Ughr.
|
||||||
|
mainFile.changelog = "Release notes can be found on the [GitHub repository](https://github.com/cc-tweaked/CC-Tweaked/releases/tag/v$mcVersion-$modVersion)."
|
||||||
|
mainFile.changelogType = "markdown"
|
||||||
|
mainFile.releaseType = if (isStable) "release" else "alpha"
|
||||||
|
mainFile.gameVersions.add(mcVersion)
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.publish { dependsOn(publishCurseForge) }
|
||||||
|
|
||||||
|
modrinth {
|
||||||
|
token.set(findProperty("modrinthApiKey") as String? ?: "")
|
||||||
|
projectId.set("gu7yAYhd")
|
||||||
|
versionNumber.set("$mcVersion-$modVersion")
|
||||||
|
versionName.set(modVersion)
|
||||||
|
versionType.set(if (isStable) "release" else "alpha")
|
||||||
|
uploadFile.set(tasks.shadowJar as Any)
|
||||||
|
gameVersions.add(mcVersion)
|
||||||
|
changelog.set("Release notes can be found on the [GitHub repository](https://github.com/cc-tweaked/CC-Tweaked/releases/tag/v$mcVersion-$modVersion).")
|
||||||
|
|
||||||
|
syncBodyFrom.set(provider { file("doc/mod-page.md").readText() })
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.publish { dependsOn(tasks.modrinth) }
|
||||||
|
|
||||||
|
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 {
|
||||||
|
"## " + 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(!isStable)
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.publish { dependsOn(tasks.githubRelease) }
|
||||||
|
|
||||||
|
publishing {
|
||||||
|
publications {
|
||||||
|
register<MavenPublication>("maven") {
|
||||||
|
artifactId = base.archivesName.get()
|
||||||
|
from(components["java"])
|
||||||
|
artifact(apiJar)
|
||||||
|
fg.component(this)
|
||||||
|
|
||||||
|
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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
33
buildSrc/build.gradle.kts
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
plugins {
|
||||||
|
`java-gradle-plugin`
|
||||||
|
`kotlin-dsl`
|
||||||
|
}
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
gradlePluginPortal()
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation(libs.kotlin.plugin)
|
||||||
|
implementation(libs.spotless)
|
||||||
|
}
|
||||||
|
|
||||||
|
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
@@ -0,0 +1,7 @@
|
|||||||
|
dependencyResolutionManagement {
|
||||||
|
versionCatalogs {
|
||||||
|
create("libs") {
|
||||||
|
from(files("../gradle/libs.versions.toml"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
53
buildSrc/src/main/kotlin/cc-tweaked.gametest.gradle.kts
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.get()
|
||||||
|
|
||||||
|
// Both testMod and testFixtures inherit from the main classpath, just so we have access to Minecraft classes.
|
||||||
|
val testMod by sourceSets.creating {
|
||||||
|
compileClasspath += main.compileClasspath
|
||||||
|
runtimeClasspath += main.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)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Similar to java-test-fixtures, but tries to avoid putting the obfuscated jar on the classpath.
|
||||||
|
|
||||||
|
val testFixtures by sourceSets.creating {
|
||||||
|
compileClasspath += main.compileClasspath
|
||||||
|
}
|
||||||
|
|
||||||
|
java.registerFeature("testFixtures") {
|
||||||
|
usingSourceSet(testFixtures)
|
||||||
|
disablePublication()
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
add(testFixtures.implementationConfigurationName, main.output)
|
||||||
|
|
||||||
|
testImplementation(testFixtures(project))
|
||||||
|
add(testMod.implementationConfigurationName, testFixtures(project))
|
||||||
|
}
|
106
buildSrc/src/main/kotlin/cc-tweaked.java-convention.gradle.kts
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
import cc.tweaked.gradle.CCTweakedPlugin
|
||||||
|
import cc.tweaked.gradle.LicenseHeader
|
||||||
|
import com.diffplug.gradle.spotless.FormatExtension
|
||||||
|
import com.diffplug.spotless.LineEnding
|
||||||
|
import java.nio.charset.StandardCharsets
|
||||||
|
|
||||||
|
plugins {
|
||||||
|
`java-library`
|
||||||
|
jacoco
|
||||||
|
checkstyle
|
||||||
|
id("com.diffplug.spotless")
|
||||||
|
}
|
||||||
|
|
||||||
|
java {
|
||||||
|
toolchain {
|
||||||
|
languageVersion.set(CCTweakedPlugin.JAVA_VERSION)
|
||||||
|
}
|
||||||
|
|
||||||
|
withSourcesJar()
|
||||||
|
withJavadocJar()
|
||||||
|
}
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
maven("https://squiddev.cc/maven") {
|
||||||
|
name = "SquidDev"
|
||||||
|
content {
|
||||||
|
includeGroup("org.squiddev")
|
||||||
|
includeGroup("cc.tweaked")
|
||||||
|
// Things we mirror
|
||||||
|
includeGroup("com.blamejared.crafttweaker")
|
||||||
|
includeGroup("commoble.morered")
|
||||||
|
includeGroup("maven.modrinth")
|
||||||
|
includeGroup("mezz.jei")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
val libs = project.extensions.getByType<VersionCatalogsExtension>().named("libs")
|
||||||
|
checkstyle(libs.findLibrary("checkstyle").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"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.withType(JavaCompile::class.java).configureEach {
|
||||||
|
options.encoding = "UTF-8"
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
spotless {
|
||||||
|
encoding = StandardCharsets.UTF_8
|
||||||
|
lineEndings = LineEnding.UNIX
|
||||||
|
|
||||||
|
fun FormatExtension.defaults() {
|
||||||
|
endWithNewline()
|
||||||
|
trimTrailingWhitespace()
|
||||||
|
indentWithSpaces(4)
|
||||||
|
}
|
||||||
|
|
||||||
|
val licenser = LicenseHeader.create(
|
||||||
|
api = file("config/license/api.txt"),
|
||||||
|
main = 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)
|
||||||
|
}
|
||||||
|
}
|
@@ -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()}"
|
||||||
|
}
|
||||||
|
}
|
124
buildSrc/src/main/kotlin/cc/tweaked/gradle/CCTweakedExtension.kt
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
package cc.tweaked.gradle
|
||||||
|
|
||||||
|
import org.gradle.api.NamedDomainObjectProvider
|
||||||
|
import org.gradle.api.Project
|
||||||
|
import org.gradle.api.attributes.TestSuiteType
|
||||||
|
import org.gradle.api.file.FileSystemOperations
|
||||||
|
import org.gradle.api.provider.Provider
|
||||||
|
import org.gradle.api.reporting.ReportingExtension
|
||||||
|
import org.gradle.api.tasks.JavaExec
|
||||||
|
import org.gradle.api.tasks.SourceSetContainer
|
||||||
|
import org.gradle.configurationcache.extensions.capitalized
|
||||||
|
import org.gradle.kotlin.dsl.get
|
||||||
|
import org.gradle.language.base.plugins.LifecycleBasePlugin
|
||||||
|
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 java.io.BufferedWriter
|
||||||
|
import java.io.IOException
|
||||||
|
import java.io.OutputStreamWriter
|
||||||
|
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.projectDir.absolutePath, "rev-parse", "HEAD")
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get the current git branch. */
|
||||||
|
val gitBranch: Provider<String> = gitProvider(project, "<no git branch>") {
|
||||||
|
ProcessHelpers.captureOut("git", "-C", project.projectDir.absolutePath, "rev-parse", "--abbrev-ref", "HEAD")
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get a list of all contributors to the project. */
|
||||||
|
val gitContributors: Provider<List<String>> = gitProvider(project, listOf()) {
|
||||||
|
val authors: Set<String> = HashSet(
|
||||||
|
ProcessHelpers.captureLines(
|
||||||
|
"git", "-C", project.projectDir.absolutePath, "log",
|
||||||
|
"--format=tformat:%an <%ae>%n%cn <%ce>%n%(trailers:key=Co-authored-by,valueonly)",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
val process = ProcessHelpers.startProcess("git", "check-mailmap", "--stdin")
|
||||||
|
BufferedWriter(OutputStreamWriter(process.outputStream)).use { writer ->
|
||||||
|
for (authorName in authors) {
|
||||||
|
var author = authorName
|
||||||
|
|
||||||
|
if (author.isEmpty()) continue
|
||||||
|
if (!author.endsWith(">")) author += ">" // Some commits have broken Co-Authored-By lines!
|
||||||
|
writer.write(author)
|
||||||
|
writer.newLine()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val contributors: MutableSet<String> = HashSet()
|
||||||
|
for (authorLine in ProcessHelpers.captureLines(process)) {
|
||||||
|
val matcher = EMAIL.matcher(authorLine)
|
||||||
|
matcher.find()
|
||||||
|
val name = matcher.group(1)
|
||||||
|
if (!IGNORED_USERS.contains(name)) contributors.add(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
contributors.sortedWith(String.CASE_INSENSITIVE_ORDER)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun jacoco(task: NamedDomainObjectProvider<JavaExec>) {
|
||||||
|
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.
|
||||||
|
val sourceSets = project.extensions.getByType(SourceSetContainer::class.java)
|
||||||
|
sourceDirectories.from(sourceSets["main"].allSource.sourceDirectories)
|
||||||
|
}
|
||||||
|
|
||||||
|
project.extensions.configure(ReportingExtension::class.java) {
|
||||||
|
reports.register("${task.name}CodeCoverageReport", JacocoCoverageReport::class.java) {
|
||||||
|
testType.set(TestSuiteType.INTEGRATION_TEST)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val EMAIL = Pattern.compile("^([^<]+) <.+>$")
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,18 @@
|
|||||||
|
package cc.tweaked.gradle
|
||||||
|
|
||||||
|
import org.gradle.api.Plugin
|
||||||
|
import org.gradle.api.Project
|
||||||
|
import org.gradle.jvm.toolchain.JavaLanguageVersion
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configures projects to match a shared configuration.
|
||||||
|
*/
|
||||||
|
class CCTweakedPlugin : Plugin<Project> {
|
||||||
|
override fun apply(project: Project) {
|
||||||
|
project.extensions.create("cct", CCTweakedExtension::class.java)
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
val JAVA_VERSION = JavaLanguageVersion.of(17)
|
||||||
|
}
|
||||||
|
}
|
65
buildSrc/src/main/kotlin/cc/tweaked/gradle/CheckChangelog.kt
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
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
|
||||||
|
import java.nio.charset.StandardCharsets
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 targetting 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")
|
||||||
|
}
|
||||||
|
}
|
73
buildSrc/src/main/kotlin/cc/tweaked/gradle/CheckLicense.kt
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
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
|
||||||
|
import java.nio.charset.StandardCharsets
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
@@ -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>
|
||||||
|
}
|
20
buildSrc/src/main/kotlin/cc/tweaked/gradle/Extensions.kt
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
package cc.tweaked.gradle
|
||||||
|
|
||||||
|
import org.gradle.api.artifacts.dsl.DependencyHandler
|
||||||
|
import org.gradle.api.tasks.JavaExec
|
||||||
|
|
||||||
|
fun DependencyHandler.annotationProcessorEverywhere(dep: Any) {
|
||||||
|
add("compileOnly", dep)
|
||||||
|
add("annotationProcessor", dep)
|
||||||
|
|
||||||
|
add("testCompileOnly", dep)
|
||||||
|
add("testAnnotationProcessor", dep)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun JavaExec.copyToFull(spec: JavaExec) {
|
||||||
|
copyTo(spec)
|
||||||
|
spec.classpath = classpath
|
||||||
|
spec.mainClass.set(mainClass)
|
||||||
|
spec.javaLauncher.set(javaLauncher)
|
||||||
|
spec.args = args
|
||||||
|
}
|
121
buildSrc/src/main/kotlin/cc/tweaked/gradle/Illuaminate.kt
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
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 {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
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"
|
||||||
|
}
|
||||||
|
}
|
35
buildSrc/src/main/kotlin/cc/tweaked/gradle/ProcessHelpers.kt
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
package cc.tweaked.gradle
|
||||||
|
|
||||||
|
import org.codehaus.groovy.runtime.ProcessGroovyMethods
|
||||||
|
import java.io.BufferedReader
|
||||||
|
import java.io.IOException
|
||||||
|
import java.io.InputStreamReader
|
||||||
|
import java.util.stream.Collectors
|
||||||
|
|
||||||
|
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 Runtime.getRuntime().exec(command, arrayOfNulls(0))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun captureOut(vararg command: String): String {
|
||||||
|
val process = startProcess(*command)
|
||||||
|
val result = ProcessGroovyMethods.getText(process)
|
||||||
|
if (process.waitFor() != 0) throw IOException("Command exited with a non-0 status")
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
fun captureLines(vararg command: String): List<String> {
|
||||||
|
return captureLines(startProcess(*command))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun captureLines(process: Process): List<String> {
|
||||||
|
val out = BufferedReader(InputStreamReader(process.inputStream)).use { reader ->
|
||||||
|
reader.lines().filter { it.isNotEmpty() }.collect(Collectors.toList())
|
||||||
|
}
|
||||||
|
ProcessGroovyMethods.closeStreams(process)
|
||||||
|
if (process.waitFor() != 0) throw IOException("Command exited with a non-0 status")
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
}
|
@@ -78,12 +78,16 @@
|
|||||||
|
|
||||||
<!-- 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"/>
|
||||||
<module name="JavadocType"/>
|
<module name="JavadocType"/>
|
||||||
<module name="JavadocStyle" />
|
<module name="JavadocStyle">
|
||||||
|
<property name="checkHtml" value="false" />
|
||||||
|
</module>
|
||||||
<module name="NonEmptyAtclauseDescription" />
|
<module name="NonEmptyAtclauseDescription" />
|
||||||
<module name="SingleLineJavadoc" />
|
<module name="SingleLineJavadoc" />
|
||||||
<module name="SummaryJavadocCheck"/>
|
<module name="SummaryJavadocCheck"/>
|
||||||
@@ -105,7 +109,9 @@
|
|||||||
<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(\.[a-z][a-z0-9]*)*" />
|
||||||
@@ -113,11 +119,6 @@
|
|||||||
<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" />
|
||||||
|
|
||||||
|
@@ -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
|
|
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, that in turn has a single method @{TransferredFiles.getFiles|getFiles}. This
|
||||||
|
returns the list of files that are being transferred. Each file 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
|
||||||
|
```
|
@@ -2,7 +2,7 @@
|
|||||||
module: [kind=event] modem_message
|
module: [kind=event] modem_message
|
||||||
---
|
---
|
||||||
|
|
||||||
The @{modem_message} event is fired when a message is received on an open channel on any modem.
|
The @{modem_message} event is fired when a message is received on an open channel on any @{modem}.
|
||||||
|
|
||||||
## Return Values
|
## Return Values
|
||||||
1. @{string}: The event name.
|
1. @{string}: The event name.
|
||||||
@@ -10,11 +10,15 @@ 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 (decimal).
|
6. @{number}: The distance between the sender and the receiver, in blocks.
|
||||||
|
|
||||||
## Example
|
## Example
|
||||||
Prints a message when one is sent:
|
Wraps a @{modem} peripheral, opens channel 0 for listening, and prints all received messages.
|
||||||
|
|
||||||
```lua
|
```lua
|
||||||
|
local modem = peripheral.find("modem") or error("No modem attached", 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)))
|
||||||
|
@@ -15,13 +15,11 @@ advanced turtles and pocket computers).
|
|||||||
Several mouse events (@{mouse_click}, @{mouse_up}, @{mouse_scroll}) contain a "mouse button" code. This takes a
|
Several mouse events (@{mouse_click}, @{mouse_up}, @{mouse_scroll}) contain a "mouse button" code. This takes a
|
||||||
numerical value depending on which button on your mouse was last pressed when this event occurred.
|
numerical value depending on which button on your mouse was last pressed when this event occurred.
|
||||||
|
|
||||||
<table class="pretty-table">
|
| Button Code | Mouse Button |
|
||||||
<!-- Our markdown parser doesn't work on tables!? Guess I'll have to roll my own soonish :/. -->
|
|------------:|---------------|
|
||||||
<tr><th>Button code</th><th>Mouse button</th></tr>
|
| 1 | Left button |
|
||||||
<tr><td align="right">1</td><td>Left button</td></tr>
|
| 2 | Right button |
|
||||||
<tr><td align="right">2</td><td>Middle button</td></tr>
|
| 3 | Middle button |
|
||||||
<tr><td align="right">3</td><td>Right button</td></tr>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
## Example
|
## Example
|
||||||
Print the button and the coordinates whenever the mouse is clicked.
|
Print the button and the coordinates whenever the mouse is clicked.
|
||||||
|
27
doc/events/speaker_audio_empty.md
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
---
|
||||||
|
module: [kind=event] speaker_audio_empty
|
||||||
|
see: speaker.playAudio To play audio using the speaker
|
||||||
|
---
|
||||||
|
|
||||||
|
## Return Values
|
||||||
|
1. @{string}: The event name.
|
||||||
|
2. @{string}: The name of the speaker which is available to play more audio.
|
||||||
|
|
||||||
|
|
||||||
|
## Example
|
||||||
|
This uses @{io.lines} to read audio data in blocks of 16KiB from "example_song.dfpwm", and then attempts to play it
|
||||||
|
using @{speaker.playAudio}. If the speaker's buffer is full, it waits for an event and tries again.
|
||||||
|
|
||||||
|
```lua {data-peripheral=speaker}
|
||||||
|
local dfpwm = require("cc.audio.dfpwm")
|
||||||
|
local speaker = peripheral.find("speaker")
|
||||||
|
|
||||||
|
local decoder = dfpwm.make_decoder()
|
||||||
|
for chunk in io.lines("data/example.dfpwm", 16 * 1024) do
|
||||||
|
local buffer = decoder(chunk)
|
||||||
|
|
||||||
|
while not speaker.playAudio(buffer) do
|
||||||
|
os.pullEvent("speaker_audio_empty")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
```
|
@@ -2,7 +2,12 @@
|
|||||||
module: [kind=event] term_resize
|
module: [kind=event] term_resize
|
||||||
---
|
---
|
||||||
|
|
||||||
The @{term_resize} event is fired when the main terminal is resized, mainly when a new tab is opened or closed in @{multishell}.
|
The @{term_resize} event is fired when the main terminal is resized. For instance:
|
||||||
|
- When a the tab bar is shown or hidden in @{multishell}.
|
||||||
|
- When the terminal is redirected to a monitor via the "monitor" program and the monitor is resized.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
## Example
|
## Example
|
||||||
Prints :
|
Prints :
|
||||||
|
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.
|
||||||
|
:::
|
99
doc/guides/local_ips.md
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
---
|
||||||
|
module: [kind=guide] local_ips
|
||||||
|
---
|
||||||
|
|
||||||
|
# Allowing access to local IPs
|
||||||
|
By default, ComputerCraft blocks access to local IP addresses for security. This means you can't normally access any
|
||||||
|
HTTP server running on your computer. However, this may be useful for testing programs without having a remote
|
||||||
|
server. You can unblock these IPs in the ComputerCraft config.
|
||||||
|
|
||||||
|
- [Minecraft 1.13 and later, CC:T 1.87.0 and later](#cc-1.87.0)
|
||||||
|
- [Minecraft 1.13 and later, CC:T 1.86.2 and earlier](#cc-1.86.2)
|
||||||
|
- [Minecraft 1.12.2 and earlier](#mc-1.12)
|
||||||
|
|
||||||
|
## Minecraft 1.13 and later, CC:T 1.87.0 and later {#cc-1.87.0}
|
||||||
|
The configuration file can be located at `serverconfig/computercraft-server.toml` inside the world folder on either
|
||||||
|
single-player or multiplayer. Look for lines that look like this:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
#A list of rules which control behaviour of the "http" API for specific domains or IPs.
|
||||||
|
#Each rule is an item with a 'host' to match against, and a series of properties. The host may be a domain name ("pastebin.com"),
|
||||||
|
#wildcard ("*.pastebin.com") or CIDR notation ("127.0.0.0/8"). If no rules, the domain is blocked.
|
||||||
|
[[http.rules]]
|
||||||
|
host = "$private"
|
||||||
|
action = "deny"
|
||||||
|
```
|
||||||
|
|
||||||
|
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
|
||||||
|
`action = "deny"`. Then save the file and relaunch Minecraft (Server).
|
||||||
|
|
||||||
|
Here's what it should look like after removing:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
#A list of rules which control behaviour of the "http" API for specific domains or IPs.
|
||||||
|
#Each rule is an item with a 'host' to match against, and a series of properties. The host may be a domain name ("pastebin.com"),
|
||||||
|
#wildcard ("*.pastebin.com") or CIDR notation ("127.0.0.0/8"). If no rules, the domain is blocked.
|
||||||
|
[[http.rules]]
|
||||||
|
#The maximum size (in bytes) that a computer can send or receive in one websocket packet.
|
||||||
|
max_websocket_message = 131072
|
||||||
|
host = "*"
|
||||||
|
#The maximum size (in bytes) that a computer can upload in a single request. This includes headers and POST text.
|
||||||
|
max_upload = 4194304
|
||||||
|
action = "allow"
|
||||||
|
#The maximum size (in bytes) that a computer can download in a single request. Note that responses may receive more data than allowed, but this data will not be returned to the client.
|
||||||
|
max_download = 16777216
|
||||||
|
#The period of time (in milliseconds) to wait before a HTTP request times out. Set to 0 for unlimited.
|
||||||
|
timeout = 30000
|
||||||
|
```
|
||||||
|
|
||||||
|
## Minecraft 1.13 and later, CC:T 1.86.2 and earlier {#cc-1.86.2}
|
||||||
|
The configuration file for singleplayer is at `.minecraft/config/computercraft-common.toml`. Look for lines that look
|
||||||
|
like this:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
#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.
|
||||||
|
#You can use domain names ("pastebin.com"), wilcards ("*.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"]
|
||||||
|
```
|
||||||
|
|
||||||
|
Remove everything inside the array, leaving the last line as `blacklist = []`. Then save the file and relaunch Minecraft.
|
||||||
|
|
||||||
|
Here's what it should look like after removing:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
#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.
|
||||||
|
#You can use domain names ("pastebin.com"), wilcards ("*.pastebin.com") or CIDR notation ("127.0.0.0/8").
|
||||||
|
blacklist = []
|
||||||
|
```
|
||||||
|
|
||||||
|
## Minecraft 1.12.2 and earlier {#mc-1.12}
|
||||||
|
On singleplayer, the configuration file is located at `.minecraft\config\ComputerCraft.cfg`. On multiplayer, the
|
||||||
|
configuration file is located at `<server folder>\config\ComputerCraft.cfg`. Look for lines that look like this:
|
||||||
|
|
||||||
|
```ini
|
||||||
|
# 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 explicitly allowed domains will be accessible. Example: "*.github.com" will block access to all subdomains of github.com.
|
||||||
|
# You can use domain names ("pastebin.com"), wildcards ("*.pastebin.com") or CIDR notation ("127.0.0.0/8").
|
||||||
|
S:blocked_domains <
|
||||||
|
127.0.0.0/8
|
||||||
|
10.0.0.0/8
|
||||||
|
172.16.0.0/12
|
||||||
|
192.168.0.0/16
|
||||||
|
fd00::/8
|
||||||
|
>
|
||||||
|
```
|
||||||
|
|
||||||
|
Delete everything between the `<>`, leaving the last line as `S:blocked_domains = <>`. Then save the file and relaunch
|
||||||
|
Minecraft (Server).
|
||||||
|
|
||||||
|
Here's what it should look like after removing:
|
||||||
|
|
||||||
|
```ini
|
||||||
|
# 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 explicitly allowed domains will be accessible. Example: "*.github.com" will block access to all subdomains of github.com.
|
||||||
|
# You can use domain names ("pastebin.com"), wildcards ("*.pastebin.com") or CIDR notation ("127.0.0.0/8").
|
||||||
|
S:blocked_domains <>
|
||||||
|
```
|
204
doc/guides/speaker_audio.md
Normal file
@@ -0,0 +1,204 @@
|
|||||||
|
---
|
||||||
|
module: [kind=guide] speaker_audio
|
||||||
|
see: speaker.playAudio Play PCM audio using a speaker.
|
||||||
|
see: cc.audio.dfpwm Provides utilities for encoding and decoding DFPWM files.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Playing audio with speakers
|
||||||
|
CC: Tweaked's speaker peripheral provides a powerful way to play any audio you like with the @{speaker.playAudio}
|
||||||
|
method. However, for people unfamiliar with digital audio, it's not the most intuitive thing to use. This guide provides
|
||||||
|
an introduction to digital audio, demonstrates how to play music with CC: Tweaked's speakers, and then briefly discusses
|
||||||
|
the more complex topic of audio processing.
|
||||||
|
|
||||||
|
## A short introduction to digital audio
|
||||||
|
When sound is recorded it is captured as an analogue signal, effectively the electrical version of a sound
|
||||||
|
wave. However, this signal is continuous, and so can't be used directly by a computer. Instead, we measure (or *sample*)
|
||||||
|
the amplitude of the wave many times a second and then *quantise* that amplitude, rounding it to the nearest
|
||||||
|
representable value.
|
||||||
|
|
||||||
|
This representation of sound - a long, uniformally sampled list of amplitudes is referred to as [Pulse-code
|
||||||
|
Modulation][PCM] (PCM). PCM can be thought of as the "standard" audio format, as it's incredibly easy to work with. For
|
||||||
|
instance, to mix two pieces of audio together, you can just add samples from the two tracks together and take the average.
|
||||||
|
|
||||||
|
CC: Tweaked's speakers also work with PCM audio. It plays back 48,000 samples a second, where each sample is an integer
|
||||||
|
between -128 and 127. This is more commonly referred to as 48kHz and an 8-bit resolution.
|
||||||
|
|
||||||
|
Let's now look at a quick example. We're going to generate a [Sine Wave] at 220Hz, which sounds like a low monotonous
|
||||||
|
hum. First we wrap our speaker peripheral, and then we fill a table (also referred to as a *buffer*) with 128×1024
|
||||||
|
samples - this is the maximum number of samples a speaker can accept in one go.
|
||||||
|
|
||||||
|
In order to fill this buffer, we need to do a little maths. We want to play 220 sine waves each second, where each sine
|
||||||
|
wave completes a full oscillation in 2π "units". This means one seconds worth of audio is 2×π×220 "units" long. We then
|
||||||
|
need to split this into 48k samples, basically meaning for each sample we move 2×π×220/48k "along" the sine curve.
|
||||||
|
|
||||||
|
```lua {data-peripheral=speaker}
|
||||||
|
local speaker = peripheral.find("speaker")
|
||||||
|
|
||||||
|
local buffer = {}
|
||||||
|
local t, dt = 0, 2 * math.pi * 220 / 48000
|
||||||
|
for i = 1, 128 * 1024 do
|
||||||
|
buffer[i] = math.floor(math.sin(t) * 127)
|
||||||
|
t = (t + dt) % (math.pi * 2)
|
||||||
|
end
|
||||||
|
|
||||||
|
speaker.playAudio(buffer)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Streaming audio
|
||||||
|
You might notice that the above snippet only generates a short bit of audio - 2.7s seconds to be precise. While we could
|
||||||
|
try increasing the number of loop iterations, we'll get an error when we try to play it through the speaker: the sound
|
||||||
|
buffer is too large for it to handle.
|
||||||
|
|
||||||
|
Our 2.7 seconds of audio is stored in a table with over 130 _thousand_ elements. If we wanted to play a full minute of
|
||||||
|
sine waves (and why wouldn't you?), you'd need a table with almost 3 _million_. Suddenly you find these numbers adding
|
||||||
|
up very quickly, and these tables take up more and more memory.
|
||||||
|
|
||||||
|
Instead of building our entire song (well, sine wave) in one go, we can produce it in small batches, each of which get
|
||||||
|
passed off to @{speaker.playAudio} when the time is right. This allows us to build a _stream_ of audio, where we read
|
||||||
|
chunks of audio one at a time (either from a file or a tone generator like above), do some optional processing to each
|
||||||
|
one, and then play them.
|
||||||
|
|
||||||
|
Let's adapt our example from above to do that instead.
|
||||||
|
|
||||||
|
```lua {data-peripheral=speaker}
|
||||||
|
local speaker = peripheral.find("speaker")
|
||||||
|
|
||||||
|
local t, dt = 0, 2 * math.pi * 220 / 48000
|
||||||
|
while true do
|
||||||
|
local buffer = {}
|
||||||
|
for i = 1, 16 * 1024 * 8 do
|
||||||
|
buffer[i] = math.floor(math.sin(t) * 127)
|
||||||
|
t = (t + dt) % (math.pi * 2)
|
||||||
|
end
|
||||||
|
|
||||||
|
while not speaker.playAudio(buffer) do
|
||||||
|
os.pullEvent("speaker_audio_empty")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
It looks pretty similar to before, aside from we've wrapped the generation and playing code in a while loop, and added a
|
||||||
|
rather odd loop with @{speaker.playAudio} and @{os.pullEvent}.
|
||||||
|
|
||||||
|
Let's talk about this loop, why do we need to keep calling @{speaker.playAudio}? Remember that what we're trying to do
|
||||||
|
here is avoid keeping too much audio in memory at once. However, if we're generating audio quicker than the speakers can
|
||||||
|
play it, we're not helping at all - all this audio is still hanging around waiting to be played!
|
||||||
|
|
||||||
|
In order to avoid this, the speaker rejects any new chunks of audio if its backlog is too large. When this happens,
|
||||||
|
@{speaker.playAudio} returns false. Once enough audio has played, and the backlog has been reduced, a
|
||||||
|
@{speaker_audio_empty} event is queued, and we can try to play our chunk once more.
|
||||||
|
|
||||||
|
## Storing audio
|
||||||
|
PCM is a fantastic way of representing audio when we want to manipulate it, but it's not very efficient when we want to
|
||||||
|
store it to disk. Compare the size of a WAV file (which uses PCM) to an equivalent MP3, it's often 5 times the size.
|
||||||
|
Instead, we store audio in special formats (or *codecs*) and then convert them to PCM when we need to do processing on
|
||||||
|
them.
|
||||||
|
|
||||||
|
Modern audio codecs use some incredibly impressive techniques to compress the audio as much as possible while preserving
|
||||||
|
sound quality. However, due to CC: Tweaked's limited processing power, it's not really possible to use these from your
|
||||||
|
computer. Instead, we need something much simpler.
|
||||||
|
|
||||||
|
DFPWM (Dynamic Filter Pulse Width Modulation) is the de facto standard audio format of the ComputerCraft (and
|
||||||
|
OpenComputers) world. Originally popularised by the addon mod [Computronics], CC:T now has built-in support for it with
|
||||||
|
the @{cc.audio.dfpwm} module. This allows you to read DFPWM files from disk, decode them to PCM, and then play them
|
||||||
|
using the speaker.
|
||||||
|
|
||||||
|
Let's dive in with an example, and we'll explain things afterwards:
|
||||||
|
|
||||||
|
```lua {data-peripheral=speaker}
|
||||||
|
local dfpwm = require("cc.audio.dfpwm")
|
||||||
|
local speaker = peripheral.find("speaker")
|
||||||
|
|
||||||
|
local decoder = dfpwm.make_decoder()
|
||||||
|
for chunk in io.lines("data/example.dfpwm", 16 * 1024) do
|
||||||
|
local buffer = decoder(chunk)
|
||||||
|
|
||||||
|
while not speaker.playAudio(buffer) do
|
||||||
|
os.pullEvent("speaker_audio_empty")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
Once again, we see the @{speaker.playAudio}/@{speaker_audio_empty} loop. However, the rest of the program is a little
|
||||||
|
different.
|
||||||
|
|
||||||
|
First, we require the dfpwm module and call @{cc.audio.dfpwm.make_decoder} to construct a new decoder. This decoder
|
||||||
|
accepts blocks of DFPWM data and converts it to a list of 8-bit amplitudes, which we can then play with our speaker.
|
||||||
|
|
||||||
|
As mentioned above, @{speaker.playAudio} accepts at most 128×1024 samples in one go. DFPMW uses a single bit for each
|
||||||
|
sample, which means we want to process our audio in chunks of 16×1024 bytes (16KiB). In order to do this, we use
|
||||||
|
@{io.lines}, which provides a nice way to loop over chunks of a file. You can of course just use @{fs.open} and
|
||||||
|
@{fs.BinaryReadHandle.read} if you prefer.
|
||||||
|
|
||||||
|
## Processing audio
|
||||||
|
As mentioned near the beginning of this guide, PCM audio is pretty easy to work with as it's just a list of amplitudes.
|
||||||
|
You can mix together samples from different streams by adding their amplitudes, change the rate of playback by removing
|
||||||
|
samples, etc...
|
||||||
|
|
||||||
|
Let's put together a small demonstration here. We're going to add a small delay effect to the song above, so that you
|
||||||
|
hear a faint echo a second and a half later.
|
||||||
|
|
||||||
|
In order to do this, we'll follow a format similar to the previous example, decoding the audio and then playing it.
|
||||||
|
However, we'll also add some new logic between those two steps, which loops over every sample in our chunk of audio, and
|
||||||
|
adds the sample from 1.5 seconds ago to it.
|
||||||
|
|
||||||
|
For this, we'll need to keep track of the last 72k samples - exactly 1.5 seconds worth of audio. We can do this using a
|
||||||
|
[Ring Buffer], which helps makes things a little more efficient.
|
||||||
|
|
||||||
|
```lua {data-peripheral=speaker}
|
||||||
|
local dfpwm = require("cc.audio.dfpwm")
|
||||||
|
local speaker = peripheral.find("speaker")
|
||||||
|
|
||||||
|
-- Speakers play at 48kHz, so 1.5 seconds is 72k samples. We first fill our buffer
|
||||||
|
-- with 0s, as there's nothing to echo at the start of the track!
|
||||||
|
local samples_i, samples_n = 1, 48000 * 1.5
|
||||||
|
local samples = {}
|
||||||
|
for i = 1, samples_n do samples[i] = 0 end
|
||||||
|
|
||||||
|
local decoder = dfpwm.make_decoder()
|
||||||
|
for chunk in io.lines("data/example.dfpwm", 16 * 1024) do
|
||||||
|
local buffer = decoder(chunk)
|
||||||
|
|
||||||
|
for i = 1, #buffer do
|
||||||
|
local original_value = buffer[i]
|
||||||
|
|
||||||
|
-- Replace this sample with its current amplitude plus the amplitude from 1.5 seconds ago.
|
||||||
|
-- We scale both to ensure the resulting value is still between -128 and 127.
|
||||||
|
buffer[i] = original_value * 0.6 + samples[samples_i] * 0.4
|
||||||
|
|
||||||
|
-- Now store the current sample, and move the "head" of our ring buffer forward one place.
|
||||||
|
samples[samples_i] = original_value
|
||||||
|
samples_i = samples_i + 1
|
||||||
|
if samples_i > samples_n then samples_i = 1 end
|
||||||
|
end
|
||||||
|
|
||||||
|
while not speaker.playAudio(buffer) do
|
||||||
|
os.pullEvent("speaker_audio_empty")
|
||||||
|
end
|
||||||
|
|
||||||
|
-- The audio processing above can be quite slow and preparing the first batch of audio
|
||||||
|
-- may timeout the computer. We sleep to avoid this.
|
||||||
|
-- There's definitely better ways of handling this - this is just an example!
|
||||||
|
sleep(0.05)
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
:::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
|
||||||
|
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
|
||||||
|
the wave. If you wanted to modify the _frequency_ (for instance, shifting the pitch), things get rather more complex.
|
||||||
|
For this, you'd need to use the [Fast Fourier transform][FFT] to convert the stream of amplitudes to frequencies,
|
||||||
|
process those, and then convert them back to amplitudes.
|
||||||
|
|
||||||
|
This is, I'm afraid, left as an exercise to the reader.
|
||||||
|
|
||||||
|
[Computronics]: https://github.com/Vexatos/Computronics/ "Computronics on GitHub"
|
||||||
|
[FFT]: https://en.wikipedia.org/wiki/Fast_Fourier_transform "Fast Fourier transform - 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"
|
||||||
|
[Sine Wave]: https://en.wikipedia.org/wiki/Sine_wave "Sine wave - Wikipedia"
|
||||||
|
[GitHub Discussions]: https://github.com/cc-tweaked/CC-Tweaked/discussions
|
||||||
|
[IRC]: https://webchat.esper.net/?channels=computercraft "#computercraft on EsperNet"
|
83
doc/guides/using_require.md
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
---
|
||||||
|
module: [kind=guide] using_require
|
||||||
|
---
|
||||||
|
|
||||||
|
# Reusing code with require
|
||||||
|
A library is a collection of useful functions and other definitions which is stored separately to your main program. You
|
||||||
|
might want to create a library because you have some functions which are used in multiple programs, or just to split
|
||||||
|
your program into multiple more modular files.
|
||||||
|
|
||||||
|
Let's say we want to create a small library to make working with the @{term|terminal} a little easier. We'll provide two
|
||||||
|
functions: `reset`, which clears the terminal and sets the cursor to (1, 1), and `write_center`, which prints some text
|
||||||
|
in the middle of the screen.
|
||||||
|
|
||||||
|
Start off by creating a file called `more_term.lua`:
|
||||||
|
|
||||||
|
```lua {data-snippet=more_term}
|
||||||
|
local function reset()
|
||||||
|
term.clear()
|
||||||
|
term.setCursorPos(1, 1)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function write_center(text)
|
||||||
|
local x, y = term.getCursorPos()
|
||||||
|
local width, height = term.getSize()
|
||||||
|
term.setCursorPos(math.floor((width - #text) / 2) + 1, y)
|
||||||
|
term.write(text)
|
||||||
|
end
|
||||||
|
|
||||||
|
return { reset = reset, write_center = write_center }
|
||||||
|
```
|
||||||
|
|
||||||
|
Now, what's going on here? We define our two functions as one might expect, and then at the bottom return a table with
|
||||||
|
the two functions. When we require this library, this table is what is returned. With that, we can then call the
|
||||||
|
original functions. Now create a new file, with the following:
|
||||||
|
|
||||||
|
```lua {data-mount=more_term:more_term.lua}
|
||||||
|
local more_term = require("more_term")
|
||||||
|
more_term.reset()
|
||||||
|
more_term.write_center("Hello, world!")
|
||||||
|
```
|
||||||
|
|
||||||
|
When run, this'll clear the screen and print some text in the middle of the first line.
|
||||||
|
|
||||||
|
## require in depth
|
||||||
|
While the previous section is a good introduction to how @{require} operates, there are a couple of remaining points
|
||||||
|
which are worth mentioning for more advanced usage.
|
||||||
|
|
||||||
|
### Libraries can return anything
|
||||||
|
In our above example, we return a table containing the functions we want to expose. However, it's worth pointing out
|
||||||
|
that you can return ''anything'' from your library - a table, a function or even just a string! @{require} treats them
|
||||||
|
all the same, and just returns whatever your library provides.
|
||||||
|
|
||||||
|
### Module resolution and the package path
|
||||||
|
In the above examples, we defined our library in a file, and @{require} read from it. While this is what you'll do most
|
||||||
|
of the time, it is possible to make @{require} look elsewhere for your library, such as downloading from a website or
|
||||||
|
loading from an in-memory library store.
|
||||||
|
|
||||||
|
As a result, the *module name* you pass to @{require} doesn't correspond to a file path. One common mistake is to load
|
||||||
|
code from a sub-directory using `require("folder/library")` or even `require("folder/library.lua")`, neither of which
|
||||||
|
will do quite what you expect.
|
||||||
|
|
||||||
|
When loading libraries (also referred to as *modules*) from files, @{require} searches along the *@{package.path|module
|
||||||
|
path}*. By default, this looks something like:
|
||||||
|
|
||||||
|
* `?.lua`
|
||||||
|
* `?/init.lua`
|
||||||
|
* `/rom/modules/main/?.lua`
|
||||||
|
* etc...
|
||||||
|
|
||||||
|
When you call `require("my_library")`, @{require} replaces the `?` in each element of the path with your module name, and
|
||||||
|
checks if the file exists. In this case, we'd look for `my_library.lua`, `my_library/init.lua`,
|
||||||
|
`/rom/modules/main/my_library.lua` and so on. Note that this works *relative to the current program*, so if your
|
||||||
|
program is actually called `folder/program`, then we'll look for `folder/my_library.lua`, etc...
|
||||||
|
|
||||||
|
One other caveat is loading libraries from sub-directories. For instance, say we have a file
|
||||||
|
`my/fancy/library.lua`. This can be loaded by using `require("my.fancy.library")` - the '.'s are replaced with '/'
|
||||||
|
before we start looking for the library.
|
||||||
|
|
||||||
|
## External links
|
||||||
|
There are several external resources which go into require in a little more detail:
|
||||||
|
|
||||||
|
- The [Lua Module tutorial](http://lua-users.org/wiki/ModulesTutorial) on the Lua wiki.
|
||||||
|
- [Lua's manual section on @{require}](https://www.lua.org/manual/5.1/manual.html#pdf-require).
|
BIN
doc/images/gps-constellation-example.png
Normal file
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
@@ -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"
|
95
doc/reference/feature_compat.md
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
---
|
||||||
|
module: [kind=reference] feature_compat
|
||||||
|
---
|
||||||
|
|
||||||
|
# Lua 5.2/5.3 features in CC: Tweaked
|
||||||
|
CC: Tweaked is based off of the Cobalt Lua runtime, which uses Lua 5.1. However, Cobalt and CC:T implement additional features from Lua 5.2 and 5.3 (as well as some deprecated 5.0 features) that are not available in base 5.1. This page lists all of the compatibility for these newer versions.
|
||||||
|
|
||||||
|
## Lua 5.2
|
||||||
|
| Feature | Supported? | Notes |
|
||||||
|
|---------------------------------------------------------------|------------|-------------------------------------------------------------------|
|
||||||
|
| `goto`/labels | ❌ | |
|
||||||
|
| `_ENV` | 🔶 | The `_ENV` global points to `getfenv()`, but it cannot be set. |
|
||||||
|
| `\z` escape | ✔ | |
|
||||||
|
| `\xNN` escape | ✔ | |
|
||||||
|
| Hex literal fractional/exponent parts | ✔ | |
|
||||||
|
| Empty statements | ❌ | |
|
||||||
|
| `__len` metamethod | ✔ | |
|
||||||
|
| `__ipairs` metamethod | ❌ | |
|
||||||
|
| `__pairs` metamethod | ✔ | |
|
||||||
|
| `bit32` library | ✔ | |
|
||||||
|
| `collectgarbage` isrunning, generational, incremental options | ❌ | `collectgarbage` does not exist in CC:T. |
|
||||||
|
| New `load` syntax | ✔ | |
|
||||||
|
| `loadfile` mode parameter | ✔ | Supports both 5.1 and 5.2+ syntax. |
|
||||||
|
| Removed `loadstring` | 🔶 | Only if `disable_lua51_features` is enabled in the configuration. |
|
||||||
|
| Removed `getfenv`, `setfenv` | 🔶 | Only if `disable_lua51_features` is enabled in the configuration. |
|
||||||
|
| `rawlen` function | ✔ | |
|
||||||
|
| Negative index to `select` | ✔ | |
|
||||||
|
| Removed `unpack` | 🔶 | Only if `disable_lua51_features` is enabled in the configuration. |
|
||||||
|
| Arguments to `xpcall` | ❌ | |
|
||||||
|
| Second return value from `coroutine.running` | ❌ | |
|
||||||
|
| Removed `module` | ✔ | |
|
||||||
|
| `package.loaders` -> `package.searchers` | ❌ | |
|
||||||
|
| Second argument to loader functions | ✔ | |
|
||||||
|
| `package.config` | ✔ | |
|
||||||
|
| `package.searchpath` | ✔ | |
|
||||||
|
| Removed `package.seeall` | ✔ | |
|
||||||
|
| `string.dump` on functions with upvalues (blanks them out) | ✔ | |
|
||||||
|
| `string.rep` separator | ❌ | |
|
||||||
|
| `%g` match group | ❌ | |
|
||||||
|
| Removal of `%z` match group | ❌ | |
|
||||||
|
| Removed `table.maxn` | 🔶 | Only if `disable_lua51_features` is enabled in the configuration. |
|
||||||
|
| `table.pack`/`table.unpack` | ✔ | |
|
||||||
|
| `math.log` base argument | ✔ | |
|
||||||
|
| Removed `math.log10` | 🔶 | Only if `disable_lua51_features` is enabled in the configuration. |
|
||||||
|
| `*L` mode to `file:read` | ✔ | |
|
||||||
|
| `os.execute` exit type + return value | ❌ | `os.execute` does not exist in CC:T. |
|
||||||
|
| `os.exit` close argument | ❌ | `os.exit` does not exist in CC:T. |
|
||||||
|
| `istailcall` field in `debug.getinfo` | ❌ | |
|
||||||
|
| `nparams` field in `debug.getinfo` | ✔ | |
|
||||||
|
| `isvararg` field in `debug.getinfo` | ✔ | |
|
||||||
|
| `debug.getlocal` negative indices for varargs | ❌ | |
|
||||||
|
| `debug.getuservalue`/`debug.setuservalue` | ❌ | Userdata are rarely used in CC:T, so this is not necessary. |
|
||||||
|
| `debug.upvalueid` | ✔ | |
|
||||||
|
| `debug.upvaluejoin` | ✔ | |
|
||||||
|
| Tail call hooks | ❌ | |
|
||||||
|
| `=` prefix for chunks | ✔ | |
|
||||||
|
| Yield across C boundary | ✔ | |
|
||||||
|
| Removal of ambiguity error | ❌ | |
|
||||||
|
| Identifiers may no longer use locale-dependent letters | ✔ | |
|
||||||
|
| Ephemeron tables | ❌ | |
|
||||||
|
| Identical functions may be reused | ❌ | |
|
||||||
|
| Generational garbage collector | ❌ | Cobalt uses the built-in Java garbage collector. |
|
||||||
|
|
||||||
|
## Lua 5.3
|
||||||
|
| Feature | Supported? | Notes |
|
||||||
|
|---------------------------------------------------------------------------------------|------------|---------------------------|
|
||||||
|
| Integer subtype | ❌ | |
|
||||||
|
| Bitwise operators/floor division | ❌ | |
|
||||||
|
| `\u{XXX}` escape sequence | ✔ | |
|
||||||
|
| `utf8` library | ✔ | |
|
||||||
|
| removed `__ipairs` metamethod | ✔ | |
|
||||||
|
| `coroutine.isyieldable` | ❌ | |
|
||||||
|
| `string.dump` strip argument | ✔ | |
|
||||||
|
| `string.pack`/`string.unpack`/`string.packsize` | ✔ | |
|
||||||
|
| `table.move` | ❌ | |
|
||||||
|
| `math.atan2` -> `math.atan` | ❌ | |
|
||||||
|
| Removed `math.frexp`, `math.ldexp`, `math.pow`, `math.cosh`, `math.sinh`, `math.tanh` | ❌ | |
|
||||||
|
| `math.maxinteger`/`math.mininteger` | ❌ | |
|
||||||
|
| `math.tointeger` | ❌ | |
|
||||||
|
| `math.type` | ❌ | |
|
||||||
|
| `math.ult` | ❌ | |
|
||||||
|
| Removed `bit32` library | ❌ | |
|
||||||
|
| Remove `*` from `file:read` modes | ✔ | |
|
||||||
|
| Metamethods respected in `table.*`, `ipairs` | 🔶 | Only `__lt` is respected. |
|
||||||
|
|
||||||
|
## Lua 5.0
|
||||||
|
| Feature | Supported? | Notes |
|
||||||
|
|----------------------------------|------------|--------------------------------------------------|
|
||||||
|
| `arg` table | 🔶 | Only set in the shell - not used in functions. |
|
||||||
|
| `string.gfind` | ✔ | Equal to `string.gmatch`. |
|
||||||
|
| `table.getn` | ✔ | Equal to `#tbl`. |
|
||||||
|
| `table.setn` | ❌ | |
|
||||||
|
| `math.mod` | ✔ | Equal to `math.fmod`. |
|
||||||
|
| `table.foreach`/`table.foreachi` | ✔ | |
|
||||||
|
| `gcinfo` | ❌ | Cobalt uses the built-in Java garbage collector. |
|
@@ -1,6 +1,4 @@
|
|||||||
--- The FS API allows you to manipulate files and the filesystem.
|
--- @module fs
|
||||||
--
|
|
||||||
-- @module fs
|
|
||||||
|
|
||||||
--- Returns true if a path is mounted to the parent filesystem.
|
--- Returns true if a path is mounted to the parent filesystem.
|
||||||
--
|
--
|
||||||
@@ -24,13 +22,43 @@ directory exist) and one without it (meaning this entry is an immediate
|
|||||||
completion candidate). `include_dirs` can be set to @{false} to only include
|
completion candidate). `include_dirs` can be set to @{false} to only include
|
||||||
those with a trailing slash.
|
those with a trailing slash.
|
||||||
|
|
||||||
@tparam string path The path to complete.
|
@tparam[1] string path The path to complete.
|
||||||
@tparam string location The location where paths are resolved from.
|
@tparam[1] string location The location where paths are resolved from.
|
||||||
@tparam[opt] boolean include_files When @{false}, only directories will be
|
@tparam[1,opt=true] boolean include_files When @{false}, only directories will
|
||||||
included in the returned list.
|
be included in the returned list.
|
||||||
@tparam[opt] boolean include_dirs When @{false}, "raw" directories will not be
|
@tparam[1,opt=true] boolean include_dirs When @{false}, "raw" directories will
|
||||||
included in the returned list.
|
not be included in the returned list.
|
||||||
|
|
||||||
|
@tparam[2] string path The path to complete.
|
||||||
|
@tparam[2] string location The location where paths are resolved from.
|
||||||
|
@tparam[2] {
|
||||||
|
include_dirs? = boolean, include_files? = boolean,
|
||||||
|
include_hidden? = boolean
|
||||||
|
} options
|
||||||
|
This table form is an expanded version of the previous syntax. The
|
||||||
|
`include_files` and `include_dirs` arguments from above are passed in as fields.
|
||||||
|
|
||||||
|
This table also accepts the following options:
|
||||||
|
- `include_hidden`: Whether to include hidden files (those starting with `.`)
|
||||||
|
by default. They will still be shown when typing a `.`.
|
||||||
|
|
||||||
@treturn { string... } A list of possible completion candidates.
|
@treturn { string... } A list of possible completion candidates.
|
||||||
@since 1.74
|
@since 1.74
|
||||||
|
@changed 1.101.0
|
||||||
|
@usage Complete files in the root directory.
|
||||||
|
|
||||||
|
read(nil, nil, function(str)
|
||||||
|
return fs.complete(str, "", true, false)
|
||||||
|
end)
|
||||||
|
|
||||||
|
@usage Complete files in the root directory, hiding hidden files by default.
|
||||||
|
|
||||||
|
read(nil, nil, function(str)
|
||||||
|
return fs.complete(str, "", {
|
||||||
|
include_files = true,
|
||||||
|
include_dirs = false,
|
||||||
|
included_hidden = false,
|
||||||
|
})
|
||||||
|
end)
|
||||||
]]
|
]]
|
||||||
function complete(path, location, include_files, include_dirs) end
|
function complete(path, location, include_files, include_dirs) end
|
||||||
|
@@ -12,16 +12,19 @@ rounded up to the nearest multiple of 0.05 seconds. If you are using coroutines
|
|||||||
or the @{parallel|parallel API}, it will only pause execution of the current
|
or the @{parallel|parallel API}, it will only pause execution of the current
|
||||||
thread, not the whole program.
|
thread, not the whole program.
|
||||||
|
|
||||||
**Note** Because sleep internally uses timers, it is a function that yields.
|
:::tip
|
||||||
This means that you can use it to prevent "Too long without yielding" errors,
|
Because sleep internally uses timers, it is a function that yields. This means
|
||||||
however, as the minimum sleep time is 0.05 seconds, it will slow your program
|
that you can use it to prevent "Too long without yielding" errors. However, as
|
||||||
down.
|
the minimum sleep time is 0.05 seconds, it will slow your program down.
|
||||||
|
:::
|
||||||
|
|
||||||
**Warning** Internally, this function queues and waits for a timer event (using
|
:::caution
|
||||||
|
Internally, this function queues and waits for a timer event (using
|
||||||
@{os.startTimer}), however it does not listen for any other events. This means
|
@{os.startTimer}), however it does not listen for any other events. This means
|
||||||
that any event that occurs while sleeping will be entirely discarded. If you
|
that any event that occurs while sleeping will be entirely discarded. If you
|
||||||
need to receive events while sleeping, consider using @{os.startTimer|timers},
|
need to receive events while sleeping, consider using @{os.startTimer|timers},
|
||||||
or the @{parallel|parallel API}.
|
or the @{parallel|parallel API}.
|
||||||
|
:::
|
||||||
|
|
||||||
@tparam number time The number of seconds to sleep for, rounded up to the
|
@tparam number time The number of seconds to sleep for, rounded up to the
|
||||||
nearest multiple of 0.05.
|
nearest multiple of 0.05.
|
||||||
@@ -59,7 +62,7 @@ function print(...) end
|
|||||||
-- @usage printError("Something went wrong!")
|
-- @usage printError("Something went wrong!")
|
||||||
function printError(...) end
|
function printError(...) end
|
||||||
|
|
||||||
--[[- Reads user input from the terminal, automatically handling arrow keys,
|
--[[- Reads user input from the terminal. This automatically handles arrow keys,
|
||||||
pasting, character replacement, history scrollback, auto-completion, and
|
pasting, character replacement, history scrollback, auto-completion, and
|
||||||
default values.
|
default values.
|
||||||
|
|
||||||
@@ -107,10 +110,15 @@ the prompt.
|
|||||||
]]
|
]]
|
||||||
function read(replaceChar, history, completeFn, default) end
|
function read(replaceChar, history, completeFn, default) end
|
||||||
|
|
||||||
--- The ComputerCraft and Minecraft version of the current computer environment.
|
--- Stores the current ComputerCraft and Minecraft versions.
|
||||||
|
--
|
||||||
|
-- Outside of Minecraft (for instance, in an emulator) @{_HOST} will contain the
|
||||||
|
-- emulator's version instead.
|
||||||
--
|
--
|
||||||
-- For example, `ComputerCraft 1.93.0 (Minecraft 1.15.2)`.
|
-- For example, `ComputerCraft 1.93.0 (Minecraft 1.15.2)`.
|
||||||
-- @usage _HOST
|
-- @usage Print the current computer's environment.
|
||||||
|
--
|
||||||
|
-- print(_HOST)
|
||||||
-- @since 1.76
|
-- @since 1.76
|
||||||
_HOST = _HOST
|
_HOST = _HOST
|
||||||
|
|
||||||
|
@@ -1,14 +1,13 @@
|
|||||||
--- The http library allows communicating with web servers, sending and
|
--- Make HTTP requests, sending and receiving data to a remote web server.
|
||||||
-- receiving data from them.
|
|
||||||
--
|
--
|
||||||
-- @module http
|
-- @module http
|
||||||
-- @since 1.1
|
-- @since 1.1
|
||||||
|
-- @see local_ips To allow accessing servers running on your local network.
|
||||||
|
|
||||||
--- Asynchronously make a HTTP request to the given url.
|
--- Asynchronously make a HTTP request to the given url.
|
||||||
--
|
--
|
||||||
-- This returns immediately, a [`http_success`](#http-success-event) or
|
-- This returns immediately, a @{http_success} or @{http_failure} will be queued
|
||||||
-- [`http_failure`](#http-failure-event) will be queued once the request has
|
-- once the request has completed.
|
||||||
-- completed.
|
|
||||||
--
|
--
|
||||||
-- @tparam string url The url to request
|
-- @tparam string url The url to request
|
||||||
-- @tparam[opt] string body An optional string containing the body of the
|
-- @tparam[opt] string body An optional string containing the body of the
|
||||||
@@ -112,9 +111,8 @@ function post(...) end
|
|||||||
|
|
||||||
--- Asynchronously determine whether a URL can be requested.
|
--- Asynchronously determine whether a URL can be requested.
|
||||||
--
|
--
|
||||||
-- If this returns `true`, one should also listen for [`http_check`
|
-- If this returns `true`, one should also listen for @{http_check} which will
|
||||||
-- events](#http-check-event) which will container further information about
|
-- container further information about whether the URL is allowed or not.
|
||||||
-- whether the URL is allowed or not.
|
|
||||||
--
|
--
|
||||||
-- @tparam string url The URL to check.
|
-- @tparam string url The URL to check.
|
||||||
-- @treturn true When this url is not invalid. This does not imply that it is
|
-- @treturn true When this url is not invalid. This does not imply that it is
|
||||||
@@ -128,9 +126,8 @@ function checkURLAsync(url) end
|
|||||||
|
|
||||||
--- Determine whether a URL can be requested.
|
--- Determine whether a URL can be requested.
|
||||||
--
|
--
|
||||||
-- If this returns `true`, one should also listen for [`http_check`
|
-- If this returns `true`, one should also listen for @{http_check} which will
|
||||||
-- events](#http-check-event) which will container further information about
|
-- container further information about whether the URL is allowed or not.
|
||||||
-- whether the URL is allowed or not.
|
|
||||||
--
|
--
|
||||||
-- @tparam string url The URL to check.
|
-- @tparam string url The URL to check.
|
||||||
-- @treturn true When this url is valid and can be requested via @{http.request}.
|
-- @treturn true When this url is valid and can be requested via @{http.request}.
|
||||||
@@ -168,9 +165,8 @@ function websocket(url, headers) end
|
|||||||
|
|
||||||
--- Asynchronously open a websocket.
|
--- Asynchronously open a websocket.
|
||||||
--
|
--
|
||||||
-- This returns immediately, a [`websocket_success`](#websocket-success-event)
|
-- This returns immediately, a @{websocket_success} or @{websocket_failure}
|
||||||
-- or [`websocket_failure`](#websocket-failure-event) will be queued once the
|
-- will be queued once the request has completed.
|
||||||
-- request has completed.
|
|
||||||
--
|
--
|
||||||
-- @tparam string url The websocket url to connect to. This should have the
|
-- @tparam string url The websocket url to connect to. This should have the
|
||||||
-- `ws://` or `wss://` protocol.
|
-- `ws://` or `wss://` protocol.
|
||||||
|
@@ -1,10 +1,11 @@
|
|||||||
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.99.0
|
modVersion=1.101.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.18
|
mcVersion=1.19.2
|
||||||
mapping_version=2021.09.05
|
|
||||||
forge_version=38.0.0
|
|
||||||
# NO SERIOUSLY, UPDATE mods.toml WHEN CHANGING
|
|
||||||
|
71
gradle/libs.versions.toml
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
[versions]
|
||||||
|
|
||||||
|
# Minecraft
|
||||||
|
# MC version is specified in gradle.properties, as we need that in settings.gradle.
|
||||||
|
forge = "43.1.1"
|
||||||
|
parchment = "2022.10.16"
|
||||||
|
parchmentMc = "1.19.2"
|
||||||
|
|
||||||
|
autoService = "1.0.1"
|
||||||
|
cobalt = { strictly = "[0.5.8,0.6.0)", prefer = "0.5.8" }
|
||||||
|
jetbrainsAnnotations = "23.0.0"
|
||||||
|
kotlin = "1.7.10"
|
||||||
|
kotlin-coroutines = "1.6.0"
|
||||||
|
|
||||||
|
# Testing
|
||||||
|
hamcrest = "2.2"
|
||||||
|
jqwik = "1.7.0"
|
||||||
|
junit = "5.9.1"
|
||||||
|
|
||||||
|
# Build tools
|
||||||
|
cctJavadoc = "1.5.2"
|
||||||
|
checkstyle = "10.3.4"
|
||||||
|
curseForgeGradle = "1.0.11"
|
||||||
|
forgeGradle = "5.1.+"
|
||||||
|
githubRelease = "2.2.12"
|
||||||
|
illuaminate = "0.1.0-7-g2a5a89c"
|
||||||
|
librarian = "1.+"
|
||||||
|
minotaur = "2.+"
|
||||||
|
mixinGradle = "0.7.+"
|
||||||
|
shadow = "7.1.2"
|
||||||
|
spotless = "6.8.0"
|
||||||
|
taskTree = "2.1.0"
|
||||||
|
|
||||||
|
[libraries]
|
||||||
|
autoService = { module = "com.google.auto.service:auto-service", version.ref = "autoService" }
|
||||||
|
cobalt = { module = "org.squiddev:Cobalt", version.ref = "cobalt" }
|
||||||
|
jetbrainsAnnotations = { module = "org.jetbrains:annotations", version.ref = "jetbrainsAnnotations" }
|
||||||
|
kotlin-coroutines = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kotlin-coroutines" }
|
||||||
|
kotlin-stdlib = { module = "org.jetbrains.kotlin:kotlin-stdlib-jdk8", version.ref = "kotlin" }
|
||||||
|
|
||||||
|
# Testing
|
||||||
|
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" }
|
||||||
|
|
||||||
|
# Build tools
|
||||||
|
cctJavadoc = { module = "cc.tweaked:cct-javadoc", version.ref = "cctJavadoc" }
|
||||||
|
checkstyle = { module = "com.puppycrawl.tools:checkstyle", version.ref = "checkstyle" }
|
||||||
|
kotlin-plugin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" }
|
||||||
|
spotless = { module = "com.diffplug.spotless:spotless-plugin-gradle", version.ref = "spotless" }
|
||||||
|
|
||||||
|
[plugins]
|
||||||
|
kotlin = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
|
||||||
|
taskTree = { id = "com.dorongold.task-tree", version.ref = "taskTree" }
|
||||||
|
curseForgeGradle = { id = "net.darkhax.curseforgegradle", version.ref = "curseForgeGradle" }
|
||||||
|
mixinGradle = { id = "org.spongepowered.mixin", version.ref = "mixinGradle" }
|
||||||
|
minotaur = { id = "com.modrinth.minotaur", version.ref = "minotaur" }
|
||||||
|
githubRelease = { id = "com.github.breadmoirai.github-release", version.ref = "githubRelease" }
|
||||||
|
forgeGradle = { id = "net.minecraftforge.gradle", version.ref = "forgeGradle" }
|
||||||
|
librarian = { id = "org.parchmentmc.librarian.forgegradle", version.ref = "librarian" }
|
||||||
|
shadow = { id = "com.github.johnrengelman.shadow", version.ref = "shadow" }
|
||||||
|
|
||||||
|
[bundles]
|
||||||
|
kotlin = ["kotlin-stdlib", "kotlin-coroutines"]
|
||||||
|
|
||||||
|
# Testing
|
||||||
|
test = ["junit-jupiter-api", "junit-jupiter-params", "hamcrest", "jqwik-api"]
|
||||||
|
testRuntime = ["junit-jupiter-engine", "jqwik-engine"]
|
BIN
gradle/wrapper/gradle-wrapper.jar
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
|
||||||
|
@@ -1,8 +1,7 @@
|
|||||||
; -*- mode: Lisp;-*-
|
; -*- mode: Lisp;-*-
|
||||||
|
|
||||||
(sources
|
(sources
|
||||||
/doc/stub/
|
/doc/
|
||||||
/doc/events/
|
|
||||||
/build/docs/luaJavadoc/
|
/build/docs/luaJavadoc/
|
||||||
/src/main/resources/*/computercraft/lua/bios.lua
|
/src/main/resources/*/computercraft/lua/bios.lua
|
||||||
/src/main/resources/*/computercraft/lua/rom/
|
/src/main/resources/*/computercraft/lua/rom/
|
||||||
@@ -11,7 +10,7 @@
|
|||||||
|
|
||||||
|
|
||||||
(doc
|
(doc
|
||||||
(destination build/docs/lua)
|
(destination build/illuaminate)
|
||||||
(index doc/index.md)
|
(index doc/index.md)
|
||||||
|
|
||||||
(site
|
(site
|
||||||
@@ -27,7 +26,9 @@
|
|||||||
(module-kinds
|
(module-kinds
|
||||||
(peripheral Peripherals)
|
(peripheral Peripherals)
|
||||||
(generic_peripheral "Generic Peripherals")
|
(generic_peripheral "Generic Peripherals")
|
||||||
(event Events))
|
(event Events)
|
||||||
|
(guide Guides)
|
||||||
|
(reference Reference))
|
||||||
|
|
||||||
(library-path
|
(library-path
|
||||||
/doc/stub/
|
/doc/stub/
|
||||||
|
2165
package-lock.json
generated
11
package.json
@@ -4,15 +4,26 @@
|
|||||||
"description": "Website additions for tweaked.cc",
|
"description": "Website additions for tweaked.cc",
|
||||||
"author": "SquidDev",
|
"author": "SquidDev",
|
||||||
"license": "BSD-3-Clause",
|
"license": "BSD-3-Clause",
|
||||||
|
"type": "module",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"preact": "^10.5.5",
|
"preact": "^10.5.5",
|
||||||
"tslib": "^2.0.3"
|
"tslib": "^2.0.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@rollup/plugin-typescript": "^8.2.5",
|
"@rollup/plugin-typescript": "^8.2.5",
|
||||||
|
"@rollup/plugin-url": "^7.0.0",
|
||||||
|
"@types/glob": "^7.2.0",
|
||||||
|
"@types/react-dom": "^18.0.5",
|
||||||
|
"glob": "^8.0.3",
|
||||||
|
"react": "^18.1.0",
|
||||||
|
"react-dom": "^18.1.0",
|
||||||
|
"rehype": "^12.0.1",
|
||||||
|
"rehype-highlight": "^5.0.2",
|
||||||
|
"rehype-react": "^7.1.1",
|
||||||
"requirejs": "^2.3.6",
|
"requirejs": "^2.3.6",
|
||||||
"rollup": "^2.33.1",
|
"rollup": "^2.33.1",
|
||||||
"rollup-plugin-terser": "^7.0.2",
|
"rollup-plugin-terser": "^7.0.2",
|
||||||
|
"ts-node": "^10.8.0",
|
||||||
"typescript": "^4.0.5"
|
"typescript": "^4.0.5"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,7 +1,8 @@
|
|||||||
import { readFileSync } from "fs";
|
import { readFileSync } from "fs";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
|
|
||||||
import typescript from "@rollup/plugin-typescript";
|
import typescript from "@rollup/plugin-typescript";
|
||||||
|
import url from '@rollup/plugin-url';
|
||||||
import { terser } from "rollup-plugin-terser";
|
import { terser } from "rollup-plugin-terser";
|
||||||
|
|
||||||
const input = "src/web";
|
const input = "src/web";
|
||||||
@@ -10,7 +11,7 @@ const requirejs = readFileSync("node_modules/requirejs/require.js");
|
|||||||
export default {
|
export default {
|
||||||
input: [`${input}/index.tsx`],
|
input: [`${input}/index.tsx`],
|
||||||
output: {
|
output: {
|
||||||
file: "build/rollup/index.js",
|
dir: "build/rollup/",
|
||||||
// We bundle requirejs (and config) into the header. It's rather gross
|
// We bundle requirejs (and config) into the header. It's rather gross
|
||||||
// but also works reasonably well.
|
// but also works reasonably well.
|
||||||
// Also suffix a ?v=${date} onto the end in the event we need to require a specific copy-cat version.
|
// Also suffix a ?v=${date} onto the end in the event we need to require a specific copy-cat version.
|
||||||
@@ -18,7 +19,7 @@ export default {
|
|||||||
${requirejs}
|
${requirejs}
|
||||||
require.config({
|
require.config({
|
||||||
paths: { copycat: "https://copy-cat.squiddev.cc" },
|
paths: { copycat: "https://copy-cat.squiddev.cc" },
|
||||||
urlArgs: function(id) { return id == "copycat/embed" ? "?v=20211127" : ""; }
|
urlArgs: function(id) { return id == "copycat/embed" ? "?v=20211221" : ""; }
|
||||||
});
|
});
|
||||||
`,
|
`,
|
||||||
format: "amd",
|
format: "amd",
|
||||||
@@ -33,12 +34,18 @@ export default {
|
|||||||
plugins: [
|
plugins: [
|
||||||
typescript(),
|
typescript(),
|
||||||
|
|
||||||
|
url({
|
||||||
|
include: "**/*.dfpwm",
|
||||||
|
fileName: "[name]-[hash][extname]",
|
||||||
|
publicPath: "/",
|
||||||
|
}),
|
||||||
|
|
||||||
{
|
{
|
||||||
name: "cc-tweaked",
|
name: "cc-tweaked",
|
||||||
async transform(code, file) {
|
async transform(code, file) {
|
||||||
// Allow loading files in /mount.
|
// Allow loading files in /mount.
|
||||||
const ext = path.extname(file);
|
const ext = path.extname(file);
|
||||||
return ext != '.tsx' && ext != '.ts' && path.dirname(file) === path.resolve(`${input}/mount`)
|
return ext != '.dfpwm' && path.dirname(file) === path.resolve(`${input}/mount`)
|
||||||
? `export default ${JSON.stringify(code)};\n`
|
? `export default ${JSON.stringify(code)};\n`
|
||||||
: null;
|
: null;
|
||||||
},
|
},
|
||||||
|
@@ -1 +0,0 @@
|
|||||||
rootProject.name = "cc-tweaked-${mc_version}"
|
|
17
settings.gradle.kts
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
pluginManagement {
|
||||||
|
repositories {
|
||||||
|
gradlePluginPortal()
|
||||||
|
maven("https://maven.minecraftforge.net")
|
||||||
|
maven("https://maven.parchmentmc.org")
|
||||||
|
}
|
||||||
|
resolutionStrategy {
|
||||||
|
eachPlugin {
|
||||||
|
if (requested.id.id == "org.spongepowered.mixin") {
|
||||||
|
useModule("org.spongepowered:mixingradle:${requested.version}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val mcVersion: String by settings
|
||||||
|
rootProject.name = "cc-tweaked-$mcVersion"
|
760
src/generated/export/index.json
generated
Normal file
@@ -0,0 +1,760 @@
|
|||||||
|
{
|
||||||
|
"itemNames": {
|
||||||
|
"computercraft:cable": "Networking Cable",
|
||||||
|
"computercraft:computer_advanced": "Advanced Computer",
|
||||||
|
"computercraft:computer_command": "Command Computer",
|
||||||
|
"computercraft:computer_normal": "Computer",
|
||||||
|
"computercraft:disk": "Floppy Disk",
|
||||||
|
"computercraft:disk_drive": "Disk Drive",
|
||||||
|
"computercraft:monitor_advanced": "Advanced Monitor",
|
||||||
|
"computercraft:monitor_normal": "Monitor",
|
||||||
|
"computercraft:pocket_computer_advanced": "Advanced Pocket Computer",
|
||||||
|
"computercraft:pocket_computer_normal": "Pocket Computer",
|
||||||
|
"computercraft:printed_book": "Printed Book",
|
||||||
|
"computercraft:printed_page": "Printed Page",
|
||||||
|
"computercraft:printed_pages": "Printed Pages",
|
||||||
|
"computercraft:printer": "Printer",
|
||||||
|
"computercraft:speaker": "Speaker",
|
||||||
|
"computercraft:treasure_disk": "Floppy Disk",
|
||||||
|
"computercraft:turtle_advanced": "Advanced Turtle",
|
||||||
|
"computercraft:turtle_normal": "Turtle",
|
||||||
|
"computercraft:wired_modem": "Wired Modem",
|
||||||
|
"computercraft:wired_modem_full": "Wired Modem",
|
||||||
|
"computercraft:wireless_modem_advanced": "Ender Modem",
|
||||||
|
"computercraft:wireless_modem_normal": "Wireless Modem",
|
||||||
|
"minecraft:black_dye": "Black Dye",
|
||||||
|
"minecraft:blue_dye": "Blue Dye",
|
||||||
|
"minecraft:brown_dye": "Brown Dye",
|
||||||
|
"minecraft:chest": "Chest",
|
||||||
|
"minecraft:command_block": "Command Block",
|
||||||
|
"minecraft:cyan_dye": "Cyan Dye",
|
||||||
|
"minecraft:ender_eye": "Eye of Ender",
|
||||||
|
"minecraft:ender_pearl": "Ender Pearl",
|
||||||
|
"minecraft:glass_pane": "Glass Pane",
|
||||||
|
"minecraft:gold_block": "Block of Gold",
|
||||||
|
"minecraft:gold_ingot": "Gold Ingot",
|
||||||
|
"minecraft:golden_apple": "Golden Apple",
|
||||||
|
"minecraft:gray_dye": "Gray Dye",
|
||||||
|
"minecraft:green_dye": "Green Dye",
|
||||||
|
"minecraft:iron_ingot": "Iron Ingot",
|
||||||
|
"minecraft:leather": "Leather",
|
||||||
|
"minecraft:light_blue_dye": "Light Blue Dye",
|
||||||
|
"minecraft:light_gray_dye": "Light Gray Dye",
|
||||||
|
"minecraft:lime_dye": "Lime Dye",
|
||||||
|
"minecraft:magenta_dye": "Magenta Dye",
|
||||||
|
"minecraft:note_block": "Note Block",
|
||||||
|
"minecraft:orange_dye": "Orange Dye",
|
||||||
|
"minecraft:pink_dye": "Pink Dye",
|
||||||
|
"minecraft:purple_dye": "Purple Dye",
|
||||||
|
"minecraft:red_dye": "Red Dye",
|
||||||
|
"minecraft:redstone": "Redstone Dust",
|
||||||
|
"minecraft:stone": "Stone",
|
||||||
|
"minecraft:string": "String",
|
||||||
|
"minecraft:white_dye": "White Dye",
|
||||||
|
"minecraft:yellow_dye": "Yellow Dye"
|
||||||
|
},
|
||||||
|
"recipes": {
|
||||||
|
"computercraft:cable": {
|
||||||
|
"inputs": [
|
||||||
|
null,
|
||||||
|
[
|
||||||
|
"minecraft:stone"
|
||||||
|
],
|
||||||
|
null,
|
||||||
|
[
|
||||||
|
"minecraft:stone"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:redstone"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:stone"
|
||||||
|
],
|
||||||
|
null,
|
||||||
|
[
|
||||||
|
"minecraft:stone"
|
||||||
|
],
|
||||||
|
null
|
||||||
|
],
|
||||||
|
"output": "computercraft:cable",
|
||||||
|
"count": 6
|
||||||
|
},
|
||||||
|
"computercraft:computer_advanced": {
|
||||||
|
"inputs": [
|
||||||
|
[
|
||||||
|
"minecraft:gold_ingot"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:gold_ingot"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:gold_ingot"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:gold_ingot"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:redstone"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:gold_ingot"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:gold_ingot"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:glass_pane"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:gold_ingot"
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"output": "computercraft:computer_advanced",
|
||||||
|
"count": 1
|
||||||
|
},
|
||||||
|
"computercraft:computer_advanced_upgrade": {
|
||||||
|
"inputs": [
|
||||||
|
[
|
||||||
|
"minecraft:gold_ingot"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:gold_ingot"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:gold_ingot"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:gold_ingot"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"computercraft:computer_normal"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:gold_ingot"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:gold_ingot"
|
||||||
|
],
|
||||||
|
null,
|
||||||
|
[
|
||||||
|
"minecraft:gold_ingot"
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"output": "computercraft:computer_advanced",
|
||||||
|
"count": 1
|
||||||
|
},
|
||||||
|
"computercraft:computer_command": {
|
||||||
|
"inputs": [
|
||||||
|
[
|
||||||
|
"minecraft:gold_ingot"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:gold_ingot"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:gold_ingot"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:gold_ingot"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:command_block"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:gold_ingot"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:gold_ingot"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:glass_pane"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:gold_ingot"
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"output": "computercraft:computer_command",
|
||||||
|
"count": 1
|
||||||
|
},
|
||||||
|
"computercraft:computer_normal": {
|
||||||
|
"inputs": [
|
||||||
|
[
|
||||||
|
"minecraft:stone"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:stone"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:stone"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:stone"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:redstone"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:stone"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:stone"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:glass_pane"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:stone"
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"output": "computercraft:computer_normal",
|
||||||
|
"count": 1
|
||||||
|
},
|
||||||
|
"computercraft:disk_drive": {
|
||||||
|
"inputs": [
|
||||||
|
[
|
||||||
|
"minecraft:stone"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:stone"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:stone"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:stone"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:redstone"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:stone"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:stone"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:redstone"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:stone"
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"output": "computercraft:disk_drive",
|
||||||
|
"count": 1
|
||||||
|
},
|
||||||
|
"computercraft:monitor_advanced": {
|
||||||
|
"inputs": [
|
||||||
|
[
|
||||||
|
"minecraft:gold_ingot"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:gold_ingot"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:gold_ingot"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:gold_ingot"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:glass_pane"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:gold_ingot"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:gold_ingot"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:gold_ingot"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:gold_ingot"
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"output": "computercraft:monitor_advanced",
|
||||||
|
"count": 4
|
||||||
|
},
|
||||||
|
"computercraft:monitor_normal": {
|
||||||
|
"inputs": [
|
||||||
|
[
|
||||||
|
"minecraft:stone"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:stone"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:stone"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:stone"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:glass_pane"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:stone"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:stone"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:stone"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:stone"
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"output": "computercraft:monitor_normal",
|
||||||
|
"count": 1
|
||||||
|
},
|
||||||
|
"computercraft:pocket_computer_advanced": {
|
||||||
|
"inputs": [
|
||||||
|
[
|
||||||
|
"minecraft:gold_ingot"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:gold_ingot"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:gold_ingot"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:gold_ingot"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:golden_apple"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:gold_ingot"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:gold_ingot"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:glass_pane"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:gold_ingot"
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"output": "computercraft:pocket_computer_advanced",
|
||||||
|
"count": 1
|
||||||
|
},
|
||||||
|
"computercraft:pocket_computer_advanced_upgrade": {
|
||||||
|
"inputs": [
|
||||||
|
[
|
||||||
|
"minecraft:gold_ingot"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:gold_ingot"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:gold_ingot"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:gold_ingot"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"computercraft:pocket_computer_normal"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:gold_ingot"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:gold_ingot"
|
||||||
|
],
|
||||||
|
null,
|
||||||
|
[
|
||||||
|
"minecraft:gold_ingot"
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"output": "computercraft:pocket_computer_advanced",
|
||||||
|
"count": 1
|
||||||
|
},
|
||||||
|
"computercraft:pocket_computer_normal": {
|
||||||
|
"inputs": [
|
||||||
|
[
|
||||||
|
"minecraft:stone"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:stone"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:stone"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:stone"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:golden_apple"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:stone"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:stone"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:glass_pane"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:stone"
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"output": "computercraft:pocket_computer_normal",
|
||||||
|
"count": 1
|
||||||
|
},
|
||||||
|
"computercraft:printed_book": {
|
||||||
|
"inputs": [
|
||||||
|
[
|
||||||
|
"minecraft:leather"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"computercraft:printed_page"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:string"
|
||||||
|
],
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null
|
||||||
|
],
|
||||||
|
"output": "computercraft:printed_book",
|
||||||
|
"count": 1
|
||||||
|
},
|
||||||
|
"computercraft:printed_pages": {
|
||||||
|
"inputs": [
|
||||||
|
[
|
||||||
|
"computercraft:printed_page"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"computercraft:printed_page"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:string"
|
||||||
|
],
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null
|
||||||
|
],
|
||||||
|
"output": "computercraft:printed_pages",
|
||||||
|
"count": 1
|
||||||
|
},
|
||||||
|
"computercraft:printer": {
|
||||||
|
"inputs": [
|
||||||
|
[
|
||||||
|
"minecraft:stone"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:stone"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:stone"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:stone"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:redstone"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:stone"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:stone"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:black_dye",
|
||||||
|
"minecraft:blue_dye",
|
||||||
|
"minecraft:brown_dye",
|
||||||
|
"minecraft:cyan_dye",
|
||||||
|
"minecraft:gray_dye",
|
||||||
|
"minecraft:green_dye",
|
||||||
|
"minecraft:light_blue_dye",
|
||||||
|
"minecraft:light_gray_dye",
|
||||||
|
"minecraft:lime_dye",
|
||||||
|
"minecraft:magenta_dye",
|
||||||
|
"minecraft:orange_dye",
|
||||||
|
"minecraft:pink_dye",
|
||||||
|
"minecraft:purple_dye",
|
||||||
|
"minecraft:red_dye",
|
||||||
|
"minecraft:white_dye",
|
||||||
|
"minecraft:yellow_dye"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:stone"
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"output": "computercraft:printer",
|
||||||
|
"count": 1
|
||||||
|
},
|
||||||
|
"computercraft:speaker": {
|
||||||
|
"inputs": [
|
||||||
|
[
|
||||||
|
"minecraft:stone"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:stone"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:stone"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:stone"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:note_block"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:stone"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:stone"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:redstone"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:stone"
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"output": "computercraft:speaker",
|
||||||
|
"count": 1
|
||||||
|
},
|
||||||
|
"computercraft:turtle_advanced": {
|
||||||
|
"inputs": [
|
||||||
|
[
|
||||||
|
"minecraft:gold_ingot"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:gold_ingot"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:gold_ingot"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:gold_ingot"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"computercraft:computer_advanced"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:gold_ingot"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:gold_ingot"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:chest"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:gold_ingot"
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"output": "computercraft:turtle_advanced",
|
||||||
|
"count": 1
|
||||||
|
},
|
||||||
|
"computercraft:turtle_advanced_upgrade": {
|
||||||
|
"inputs": [
|
||||||
|
[
|
||||||
|
"minecraft:gold_ingot"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:gold_ingot"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:gold_ingot"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:gold_ingot"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"computercraft:turtle_normal"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:gold_ingot"
|
||||||
|
],
|
||||||
|
null,
|
||||||
|
[
|
||||||
|
"minecraft:gold_block"
|
||||||
|
],
|
||||||
|
null
|
||||||
|
],
|
||||||
|
"output": "computercraft:turtle_advanced",
|
||||||
|
"count": 1
|
||||||
|
},
|
||||||
|
"computercraft:turtle_normal": {
|
||||||
|
"inputs": [
|
||||||
|
[
|
||||||
|
"minecraft:iron_ingot"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:iron_ingot"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:iron_ingot"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:iron_ingot"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"computercraft:computer_normal"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:iron_ingot"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:iron_ingot"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:chest"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:iron_ingot"
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"output": "computercraft:turtle_normal",
|
||||||
|
"count": 1
|
||||||
|
},
|
||||||
|
"computercraft:wired_modem": {
|
||||||
|
"inputs": [
|
||||||
|
[
|
||||||
|
"minecraft:stone"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:stone"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:stone"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:stone"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:redstone"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:stone"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:stone"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:stone"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:stone"
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"output": "computercraft:wired_modem",
|
||||||
|
"count": 1
|
||||||
|
},
|
||||||
|
"computercraft:wired_modem_full_from": {
|
||||||
|
"inputs": [
|
||||||
|
[
|
||||||
|
"computercraft:wired_modem"
|
||||||
|
],
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null
|
||||||
|
],
|
||||||
|
"output": "computercraft:wired_modem_full",
|
||||||
|
"count": 1
|
||||||
|
},
|
||||||
|
"computercraft:wired_modem_full_to": {
|
||||||
|
"inputs": [
|
||||||
|
[
|
||||||
|
"computercraft:wired_modem_full"
|
||||||
|
],
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null
|
||||||
|
],
|
||||||
|
"output": "computercraft:wired_modem",
|
||||||
|
"count": 1
|
||||||
|
},
|
||||||
|
"computercraft:wireless_modem_advanced": {
|
||||||
|
"inputs": [
|
||||||
|
[
|
||||||
|
"minecraft:gold_ingot"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:gold_ingot"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:gold_ingot"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:gold_ingot"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:ender_eye"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:gold_ingot"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:gold_ingot"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:gold_ingot"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:gold_ingot"
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"output": "computercraft:wireless_modem_advanced",
|
||||||
|
"count": 1
|
||||||
|
},
|
||||||
|
"computercraft:wireless_modem_normal": {
|
||||||
|
"inputs": [
|
||||||
|
[
|
||||||
|
"minecraft:stone"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:stone"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:stone"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:stone"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:ender_pearl"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:stone"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:stone"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:stone"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"minecraft:stone"
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"output": "computercraft:wireless_modem_normal",
|
||||||
|
"count": 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
BIN
src/generated/export/items/computercraft/cable.png
generated
Normal file
After Width: | Height: | Size: 375 B |
BIN
src/generated/export/items/computercraft/computer_advanced.png
generated
Normal file
After Width: | Height: | Size: 2.5 KiB |
BIN
src/generated/export/items/computercraft/computer_command.png
generated
Normal file
After Width: | Height: | Size: 3.1 KiB |
BIN
src/generated/export/items/computercraft/computer_normal.png
generated
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
src/generated/export/items/computercraft/disk.png
generated
Normal file
After Width: | Height: | Size: 189 B |
BIN
src/generated/export/items/computercraft/disk_drive.png
generated
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
src/generated/export/items/computercraft/monitor_advanced.png
generated
Normal file
After Width: | Height: | Size: 2.4 KiB |
BIN
src/generated/export/items/computercraft/monitor_normal.png
generated
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
src/generated/export/items/computercraft/pocket_computer_advanced.png
generated
Normal file
After Width: | Height: | Size: 158 B |
BIN
src/generated/export/items/computercraft/pocket_computer_normal.png
generated
Normal file
After Width: | Height: | Size: 158 B |
BIN
src/generated/export/items/computercraft/printed_book.png
generated
Normal file
After Width: | Height: | Size: 287 B |
BIN
src/generated/export/items/computercraft/printed_page.png
generated
Normal file
After Width: | Height: | Size: 208 B |
BIN
src/generated/export/items/computercraft/printed_pages.png
generated
Normal file
After Width: | Height: | Size: 259 B |
BIN
src/generated/export/items/computercraft/printer.png
generated
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
src/generated/export/items/computercraft/speaker.png
generated
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
src/generated/export/items/computercraft/treasure_disk.png
generated
Normal file
After Width: | Height: | Size: 189 B |
BIN
src/generated/export/items/computercraft/turtle_advanced.png
generated
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
src/generated/export/items/computercraft/turtle_normal.png
generated
Normal file
After Width: | Height: | Size: 977 B |
BIN
src/generated/export/items/computercraft/wired_modem.png
generated
Normal file
After Width: | Height: | Size: 593 B |
BIN
src/generated/export/items/computercraft/wired_modem_full.png
generated
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
src/generated/export/items/computercraft/wireless_modem_advanced.png
generated
Normal file
After Width: | Height: | Size: 1.0 KiB |
BIN
src/generated/export/items/computercraft/wireless_modem_normal.png
generated
Normal file
After Width: | Height: | Size: 620 B |
BIN
src/generated/export/items/minecraft/black_dye.png
generated
Normal file
After Width: | Height: | Size: 218 B |
BIN
src/generated/export/items/minecraft/blue_dye.png
generated
Normal file
After Width: | Height: | Size: 228 B |
BIN
src/generated/export/items/minecraft/brown_dye.png
generated
Normal file
After Width: | Height: | Size: 218 B |
BIN
src/generated/export/items/minecraft/chest.png
generated
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
src/generated/export/items/minecraft/command_block.png
generated
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
src/generated/export/items/minecraft/cyan_dye.png
generated
Normal file
After Width: | Height: | Size: 243 B |
BIN
src/generated/export/items/minecraft/ender_eye.png
generated
Normal file
After Width: | Height: | Size: 278 B |
BIN
src/generated/export/items/minecraft/ender_pearl.png
generated
Normal file
After Width: | Height: | Size: 266 B |
BIN
src/generated/export/items/minecraft/glass_pane.png
generated
Normal file
After Width: | Height: | Size: 186 B |
BIN
src/generated/export/items/minecraft/gold_block.png
generated
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
src/generated/export/items/minecraft/gold_ingot.png
generated
Normal file
After Width: | Height: | Size: 240 B |
BIN
src/generated/export/items/minecraft/golden_apple.png
generated
Normal file
After Width: | Height: | Size: 257 B |
BIN
src/generated/export/items/minecraft/gray_dye.png
generated
Normal file
After Width: | Height: | Size: 229 B |
BIN
src/generated/export/items/minecraft/green_dye.png
generated
Normal file
After Width: | Height: | Size: 236 B |
BIN
src/generated/export/items/minecraft/iron_ingot.png
generated
Normal file
After Width: | Height: | Size: 240 B |
BIN
src/generated/export/items/minecraft/leather.png
generated
Normal file
After Width: | Height: | Size: 242 B |
BIN
src/generated/export/items/minecraft/light_blue_dye.png
generated
Normal file
After Width: | Height: | Size: 224 B |
BIN
src/generated/export/items/minecraft/light_gray_dye.png
generated
Normal file
After Width: | Height: | Size: 229 B |
BIN
src/generated/export/items/minecraft/lime_dye.png
generated
Normal file
After Width: | Height: | Size: 221 B |
BIN
src/generated/export/items/minecraft/magenta_dye.png
generated
Normal file
After Width: | Height: | Size: 220 B |
BIN
src/generated/export/items/minecraft/note_block.png
generated
Normal file
After Width: | Height: | Size: 1.0 KiB |