mirror of
https://github.com/SquidDev-CC/CC-Tweaked
synced 2025-11-07 08:52:59 +00:00
Compare commits
159 Commits
v1.20.1-1.
...
v1.20.1-1.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bcb3e9bd53 | ||
|
|
c30bffbd0f | ||
|
|
91c41856c5 | ||
|
|
18c9723308 | ||
|
|
aee382ed70 | ||
|
|
6656da5877 | ||
|
|
09e521727f | ||
|
|
cab66a2d6e | ||
|
|
8eabd4f303 | ||
|
|
e3ced84885 | ||
|
|
0929ab577d | ||
|
|
2228733abc | ||
|
|
e67c94d1bd | ||
|
|
ae5a661a47 | ||
|
|
0ff58cdc3e | ||
|
|
1747c74770 | ||
|
|
71669cf49c | ||
|
|
bd327e37eb | ||
|
|
bdce9a8170 | ||
|
|
7e5598d084 | ||
|
|
440fca6535 | ||
|
|
6635edd35c | ||
|
|
93ad40efbb | ||
|
|
27dc8b5b2c | ||
|
|
3ebdf7ef5e | ||
|
|
905d4cb091 | ||
|
|
e7ab05d064 | ||
|
|
6ec34b42e5 | ||
|
|
ab785a0906 | ||
|
|
4541decd40 | ||
|
|
747a5a53b4 | ||
|
|
c0643fadca | ||
|
|
0a31de43c2 | ||
|
|
96b6947ef2 | ||
|
|
e7a1065bfc | ||
|
|
663eecff0c | ||
|
|
e6125bcf60 | ||
|
|
0d6c6e7ae7 | ||
|
|
ae71eb3cae | ||
|
|
3188197447 | ||
|
|
6c8b391dab | ||
|
|
b1248e4901 | ||
|
|
56d97630e8 | ||
|
|
e660192f08 | ||
|
|
4e82bd352d | ||
|
|
07113c3e9b | ||
|
|
d562a051c7 | ||
|
|
6ac09742fc | ||
|
|
5dd6b9a637 | ||
|
|
8fb1dd346c | ||
|
|
cdc8592aa3 | ||
|
|
0f6ea3deaf | ||
|
|
13ed422bd5 | ||
|
|
5b58271b92 | ||
|
|
4e42394f33 | ||
|
|
53546b9f57 | ||
|
|
6dfdeb9321 | ||
|
|
500406f9eb | ||
|
|
b3738a7a63 | ||
|
|
5f8b1dd67f | ||
|
|
053751b190 | ||
|
|
52b78f92cd | ||
|
|
2055052a57 | ||
|
|
12ee47ff19 | ||
|
|
25776abf61 | ||
|
|
f7411b40a2 | ||
|
|
8c8924f54e | ||
|
|
c1628d077a | ||
|
|
5b2fdec6ca | ||
|
|
5a7259e4c9 | ||
|
|
92b335f45f | ||
|
|
b93ea9c62e | ||
|
|
b9edd7c7f6 | ||
|
|
84a761ddd5 | ||
|
|
3371c4651c | ||
|
|
2a04fb71fd | ||
|
|
df61389304 | ||
|
|
e6bc1e4e27 | ||
|
|
b6632c9ed9 | ||
|
|
41b6711b38 | ||
|
|
02f0b7ec14 | ||
|
|
a9d31cd3c6 | ||
|
|
43744b0e85 | ||
|
|
55510c42db | ||
|
|
57a944fd90 | ||
|
|
940f59b116 | ||
|
|
dd08d1ec8e | ||
|
|
90ed0b24e7 | ||
|
|
0ad399a528 | ||
|
|
1b88213eca | ||
|
|
eef05b9854 | ||
|
|
24d74f5c80 | ||
|
|
ae50f900af | ||
|
|
48889ceb89 | ||
|
|
c2988366d8 | ||
|
|
ec0765ead1 | ||
|
|
b94e34f372 | ||
|
|
af3263dec2 | ||
|
|
7f25c9a66b | ||
|
|
9ca3efff3c | ||
|
|
8f1bf4341c | ||
|
|
aaf8c248a8 | ||
|
|
df26cd267a | ||
|
|
8914b78816 | ||
|
|
9a48b53a83 | ||
|
|
9519448e43 | ||
|
|
9ea7f45fa7 | ||
|
|
915b6f9d81 | ||
|
|
a98f3b2a4c | ||
|
|
d351bc33c6 | ||
|
|
5d71770931 | ||
|
|
4bbde8c50c | ||
|
|
cc8c1f38e7 | ||
|
|
cab9c9772a | ||
|
|
e337a63712 | ||
|
|
efa92b741b | ||
|
|
a91ac6f214 | ||
|
|
943a9406b1 | ||
|
|
0b2bb5e7b5 | ||
|
|
8708048b6e | ||
|
|
d138d9c4a5 | ||
|
|
f54cb8a432 | ||
|
|
94f5ede75a | ||
|
|
1977556da4 | ||
|
|
9eabb29999 | ||
|
|
ecf880ed82 | ||
|
|
655d5aeca8 | ||
|
|
34f41c4039 | ||
|
|
f5b16261cc | ||
|
|
7eb3b691da | ||
|
|
910a63214e | ||
|
|
591a7eca23 | ||
|
|
a29a516a3f | ||
|
|
4a5e03c11a | ||
|
|
50d460624f | ||
|
|
bc500df921 | ||
|
|
4accda6b8e | ||
|
|
54ab98473f | ||
|
|
7ffdbb2316 | ||
|
|
672c2cf029 | ||
|
|
c3bdb0440e | ||
|
|
88f0c44152 | ||
|
|
ebaf49508f | ||
|
|
c8523bf479 | ||
|
|
953372b1b7 | ||
|
|
36b9f4ec55 | ||
|
|
ccfed0059b | ||
|
|
c45fc94752 | ||
|
|
7b4ba11fb4 | ||
|
|
8ccd5a560c | ||
|
|
0f866836a0 | ||
|
|
df591cd7c6 | ||
|
|
c7f3d4f45d | ||
|
|
77ac04cb7a | ||
|
|
fd1f6dda32 | ||
|
|
201df7e987 | ||
|
|
5722e51735 | ||
|
|
7a291619ab | ||
|
|
4b9b19b02d |
44
.github/workflows/main-ci.yml
vendored
44
.github/workflows/main-ci.yml
vendored
@@ -8,16 +8,16 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Clone repository
|
- name: 📥 Clone repository
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Set up Java
|
- name: 📥 Set up Java
|
||||||
uses: actions/setup-java@v3
|
uses: actions/setup-java@v3
|
||||||
with:
|
with:
|
||||||
java-version: 17
|
java-version: 17
|
||||||
distribution: 'temurin'
|
distribution: 'temurin'
|
||||||
|
|
||||||
- name: Setup Gradle
|
- name: 📥 Setup Gradle
|
||||||
uses: gradle/gradle-build-action@v2
|
uses: gradle/gradle-build-action@v2
|
||||||
with:
|
with:
|
||||||
cache-read-only: ${{ !startsWith(github.ref, 'refs/heads/mc-') }}
|
cache-read-only: ${{ !startsWith(github.ref, 'refs/heads/mc-') }}
|
||||||
@@ -27,39 +27,45 @@ jobs:
|
|||||||
mkdir -p ~/.gradle
|
mkdir -p ~/.gradle
|
||||||
echo "org.gradle.daemon=false" >> ~/.gradle/gradle.properties
|
echo "org.gradle.daemon=false" >> ~/.gradle/gradle.properties
|
||||||
|
|
||||||
- name: Build with Gradle
|
- name: ⚒️ Build
|
||||||
run: |
|
run: ./gradlew assemble || ./gradlew assemble
|
||||||
./gradlew assemble || ./gradlew assemble
|
|
||||||
./gradlew downloadAssets || ./gradlew downloadAssets
|
|
||||||
./gradlew build
|
|
||||||
|
|
||||||
- name: Run client tests
|
- name: 💡 Lint
|
||||||
|
uses: pre-commit/action@v3.0.0
|
||||||
|
|
||||||
|
- name: 🧪 Run tests
|
||||||
|
run: ./gradlew test validateMixinNames checkChangelog
|
||||||
|
|
||||||
|
- name: 📥 Download assets for game tests
|
||||||
|
run: ./gradlew downloadAssets || ./gradlew downloadAssets
|
||||||
|
|
||||||
|
- name: 🧪 Run integration tests
|
||||||
|
run: ./gradlew runGametest
|
||||||
|
|
||||||
|
- name: 🧪 Run client tests
|
||||||
run: ./gradlew runGametestClient # Not checkClient, as no point running rendering tests.
|
run: ./gradlew runGametestClient # Not checkClient, as no point running rendering tests.
|
||||||
# These are a little flaky on GH actions: its useful to run them, but don't break the build.
|
# These are a little flaky on GH actions: its useful to run them, but don't break the build.
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
|
|
||||||
- name: Prepare Jars
|
- name: 🧪 Parse test reports
|
||||||
|
run: ./tools/parse-reports.py
|
||||||
|
if: ${{ failure() }}
|
||||||
|
|
||||||
|
- name: 📦 Prepare Jars
|
||||||
run: |
|
run: |
|
||||||
# Find the main jar and append the git hash onto it.
|
# Find the main jar and append the git hash onto it.
|
||||||
mkdir -p jars
|
mkdir -p jars
|
||||||
find projects/forge/build/libs projects/fabric/build/libs -type f -regex '.*[0-9.]+\(-SNAPSHOT\)?\.jar$' -exec bash -c 'cp {} "jars/$(basename {} .jar)-$(git rev-parse HEAD).jar"' \;
|
find projects/forge/build/libs projects/fabric/build/libs -type f -regex '.*[0-9.]+\(-SNAPSHOT\)?\.jar$' -exec bash -c 'cp {} "jars/$(basename {} .jar)-$(git rev-parse HEAD).jar"' \;
|
||||||
|
|
||||||
- name: Upload Jar
|
- name: 📤 Upload Jar
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: CC-Tweaked
|
name: CC-Tweaked
|
||||||
path: ./jars
|
path: ./jars
|
||||||
|
|
||||||
- name: Upload coverage
|
- name: 📤 Upload coverage
|
||||||
uses: codecov/codecov-action@v3
|
uses: codecov/codecov-action@v3
|
||||||
|
|
||||||
- name: Parse test reports
|
|
||||||
run: ./tools/parse-reports.py
|
|
||||||
if: ${{ failure() }}
|
|
||||||
|
|
||||||
- name: Run linters
|
|
||||||
uses: pre-commit/action@v3.0.0
|
|
||||||
|
|
||||||
build-core:
|
build-core:
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
|
|||||||
1
.github/workflows/make-doc.yml
vendored
1
.github/workflows/make-doc.yml
vendored
@@ -4,6 +4,7 @@ on:
|
|||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- mc-1.19.x
|
- mc-1.19.x
|
||||||
|
- mc-1.20.x
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
make_doc:
|
make_doc:
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
# See https://pre-commit.com/hooks.html for more hooks
|
# See https://pre-commit.com/hooks.html for more hooks
|
||||||
repos:
|
repos:
|
||||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||||
rev: v4.0.1
|
rev: v4.4.0
|
||||||
hooks:
|
hooks:
|
||||||
- id: trailing-whitespace
|
- id: trailing-whitespace
|
||||||
- id: end-of-file-fixer
|
- id: end-of-file-fixer
|
||||||
@@ -20,14 +20,14 @@ repos:
|
|||||||
exclude: "tsconfig\\.json$"
|
exclude: "tsconfig\\.json$"
|
||||||
|
|
||||||
- repo: https://github.com/editorconfig-checker/editorconfig-checker.python
|
- repo: https://github.com/editorconfig-checker/editorconfig-checker.python
|
||||||
rev: 2.3.54
|
rev: 2.7.2
|
||||||
hooks:
|
hooks:
|
||||||
- id: editorconfig-checker
|
- id: editorconfig-checker
|
||||||
args: ['-disable-indentation']
|
args: ['-disable-indentation']
|
||||||
exclude: "^(.*\\.(bat)|LICENSE)$"
|
exclude: "^(.*\\.(bat)|LICENSE)$"
|
||||||
|
|
||||||
- repo: https://github.com/fsfe/reuse-tool
|
- repo: https://github.com/fsfe/reuse-tool
|
||||||
rev: v1.1.0
|
rev: v2.1.0
|
||||||
hooks:
|
hooks:
|
||||||
- id: reuse
|
- id: reuse
|
||||||
|
|
||||||
@@ -44,7 +44,7 @@ repos:
|
|||||||
name: Check Java codestyle
|
name: Check Java codestyle
|
||||||
files: ".*\\.java$"
|
files: ".*\\.java$"
|
||||||
language: system
|
language: system
|
||||||
entry: ./gradlew checkstyleMain checkstyleTest
|
entry: ./gradlew checkstyle
|
||||||
pass_filenames: false
|
pass_filenames: false
|
||||||
require_serial: true
|
require_serial: true
|
||||||
- id: illuaminate
|
- id: illuaminate
|
||||||
|
|||||||
31
.reuse/dep5
31
.reuse/dep5
@@ -6,11 +6,12 @@ Upstream-Contact: Jonathan Coates <git@squiddev.cc>
|
|||||||
Files:
|
Files:
|
||||||
projects/common/src/main/resources/assets/computercraft/sounds.json
|
projects/common/src/main/resources/assets/computercraft/sounds.json
|
||||||
projects/common/src/main/resources/assets/computercraft/sounds/empty.ogg
|
projects/common/src/main/resources/assets/computercraft/sounds/empty.ogg
|
||||||
|
projects/common/src/testMod/resources/data/cctest/computercraft/turtle_upgrades/*
|
||||||
projects/common/src/testMod/resources/data/cctest/structures/*
|
projects/common/src/testMod/resources/data/cctest/structures/*
|
||||||
projects/fabric/src/generated/*
|
projects/fabric/src/generated/*
|
||||||
projects/forge/src/generated/*
|
projects/forge/src/generated/*
|
||||||
projects/web/src/export/index.json
|
projects/web/src/htmlTransform/export/index.json
|
||||||
projects/web/src/export/items/minecraft/*
|
projects/web/src/htmlTransform/export/items/minecraft/*
|
||||||
Comment: Generated/data files are CC0.
|
Comment: Generated/data files are CC0.
|
||||||
Copyright: The CC: Tweaked Developers
|
Copyright: The CC: Tweaked Developers
|
||||||
License: CC0-1.0
|
License: CC0-1.0
|
||||||
@@ -36,10 +37,10 @@ Files:
|
|||||||
projects/fabric/src/testMod/resources/computercraft-gametest.fabric.mixins.json
|
projects/fabric/src/testMod/resources/computercraft-gametest.fabric.mixins.json
|
||||||
projects/fabric/src/testMod/resources/fabric.mod.json
|
projects/fabric/src/testMod/resources/fabric.mod.json
|
||||||
projects/forge/src/client/resources/computercraft-client.forge.mixins.json
|
projects/forge/src/client/resources/computercraft-client.forge.mixins.json
|
||||||
projects/web/src/mount/.settings
|
projects/web/src/frontend/mount/.settings
|
||||||
projects/web/src/mount/example.nfp
|
projects/web/src/frontend/mount/example.nfp
|
||||||
projects/web/src/mount/example.nft
|
projects/web/src/frontend/mount/example.nft
|
||||||
projects/web/src/mount/expr_template.lua
|
projects/web/src/frontend/mount/expr_template.lua
|
||||||
projects/web/tsconfig.json
|
projects/web/tsconfig.json
|
||||||
Comment: Several assets where it's inconvenient to create a .license file.
|
Comment: Several assets where it's inconvenient to create a .license file.
|
||||||
Copyright: The CC: Tweaked Developers
|
Copyright: The CC: Tweaked Developers
|
||||||
@@ -47,23 +48,37 @@ License: MPL-2.0
|
|||||||
|
|
||||||
Files:
|
Files:
|
||||||
doc/logo.png
|
doc/logo.png
|
||||||
|
doc/logo-darkmode.png
|
||||||
projects/common/src/main/resources/assets/computercraft/models/*
|
projects/common/src/main/resources/assets/computercraft/models/*
|
||||||
projects/common/src/main/resources/assets/computercraft/textures/*
|
projects/common/src/main/resources/assets/computercraft/textures/*
|
||||||
projects/common/src/main/resources/pack.mcmeta
|
projects/common/src/main/resources/pack.mcmeta
|
||||||
projects/common/src/main/resources/pack.png
|
projects/common/src/main/resources/pack.png
|
||||||
|
projects/core/src/main/resources/assets/computercraft/textures/gui/term_font.png
|
||||||
projects/core/src/main/resources/data/computercraft/lua/rom/autorun/.ignoreme
|
projects/core/src/main/resources/data/computercraft/lua/rom/autorun/.ignoreme
|
||||||
projects/core/src/main/resources/data/computercraft/lua/rom/help/*
|
projects/core/src/main/resources/data/computercraft/lua/rom/help/*
|
||||||
projects/core/src/main/resources/data/computercraft/lua/rom/programs/fun/advanced/levels/*
|
projects/core/src/main/resources/data/computercraft/lua/rom/programs/fun/advanced/levels/*
|
||||||
projects/web/src/export/items/computercraft/*
|
projects/web/src/htmlTransform/export/items/computercraft/*
|
||||||
Comment: Bulk-license original assets as CCPL.
|
Comment: Bulk-license original assets as CCPL.
|
||||||
Copyright: 2011 Daniel Ratcliffe
|
Copyright: 2011 Daniel Ratcliffe
|
||||||
License: LicenseRef-CCPL
|
License: LicenseRef-CCPL
|
||||||
|
|
||||||
|
Files:
|
||||||
|
projects/common/src/main/resources/assets/computercraft/lang/cs_cz.json
|
||||||
|
projects/common/src/main/resources/assets/computercraft/lang/ko_kr.json
|
||||||
|
projects/common/src/main/resources/assets/computercraft/lang/pl_pl.json
|
||||||
|
projects/common/src/main/resources/assets/computercraft/lang/pt_br.json
|
||||||
|
projects/common/src/main/resources/assets/computercraft/lang/ru_ru.json
|
||||||
|
projects/common/src/main/resources/assets/computercraft/lang/uk_ua.json
|
||||||
|
projects/common/src/main/resources/assets/computercraft/lang/zh_cn.json
|
||||||
|
Comment: Community-contributed license files
|
||||||
|
Copyright: 2017 The CC: Tweaked Developers
|
||||||
|
License: LicenseRef-CCPL
|
||||||
|
|
||||||
Files:
|
Files:
|
||||||
projects/common/src/main/resources/assets/computercraft/lang/*
|
projects/common/src/main/resources/assets/computercraft/lang/*
|
||||||
Comment: Community-contributed license files
|
Comment: Community-contributed license files
|
||||||
Copyright: 2017 The CC: Tweaked Developers
|
Copyright: 2017 The CC: Tweaked Developers
|
||||||
License: LicenseRef-CCPL
|
License: MPL-2.0
|
||||||
|
|
||||||
Files:
|
Files:
|
||||||
.github/*
|
.github/*
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ SPDX-License-Identifier: MPL-2.0
|
|||||||
|
|
||||||
# Contributing to CC: Tweaked
|
# Contributing to CC: Tweaked
|
||||||
As with many open source projects, CC: Tweaked thrives on contributions from other people! This document (hopefully)
|
As with many open source projects, CC: Tweaked thrives on contributions from other people! This document (hopefully)
|
||||||
provides an introduction as to how to get started in helping out.
|
provides an introduction as to how to get started with helping out.
|
||||||
|
|
||||||
If you've any other questions, [just ask the community][community] or [open an issue][new-issue].
|
If you've any other questions, [just ask the community][community] or [open an issue][new-issue].
|
||||||
|
|
||||||
@@ -28,7 +28,7 @@ automatically with GitHub, so please don't submit PRs adding/changing translatio
|
|||||||
## Setting up a development environment
|
## Setting up a development environment
|
||||||
In order to develop CC: Tweaked, you'll need to download the source code and then run it.
|
In order to develop CC: Tweaked, you'll need to download the source code and then run it.
|
||||||
|
|
||||||
- Make sure you've got the following software instealled:
|
- Make sure you've got the following software installed:
|
||||||
- Java Development Kit (JDK) installed. This can be downloaded from [Adoptium].
|
- Java Development Kit (JDK) installed. This can be downloaded from [Adoptium].
|
||||||
- [Git](https://git-scm.com/).
|
- [Git](https://git-scm.com/).
|
||||||
- If you want to work on documentation, [NodeJS][node].
|
- If you want to work on documentation, [NodeJS][node].
|
||||||
@@ -51,10 +51,10 @@ If you want to run CC:T in a normal Minecraft instance, run `./gradlew assemble`
|
|||||||
## Developing CC: Tweaked
|
## Developing CC: Tweaked
|
||||||
Before making any major changes to CC: Tweaked, I'd recommend you have a read of the [the architecture
|
Before making any major changes to CC: Tweaked, I'd recommend you have a read of the [the architecture
|
||||||
document][architecture] first. While it's not a comprehensive document, it gives a good hint of where you should start
|
document][architecture] first. While it's not a comprehensive document, it gives a good hint of where you should start
|
||||||
looking to make your changes. As always, if you're not sure [do ask the community][community]!
|
looking to make your changes. As always, if you're not sure, [do ask the community][community]!
|
||||||
|
|
||||||
### Testing
|
### Testing
|
||||||
When making larger changes, it's may be useful to write a test to make sure your code works as expected.
|
When making larger changes, it may be useful to write a test to make sure your code works as expected.
|
||||||
|
|
||||||
CC: Tweaked has several test suites, each designed to test something different:
|
CC: Tweaked has several test suites, each designed to test something different:
|
||||||
|
|
||||||
@@ -91,16 +91,15 @@ file.
|
|||||||
|
|
||||||
Documentation is built using [illuaminate] which, while not currently documented (somewhat ironic), is largely the same
|
Documentation is built using [illuaminate] which, while not currently documented (somewhat ironic), is largely the same
|
||||||
as [ldoc][ldoc]. Documentation comments are written in Markdown, though note that we do not support many GitHub-specific
|
as [ldoc][ldoc]. Documentation comments are written in Markdown, though note that we do not support many GitHub-specific
|
||||||
markdown features - if you can, do check what the documentation looks like locally!
|
markdown features. If you can, do check what the documentation looks like locally!
|
||||||
|
|
||||||
When writing long-form documentation (such as the guides in [doc/guides](doc/guides)), I find it useful to tell a
|
When writing long-form documentation (such as the guides in [doc/guides](doc/guides)), I find it useful to tell a
|
||||||
narrative. Think of what you want the user to learn or achieve, then start introducing a simple concept and then talk
|
narrative. Think of what you want the user to learn or achieve, then start introducing a simple concept, and then talk
|
||||||
about how you can build on that, until you've covered everything!
|
about how you can build on that until you've covered everything!
|
||||||
|
|
||||||
[new-issue]: https://github.com/cc-tweaked/CC-Tweaked/issues/new/choose "Create a new issue"
|
[new-issue]: https://github.com/cc-tweaked/CC-Tweaked/issues/new/choose "Create a new issue"
|
||||||
[community]: README.md#community "Get in touch with the community."
|
[community]: README.md#community "Get in touch with the community."
|
||||||
[Adoptium]: https://adoptium.net/temurin/releases?version=17 "Download OpenJDK 17"
|
[Adoptium]: https://adoptium.net/temurin/releases?version=17 "Download OpenJDK 17"
|
||||||
[checkstyle]: https://checkstyle.org/
|
|
||||||
[illuaminate]: https://github.com/SquidDev/illuaminate/ "Illuaminate on GitHub"
|
[illuaminate]: https://github.com/SquidDev/illuaminate/ "Illuaminate on GitHub"
|
||||||
[weblate]: https://i18n.tweaked.cc/projects/cc-tweaked/minecraft/ "CC: Tweaked weblate instance"
|
[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"
|
||||||
|
|||||||
22
README.md
22
README.md
@@ -4,7 +4,12 @@ SPDX-FileCopyrightText: 2017 The CC: Tweaked Developers
|
|||||||
SPDX-License-Identifier: MPL-2.0
|
SPDX-License-Identifier: MPL-2.0
|
||||||
-->
|
-->
|
||||||
|
|
||||||
# 
|
<picture>
|
||||||
|
<source media="(prefers-color-scheme: dark)" srcset="./doc/logo-darkmode.png">
|
||||||
|
<source media="(prefers-color-scheme: light)" srcset="./doc/logo.png">
|
||||||
|
<img alt="CC: Tweaked" src="./doc/logo.png">
|
||||||
|
</picture>
|
||||||
|
|
||||||
[](https://github.com/cc-tweaked/CC-Tweaked/actions "Current build status")
|
[](https://github.com/cc-tweaked/CC-Tweaked/actions "Current build status")
|
||||||
[][CurseForge]
|
[][CurseForge]
|
||||||
[][Modrinth]
|
[][Modrinth]
|
||||||
@@ -44,7 +49,7 @@ repositories {
|
|||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
// Vanilla (i.e. for multi-loader systems)
|
// Vanilla (i.e. for multi-loader systems)
|
||||||
compileOnly("cc.tweaked:cc-tweaked-$mcVersion-common-api")
|
compileOnly("cc.tweaked:cc-tweaked-$mcVersion-common-api:$cctVersion")
|
||||||
|
|
||||||
// Forge Gradle
|
// Forge Gradle
|
||||||
compileOnly("cc.tweaked:cc-tweaked-$mcVersion-core-api:$cctVersion")
|
compileOnly("cc.tweaked:cc-tweaked-$mcVersion-core-api:$cctVersion")
|
||||||
@@ -57,6 +62,19 @@ dependencies {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
When using ForgeGradle, you may also need to add the following:
|
||||||
|
|
||||||
|
```groovy
|
||||||
|
minecraft {
|
||||||
|
runs {
|
||||||
|
configureEach {
|
||||||
|
property 'mixin.env.remapRefMap', 'true'
|
||||||
|
property 'mixin.env.refMapRemappingFile', "${buildDir}/createSrgToMcp/output.srg"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
You should also be careful to only use classes within the `dan200.computercraft.api` package. Non-API classes are
|
You should also be careful to only use classes within the `dan200.computercraft.api` package. Non-API classes are
|
||||||
subject to change at any point. If you depend on functionality outside the API, file an issue, and we can look into
|
subject to change at any point. If you depend on functionality outside the API, file an issue, and we can look into
|
||||||
exposing more features.
|
exposing more features.
|
||||||
|
|||||||
@@ -2,7 +2,11 @@
|
|||||||
//
|
//
|
||||||
// SPDX-License-Identifier: MPL-2.0
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
|
import cc.tweaked.gradle.JUnitExt
|
||||||
|
import net.fabricmc.loom.api.LoomGradleExtensionAPI
|
||||||
|
import net.fabricmc.loom.util.gradle.SourceSetHelper
|
||||||
import org.jetbrains.gradle.ext.compiler
|
import org.jetbrains.gradle.ext.compiler
|
||||||
|
import org.jetbrains.gradle.ext.runConfigurations
|
||||||
import org.jetbrains.gradle.ext.settings
|
import org.jetbrains.gradle.ext.settings
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
@@ -38,6 +42,50 @@ githubRelease {
|
|||||||
|
|
||||||
tasks.publish { dependsOn(tasks.githubRelease) }
|
tasks.publish { dependsOn(tasks.githubRelease) }
|
||||||
|
|
||||||
|
idea.project.settings.runConfigurations {
|
||||||
|
register<JUnitExt>("Core Tests") {
|
||||||
|
vmParameters = "-ea"
|
||||||
|
moduleName = "${idea.project.name}.core.test"
|
||||||
|
packageName = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
register<JUnitExt>("CraftOS Tests") {
|
||||||
|
vmParameters = "-ea"
|
||||||
|
moduleName = "${idea.project.name}.core.test"
|
||||||
|
className = "dan200.computercraft.core.ComputerTestDelegate"
|
||||||
|
}
|
||||||
|
|
||||||
|
register<JUnitExt>("CraftOS Tests (Fast)") {
|
||||||
|
vmParameters = "-ea -Dcc.skip_keywords=slow"
|
||||||
|
moduleName = "${idea.project.name}.core.test"
|
||||||
|
className = "dan200.computercraft.core.ComputerTestDelegate"
|
||||||
|
}
|
||||||
|
|
||||||
|
register<JUnitExt>("Common Tests") {
|
||||||
|
vmParameters = "-ea"
|
||||||
|
moduleName = "${idea.project.name}.common.test"
|
||||||
|
packageName = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
register<JUnitExt>("Fabric Tests") {
|
||||||
|
val fabricProject = evaluationDependsOn(":fabric")
|
||||||
|
val classPathGroup = fabricProject.extensions.getByType<LoomGradleExtensionAPI>().mods
|
||||||
|
.joinToString(File.pathSeparator + File.pathSeparator) { modSettings ->
|
||||||
|
SourceSetHelper.getClasspath(modSettings, project).joinToString(File.pathSeparator) { it.absolutePath }
|
||||||
|
}
|
||||||
|
|
||||||
|
vmParameters = "-ea -Dfabric.classPathGroups=$classPathGroup"
|
||||||
|
moduleName = "${idea.project.name}.fabric.test"
|
||||||
|
packageName = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
register<JUnitExt>("Forge Tests") {
|
||||||
|
vmParameters = "-ea"
|
||||||
|
moduleName = "${idea.project.name}.forge.test"
|
||||||
|
packageName = ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
idea.project.settings.compiler.javac {
|
idea.project.settings.compiler.javac {
|
||||||
// We want ErrorProne to be present when compiling via IntelliJ, as it offers some helpful warnings
|
// We want ErrorProne to be present when compiling via IntelliJ, as it offers some helpful warnings
|
||||||
// and errors. Loop through our source sets and find the appropriate flags.
|
// and errors. Loop through our source sets and find the appropriate flags.
|
||||||
|
|||||||
@@ -50,10 +50,11 @@ dependencies {
|
|||||||
implementation(libs.curseForgeGradle)
|
implementation(libs.curseForgeGradle)
|
||||||
implementation(libs.fabric.loom)
|
implementation(libs.fabric.loom)
|
||||||
implementation(libs.forgeGradle)
|
implementation(libs.forgeGradle)
|
||||||
|
implementation(libs.ideaExt)
|
||||||
implementation(libs.librarian)
|
implementation(libs.librarian)
|
||||||
implementation(libs.minotaur)
|
implementation(libs.minotaur)
|
||||||
implementation(libs.quiltflower)
|
|
||||||
implementation(libs.vanillaGradle)
|
implementation(libs.vanillaGradle)
|
||||||
|
implementation(libs.vineflower)
|
||||||
}
|
}
|
||||||
|
|
||||||
gradlePlugin {
|
gradlePlugin {
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import cc.tweaked.gradle.MinecraftConfigurations
|
|||||||
plugins {
|
plugins {
|
||||||
`java-library`
|
`java-library`
|
||||||
id("fabric-loom")
|
id("fabric-loom")
|
||||||
id("io.github.juuxel.loom-quiltflower")
|
id("io.github.juuxel.loom-vineflower")
|
||||||
id("cc-tweaked.java-convention")
|
id("cc-tweaked.java-convention")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,8 +10,9 @@ import cc.tweaked.gradle.IdeaRunConfigurations
|
|||||||
import cc.tweaked.gradle.MinecraftConfigurations
|
import cc.tweaked.gradle.MinecraftConfigurations
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
id("cc-tweaked.java-convention")
|
|
||||||
id("net.minecraftforge.gradle")
|
id("net.minecraftforge.gradle")
|
||||||
|
// We must apply java-convention after Forge, as we need the fg extension to be present.
|
||||||
|
id("cc-tweaked.java-convention")
|
||||||
id("org.parchmentmc.librarian.forgegradle")
|
id("org.parchmentmc.librarian.forgegradle")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -37,22 +37,40 @@ java {
|
|||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
maven("https://squiddev.cc/maven") {
|
|
||||||
|
val mainMaven = maven("https://squiddev.cc/maven") {
|
||||||
name = "SquidDev"
|
name = "SquidDev"
|
||||||
content {
|
content {
|
||||||
includeGroup("org.squiddev")
|
|
||||||
includeGroup("cc.tweaked")
|
|
||||||
// Things we mirror
|
|
||||||
includeGroup("dev.architectury")
|
|
||||||
includeGroup("maven.modrinth")
|
|
||||||
includeGroup("me.shedaniel")
|
|
||||||
includeGroup("me.shedaniel.cloth")
|
|
||||||
includeGroup("mezz.jei")
|
|
||||||
includeModule("com.terraformersmc", "modmenu")
|
|
||||||
// Until https://github.com/SpongePowered/Mixin/pull/593 is merged
|
// Until https://github.com/SpongePowered/Mixin/pull/593 is merged
|
||||||
includeModule("org.spongepowered", "mixin")
|
includeModule("org.spongepowered", "mixin")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
exclusiveContent {
|
||||||
|
forRepositories(mainMaven)
|
||||||
|
|
||||||
|
// Include the ForgeGradle repository if present. This requires that ForgeGradle is already present, which we
|
||||||
|
// enforce in our Forge overlay.
|
||||||
|
val fg =
|
||||||
|
project.extensions.findByType(net.minecraftforge.gradle.userdev.DependencyManagementExtension::class.java)
|
||||||
|
if (fg != null) forRepositories(fg.repository)
|
||||||
|
|
||||||
|
filter {
|
||||||
|
includeGroup("cc.tweaked")
|
||||||
|
includeModule("org.squiddev", "Cobalt")
|
||||||
|
// Things we mirror
|
||||||
|
includeGroup("commoble.morered")
|
||||||
|
includeGroup("dev.architectury")
|
||||||
|
includeGroup("dev.emi")
|
||||||
|
includeGroup("maven.modrinth")
|
||||||
|
includeGroup("me.shedaniel.cloth")
|
||||||
|
includeGroup("me.shedaniel")
|
||||||
|
includeGroup("mezz.jei")
|
||||||
|
includeGroup("org.teavm")
|
||||||
|
includeModule("com.terraformersmc", "modmenu")
|
||||||
|
includeModule("me.lucko", "fabric-permissions-api")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
@@ -82,7 +100,10 @@ sourceSets.all {
|
|||||||
check("FutureReturnValueIgnored", CheckSeverity.OFF) // Too many false positives with Netty
|
check("FutureReturnValueIgnored", CheckSeverity.OFF) // Too many false positives with Netty
|
||||||
|
|
||||||
check("NullAway", CheckSeverity.ERROR)
|
check("NullAway", CheckSeverity.ERROR)
|
||||||
option("NullAway:AnnotatedPackages", listOf("dan200.computercraft", "net.fabricmc.fabric.api").joinToString(","))
|
option(
|
||||||
|
"NullAway:AnnotatedPackages",
|
||||||
|
listOf("dan200.computercraft", "cc.tweaked", "net.fabricmc.fabric.api").joinToString(","),
|
||||||
|
)
|
||||||
option("NullAway:ExcludedFieldAnnotations", listOf("org.spongepowered.asm.mixin.Shadow").joinToString(","))
|
option("NullAway:ExcludedFieldAnnotations", listOf("org.spongepowered.asm.mixin.Shadow").joinToString(","))
|
||||||
option("NullAway:CastToNonNullMethod", "dan200.computercraft.core.util.Nullability.assertNonNull")
|
option("NullAway:CastToNonNullMethod", "dan200.computercraft.core.util.Nullability.assertNonNull")
|
||||||
option("NullAway:CheckOptionalEmptiness")
|
option("NullAway:CheckOptionalEmptiness")
|
||||||
@@ -104,6 +125,7 @@ tasks.withType(JavaCompile::class.java).configureEach {
|
|||||||
|
|
||||||
tasks.processResources {
|
tasks.processResources {
|
||||||
exclude("**/*.license")
|
exclude("**/*.license")
|
||||||
|
exclude(".cache")
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.withType(AbstractArchiveTask::class.java).configureEach {
|
tasks.withType(AbstractArchiveTask::class.java).configureEach {
|
||||||
@@ -156,6 +178,12 @@ project.plugins.withType(CCTweakedPlugin::class.java) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tasks.register("checkstyle") {
|
||||||
|
description = "Run Checkstyle on all sources"
|
||||||
|
group = LifecycleBasePlugin.VERIFICATION_GROUP
|
||||||
|
dependsOn(tasks.withType(Checkstyle::class.java))
|
||||||
|
}
|
||||||
|
|
||||||
spotless {
|
spotless {
|
||||||
encoding = StandardCharsets.UTF_8
|
encoding = StandardCharsets.UTF_8
|
||||||
lineEndings = LineEnding.UNIX
|
lineEndings = LineEnding.UNIX
|
||||||
@@ -173,6 +201,7 @@ spotless {
|
|||||||
|
|
||||||
val ktlintConfig = mapOf(
|
val ktlintConfig = mapOf(
|
||||||
"ktlint_standard_no-wildcard-imports" to "disabled",
|
"ktlint_standard_no-wildcard-imports" to "disabled",
|
||||||
|
"ktlint_standard_class-naming" to "disabled",
|
||||||
"ij_kotlin_allow_trailing_comma" to "true",
|
"ij_kotlin_allow_trailing_comma" to "true",
|
||||||
"ij_kotlin_allow_trailing_comma_on_call_site" to "true",
|
"ij_kotlin_allow_trailing_comma_on_call_site" to "true",
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -173,7 +173,7 @@ abstract class CCTweakedExtension(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun <T> jacoco(task: NamedDomainObjectProvider<T>) where T : Task, T : JavaForkOptions {
|
fun <T> jacoco(task: NamedDomainObjectProvider<T>) where T : Task, T : JavaForkOptions {
|
||||||
val classDump = project.buildDir.resolve("jacocoClassDump/${task.name}")
|
val classDump = project.layout.buildDirectory.dir("jacocoClassDump/${task.name}")
|
||||||
val reportTaskName = "jacoco${task.name.capitalized()}Report"
|
val reportTaskName = "jacoco${task.name.capitalized()}Report"
|
||||||
|
|
||||||
val jacoco = project.extensions.getByType(JacocoPluginExtension::class.java)
|
val jacoco = project.extensions.getByType(JacocoPluginExtension::class.java)
|
||||||
@@ -185,7 +185,7 @@ abstract class CCTweakedExtension(
|
|||||||
jacoco.applyTo(this)
|
jacoco.applyTo(this)
|
||||||
extensions.configure(JacocoTaskExtension::class.java) {
|
extensions.configure(JacocoTaskExtension::class.java) {
|
||||||
includes = listOf("dan200.computercraft.*")
|
includes = listOf("dan200.computercraft.*")
|
||||||
classDumpDir = classDump
|
classDumpDir = classDump.get().asFile
|
||||||
|
|
||||||
// Older versions of modlauncher don't include a protection domain (and thus no code
|
// 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.
|
// source). Jacoco skips such classes by default, so we need to explicitly include them.
|
||||||
|
|||||||
@@ -9,6 +9,10 @@ import org.gradle.api.Project
|
|||||||
import org.gradle.api.plugins.JavaPlugin
|
import org.gradle.api.plugins.JavaPlugin
|
||||||
import org.gradle.api.plugins.JavaPluginExtension
|
import org.gradle.api.plugins.JavaPluginExtension
|
||||||
import org.gradle.jvm.toolchain.JavaLanguageVersion
|
import org.gradle.jvm.toolchain.JavaLanguageVersion
|
||||||
|
import org.gradle.plugins.ide.idea.model.IdeaModel
|
||||||
|
import org.jetbrains.gradle.ext.IdeaExtPlugin
|
||||||
|
import org.jetbrains.gradle.ext.runConfigurations
|
||||||
|
import org.jetbrains.gradle.ext.settings
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Configures projects to match a shared configuration.
|
* Configures projects to match a shared configuration.
|
||||||
@@ -21,6 +25,20 @@ class CCTweakedPlugin : Plugin<Project> {
|
|||||||
val sourceSets = project.extensions.getByType(JavaPluginExtension::class.java).sourceSets
|
val sourceSets = project.extensions.getByType(JavaPluginExtension::class.java).sourceSets
|
||||||
cct.sourceDirectories.add(SourceSetReference.internal(sourceSets.getByName("main")))
|
cct.sourceDirectories.add(SourceSetReference.internal(sourceSets.getByName("main")))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
project.plugins.withType(IdeaExtPlugin::class.java) { extendIdea(project) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extend the [IdeaExtPlugin] plugin's `runConfiguration` container to also support [JUnitExt].
|
||||||
|
*/
|
||||||
|
private fun extendIdea(project: Project) {
|
||||||
|
val ideaModel = project.extensions.findByName("idea") as IdeaModel? ?: return
|
||||||
|
val ideaProject = ideaModel.project ?: return
|
||||||
|
|
||||||
|
ideaProject.settings.runConfigurations {
|
||||||
|
registerFactory(JUnitExt::class.java) { name -> project.objects.newInstance(JUnitExt::class.java, name) }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
package cc.tweaked.gradle
|
package cc.tweaked.gradle
|
||||||
|
|
||||||
|
import org.gradle.api.file.DirectoryProperty
|
||||||
import org.gradle.api.provider.Property
|
import org.gradle.api.provider.Property
|
||||||
import org.gradle.api.tasks.AbstractExecTask
|
import org.gradle.api.tasks.AbstractExecTask
|
||||||
import org.gradle.api.tasks.OutputDirectory
|
import org.gradle.api.tasks.OutputDirectory
|
||||||
@@ -11,5 +12,5 @@ import java.io.File
|
|||||||
|
|
||||||
abstract class ExecToDir : AbstractExecTask<ExecToDir>(ExecToDir::class.java) {
|
abstract class ExecToDir : AbstractExecTask<ExecToDir>(ExecToDir::class.java) {
|
||||||
@get:OutputDirectory
|
@get:OutputDirectory
|
||||||
abstract val output: Property<File>
|
abstract val output: DirectoryProperty
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,8 @@
|
|||||||
package cc.tweaked.gradle
|
package cc.tweaked.gradle
|
||||||
|
|
||||||
import org.gradle.api.artifacts.dsl.DependencyHandler
|
import org.gradle.api.artifacts.dsl.DependencyHandler
|
||||||
|
import org.gradle.api.file.FileSystemLocation
|
||||||
|
import org.gradle.api.file.FileSystemLocationProperty
|
||||||
import org.gradle.api.provider.Property
|
import org.gradle.api.provider.Property
|
||||||
import org.gradle.api.provider.Provider
|
import org.gradle.api.provider.Provider
|
||||||
import org.gradle.api.tasks.JavaExec
|
import org.gradle.api.tasks.JavaExec
|
||||||
@@ -124,3 +126,6 @@ class CloseScope : AutoCloseable {
|
|||||||
|
|
||||||
/** Proxy method to avoid overload ambiguity. */
|
/** Proxy method to avoid overload ambiguity. */
|
||||||
fun <T> Property<T>.setProvider(provider: Provider<out T>) = set(provider)
|
fun <T> Property<T>.setProvider(provider: Provider<out T>) = set(provider)
|
||||||
|
|
||||||
|
/** Short-cut method to get the absolute path of a [FileSystemLocation] provider. */
|
||||||
|
fun Provider<out FileSystemLocation>.getAbsolutePath(): String = get().asFile.absolutePath
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
|
package cc.tweaked.gradle
|
||||||
|
|
||||||
|
import net.minecraftforge.gradle.common.util.RunConfig
|
||||||
|
import net.minecraftforge.gradle.common.util.runs.setRunConfigInternal
|
||||||
|
import org.gradle.api.plugins.JavaPluginExtension
|
||||||
|
import org.gradle.api.tasks.JavaExec
|
||||||
|
import org.gradle.jvm.toolchain.JavaToolchainService
|
||||||
|
import java.nio.file.Files
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set [JavaExec] task to run a given [RunConfig].
|
||||||
|
*/
|
||||||
|
fun JavaExec.setRunConfig(config: RunConfig) {
|
||||||
|
dependsOn("prepareRuns")
|
||||||
|
setRunConfigInternal(project, this, config)
|
||||||
|
doFirst("Create working directory") { Files.createDirectories(workingDir.toPath()) }
|
||||||
|
|
||||||
|
javaLauncher.set(
|
||||||
|
project.extensions.getByType(JavaToolchainService::class.java)
|
||||||
|
.launcherFor(project.extensions.getByType(JavaPluginExtension::class.java).toolchain),
|
||||||
|
)
|
||||||
|
}
|
||||||
23
buildSrc/src/main/kotlin/cc/tweaked/gradle/IdeaExt.kt
Normal file
23
buildSrc/src/main/kotlin/cc/tweaked/gradle/IdeaExt.kt
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
|
package cc.tweaked.gradle
|
||||||
|
|
||||||
|
import org.jetbrains.gradle.ext.JUnit
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A version of [JUnit] with a functional [className].
|
||||||
|
*
|
||||||
|
* See [#92](https://github.com/JetBrains/gradle-idea-ext-plugin/issues/92).
|
||||||
|
*/
|
||||||
|
open class JUnitExt @Inject constructor(nameParam: String) : JUnit(nameParam) {
|
||||||
|
override fun toMap(): MutableMap<String, *> {
|
||||||
|
val map = HashMap(super.toMap())
|
||||||
|
// Should be "class" instead of "className".
|
||||||
|
// See https://github.com/JetBrains/intellij-community/blob/9ba394021dc73a3926f13d6d6cdf434f9ee7046d/plugins/junit/src/com/intellij/execution/junit/JUnitRunConfigurationImporter.kt#L39
|
||||||
|
map["class"] = className
|
||||||
|
return map
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -60,7 +60,7 @@ class IlluaminatePlugin : Plugin<Project> {
|
|||||||
|
|
||||||
/** Define a dependency for illuaminate from a version number and the current operating system. */
|
/** Define a dependency for illuaminate from a version number and the current operating system. */
|
||||||
private fun illuaminateArtifact(project: Project, version: String): Dependency {
|
private fun illuaminateArtifact(project: Project, version: String): Dependency {
|
||||||
val osName = System.getProperty("os.name").toLowerCase()
|
val osName = System.getProperty("os.name").lowercase()
|
||||||
val (os, suffix) = when {
|
val (os, suffix) = when {
|
||||||
osName.contains("windows") -> Pair("windows", ".exe")
|
osName.contains("windows") -> Pair("windows", ".exe")
|
||||||
osName.contains("mac os") || osName.contains("darwin") -> Pair("macos", "")
|
osName.contains("mac os") || osName.contains("darwin") -> Pair("macos", "")
|
||||||
@@ -68,7 +68,7 @@ class IlluaminatePlugin : Plugin<Project> {
|
|||||||
else -> error("Unsupported OS $osName for illuaminate")
|
else -> error("Unsupported OS $osName for illuaminate")
|
||||||
}
|
}
|
||||||
|
|
||||||
val osArch = System.getProperty("os.arch").toLowerCase()
|
val osArch = System.getProperty("os.arch").lowercase()
|
||||||
val arch = when {
|
val arch = when {
|
||||||
// On macOS the x86_64 binary will work for both ARM and Intel Macs through Rosetta.
|
// On macOS the x86_64 binary will work for both ARM and Intel Macs through Rosetta.
|
||||||
os == "macos" -> "x86_64"
|
os == "macos" -> "x86_64"
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
package cc.tweaked.gradle
|
package cc.tweaked.gradle
|
||||||
|
|
||||||
|
import net.minecraftforge.gradle.common.util.RunConfig
|
||||||
import org.gradle.api.GradleException
|
import org.gradle.api.GradleException
|
||||||
import org.gradle.api.file.FileSystemOperations
|
import org.gradle.api.file.FileSystemOperations
|
||||||
import org.gradle.api.invocation.Gradle
|
import org.gradle.api.invocation.Gradle
|
||||||
@@ -32,11 +33,14 @@ abstract class ClientJavaExec : JavaExec() {
|
|||||||
usesService(clientRunner)
|
usesService(clientRunner)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@get:Input
|
||||||
|
val renderdoc get() = project.hasProperty("renderdoc")
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* When [false], tests will not be run automatically, allowing the user to debug rendering.
|
* When [false], tests will not be run automatically, allowing the user to debug rendering.
|
||||||
*/
|
*/
|
||||||
@get:Input
|
@get:Input
|
||||||
val clientDebug get() = project.hasProperty("clientDebug")
|
val clientDebug get() = renderdoc || project.hasProperty("clientDebug")
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* When [false], tests will not run under a framebuffer.
|
* When [false], tests will not run under a framebuffer.
|
||||||
@@ -50,6 +54,25 @@ abstract class ClientJavaExec : JavaExec() {
|
|||||||
@get:OutputFile
|
@get:OutputFile
|
||||||
val testResults = project.layout.buildDirectory.file("test-results/$name.xml")
|
val testResults = project.layout.buildDirectory.file("test-results/$name.xml")
|
||||||
|
|
||||||
|
private fun setTestProperties() {
|
||||||
|
if (!clientDebug) systemProperty("cctest.client", "")
|
||||||
|
if (renderdoc) environment("LD_PRELOAD", "/usr/lib/librenderdoc.so")
|
||||||
|
systemProperty("cctest.gametest-report", testResults.get().asFile.absoluteFile)
|
||||||
|
workingDir(project.layout.buildDirectory.dir("gametest/$name"))
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
setTestProperties()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set this task to run a given [RunConfig].
|
||||||
|
*/
|
||||||
|
fun setRunConfig(config: RunConfig) {
|
||||||
|
(this as JavaExec).setRunConfig(config)
|
||||||
|
setTestProperties() // setRunConfig may clobber some properties, ensure everything is set.
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Copy configuration from a task with the given name.
|
* Copy configuration from a task with the given name.
|
||||||
*/
|
*/
|
||||||
@@ -61,10 +84,7 @@ abstract class ClientJavaExec : JavaExec() {
|
|||||||
fun copyFrom(task: JavaExec) {
|
fun copyFrom(task: JavaExec) {
|
||||||
for (dep in task.dependsOn) dependsOn(dep)
|
for (dep in task.dependsOn) dependsOn(dep)
|
||||||
task.copyToFull(this)
|
task.copyToFull(this)
|
||||||
|
setTestProperties() // copyToFull may clobber some properties, ensure everything is set.
|
||||||
if (!clientDebug) systemProperty("cctest.client", "")
|
|
||||||
systemProperty("cctest.gametest-report", testResults.get().asFile.absoluteFile)
|
|
||||||
workingDir(project.buildDir.resolve("gametest").resolve(name))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -0,0 +1,51 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
|
package net.minecraftforge.gradle.common.util.runs
|
||||||
|
|
||||||
|
import net.minecraftforge.gradle.common.util.RunConfig
|
||||||
|
import org.gradle.api.Project
|
||||||
|
import org.gradle.process.CommandLineArgumentProvider
|
||||||
|
import org.gradle.process.JavaExecSpec
|
||||||
|
import java.io.File
|
||||||
|
import java.util.function.Supplier
|
||||||
|
import java.util.stream.Collectors
|
||||||
|
import java.util.stream.Stream
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set up a [JavaExecSpec] to execute a [RunConfig].
|
||||||
|
*
|
||||||
|
* [MinecraftRunTask] sets up all its properties when the task is executed, rather than when configured. As such, it's
|
||||||
|
* not possible to use [cc.tweaked.gradle.copyToFull] like we do for Fabric. Instead, we set up the task manually.
|
||||||
|
*
|
||||||
|
* Unfortunately most of the functionality we need is package-private, and so we have to put our code into the package.
|
||||||
|
*/
|
||||||
|
internal fun setRunConfigInternal(project: Project, spec: JavaExecSpec, config: RunConfig) {
|
||||||
|
spec.workingDir = File(config.workingDirectory)
|
||||||
|
|
||||||
|
spec.mainClass.set(config.main)
|
||||||
|
for (source in config.allSources) spec.classpath(source.runtimeClasspath)
|
||||||
|
|
||||||
|
val originalTask = project.tasks.named(config.taskName, MinecraftRunTask::class.java)
|
||||||
|
|
||||||
|
// Add argument and JVM argument via providers, to be as lazy as possible with fetching artifacts.
|
||||||
|
val lazyTokens = RunConfigGenerator.configureTokensLazy(
|
||||||
|
project, config, RunConfigGenerator.mapModClassesToGradle(project, config),
|
||||||
|
originalTask.get().minecraftArtifacts,
|
||||||
|
originalTask.get().runtimeClasspathArtifacts,
|
||||||
|
)
|
||||||
|
spec.argumentProviders.add(
|
||||||
|
CommandLineArgumentProvider {
|
||||||
|
RunConfigGenerator.getArgsStream(config, lazyTokens, false).toList()
|
||||||
|
},
|
||||||
|
)
|
||||||
|
spec.jvmArgumentProviders.add(
|
||||||
|
CommandLineArgumentProvider {
|
||||||
|
(if (config.isClient) config.jvmArgs + originalTask.get().additionalClientArgs.get() else config.jvmArgs).map { config.replace(lazyTokens, it) } +
|
||||||
|
config.properties.map { (k, v) -> "-D${k}=${config.replace(lazyTokens, v)}" }
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
for ((key, value) in config.environment) spec.environment(key, config.replace(lazyTokens, value))
|
||||||
|
}
|
||||||
@@ -112,7 +112,9 @@ SPDX-License-Identifier: MPL-2.0
|
|||||||
<module name="LambdaParameterName" />
|
<module name="LambdaParameterName" />
|
||||||
<module name="LocalFinalVariableName" />
|
<module name="LocalFinalVariableName" />
|
||||||
<module name="LocalVariableName" />
|
<module name="LocalVariableName" />
|
||||||
<module name="MemberName" />
|
<module name="MemberName">
|
||||||
|
<property name="format" value="^\$?[a-z][a-zA-Z0-9]*$" />
|
||||||
|
</module>
|
||||||
<module name="MethodName">
|
<module name="MethodName">
|
||||||
<property name="format" value="^(computercraft\$)?[a-z][a-zA-Z0-9]*$" />
|
<property name="format" value="^(computercraft\$)?[a-z][a-zA-Z0-9]*$" />
|
||||||
</module>
|
</module>
|
||||||
@@ -122,7 +124,7 @@ SPDX-License-Identifier: MPL-2.0
|
|||||||
</module>
|
</module>
|
||||||
<module name="ParameterName" />
|
<module name="ParameterName" />
|
||||||
<module name="StaticVariableName">
|
<module name="StaticVariableName">
|
||||||
<property name="format" value="^[a-z][a-zA-Z0-9]*|CAPABILITY(_[A-Z_]+)?$" />
|
<property name="format" value="^[a-z][a-zA-Z0-9]*$" />
|
||||||
</module>
|
</module>
|
||||||
<module name="TypeName" />
|
<module name="TypeName" />
|
||||||
|
|
||||||
|
|||||||
@@ -16,4 +16,10 @@ SPDX-License-Identifier: MPL-2.0
|
|||||||
|
|
||||||
<!-- The commands API is documented in Lua. -->
|
<!-- The commands API is documented in Lua. -->
|
||||||
<suppress checks="SummaryJavadocCheck" files=".*[\\/]CommandAPI.java" />
|
<suppress checks="SummaryJavadocCheck" files=".*[\\/]CommandAPI.java" />
|
||||||
|
|
||||||
|
<!-- Allow putting files in other packages if they look like our TeaVM stubs. -->
|
||||||
|
<suppress checks="PackageName" files=".*[\\/]T[A-Za-z]+.java" />
|
||||||
|
|
||||||
|
<!-- Allow underscores in our test classes. -->
|
||||||
|
<suppress checks="MethodName" files=".*Contract.java" />
|
||||||
</suppressions>
|
</suppressions>
|
||||||
|
|||||||
@@ -9,11 +9,11 @@ SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
|
|||||||
SPDX-License-Identifier: MPL-2.0
|
SPDX-License-Identifier: MPL-2.0
|
||||||
-->
|
-->
|
||||||
|
|
||||||
The @{alarm} event is fired when an alarm started with @{os.setAlarm} completes.
|
The [`alarm`] event is fired when an alarm started with [`os.setAlarm`] completes.
|
||||||
|
|
||||||
## Return Values
|
## Return Values
|
||||||
1. @{string}: The event name.
|
1. [`string`]: The event name.
|
||||||
2. @{number}: The ID of the alarm that finished.
|
2. [`number`]: The ID of the alarm that finished.
|
||||||
|
|
||||||
## Example
|
## Example
|
||||||
Starts a timer and then waits for it to complete.
|
Starts a timer and then waits for it to complete.
|
||||||
|
|||||||
@@ -6,18 +6,18 @@ see: key To listen to any key press.
|
|||||||
<!--
|
<!--
|
||||||
SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
|
SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
|
||||||
|
|
||||||
SPDX-License-Identifier: LicenseRef-CCPL
|
SPDX-License-Identifier: MPL-2.0
|
||||||
-->
|
-->
|
||||||
|
|
||||||
The @{char} event is fired when a character is typed on the keyboard.
|
The [`char`] event is fired when a character is typed on the keyboard.
|
||||||
|
|
||||||
The @{char} event is different to a key press. Sometimes multiple key presses may result in one character being
|
The [`char`] event is different to a key press. Sometimes multiple key presses may result in one character being
|
||||||
typed (for instance, on some European keyboards). Similarly, some keys (e.g. <kbd>Ctrl</kbd>) do not have any
|
typed (for instance, on some European keyboards). Similarly, some keys (e.g. <kbd>Ctrl</kbd>) do not have any
|
||||||
corresponding character. The @{key} should be used if you want to listen to key presses themselves.
|
corresponding character. The [`key`] should be used if you want to listen to key presses themselves.
|
||||||
|
|
||||||
## Return values
|
## Return values
|
||||||
1. @{string}: The event name.
|
1. [`string`]: The event name.
|
||||||
2. @{string}: The string representing the character that was pressed.
|
2. [`string`]: The string representing the character that was pressed.
|
||||||
|
|
||||||
|
|
||||||
## Example
|
## Example
|
||||||
|
|||||||
@@ -8,11 +8,11 @@ SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
|
|||||||
SPDX-License-Identifier: MPL-2.0
|
SPDX-License-Identifier: MPL-2.0
|
||||||
-->
|
-->
|
||||||
|
|
||||||
The @{computer_command} event is fired when the `/computercraft queue` command is run for the current computer.
|
The [`computer_command`] event is fired when the `/computercraft queue` command is run for the current computer.
|
||||||
|
|
||||||
## Return Values
|
## Return Values
|
||||||
1. @{string}: The event name.
|
1. [`string`]: The event name.
|
||||||
2. @{string}<abbr title="Variable number of arguments">…</abbr>: The arguments passed to the command.
|
2. [`string`]<abbr title="Variable number of arguments">…</abbr>: The arguments passed to the command.
|
||||||
|
|
||||||
## Example
|
## Example
|
||||||
Prints the contents of messages sent:
|
Prints the contents of messages sent:
|
||||||
|
|||||||
@@ -9,11 +9,11 @@ SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
|
|||||||
SPDX-License-Identifier: MPL-2.0
|
SPDX-License-Identifier: MPL-2.0
|
||||||
-->
|
-->
|
||||||
|
|
||||||
The @{disk} event is fired when a disk is inserted into an adjacent or networked disk drive.
|
The [`disk`] event is fired when a disk is inserted into an adjacent or networked disk drive.
|
||||||
|
|
||||||
## Return Values
|
## Return Values
|
||||||
1. @{string}: The event name.
|
1. [`string`]: The event name.
|
||||||
2. @{string}: The side of the disk drive that had a disk inserted.
|
2. [`string`]: The side of the disk drive that had a disk inserted.
|
||||||
|
|
||||||
## Example
|
## Example
|
||||||
Prints a message when a disk is inserted:
|
Prints a message when a disk is inserted:
|
||||||
|
|||||||
@@ -9,11 +9,11 @@ SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
|
|||||||
SPDX-License-Identifier: MPL-2.0
|
SPDX-License-Identifier: MPL-2.0
|
||||||
-->
|
-->
|
||||||
|
|
||||||
The @{disk_eject} event is fired when a disk is removed from an adjacent or networked disk drive.
|
The [`disk_eject`] event is fired when a disk is removed from an adjacent or networked disk drive.
|
||||||
|
|
||||||
## Return Values
|
## Return Values
|
||||||
1. @{string}: The event name.
|
1. [`string`]: The event name.
|
||||||
2. @{string}: The side of the disk drive that had a disk removed.
|
2. [`string`]: The side of the disk drive that had a disk removed.
|
||||||
|
|
||||||
## Example
|
## Example
|
||||||
Prints a message when a disk is removed:
|
Prints a message when a disk is removed:
|
||||||
|
|||||||
@@ -9,15 +9,15 @@ SPDX-FileCopyrightText: 2022 The CC: Tweaked Developers
|
|||||||
SPDX-License-Identifier: MPL-2.0
|
SPDX-License-Identifier: MPL-2.0
|
||||||
-->
|
-->
|
||||||
|
|
||||||
The @{file_transfer} event is queued when a user drags-and-drops a file on an open computer.
|
The [`file_transfer`] event is queued when a user drags-and-drops a file on an open computer.
|
||||||
|
|
||||||
This event contains a single argument of type @{TransferredFiles}, which can be used to @{TransferredFiles.getFiles|get
|
This event contains a single argument of type [`TransferredFiles`], which can be used to [get the files to be
|
||||||
the files to be transferred}. Each file returned is a @{fs.BinaryReadHandle|binary file handle} with an additional
|
transferred][`TransferredFiles.getFiles`]. Each file returned is a [binary file handle][`fs.BinaryReadHandle`] with an
|
||||||
@{TransferredFile.getName|getName} method.
|
additional [getName][`TransferredFile.getName`] method.
|
||||||
|
|
||||||
## Return values
|
## Return values
|
||||||
1. @{string}: The event name
|
1. [`string`]: The event name
|
||||||
2. @{TransferredFiles}: The list of transferred files.
|
2. [`TransferredFiles`]: The list of transferred files.
|
||||||
|
|
||||||
## Example
|
## 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.
|
Waits for a user to drop files on top of the computer, then prints the list of files and the size of each file.
|
||||||
@@ -29,7 +29,7 @@ for _, file in ipairs(files.getFiles()) do
|
|||||||
local size = file.seek("end")
|
local size = file.seek("end")
|
||||||
file.seek("set", 0)
|
file.seek("set", 0)
|
||||||
|
|
||||||
print(file.getName() .. " " .. file.getSize())
|
print(file.getName() .. " " .. size)
|
||||||
end
|
end
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -9,12 +9,12 @@ SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
|
|||||||
SPDX-License-Identifier: MPL-2.0
|
SPDX-License-Identifier: MPL-2.0
|
||||||
-->
|
-->
|
||||||
|
|
||||||
The @{http_check} event is fired when a URL check finishes.
|
The [`http_check`] event is fired when a URL check finishes.
|
||||||
|
|
||||||
This event is normally handled inside @{http.checkURL}, but it can still be seen when using @{http.checkURLAsync}.
|
This event is normally handled inside [`http.checkURL`], but it can still be seen when using [`http.checkURLAsync`].
|
||||||
|
|
||||||
## Return Values
|
## Return Values
|
||||||
1. @{string}: The event name.
|
1. [`string`]: The event name.
|
||||||
2. @{string}: The URL requested to be checked.
|
2. [`string`]: The URL requested to be checked.
|
||||||
3. @{boolean}: Whether the check succeeded.
|
3. [`boolean`]: Whether the check succeeded.
|
||||||
4. <span class="type">@{string}|@{nil}</span>: If the check failed, a reason explaining why the check failed.
|
4. <span class="type">[`string`]|[`nil`]</span>: If the check failed, a reason explaining why the check failed.
|
||||||
|
|||||||
@@ -9,15 +9,15 @@ SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
|
|||||||
SPDX-License-Identifier: MPL-2.0
|
SPDX-License-Identifier: MPL-2.0
|
||||||
-->
|
-->
|
||||||
|
|
||||||
The @{http_failure} event is fired when an HTTP request fails.
|
The [`http_failure`] event is fired when an HTTP request fails.
|
||||||
|
|
||||||
This event is normally handled inside @{http.get} and @{http.post}, but it can still be seen when using @{http.request}.
|
This event is normally handled inside [`http.get`] and [`http.post`], but it can still be seen when using [`http.request`].
|
||||||
|
|
||||||
## Return Values
|
## Return Values
|
||||||
1. @{string}: The event name.
|
1. [`string`]: The event name.
|
||||||
2. @{string}: The URL of the site requested.
|
2. [`string`]: The URL of the site requested.
|
||||||
3. @{string}: An error describing the failure.
|
3. [`string`]: An error describing the failure.
|
||||||
4. <span class="type">@{http.Response}|@{nil}</span>: A response handle if the connection succeeded, but the server's
|
4. <span class="type">[`http.Response`]|[`nil`]</span>: A response handle if the connection succeeded, but the server's
|
||||||
response indicated failure.
|
response indicated failure.
|
||||||
|
|
||||||
## Example
|
## Example
|
||||||
|
|||||||
@@ -9,14 +9,14 @@ SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
|
|||||||
SPDX-License-Identifier: MPL-2.0
|
SPDX-License-Identifier: MPL-2.0
|
||||||
-->
|
-->
|
||||||
|
|
||||||
The @{http_success} event is fired when an HTTP request returns successfully.
|
The [`http_success`] event is fired when an HTTP request returns successfully.
|
||||||
|
|
||||||
This event is normally handled inside @{http.get} and @{http.post}, but it can still be seen when using @{http.request}.
|
This event is normally handled inside [`http.get`] and [`http.post`], but it can still be seen when using [`http.request`].
|
||||||
|
|
||||||
## Return Values
|
## Return Values
|
||||||
1. @{string}: The event name.
|
1. [`string`]: The event name.
|
||||||
2. @{string}: The URL of the site requested.
|
2. [`string`]: The URL of the site requested.
|
||||||
3. @{http.Response}: The successful HTTP response.
|
3. [`http.Response`]: The successful HTTP response.
|
||||||
|
|
||||||
## Example
|
## Example
|
||||||
Prints the content of a website (this may fail if the request fails):
|
Prints the content of a website (this may fail if the request fails):
|
||||||
|
|||||||
@@ -5,21 +5,21 @@ module: [kind=event] key
|
|||||||
<!--
|
<!--
|
||||||
SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
|
SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
|
||||||
|
|
||||||
SPDX-License-Identifier: LicenseRef-CCPL
|
SPDX-License-Identifier: MPL-2.0
|
||||||
-->
|
-->
|
||||||
|
|
||||||
This event is fired when any key is pressed while the terminal is focused.
|
This event is fired when any key is pressed while the terminal is focused.
|
||||||
|
|
||||||
This event returns a numerical "key code" (for instance, <kbd>F1</kbd> is 290). This value may vary between versions and
|
This event returns a numerical "key code" (for instance, <kbd>F1</kbd> is 290). This value may vary between versions and
|
||||||
so it is recommended to use the constants in the @{keys} API rather than hard coding numeric values.
|
so it is recommended to use the constants in the [`keys`] API rather than hard coding numeric values.
|
||||||
|
|
||||||
If the button pressed represented a printable character, then the @{key} event will be followed immediately by a @{char}
|
If the button pressed represented a printable character, then the [`key`] event will be followed immediately by a [`char`]
|
||||||
event. If you are consuming text input, use a @{char} event instead!
|
event. If you are consuming text input, use a [`char`] event instead!
|
||||||
|
|
||||||
## Return values
|
## Return values
|
||||||
1. @{string}: The event name.
|
1. [`string`]: The event name.
|
||||||
2. @{number}: The numerical key value of the key pressed.
|
2. [`number`]: The numerical key value of the key pressed.
|
||||||
3. @{boolean}: Whether the key event was generated while holding the key (@{true}), rather than pressing it the first time (@{false}).
|
3. [`boolean`]: Whether the key event was generated while holding the key ([`true`]), rather than pressing it the first time ([`false`]).
|
||||||
|
|
||||||
## Example
|
## Example
|
||||||
Prints each key when the user presses it, and if the key is being held.
|
Prints each key when the user presses it, and if the key is being held.
|
||||||
|
|||||||
@@ -6,20 +6,20 @@ see: keys For a lookup table of the given keys.
|
|||||||
<!--
|
<!--
|
||||||
SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
|
SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
|
||||||
|
|
||||||
SPDX-License-Identifier: LicenseRef-CCPL
|
SPDX-License-Identifier: MPL-2.0
|
||||||
-->
|
-->
|
||||||
|
|
||||||
Fired whenever a key is released (or the terminal is closed while a key was being pressed).
|
Fired whenever a key is released (or the terminal is closed while a key was being pressed).
|
||||||
|
|
||||||
This event returns a numerical "key code" (for instance, <kbd>F1</kbd> is 290). This value may vary between versions and
|
This event returns a numerical "key code" (for instance, <kbd>F1</kbd> is 290). This value may vary between versions and
|
||||||
so it is recommended to use the constants in the @{keys} API rather than hard coding numeric values.
|
so it is recommended to use the constants in the [`keys`] API rather than hard coding numeric values.
|
||||||
|
|
||||||
## Return values
|
## Return values
|
||||||
1. @{string}: The event name.
|
1. [`string`]: The event name.
|
||||||
2. @{number}: The numerical key value of the key pressed.
|
2. [`number`]: The numerical key value of the key pressed.
|
||||||
|
|
||||||
## Example
|
## Example
|
||||||
Prints each key released on the keyboard whenever a @{key_up} event is fired.
|
Prints each key released on the keyboard whenever a [`key_up`] event is fired.
|
||||||
|
|
||||||
```lua
|
```lua
|
||||||
while true do
|
while true do
|
||||||
|
|||||||
@@ -8,18 +8,18 @@ SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
|
|||||||
SPDX-License-Identifier: MPL-2.0
|
SPDX-License-Identifier: MPL-2.0
|
||||||
-->
|
-->
|
||||||
|
|
||||||
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.
|
||||||
2. @{string}: The side of the modem that received the message.
|
2. [`string`]: The side of the modem that received the message.
|
||||||
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. <span class="type">@{number}|@{nil}</span>: The distance between the sender and the receiver in blocks, or @{nil} if the message was sent between dimensions.
|
6. <span class="type">[`number`]|[`nil`]</span>: The distance between the sender and the receiver in blocks, or [`nil`] if the message was sent between dimensions.
|
||||||
|
|
||||||
## Example
|
## Example
|
||||||
Wraps a @{modem} peripheral, opens channel 0 for listening, and prints all received messages.
|
Wraps a [`modem`] peripheral, opens channel 0 for listening, and prints all received messages.
|
||||||
|
|
||||||
```lua
|
```lua
|
||||||
local modem = peripheral.find("modem") or error("No modem attached", 0)
|
local modem = peripheral.find("modem") or error("No modem attached", 0)
|
||||||
|
|||||||
@@ -8,11 +8,11 @@ SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
|
|||||||
SPDX-License-Identifier: MPL-2.0
|
SPDX-License-Identifier: MPL-2.0
|
||||||
-->
|
-->
|
||||||
|
|
||||||
The @{monitor_resize} event is fired when an adjacent or networked monitor's size is changed.
|
The [`monitor_resize`] event is fired when an adjacent or networked monitor's size is changed.
|
||||||
|
|
||||||
## Return Values
|
## Return Values
|
||||||
1. @{string}: The event name.
|
1. [`string`]: The event name.
|
||||||
2. @{string}: The side or network ID of the monitor that was resized.
|
2. [`string`]: The side or network ID of the monitor that was resized.
|
||||||
|
|
||||||
## Example
|
## Example
|
||||||
Prints a message when a monitor is resized:
|
Prints a message when a monitor is resized:
|
||||||
|
|||||||
@@ -8,13 +8,13 @@ SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
|
|||||||
SPDX-License-Identifier: MPL-2.0
|
SPDX-License-Identifier: MPL-2.0
|
||||||
-->
|
-->
|
||||||
|
|
||||||
The @{monitor_touch} event is fired when an adjacent or networked Advanced Monitor is right-clicked.
|
The [`monitor_touch`] event is fired when an adjacent or networked Advanced Monitor is right-clicked.
|
||||||
|
|
||||||
## Return Values
|
## Return Values
|
||||||
1. @{string}: The event name.
|
1. [`string`]: The event name.
|
||||||
2. @{string}: The side or network ID of the monitor that was touched.
|
2. [`string`]: The side or network ID of the monitor that was touched.
|
||||||
3. @{number}: The X coordinate of the touch, in characters.
|
3. [`number`]: The X coordinate of the touch, in characters.
|
||||||
4. @{number}: The Y coordinate of the touch, in characters.
|
4. [`number`]: The Y coordinate of the touch, in characters.
|
||||||
|
|
||||||
## Example
|
## Example
|
||||||
Prints a message when a monitor is touched:
|
Prints a message when a monitor is touched:
|
||||||
|
|||||||
@@ -5,20 +5,20 @@ module: [kind=event] mouse_click
|
|||||||
<!--
|
<!--
|
||||||
SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
|
SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
|
||||||
|
|
||||||
SPDX-License-Identifier: LicenseRef-CCPL
|
SPDX-License-Identifier: MPL-2.0
|
||||||
-->
|
-->
|
||||||
|
|
||||||
This event is fired when the terminal is clicked with a mouse. This event is only fired on advanced computers (including
|
This event is fired when the terminal is clicked with a mouse. This event is only fired on advanced computers (including
|
||||||
advanced turtles and pocket computers).
|
advanced turtles and pocket computers).
|
||||||
|
|
||||||
## Return values
|
## Return values
|
||||||
1. @{string}: The event name.
|
1. [`string`]: The event name.
|
||||||
2. @{number}: The mouse button that was clicked.
|
2. [`number`]: The mouse button that was clicked.
|
||||||
3. @{number}: The X-coordinate of the click.
|
3. [`number`]: The X-coordinate of the click.
|
||||||
4. @{number}: The Y-coordinate of the click.
|
4. [`number`]: The Y-coordinate of the click.
|
||||||
|
|
||||||
## Mouse buttons
|
## Mouse buttons
|
||||||
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.
|
||||||
|
|
||||||
| Button Code | Mouse Button |
|
| Button Code | Mouse Button |
|
||||||
|
|||||||
@@ -6,16 +6,16 @@ see: mouse_click For when a mouse button is initially pressed.
|
|||||||
<!--
|
<!--
|
||||||
SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
|
SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
|
||||||
|
|
||||||
SPDX-License-Identifier: LicenseRef-CCPL
|
SPDX-License-Identifier: MPL-2.0
|
||||||
-->
|
-->
|
||||||
|
|
||||||
This event is fired every time the mouse is moved while a mouse button is being held.
|
This event is fired every time the mouse is moved while a mouse button is being held.
|
||||||
|
|
||||||
## Return values
|
## Return values
|
||||||
1. @{string}: The event name.
|
1. [`string`]: The event name.
|
||||||
2. @{number}: The [mouse button](mouse_click.html#Mouse_buttons) that is being pressed.
|
2. [`number`]: The [mouse button](mouse_click.html#Mouse_buttons) that is being pressed.
|
||||||
3. @{number}: The X-coordinate of the mouse.
|
3. [`number`]: The X-coordinate of the mouse.
|
||||||
4. @{number}: The Y-coordinate of the mouse.
|
4. [`number`]: The Y-coordinate of the mouse.
|
||||||
|
|
||||||
## Example
|
## Example
|
||||||
Print the button and the coordinates whenever the mouse is dragged.
|
Print the button and the coordinates whenever the mouse is dragged.
|
||||||
|
|||||||
@@ -5,16 +5,16 @@ module: [kind=event] mouse_scroll
|
|||||||
<!--
|
<!--
|
||||||
SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
|
SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
|
||||||
|
|
||||||
SPDX-License-Identifier: LicenseRef-CCPL
|
SPDX-License-Identifier: MPL-2.0
|
||||||
-->
|
-->
|
||||||
|
|
||||||
This event is fired when a mouse wheel is scrolled in the terminal.
|
This event is fired when a mouse wheel is scrolled in the terminal.
|
||||||
|
|
||||||
## Return values
|
## Return values
|
||||||
1. @{string}: The event name.
|
1. [`string`]: The event name.
|
||||||
2. @{number}: The direction of the scroll. (-1 = up, 1 = down)
|
2. [`number`]: The direction of the scroll. (-1 = up, 1 = down)
|
||||||
3. @{number}: The X-coordinate of the mouse when scrolling.
|
3. [`number`]: The X-coordinate of the mouse when scrolling.
|
||||||
4. @{number}: The Y-coordinate of the mouse when scrolling.
|
4. [`number`]: The Y-coordinate of the mouse when scrolling.
|
||||||
|
|
||||||
## Example
|
## Example
|
||||||
Prints the direction of each scroll, and the position of the mouse at the time.
|
Prints the direction of each scroll, and the position of the mouse at the time.
|
||||||
|
|||||||
@@ -5,16 +5,16 @@ module: [kind=event] mouse_up
|
|||||||
<!--
|
<!--
|
||||||
SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
|
SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
|
||||||
|
|
||||||
SPDX-License-Identifier: LicenseRef-CCPL
|
SPDX-License-Identifier: MPL-2.0
|
||||||
-->
|
-->
|
||||||
|
|
||||||
This event is fired when a mouse button is released or a held mouse leaves the computer's terminal.
|
This event is fired when a mouse button is released or a held mouse leaves the computer's terminal.
|
||||||
|
|
||||||
## Return values
|
## Return values
|
||||||
1. @{string}: The event name.
|
1. [`string`]: The event name.
|
||||||
2. @{number}: The [mouse button](mouse_click.html#Mouse_buttons) that was released.
|
2. [`number`]: The [mouse button](mouse_click.html#Mouse_buttons) that was released.
|
||||||
3. @{number}: The X-coordinate of the mouse.
|
3. [`number`]: The X-coordinate of the mouse.
|
||||||
4. @{number}: The Y-coordinate of the mouse.
|
4. [`number`]: The Y-coordinate of the mouse.
|
||||||
|
|
||||||
## Example
|
## Example
|
||||||
Prints the coordinates and button number whenever the mouse is released.
|
Prints the coordinates and button number whenever the mouse is released.
|
||||||
|
|||||||
@@ -8,11 +8,11 @@ SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
|
|||||||
SPDX-License-Identifier: MPL-2.0
|
SPDX-License-Identifier: MPL-2.0
|
||||||
-->
|
-->
|
||||||
|
|
||||||
The @{paste} event is fired when text is pasted into the computer through Ctrl-V (or ⌘V on Mac).
|
The [`paste`] event is fired when text is pasted into the computer through Ctrl-V (or ⌘V on Mac).
|
||||||
|
|
||||||
## Return values
|
## Return values
|
||||||
1. @{string}: The event name.
|
1. [`string`]: The event name.
|
||||||
2. @{string} The text that was pasted.
|
2. [`string`] The text that was pasted.
|
||||||
|
|
||||||
## Example
|
## Example
|
||||||
Prints pasted text:
|
Prints pasted text:
|
||||||
|
|||||||
@@ -9,11 +9,11 @@ SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
|
|||||||
SPDX-License-Identifier: MPL-2.0
|
SPDX-License-Identifier: MPL-2.0
|
||||||
-->
|
-->
|
||||||
|
|
||||||
The @{peripheral} event is fired when a peripheral is attached on a side or to a modem.
|
The [`peripheral`] event is fired when a peripheral is attached on a side or to a modem.
|
||||||
|
|
||||||
## Return Values
|
## Return Values
|
||||||
1. @{string}: The event name.
|
1. [`string`]: The event name.
|
||||||
2. @{string}: The side the peripheral was attached to.
|
2. [`string`]: The side the peripheral was attached to.
|
||||||
|
|
||||||
## Example
|
## Example
|
||||||
Prints a message when a peripheral is attached:
|
Prints a message when a peripheral is attached:
|
||||||
|
|||||||
@@ -9,11 +9,11 @@ SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
|
|||||||
SPDX-License-Identifier: MPL-2.0
|
SPDX-License-Identifier: MPL-2.0
|
||||||
-->
|
-->
|
||||||
|
|
||||||
The @{peripheral_detach} event is fired when a peripheral is detached from a side or from a modem.
|
The [`peripheral_detach`] event is fired when a peripheral is detached from a side or from a modem.
|
||||||
|
|
||||||
## Return Values
|
## Return Values
|
||||||
1. @{string}: The event name.
|
1. [`string`]: The event name.
|
||||||
2. @{string}: The side the peripheral was detached from.
|
2. [`string`]: The side the peripheral was detached from.
|
||||||
|
|
||||||
## Example
|
## Example
|
||||||
Prints a message when a peripheral is detached:
|
Prints a message when a peripheral is detached:
|
||||||
|
|||||||
@@ -10,17 +10,17 @@ SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
|
|||||||
SPDX-License-Identifier: MPL-2.0
|
SPDX-License-Identifier: MPL-2.0
|
||||||
-->
|
-->
|
||||||
|
|
||||||
The @{rednet_message} event is fired when a message is sent over Rednet.
|
The [`rednet_message`] event is fired when a message is sent over Rednet.
|
||||||
|
|
||||||
This event is usually handled by @{rednet.receive}, but it can also be pulled manually.
|
This event is usually handled by [`rednet.receive`], but it can also be pulled manually.
|
||||||
|
|
||||||
@{rednet_message} events are sent by @{rednet.run} in the top-level coroutine in response to @{modem_message} events. A @{rednet_message} event is always preceded by a @{modem_message} event. They are generated inside CraftOS rather than being sent by the ComputerCraft machine.
|
[`rednet_message`] events are sent by [`rednet.run`] in the top-level coroutine in response to [`modem_message`] events. A [`rednet_message`] event is always preceded by a [`modem_message`] event. They are generated inside CraftOS rather than being sent by the ComputerCraft machine.
|
||||||
|
|
||||||
## Return Values
|
## Return Values
|
||||||
1. @{string}: The event name.
|
1. [`string`]: The event name.
|
||||||
2. @{number}: The ID of the sending computer.
|
2. [`number`]: The ID of the sending computer.
|
||||||
3. @{any}: The message sent.
|
3. [`any`]: The message sent.
|
||||||
4. <span class="type">@{string}|@{nil}</span>: The protocol of the message, if provided.
|
4. <span class="type">[`string`]|[`nil`]</span>: The protocol of the message, if provided.
|
||||||
|
|
||||||
## Example
|
## Example
|
||||||
Prints a message when one is sent:
|
Prints a message when one is sent:
|
||||||
|
|||||||
@@ -8,10 +8,10 @@ SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
|
|||||||
SPDX-License-Identifier: MPL-2.0
|
SPDX-License-Identifier: MPL-2.0
|
||||||
-->
|
-->
|
||||||
|
|
||||||
The @{event!redstone} event is fired whenever any redstone inputs on the computer change.
|
The [`event!redstone`] event is fired whenever any redstone inputs on the computer change.
|
||||||
|
|
||||||
## Return values
|
## Return values
|
||||||
1. @{string}: The event name.
|
1. [`string`]: The event name.
|
||||||
|
|
||||||
## Example
|
## Example
|
||||||
Prints a message when a redstone input changes:
|
Prints a message when a redstone input changes:
|
||||||
|
|||||||
@@ -10,13 +10,13 @@ SPDX-License-Identifier: MPL-2.0
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
## Return Values
|
## Return Values
|
||||||
1. @{string}: The event name.
|
1. [`string`]: The event name.
|
||||||
2. @{string}: The name of the speaker which is available to play more audio.
|
2. [`string`]: The name of the speaker which is available to play more audio.
|
||||||
|
|
||||||
|
|
||||||
## Example
|
## Example
|
||||||
This uses @{io.lines} to read audio data in blocks of 16KiB from "example_song.dfpwm", and then attempts to play it
|
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.
|
using [`speaker.playAudio`]. If the speaker's buffer is full, it waits for an event and tries again.
|
||||||
|
|
||||||
```lua {data-peripheral=speaker}
|
```lua {data-peripheral=speaker}
|
||||||
local dfpwm = require("cc.audio.dfpwm")
|
local dfpwm = require("cc.audio.dfpwm")
|
||||||
|
|||||||
@@ -9,13 +9,13 @@ SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
|
|||||||
SPDX-License-Identifier: MPL-2.0
|
SPDX-License-Identifier: MPL-2.0
|
||||||
-->
|
-->
|
||||||
|
|
||||||
The @{task_complete} event is fired when an asynchronous task completes. This is usually handled inside the function call that queued the task; however, functions such as @{commands.execAsync} return immediately so the user can wait for completion.
|
The [`task_complete`] event is fired when an asynchronous task completes. This is usually handled inside the function call that queued the task; however, functions such as [`commands.execAsync`] return immediately so the user can wait for completion.
|
||||||
|
|
||||||
## Return Values
|
## Return Values
|
||||||
1. @{string}: The event name.
|
1. [`string`]: The event name.
|
||||||
2. @{number}: The ID of the task that completed.
|
2. [`number`]: The ID of the task that completed.
|
||||||
3. @{boolean}: Whether the command succeeded.
|
3. [`boolean`]: Whether the command succeeded.
|
||||||
4. @{string}: If the command failed, an error message explaining the failure. (This is not present if the command succeeded.)
|
4. [`string`]: If the command failed, an error message explaining the failure. (This is not present if the command succeeded.)
|
||||||
5. <abbr title="Variable number of arguments">…</abbr>: Any parameters returned from the command.
|
5. <abbr title="Variable number of arguments">…</abbr>: Any parameters returned from the command.
|
||||||
|
|
||||||
## Example
|
## Example
|
||||||
|
|||||||
@@ -8,15 +8,15 @@ SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
|
|||||||
SPDX-License-Identifier: MPL-2.0
|
SPDX-License-Identifier: MPL-2.0
|
||||||
-->
|
-->
|
||||||
|
|
||||||
The @{term_resize} event is fired when the main terminal is resized. For instance:
|
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 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 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
|
When this event fires, some parts of the terminal may have been moved or deleted. Simple terminal programs (those
|
||||||
not using @{term.setCursorPos}) can ignore this event, but more complex GUI programs should redraw the entire screen.
|
not using [`term.setCursorPos`]) can ignore this event, but more complex GUI programs should redraw the entire screen.
|
||||||
|
|
||||||
## Return values
|
## Return values
|
||||||
1. @{string}: The event name.
|
1. [`string`]: The event name.
|
||||||
|
|
||||||
## Example
|
## Example
|
||||||
Print a message each time the terminal is resized.
|
Print a message each time the terminal is resized.
|
||||||
|
|||||||
@@ -8,14 +8,14 @@ SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
|
|||||||
SPDX-License-Identifier: MPL-2.0
|
SPDX-License-Identifier: MPL-2.0
|
||||||
-->
|
-->
|
||||||
|
|
||||||
The @{terminate} event is fired when <kbd>Ctrl-T</kbd> is held down.
|
The [`terminate`] event is fired when <kbd>Ctrl-T</kbd> is held down.
|
||||||
|
|
||||||
This event is normally handled by @{os.pullEvent}, and will not be returned. However, @{os.pullEventRaw} will return this event when fired.
|
This event is normally handled by [`os.pullEvent`], and will not be returned. However, [`os.pullEventRaw`] will return this event when fired.
|
||||||
|
|
||||||
@{terminate} will be sent even when a filter is provided to @{os.pullEventRaw}. When using @{os.pullEventRaw} with a filter, make sure to check that the event is not @{terminate}.
|
[`terminate`] will be sent even when a filter is provided to [`os.pullEventRaw`]. When using [`os.pullEventRaw`] with a filter, make sure to check that the event is not [`terminate`].
|
||||||
|
|
||||||
## Return values
|
## Return values
|
||||||
1. @{string}: The event name.
|
1. [`string`]: The event name.
|
||||||
|
|
||||||
## Example
|
## Example
|
||||||
Prints a message when Ctrl-T is held:
|
Prints a message when Ctrl-T is held:
|
||||||
|
|||||||
@@ -9,11 +9,11 @@ SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
|
|||||||
SPDX-License-Identifier: MPL-2.0
|
SPDX-License-Identifier: MPL-2.0
|
||||||
-->
|
-->
|
||||||
|
|
||||||
The @{timer} event is fired when a timer started with @{os.startTimer} completes.
|
The [`timer`] event is fired when a timer started with [`os.startTimer`] completes.
|
||||||
|
|
||||||
## Return Values
|
## Return Values
|
||||||
1. @{string}: The event name.
|
1. [`string`]: The event name.
|
||||||
2. @{number}: The ID of the timer that finished.
|
2. [`number`]: The ID of the timer that finished.
|
||||||
|
|
||||||
## Example
|
## Example
|
||||||
Start and wait for a timer to finish.
|
Start and wait for a timer to finish.
|
||||||
|
|||||||
@@ -8,10 +8,10 @@ SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
|
|||||||
SPDX-License-Identifier: MPL-2.0
|
SPDX-License-Identifier: MPL-2.0
|
||||||
-->
|
-->
|
||||||
|
|
||||||
The @{turtle_inventory} event is fired when a turtle's inventory is changed.
|
The [`turtle_inventory`] event is fired when a turtle's inventory is changed.
|
||||||
|
|
||||||
## Return values
|
## Return values
|
||||||
1. @{string}: The event name.
|
1. [`string`]: The event name.
|
||||||
|
|
||||||
## Example
|
## Example
|
||||||
Prints a message when the inventory is changed:
|
Prints a message when the inventory is changed:
|
||||||
|
|||||||
@@ -8,11 +8,20 @@ SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
|
|||||||
SPDX-License-Identifier: MPL-2.0
|
SPDX-License-Identifier: MPL-2.0
|
||||||
-->
|
-->
|
||||||
|
|
||||||
The @{websocket_closed} event is fired when an open WebSocket connection is closed.
|
The [`websocket_closed`] event is fired when an open WebSocket connection is closed.
|
||||||
|
|
||||||
## Return Values
|
## Return Values
|
||||||
1. @{string}: The event name.
|
1. [`string`]: The event name.
|
||||||
2. @{string}: The URL of the WebSocket that was closed.
|
2. [`string`]: The URL of the WebSocket that was closed.
|
||||||
|
3. <span class="type">[`string`]|[`nil`]</span>: The [server-provided reason][close_reason]
|
||||||
|
the websocket was closed. This will be [`nil`] if the connection was closed
|
||||||
|
abnormally.
|
||||||
|
4. <span class="type">[`number`]|[`nil`]</span>: The [connection close code][close_code],
|
||||||
|
indicating why the socket was closed. This will be [`nil`] if the connection
|
||||||
|
was closed abnormally.
|
||||||
|
|
||||||
|
[close_reason]: https://www.rfc-editor.org/rfc/rfc6455.html#section-7.1.6 "The WebSocket Connection Close Reason, RFC 6455"
|
||||||
|
[close_code]: https://www.rfc-editor.org/rfc/rfc6455.html#section-7.1.5 "The WebSocket Connection Close Code, RFC 6455"
|
||||||
|
|
||||||
## Example
|
## Example
|
||||||
Prints a message when a WebSocket is closed (this may take a minute):
|
Prints a message when a WebSocket is closed (this may take a minute):
|
||||||
|
|||||||
@@ -9,14 +9,14 @@ SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
|
|||||||
SPDX-License-Identifier: MPL-2.0
|
SPDX-License-Identifier: MPL-2.0
|
||||||
-->
|
-->
|
||||||
|
|
||||||
The @{websocket_failure} event is fired when a WebSocket connection request fails.
|
The [`websocket_failure`] event is fired when a WebSocket connection request fails.
|
||||||
|
|
||||||
This event is normally handled inside @{http.websocket}, but it can still be seen when using @{http.websocketAsync}.
|
This event is normally handled inside [`http.websocket`], but it can still be seen when using [`http.websocketAsync`].
|
||||||
|
|
||||||
## Return Values
|
## Return Values
|
||||||
1. @{string}: The event name.
|
1. [`string`]: The event name.
|
||||||
2. @{string}: The URL of the site requested.
|
2. [`string`]: The URL of the site requested.
|
||||||
3. @{string}: An error describing the failure.
|
3. [`string`]: An error describing the failure.
|
||||||
|
|
||||||
## Example
|
## Example
|
||||||
Prints an error why the website cannot be contacted:
|
Prints an error why the website cannot be contacted:
|
||||||
|
|||||||
@@ -8,15 +8,15 @@ SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
|
|||||||
SPDX-License-Identifier: MPL-2.0
|
SPDX-License-Identifier: MPL-2.0
|
||||||
-->
|
-->
|
||||||
|
|
||||||
The @{websocket_message} event is fired when a message is received on an open WebSocket connection.
|
The [`websocket_message`] event is fired when a message is received on an open WebSocket connection.
|
||||||
|
|
||||||
This event is normally handled by @{http.Websocket.receive}, but it can also be pulled manually.
|
This event is normally handled by [`http.Websocket.receive`], but it can also be pulled manually.
|
||||||
|
|
||||||
## Return Values
|
## Return Values
|
||||||
1. @{string}: The event name.
|
1. [`string`]: The event name.
|
||||||
2. @{string}: The URL of the WebSocket.
|
2. [`string`]: The URL of the WebSocket.
|
||||||
3. @{string}: The contents of the message.
|
3. [`string`]: The contents of the message.
|
||||||
4. @{boolean}: Whether this is a binary message.
|
4. [`boolean`]: Whether this is a binary message.
|
||||||
|
|
||||||
## Example
|
## Example
|
||||||
Prints a message sent by a WebSocket:
|
Prints a message sent by a WebSocket:
|
||||||
|
|||||||
@@ -9,14 +9,14 @@ SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
|
|||||||
SPDX-License-Identifier: MPL-2.0
|
SPDX-License-Identifier: MPL-2.0
|
||||||
-->
|
-->
|
||||||
|
|
||||||
The @{websocket_success} event is fired when a WebSocket connection request returns successfully.
|
The [`websocket_success`] event is fired when a WebSocket connection request returns successfully.
|
||||||
|
|
||||||
This event is normally handled inside @{http.websocket}, but it can still be seen when using @{http.websocketAsync}.
|
This event is normally handled inside [`http.websocket`], but it can still be seen when using [`http.websocketAsync`].
|
||||||
|
|
||||||
## Return Values
|
## Return Values
|
||||||
1. @{string}: The event name.
|
1. [`string`]: The event name.
|
||||||
2. @{string}: The URL of the site.
|
2. [`string`]: The URL of the site.
|
||||||
3. @{http.Websocket}: The handle for the WebSocket.
|
3. [`http.Websocket`]: The handle for the WebSocket.
|
||||||
|
|
||||||
## Example
|
## Example
|
||||||
Prints the content of a website (this may fail if the request fails):
|
Prints the content of a website (this may fail if the request fails):
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ SPDX-License-Identifier: MPL-2.0
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
# Setting up GPS
|
# Setting up GPS
|
||||||
The @{gps} API allows computers and turtles to find their current position using wireless modems.
|
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`
|
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
|
program, which tell other computers the host's position. Several hosts running together are known as a *GPS
|
||||||
@@ -19,22 +19,21 @@ In order to give the best results, a GPS constellation needs at least four compu
|
|||||||
constellation is redundant, but it does not cause problems.
|
constellation is redundant, but it does not cause problems.
|
||||||
|
|
||||||
## Building a GPS constellation
|
## Building a GPS constellation
|
||||||
{.big-image}
|
<img alt="An example GPS constellation." src="/images/gps-constellation-example.png" class="big-image" />
|
||||||
|
|
||||||
We are going to build our GPS constellation as shown in the image above. You will need 4 computers and either 4 wireless
|
We are going to build our GPS constellation as shown in the image above. You will need 4 computers and either 4 wireless
|
||||||
modems or 4 ender modems. Try not to mix ender and wireless modems together as you might get some odd behavior when your
|
modems or 4 ender modems. Try not to mix ender and wireless modems together as you might get some odd behavior when your
|
||||||
requesting computers are out of range.
|
requesting computers are out of range.
|
||||||
|
|
||||||
:::tip Ender modems vs wireless modems
|
> [Ender modems vs wireless modems][!TIP]
|
||||||
Ender modems have a very large range, which makes them very useful for setting up GPS hosts. If you do this then you
|
> 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).
|
> 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.
|
> 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
|
> A computer needs a wireless or ender modem and to be in range of a GPS constellation that is in the same dimension as
|
||||||
to use the GPS API. The reason for this is that ComputerCraft mimics real-life GPS by making use of the distance
|
> 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.
|
> parameter of [modem messages][`modem_message`] 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
|
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
|
6 blocks deep (6x6x6). If you are using wireless modems then you may want to build your constellation as high as you can
|
||||||
@@ -79,18 +78,16 @@ 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
|
Create similar startup files for the other computers in your constellation, making sure to input the each computer's own
|
||||||
coordinates.
|
coordinates.
|
||||||
|
|
||||||
:::caution Modem messages come from the computer's position, not the modem's
|
> [Modem messages come from the computer's position, not the modem's][!WARNING]
|
||||||
Wireless modems transmit from the block that they are attached to *not* the block space that they occupy, the
|
> 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.
|
> 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
|
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).
|
wireless modem on it, and running the `gps locate` program (or calling the [`gps.locate`] function).
|
||||||
|
|
||||||
:::info Why use Minecraft's coordinates?
|
> [Why use Minecraft's coordinates?][!INFO]
|
||||||
CC doesn't care if you use Minecraft's coordinate system, so long as all of the GPS hosts with overlapping ranges use
|
> 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,
|
> 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
|
> 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
|
> computers get their location, they use MC's command system to get their block which returns that in MC's coordinate
|
||||||
system.
|
> system.
|
||||||
:::
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ SPDX-License-Identifier: MPL-2.0
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
# Playing audio with speakers
|
# Playing audio with speakers
|
||||||
CC: Tweaked's speaker peripheral provides a powerful way to play any audio you like with the @{speaker.playAudio}
|
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
|
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
|
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.
|
the more complex topic of audio processing.
|
||||||
@@ -60,7 +60,7 @@ sine waves (and why wouldn't you?), you'd need a table with almost 3 _million_.
|
|||||||
up very quickly, and these tables take up more and more memory.
|
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
|
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
|
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
|
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.
|
one, and then play them.
|
||||||
|
|
||||||
@@ -84,15 +84,15 @@ end
|
|||||||
```
|
```
|
||||||
|
|
||||||
It looks pretty similar to before, aside from we've wrapped the generation and playing code in a while loop, and added a
|
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}.
|
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
|
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
|
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!
|
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,
|
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.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.
|
[`speaker_audio_empty`] event is queued, and we can try to play our chunk once more.
|
||||||
|
|
||||||
## Storing audio
|
## 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
|
PCM is a fantastic way of representing audio when we want to manipulate it, but it's not very efficient when we want to
|
||||||
@@ -106,7 +106,7 @@ computer. Instead, we need something much simpler.
|
|||||||
|
|
||||||
DFPWM (Dynamic Filter Pulse Width Modulation) is the de facto standard audio format of the ComputerCraft (and
|
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
|
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
|
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.
|
using the speaker.
|
||||||
|
|
||||||
Let's dive in with an example, and we'll explain things afterwards:
|
Let's dive in with an example, and we'll explain things afterwards:
|
||||||
@@ -125,16 +125,16 @@ for chunk in io.lines("data/example.dfpwm", 16 * 1024) do
|
|||||||
end
|
end
|
||||||
```
|
```
|
||||||
|
|
||||||
Once again, we see the @{speaker.playAudio}/@{speaker_audio_empty} loop. However, the rest of the program is a little
|
Once again, we see the [`speaker.playAudio`]/[`speaker_audio_empty`] loop. However, the rest of the program is a little
|
||||||
different.
|
different.
|
||||||
|
|
||||||
First, we require the dfpwm module and call @{cc.audio.dfpwm.make_decoder} to construct a new decoder. This decoder
|
First, we require the dfpwm module and call [`cc.audio.dfpwm.make_decoder`] to construct a new decoder. This decoder
|
||||||
accepts blocks of DFPWM data and converts it to a list of 8-bit amplitudes, which we can then play with our speaker.
|
accepts blocks of DFPWM data and converts it to a list of 8-bit amplitudes, which we can then play with our speaker.
|
||||||
|
|
||||||
As mentioned above, @{speaker.playAudio} accepts at most 128×1024 samples in one go. DFPMW uses a single bit for each
|
As mentioned above, [`speaker.playAudio`] accepts at most 128×1024 samples in one go. 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
|
sample, which means we want to process our audio in chunks of 16×1024 bytes (16KiB). In order to do this, we use
|
||||||
@{io.lines}, which provides a nice way to loop over chunks of a file. You can of course just use @{fs.open} and
|
[`io.lines`], which provides a nice way to loop over chunks of a file. You can of course just use [`fs.open`] and
|
||||||
@{fs.BinaryReadHandle.read} if you prefer.
|
[`fs.BinaryReadHandle.read`] if you prefer.
|
||||||
|
|
||||||
## Processing audio
|
## 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.
|
As mentioned near the beginning of this guide, PCM audio is pretty easy to work with as it's just a list of amplitudes.
|
||||||
@@ -189,10 +189,9 @@ for chunk in io.lines("data/example.dfpwm", 16 * 1024) do
|
|||||||
end
|
end
|
||||||
```
|
```
|
||||||
|
|
||||||
:::note Confused?
|
> [Confused?][!NOTE]
|
||||||
Don't worry if you don't understand this example. It's quite advanced, and does use some ideas that this guide doesn't
|
> Don't worry if you don't understand this example. It's quite advanced, and does use some ideas that this guide doesn't
|
||||||
cover. That said, don't be afraid to ask on [GitHub Discussions] or [IRC] either!
|
> cover. That said, don't be afraid to ask on [GitHub Discussions] or [IRC] either!
|
||||||
:::
|
|
||||||
|
|
||||||
It's worth noting that the examples of audio processing we've mentioned here are about manipulating the _amplitude_ of
|
It's worth noting that the examples of audio processing we've mentioned here are about manipulating the _amplitude_ of
|
||||||
the wave. If you wanted to modify the _frequency_ (for instance, shifting the pitch), things get rather more complex.
|
the wave. If you wanted to modify the _frequency_ (for instance, shifting the pitch), things get rather more complex.
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ A library is a collection of useful functions and other definitions which is sto
|
|||||||
might want to create a library because you have some functions which are used in multiple programs, or just to split
|
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.
|
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
|
Let's say we want to create a small library to make working with the [terminal][`term`] 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
|
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.
|
in the middle of the screen.
|
||||||
|
|
||||||
@@ -48,32 +48,32 @@ more_term.write_center("Hello, world!")
|
|||||||
When run, this'll clear the screen and print some text in the middle of the first line.
|
When run, this'll clear the screen and print some text in the middle of the first line.
|
||||||
|
|
||||||
## require in depth
|
## require in depth
|
||||||
While the previous section is a good introduction to how @{require} operates, there are a couple of remaining points
|
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.
|
which are worth mentioning for more advanced usage.
|
||||||
|
|
||||||
### Libraries can return anything
|
### 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
|
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
|
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.
|
all the same, and just returns whatever your library provides.
|
||||||
|
|
||||||
### Module resolution and the package path
|
### 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
|
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
|
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.
|
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
|
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
|
code from a sub-directory using `require("folder/library")` or even `require("folder/library.lua")`, neither of which
|
||||||
will do quite what you expect.
|
will do quite what you expect.
|
||||||
|
|
||||||
When loading libraries (also referred to as *modules*) from files, @{require} searches along the *@{package.path|module
|
When loading libraries (also referred to as *modules*) from files, [`require`] searches along the [*module
|
||||||
path}*. By default, this looks something like:
|
path*][`package.path`]. By default, this looks something like:
|
||||||
|
|
||||||
* `?.lua`
|
* `?.lua`
|
||||||
* `?/init.lua`
|
* `?/init.lua`
|
||||||
* `/rom/modules/main/?.lua`
|
* `/rom/modules/main/?.lua`
|
||||||
* etc...
|
* etc...
|
||||||
|
|
||||||
When you call `require("my_library")`, @{require} replaces the `?` in each element of the path with your module name, and
|
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`,
|
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
|
`/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...
|
program is actually called `folder/program`, then we'll look for `folder/my_library.lua`, etc...
|
||||||
@@ -86,4 +86,4 @@ before we start looking for the library.
|
|||||||
There are several external resources which go into require in a little more detail:
|
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.
|
- 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).
|
- [Lua's manual section on `require`](https://www.lua.org/manual/5.1/manual.html#pdf-require).
|
||||||
|
|||||||
@@ -15,13 +15,13 @@ CC: Tweaked can be installed from [CurseForge] or [Modrinth]. It runs on both [M
|
|||||||
Controlled using the [Lua programming language][lua], CC: Tweaked's computers provides all the tools you need to start
|
Controlled using the [Lua programming language][lua], CC: Tweaked's computers provides all the tools you need to start
|
||||||
writing code and automating your Minecraft world.
|
writing code and automating your Minecraft world.
|
||||||
|
|
||||||
{.big-image}
|
<img alt="A ComputerCraft terminal open and ready to be programmed." src="images/basic-terminal.png" class="big-image" />
|
||||||
|
|
||||||
While computers are incredibly powerful, they're rather limited by their inability to move about. *Turtles* are the
|
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,
|
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!
|
or whatever else you program them to!
|
||||||
|
|
||||||
{.big-image}
|
<img alt="A turtle tunneling in Minecraft." src="images/turtle.png" class="big-image" />
|
||||||
|
|
||||||
Not all problems can be solved with a pickaxe though, and so CC: Tweaked also provides a bunch of additional peripherals
|
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
|
for your computers. You can play a tune with speakers, display text or images on a monitor, connect all your
|
||||||
@@ -30,7 +30,7 @@ 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
|
Computers can now also interact with inventories such as chests, allowing you to build complex inventory and item
|
||||||
management systems.
|
management systems.
|
||||||
|
|
||||||
{.big-image}
|
<img alt="A chest's contents being read by a computer and displayed on a monitor." src="images/peripherals.png" class="big-image" />
|
||||||
|
|
||||||
## Getting Started
|
## Getting Started
|
||||||
While ComputerCraft is lovely for both experienced programmers and for people who have never coded before, it can be a
|
While ComputerCraft is lovely for both experienced programmers and for people who have never coded before, it can be a
|
||||||
|
|||||||
BIN
doc/logo-darkmode.png
Normal file
BIN
doc/logo-darkmode.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.3 KiB |
67
doc/reference/breaking_changes.md
Normal file
67
doc/reference/breaking_changes.md
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
---
|
||||||
|
module: [kind=reference] breaking_changes
|
||||||
|
---
|
||||||
|
|
||||||
|
<!--
|
||||||
|
SPDX-FileCopyrightText: 2019 The CC: Tweaked Developers
|
||||||
|
|
||||||
|
SPDX-License-Identifier: MPL-2.0
|
||||||
|
-->
|
||||||
|
|
||||||
|
# Incompatibilities between versions
|
||||||
|
|
||||||
|
CC: Tweaked tries to remain as compatible between versions as possible, meaning most programs written for older version
|
||||||
|
of the mod should run fine on later versions.
|
||||||
|
|
||||||
|
> [External peripherals][!WARNING]
|
||||||
|
>
|
||||||
|
> While CC: Tweaked is relatively stable across versions, this may not be true for other mods which add their own
|
||||||
|
> peripherals. Older programs which interact with external blocks may not work on newer versions of the game.
|
||||||
|
|
||||||
|
However, some changes to the underlying game, or CC: Tweaked's own internals may break some programs. This page serves
|
||||||
|
as documentation for breaking changes and "gotchas" one should look out for between versions.
|
||||||
|
|
||||||
|
## Minecraft 1.13 {#mc-1.13}
|
||||||
|
- The "key code" for [`key`] and [`key_up`] events has changed, due to Minecraft updating to LWJGL 3. Make sure you're
|
||||||
|
using the constants provided by the [`keys`] API, rather than hard-coding numerical values.
|
||||||
|
|
||||||
|
Related to this change, the numpad enter key now has a different key code to the enter key. You may need to adjust
|
||||||
|
your programs to handle both. (Note, the `keys.numpadEnter` constant was defined in pre-1.13 versions of CC, but the
|
||||||
|
`keys.enter` constant was queued when the key was pressed)
|
||||||
|
|
||||||
|
- Minecraft 1.13 removed the concept of item damage and block metadata (see ["The Flattening"][flattening]). As a
|
||||||
|
result `turtle.inspect` no longer provides block metadata, and `turtle.getItemDetail` no longer provides damage.
|
||||||
|
|
||||||
|
- Block states (`turtle.inspect().state`) should provide all the same information as block metadata, but in a much
|
||||||
|
more understandable format.
|
||||||
|
|
||||||
|
- Item and block names now represent a unique item type. For instance, wool is split into 16 separate items
|
||||||
|
(`minecraft:white_wool`, etc...) rather than a single `minecraft:wool` with each meta/damage value specifying the
|
||||||
|
colour.
|
||||||
|
|
||||||
|
- Custom ROMs are now provided using data packs rather than resource packs. This should mostly be a matter of renaming
|
||||||
|
the "assets" folder to "data", and placing it in "datapacks", but there are a couple of other gotchas to look out
|
||||||
|
for:
|
||||||
|
|
||||||
|
- Data packs [impose some restrictions on file names][legal_data_pack]. As a result, your programs and directories
|
||||||
|
must all be lower case.
|
||||||
|
- Due to how data packs are read by CC: Tweaked, you may need to use the `/reload` command to see changes to your
|
||||||
|
pack show up on the computer.
|
||||||
|
|
||||||
|
See [the example datapack][datapack-example] for how to get started.
|
||||||
|
|
||||||
|
- Turtles can now be waterlogged and move "through" water sources rather than breaking them.
|
||||||
|
|
||||||
|
## CC: Tweaked 1.88.0 {#cc-1.88}
|
||||||
|
- Unlabelled computers and turtles now keep their ID when broken, meaning that unlabelled computers/items do not stack.
|
||||||
|
|
||||||
|
## ComputerCraft 1.80pr1 {#cc-1.80}
|
||||||
|
- Programs run via `shell.run` are now started in their own isolated environment. This means globals set by programs
|
||||||
|
will not be accessible outside of this program.
|
||||||
|
|
||||||
|
- Programs containing `/` are looked up in the current directory and are no longer looked up on the path. For instance,
|
||||||
|
you can no longer type `turtle/excavate` to run `/rom/programs/turtle/excavate.lua`.
|
||||||
|
|
||||||
|
[flattening]: https://minecraft.wiki.com/w/Java_Edition_1.13/Flattening
|
||||||
|
[legal_data_pack]: https://minecraft.gamepedia.com/Tutorials/Creating_a_data_pack#Legal_characters
|
||||||
|
[datapack-example]: https://github.com/cc-tweaked/datapack-example "An example datapack for CC: Tweaked"
|
||||||
@@ -13,22 +13,20 @@ include standard Lua functions.
|
|||||||
|
|
||||||
As it waits for a fixed amount of world ticks, `time` will automatically be
|
As it waits for a fixed amount of world ticks, `time` will automatically be
|
||||||
rounded up to the nearest multiple of 0.05 seconds. If you are using coroutines
|
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 API][`parallel`], it will only pause execution of the current
|
||||||
thread, not the whole program.
|
thread, not the whole program.
|
||||||
|
|
||||||
:::tip
|
> [!TIP]
|
||||||
Because sleep internally uses timers, it is a function that yields. This means
|
> Because sleep internally uses timers, it is a function that yields. This means
|
||||||
that you can use it to prevent "Too long without yielding" errors. However, as
|
> that you can use it to prevent "Too long without yielding" errors. However, as
|
||||||
the minimum sleep time is 0.05 seconds, it will slow your program down.
|
> the minimum sleep time is 0.05 seconds, it will slow your program down.
|
||||||
:::
|
|
||||||
|
|
||||||
:::caution
|
> [!WARNING]
|
||||||
Internally, this function queues and waits for a timer event (using
|
> 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 [timers][`os.startTimer`],
|
||||||
or the @{parallel|parallel API}.
|
> or the [parallel API][`parallel`].
|
||||||
:::
|
|
||||||
|
|
||||||
@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.
|
||||||
@@ -116,7 +114,7 @@ function read(replaceChar, history, completeFn, default) end
|
|||||||
|
|
||||||
--- Stores the current ComputerCraft and Minecraft versions.
|
--- Stores the current ComputerCraft and Minecraft versions.
|
||||||
--
|
--
|
||||||
-- Outside of Minecraft (for instance, in an emulator) @{_HOST} will contain the
|
-- Outside of Minecraft (for instance, in an emulator) [`_HOST`] will contain the
|
||||||
-- emulator's version instead.
|
-- emulator's version instead.
|
||||||
--
|
--
|
||||||
-- For example, `ComputerCraft 1.93.0 (Minecraft 1.15.2)`.
|
-- For example, `ComputerCraft 1.93.0 (Minecraft 1.15.2)`.
|
||||||
|
|||||||
@@ -15,27 +15,27 @@ variables and functions exported by it will by available through the use of
|
|||||||
@deprecated When possible it's best to avoid using this function. It pollutes
|
@deprecated When possible it's best to avoid using this function. It pollutes
|
||||||
the global table and can mask errors.
|
the global table and can mask errors.
|
||||||
|
|
||||||
@{require} should be used to load libraries instead.
|
[`require`] should be used to load libraries instead.
|
||||||
]]
|
]]
|
||||||
function loadAPI(path) end
|
function loadAPI(path) end
|
||||||
|
|
||||||
--- Unloads an API which was loaded by @{os.loadAPI}.
|
--- Unloads an API which was loaded by [`os.loadAPI`].
|
||||||
--
|
--
|
||||||
-- This effectively removes the specified table from `_G`.
|
-- This effectively removes the specified table from `_G`.
|
||||||
--
|
--
|
||||||
-- @tparam string name The name of the API to unload.
|
-- @tparam string name The name of the API to unload.
|
||||||
-- @since 1.2
|
-- @since 1.2
|
||||||
-- @deprecated See @{os.loadAPI} for why.
|
-- @deprecated See [`os.loadAPI`] for why.
|
||||||
function unloadAPI(name) end
|
function unloadAPI(name) end
|
||||||
|
|
||||||
--[[- Pause execution of the current thread and waits for any events matching
|
--[[- Pause execution of the current thread and waits for any events matching
|
||||||
`filter`.
|
`filter`.
|
||||||
|
|
||||||
This function @{coroutine.yield|yields} the current process and waits for it
|
This function [yields][`coroutine.yield`] the current process and waits for it
|
||||||
to be resumed with a vararg list where the first element matches `filter`.
|
to be resumed with a vararg list where the first element matches `filter`.
|
||||||
If no `filter` is supplied, this will match all events.
|
If no `filter` is supplied, this will match all events.
|
||||||
|
|
||||||
Unlike @{os.pullEventRaw}, it will stop the application upon a "terminate"
|
Unlike [`os.pullEventRaw`], it will stop the application upon a "terminate"
|
||||||
event, printing the error "Terminated".
|
event, printing the error "Terminated".
|
||||||
|
|
||||||
@tparam[opt] string filter Event to filter for.
|
@tparam[opt] string filter Event to filter for.
|
||||||
@@ -69,7 +69,7 @@ function pullEvent(filter) end
|
|||||||
--[[- Pause execution of the current thread and waits for events, including the
|
--[[- Pause execution of the current thread and waits for events, including the
|
||||||
`terminate` event.
|
`terminate` event.
|
||||||
|
|
||||||
This behaves almost the same as @{os.pullEvent}, except it allows you to handle
|
This behaves almost the same as [`os.pullEvent`], except it allows you to handle
|
||||||
the `terminate` event yourself - the program will not stop execution when
|
the `terminate` event yourself - the program will not stop execution when
|
||||||
<kbd>Ctrl+T</kbd> is pressed.
|
<kbd>Ctrl+T</kbd> is pressed.
|
||||||
|
|
||||||
@@ -89,7 +89,7 @@ the `terminate` event yourself - the program will not stop execution when
|
|||||||
]]
|
]]
|
||||||
function pullEventRaw(filter) end
|
function pullEventRaw(filter) end
|
||||||
|
|
||||||
--- Pauses execution for the specified number of seconds, alias of @{_G.sleep}.
|
--- Pauses execution for the specified number of seconds, alias of [`_G.sleep`].
|
||||||
--
|
--
|
||||||
-- @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.
|
||||||
@@ -109,12 +109,12 @@ arguments.
|
|||||||
|
|
||||||
This function does not resolve program names like the shell does. This means
|
This function does not resolve program names like the shell does. This means
|
||||||
that, for example, `os.run("edit")` will not work. As well as this, it does not
|
that, for example, `os.run("edit")` will not work. As well as this, it does not
|
||||||
provide access to the @{shell} API in the environment. For this behaviour, use
|
provide access to the [`shell`] API in the environment. For this behaviour, use
|
||||||
@{shell.run} instead.
|
[`shell.run`] instead.
|
||||||
|
|
||||||
If the program cannot be found, or failed to run, it will print the error and
|
If the program cannot be found, or failed to run, it will print the error and
|
||||||
return `false`. If you want to handle this more gracefully, use an alternative
|
return `false`. If you want to handle this more gracefully, use an alternative
|
||||||
such as @{loadfile}.
|
such as [`loadfile`].
|
||||||
|
|
||||||
@tparam table env The environment to run the program with.
|
@tparam table env The environment to run the program with.
|
||||||
@tparam string path The exact path of the program to run.
|
@tparam string path The exact path of the program to run.
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ kotlin.jvm.target.validation.mode=error
|
|||||||
|
|
||||||
# Mod properties
|
# Mod properties
|
||||||
isUnstable=false
|
isUnstable=false
|
||||||
modVersion=1.105.0
|
modVersion=1.108.4
|
||||||
|
|
||||||
# Minecraft properties: We want to configure this here so we can read it in settings.gradle
|
# Minecraft properties: We want to configure this here so we can read it in settings.gradle
|
||||||
mcVersion=1.20.1
|
mcVersion=1.20.1
|
||||||
|
|||||||
@@ -7,20 +7,21 @@
|
|||||||
# Minecraft
|
# Minecraft
|
||||||
# MC version is specified in gradle.properties, as we need that in settings.gradle.
|
# MC version is specified in gradle.properties, as we need that in settings.gradle.
|
||||||
# Remember to update corresponding versions in fabric.mod.json/mods.toml
|
# Remember to update corresponding versions in fabric.mod.json/mods.toml
|
||||||
fabric-api = "0.83.1+1.20.1"
|
fabric-api = "0.86.1+1.20.1"
|
||||||
fabric-loader = "0.14.21"
|
fabric-loader = "0.14.21"
|
||||||
forge = "47.0.1"
|
forge = "47.1.0"
|
||||||
forgeSpi = "6.0.0"
|
forgeSpi = "6.0.0"
|
||||||
mixin = "0.8.5"
|
mixin = "0.8.5"
|
||||||
parchment = "2023.03.12"
|
parchment = "2023.08.20"
|
||||||
parchmentMc = "1.19.3"
|
parchmentMc = "1.20.1"
|
||||||
|
|
||||||
# Normal dependencies
|
# Normal dependencies
|
||||||
asm = "9.3"
|
asm = "9.5"
|
||||||
autoService = "1.0.1"
|
autoService = "1.1.1"
|
||||||
checkerFramework = "3.32.0"
|
checkerFramework = "3.32.0"
|
||||||
cobalt = "0.7.0"
|
cobalt = "0.7.3"
|
||||||
cobalt-next = "0.7.1" # Not a real version, used to constrain the version we accept.
|
cobalt-next = "0.7.4" # Not a real version, used to constrain the version we accept.
|
||||||
|
commonsCli = "1.3.1"
|
||||||
fastutil = "8.5.9"
|
fastutil = "8.5.9"
|
||||||
guava = "31.1-jre"
|
guava = "31.1-jre"
|
||||||
jetbrainsAnnotations = "24.0.1"
|
jetbrainsAnnotations = "24.0.1"
|
||||||
@@ -29,50 +30,56 @@ jzlib = "1.1.3"
|
|||||||
kotlin = "1.8.10"
|
kotlin = "1.8.10"
|
||||||
kotlin-coroutines = "1.6.4"
|
kotlin-coroutines = "1.6.4"
|
||||||
netty = "4.1.82.Final"
|
netty = "4.1.82.Final"
|
||||||
nightConfig = "3.6.5"
|
nightConfig = "3.6.7"
|
||||||
slf4j = "1.7.36"
|
slf4j = "2.0.1"
|
||||||
|
|
||||||
# Minecraft mods
|
# Minecraft mods
|
||||||
iris = "1.5.2+1.19.4"
|
emi = "1.0.8+1.20.1"
|
||||||
jei = "13.1.0.11"
|
fabricPermissions = "0.3.20230723"
|
||||||
modmenu = "6.1.0-rc.1"
|
iris = "1.6.4+1.20"
|
||||||
|
jei = "15.2.0.22"
|
||||||
|
modmenu = "7.1.0"
|
||||||
|
moreRed = "4.0.0.4"
|
||||||
oculus = "1.2.5"
|
oculus = "1.2.5"
|
||||||
rei = "10.0.578"
|
rei = "12.0.626"
|
||||||
rubidium = "0.6.1"
|
rubidium = "0.6.1"
|
||||||
sodium = "mc1.19.4-0.4.10"
|
sodium = "mc1.20-0.4.10"
|
||||||
|
|
||||||
# Testing
|
# Testing
|
||||||
byteBuddy = "1.14.2"
|
|
||||||
hamcrest = "2.2"
|
hamcrest = "2.2"
|
||||||
jqwik = "1.7.2"
|
jqwik = "1.7.4"
|
||||||
junit = "5.9.2"
|
junit = "5.10.0"
|
||||||
|
|
||||||
# Build tools
|
# Build tools
|
||||||
cctJavadoc = "1.7.0"
|
cctJavadoc = "1.8.0"
|
||||||
checkstyle = "10.3.4"
|
checkstyle = "10.12.3"
|
||||||
curseForgeGradle = "1.0.14"
|
curseForgeGradle = "1.0.14"
|
||||||
errorProne-core = "2.18.0"
|
errorProne-core = "2.21.1"
|
||||||
errorProne-plugin = "3.0.1"
|
errorProne-plugin = "3.1.0"
|
||||||
fabric-loom = "1.1.10"
|
fabric-loom = "1.3.7"
|
||||||
forgeGradle = "5.1.+"
|
forgeGradle = "6.0.8"
|
||||||
githubRelease = "2.2.12"
|
githubRelease = "2.4.1"
|
||||||
ideaExt = "1.1.6"
|
ideaExt = "1.1.7"
|
||||||
illuaminate = "0.1.0-24-gdb28902"
|
illuaminate = "0.1.0-44-g9ee0055"
|
||||||
librarian = "1.+"
|
librarian = "1.+"
|
||||||
|
lwjgl = "3.3.1"
|
||||||
minotaur = "2.+"
|
minotaur = "2.+"
|
||||||
mixinGradle = "0.7.+"
|
mixinGradle = "0.7.+"
|
||||||
nullAway = "0.9.9"
|
nullAway = "0.9.9"
|
||||||
quiltflower = "1.8.0"
|
spotless = "6.21.0"
|
||||||
spotless = "6.17.0"
|
|
||||||
taskTree = "2.1.1"
|
taskTree = "2.1.1"
|
||||||
|
teavm = "0.9.0-SQUID.1"
|
||||||
vanillaGradle = "0.2.1-SNAPSHOT"
|
vanillaGradle = "0.2.1-SNAPSHOT"
|
||||||
|
vineflower = "1.11.0"
|
||||||
|
|
||||||
[libraries]
|
[libraries]
|
||||||
# Normal dependencies
|
# Normal dependencies
|
||||||
asm = { module = "org.ow2.asm:asm", version.ref = "asm" }
|
asm = { module = "org.ow2.asm:asm", version.ref = "asm" }
|
||||||
|
asm-commons = { module = "org.ow2.asm:asm-commons", version.ref = "asm" }
|
||||||
autoService = { module = "com.google.auto.service:auto-service", version.ref = "autoService" }
|
autoService = { module = "com.google.auto.service:auto-service", version.ref = "autoService" }
|
||||||
checkerFramework = { module = "org.checkerframework:checker-qual", version.ref = "checkerFramework" }
|
checkerFramework = { module = "org.checkerframework:checker-qual", version.ref = "checkerFramework" }
|
||||||
cobalt = { module = "org.squiddev:Cobalt", version.ref = "cobalt" }
|
cobalt = { module = "org.squiddev:Cobalt", version.ref = "cobalt" }
|
||||||
|
commonsCli = { module = "commons-cli:commons-cli", version.ref = "commonsCli" }
|
||||||
fastutil = { module = "it.unimi.dsi:fastutil", version.ref = "fastutil" }
|
fastutil = { module = "it.unimi.dsi:fastutil", version.ref = "fastutil" }
|
||||||
forgeSpi = { module = "net.minecraftforge:forgespi", version.ref = "forgeSpi" }
|
forgeSpi = { module = "net.minecraftforge:forgespi", version.ref = "forgeSpi" }
|
||||||
guava = { module = "com.google.guava:guava", version.ref = "guava" }
|
guava = { module = "com.google.guava:guava", version.ref = "guava" }
|
||||||
@@ -92,12 +99,16 @@ slf4j = { module = "org.slf4j:slf4j-api", version.ref = "slf4j" }
|
|||||||
# Minecraft mods
|
# Minecraft mods
|
||||||
fabric-api = { module = "net.fabricmc.fabric-api:fabric-api", version.ref = "fabric-api" }
|
fabric-api = { module = "net.fabricmc.fabric-api:fabric-api", version.ref = "fabric-api" }
|
||||||
fabric-loader = { module = "net.fabricmc:fabric-loader", version.ref = "fabric-loader" }
|
fabric-loader = { module = "net.fabricmc:fabric-loader", version.ref = "fabric-loader" }
|
||||||
|
fabric-junit = { module = "net.fabricmc:fabric-loader-junit", version.ref = "fabric-loader" }
|
||||||
|
fabricPermissions = { module = "me.lucko:fabric-permissions-api", version.ref = "fabricPermissions" }
|
||||||
|
emi = { module = "dev.emi:emi-xplat-mojmap", version.ref = "emi" }
|
||||||
iris = { module = "maven.modrinth:iris", version.ref = "iris" }
|
iris = { module = "maven.modrinth:iris", version.ref = "iris" }
|
||||||
jei-api = { module = "mezz.jei:jei-1.19.4-common-api", version.ref = "jei" }
|
jei-api = { module = "mezz.jei:jei-1.20.1-common-api", version.ref = "jei" }
|
||||||
jei-fabric = { module = "mezz.jei:jei-1.19.4-fabric", version.ref = "jei" }
|
jei-fabric = { module = "mezz.jei:jei-1.20.1-fabric", version.ref = "jei" }
|
||||||
jei-forge = { module = "mezz.jei:jei-1.19.4-forge", version.ref = "jei" }
|
jei-forge = { module = "mezz.jei:jei-1.20.1-forge", version.ref = "jei" }
|
||||||
mixin = { module = "org.spongepowered:mixin", version.ref = "mixin" }
|
mixin = { module = "org.spongepowered:mixin", version.ref = "mixin" }
|
||||||
modmenu = { module = "com.terraformersmc:modmenu", version.ref = "modmenu" }
|
modmenu = { module = "com.terraformersmc:modmenu", version.ref = "modmenu" }
|
||||||
|
moreRed = { module = "commoble.morered:morered-1.20.1", version.ref = "moreRed" }
|
||||||
oculus = { module = "maven.modrinth:oculus", version.ref = "oculus" }
|
oculus = { module = "maven.modrinth:oculus", version.ref = "oculus" }
|
||||||
rei-api = { module = "me.shedaniel:RoughlyEnoughItems-api", version.ref = "rei" }
|
rei-api = { module = "me.shedaniel:RoughlyEnoughItems-api", version.ref = "rei" }
|
||||||
rei-builtin = { module = "me.shedaniel:RoughlyEnoughItems-default-plugin", version.ref = "rei" }
|
rei-builtin = { module = "me.shedaniel:RoughlyEnoughItems-default-plugin", version.ref = "rei" }
|
||||||
@@ -106,8 +117,6 @@ rubidium = { module = "maven.modrinth:rubidium", version.ref = "rubidium" }
|
|||||||
sodium = { module = "maven.modrinth:sodium", version.ref = "sodium" }
|
sodium = { module = "maven.modrinth:sodium", version.ref = "sodium" }
|
||||||
|
|
||||||
# Testing
|
# Testing
|
||||||
byteBuddyAgent = { module = "net.bytebuddy:byte-buddy-agent", version.ref = "byteBuddy" }
|
|
||||||
byteBuddy = { module = "net.bytebuddy:byte-buddy", version.ref = "byteBuddy" }
|
|
||||||
hamcrest = { module = "org.hamcrest:hamcrest", version.ref = "hamcrest" }
|
hamcrest = { module = "org.hamcrest:hamcrest", version.ref = "hamcrest" }
|
||||||
jqwik-api = { module = "net.jqwik:jqwik-api", version.ref = "jqwik" }
|
jqwik-api = { module = "net.jqwik:jqwik-api", version.ref = "jqwik" }
|
||||||
jqwik-engine = { module = "net.jqwik:jqwik-engine", version.ref = "jqwik" }
|
jqwik-engine = { module = "net.jqwik:jqwik-engine", version.ref = "jqwik" }
|
||||||
@@ -116,6 +125,12 @@ junit-jupiter-engine = { module = "org.junit.jupiter:junit-jupiter-engine", vers
|
|||||||
junit-jupiter-params = { module = "org.junit.jupiter:junit-jupiter-params", version.ref = "junit" }
|
junit-jupiter-params = { module = "org.junit.jupiter:junit-jupiter-params", version.ref = "junit" }
|
||||||
slf4j-simple = { module = "org.slf4j:slf4j-simple", version.ref = "slf4j" }
|
slf4j-simple = { module = "org.slf4j:slf4j-simple", version.ref = "slf4j" }
|
||||||
|
|
||||||
|
# LWJGL
|
||||||
|
lwjgl-bom = { module = "org.lwjgl:lwjgl-bom", version.ref = "lwjgl" }
|
||||||
|
lwjgl-core = { module = "org.lwjgl:lwjgl" }
|
||||||
|
lwjgl-opengl = { module = "org.lwjgl:lwjgl-opengl" }
|
||||||
|
lwjgl-glfw = { module = "org.lwjgl:lwjgl-glfw" }
|
||||||
|
|
||||||
# Build tools
|
# Build tools
|
||||||
cctJavadoc = { module = "cc.tweaked:cct-javadoc", version.ref = "cctJavadoc" }
|
cctJavadoc = { module = "cc.tweaked:cct-javadoc", version.ref = "cctJavadoc" }
|
||||||
checkstyle = { module = "com.puppycrawl.tools:checkstyle", version.ref = "checkstyle" }
|
checkstyle = { module = "com.puppycrawl.tools:checkstyle", version.ref = "checkstyle" }
|
||||||
@@ -127,34 +142,47 @@ errorProne-plugin = { module = "net.ltgt.gradle:gradle-errorprone-plugin", versi
|
|||||||
errorProne-testHelpers = { module = "com.google.errorprone:error_prone_test_helpers", version.ref = "errorProne-core" }
|
errorProne-testHelpers = { module = "com.google.errorprone:error_prone_test_helpers", version.ref = "errorProne-core" }
|
||||||
fabric-loom = { module = "net.fabricmc:fabric-loom", version.ref = "fabric-loom" }
|
fabric-loom = { module = "net.fabricmc:fabric-loom", version.ref = "fabric-loom" }
|
||||||
forgeGradle = { module = "net.minecraftforge.gradle:ForgeGradle", version.ref = "forgeGradle" }
|
forgeGradle = { module = "net.minecraftforge.gradle:ForgeGradle", version.ref = "forgeGradle" }
|
||||||
|
ideaExt = { module = "gradle.plugin.org.jetbrains.gradle.plugin.idea-ext:gradle-idea-ext", version.ref = "ideaExt" }
|
||||||
kotlin-plugin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" }
|
kotlin-plugin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" }
|
||||||
librarian = { module = "org.parchmentmc:librarian", version.ref = "librarian" }
|
librarian = { module = "org.parchmentmc:librarian", version.ref = "librarian" }
|
||||||
minotaur = { module = "com.modrinth.minotaur:Minotaur", version.ref = "minotaur" }
|
minotaur = { module = "com.modrinth.minotaur:Minotaur", version.ref = "minotaur" }
|
||||||
nullAway = { module = "com.uber.nullaway:nullaway", version.ref = "nullAway" }
|
nullAway = { module = "com.uber.nullaway:nullaway", version.ref = "nullAway" }
|
||||||
quiltflower = { module = "io.github.juuxel:loom-quiltflower", version.ref = "quiltflower" }
|
|
||||||
spotless = { module = "com.diffplug.spotless:spotless-plugin-gradle", version.ref = "spotless" }
|
spotless = { module = "com.diffplug.spotless:spotless-plugin-gradle", version.ref = "spotless" }
|
||||||
|
teavm-classlib = { module = "org.teavm:teavm-classlib", version.ref = "teavm" }
|
||||||
|
teavm-jso = { module = "org.teavm:teavm-jso", version.ref = "teavm" }
|
||||||
|
teavm-jso-apis = { module = "org.teavm:teavm-jso-apis", version.ref = "teavm" }
|
||||||
|
teavm-jso-impl = { module = "org.teavm:teavm-jso-impl", version.ref = "teavm" }
|
||||||
|
teavm-metaprogramming-api = { module = "org.teavm:teavm-metaprogramming-api", version.ref = "teavm" }
|
||||||
|
teavm-metaprogramming-impl = { module = "org.teavm:teavm-metaprogramming-impl", version.ref = "teavm" }
|
||||||
|
teavm-platform = { module = "org.teavm:teavm-platform", version.ref = "teavm" }
|
||||||
|
teavm-tooling = { module = "org.teavm:teavm-tooling", version.ref = "teavm" }
|
||||||
vanillaGradle = { module = "org.spongepowered:vanillagradle", version.ref = "vanillaGradle" }
|
vanillaGradle = { module = "org.spongepowered:vanillagradle", version.ref = "vanillaGradle" }
|
||||||
|
vineflower = { module = "io.github.juuxel:loom-vineflower", version.ref = "vineflower" }
|
||||||
|
|
||||||
[plugins]
|
[plugins]
|
||||||
forgeGradle = { id = "net.minecraftforge.gradle", version.ref = "forgeGradle" }
|
forgeGradle = { id = "net.minecraftforge.gradle", version.ref = "forgeGradle" }
|
||||||
githubRelease = { id = "com.github.breadmoirai.github-release", version.ref = "githubRelease" }
|
githubRelease = { id = "com.github.breadmoirai.github-release", version.ref = "githubRelease" }
|
||||||
ideaExt = { id = "org.jetbrains.gradle.plugin.idea-ext", version.ref = "ideaExt" }
|
|
||||||
kotlin = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
|
kotlin = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
|
||||||
librarian = { id = "org.parchmentmc.librarian.forgegradle", version.ref = "librarian" }
|
librarian = { id = "org.parchmentmc.librarian.forgegradle", version.ref = "librarian" }
|
||||||
mixinGradle = { id = "org.spongepowered.mixin", version.ref = "mixinGradle" }
|
mixinGradle = { id = "org.spongepowered.mixin", version.ref = "mixinGradle" }
|
||||||
taskTree = { id = "com.dorongold.task-tree", version.ref = "taskTree" }
|
taskTree = { id = "com.dorongold.task-tree", version.ref = "taskTree" }
|
||||||
|
|
||||||
[bundles]
|
[bundles]
|
||||||
|
annotations = ["jsr305", "checkerFramework", "jetbrainsAnnotations"]
|
||||||
kotlin = ["kotlin-stdlib", "kotlin-coroutines"]
|
kotlin = ["kotlin-stdlib", "kotlin-coroutines"]
|
||||||
|
|
||||||
# Minecraft
|
# Minecraft
|
||||||
externalMods-common = ["jei-api", "nightConfig-core", "nightConfig-toml"]
|
externalMods-common = ["jei-api", "nightConfig-core", "nightConfig-toml"]
|
||||||
externalMods-forge-compile = ["oculus", "jei-api"]
|
externalMods-forge-compile = ["moreRed", "oculus", "jei-api"]
|
||||||
externalMods-forge-runtime = []
|
externalMods-forge-runtime = ["jei-forge"]
|
||||||
externalMods-fabric = ["nightConfig-core", "nightConfig-toml"]
|
externalMods-fabric = ["nightConfig-core", "nightConfig-toml"]
|
||||||
externalMods-fabric-compile = ["iris", "jei-api", "rei-api", "rei-builtin"]
|
externalMods-fabric-compile = ["fabricPermissions", "iris", "jei-api", "rei-api", "rei-builtin"]
|
||||||
externalMods-fabric-runtime = []
|
externalMods-fabric-runtime = ["jei-fabric", "modmenu"]
|
||||||
|
|
||||||
# Testing
|
# Testing
|
||||||
test = ["junit-jupiter-api", "junit-jupiter-params", "hamcrest", "jqwik-api"]
|
test = ["junit-jupiter-api", "junit-jupiter-params", "hamcrest", "jqwik-api"]
|
||||||
testRuntime = ["junit-jupiter-engine", "jqwik-engine"]
|
testRuntime = ["junit-jupiter-engine", "jqwik-engine"]
|
||||||
|
|
||||||
|
# Build tools
|
||||||
|
teavm-api = [ "teavm-jso", "teavm-jso-apis", "teavm-platform", "teavm-classlib", "teavm-metaprogramming-api" ]
|
||||||
|
teavm-tooling = [ "teavm-tooling", "teavm-metaprogramming-impl", "teavm-jso-impl" ]
|
||||||
|
|||||||
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
3
gradle/wrapper/gradle-wrapper.properties
vendored
3
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,5 +1,6 @@
|
|||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-bin.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-bin.zip
|
||||||
|
networkTimeout=10000
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
|
|||||||
19
gradlew
vendored
19
gradlew
vendored
@@ -55,7 +55,7 @@
|
|||||||
# Darwin, MinGW, and NonStop.
|
# Darwin, MinGW, and NonStop.
|
||||||
#
|
#
|
||||||
# (3) This script is generated from the Groovy template
|
# (3) This script is generated from the Groovy template
|
||||||
# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||||
# within the Gradle project.
|
# within the Gradle project.
|
||||||
#
|
#
|
||||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||||
@@ -80,13 +80,10 @@ do
|
|||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
|
|
||||||
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
|
# This is normally unused
|
||||||
|
# shellcheck disable=SC2034
|
||||||
APP_NAME="Gradle"
|
|
||||||
APP_BASE_NAME=${0##*/}
|
APP_BASE_NAME=${0##*/}
|
||||||
|
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
|
||||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
|
||||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
|
||||||
|
|
||||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||||
MAX_FD=maximum
|
MAX_FD=maximum
|
||||||
@@ -143,12 +140,16 @@ fi
|
|||||||
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||||
case $MAX_FD in #(
|
case $MAX_FD in #(
|
||||||
max*)
|
max*)
|
||||||
|
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
|
||||||
|
# shellcheck disable=SC3045
|
||||||
MAX_FD=$( ulimit -H -n ) ||
|
MAX_FD=$( ulimit -H -n ) ||
|
||||||
warn "Could not query maximum file descriptor limit"
|
warn "Could not query maximum file descriptor limit"
|
||||||
esac
|
esac
|
||||||
case $MAX_FD in #(
|
case $MAX_FD in #(
|
||||||
'' | soft) :;; #(
|
'' | soft) :;; #(
|
||||||
*)
|
*)
|
||||||
|
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
|
||||||
|
# shellcheck disable=SC3045
|
||||||
ulimit -n "$MAX_FD" ||
|
ulimit -n "$MAX_FD" ||
|
||||||
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||||
esac
|
esac
|
||||||
@@ -193,6 +194,10 @@ if "$cygwin" || "$msys" ; then
|
|||||||
done
|
done
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
|
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||||
|
|
||||||
# Collect all arguments for the java command;
|
# Collect all arguments for the java command;
|
||||||
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
|
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
|
||||||
# shell script including quotes and variable substitutions, so put them in
|
# shell script including quotes and variable substitutions, so put them in
|
||||||
|
|||||||
1
gradlew.bat
vendored
1
gradlew.bat
vendored
@@ -26,6 +26,7 @@ if "%OS%"=="Windows_NT" setlocal
|
|||||||
|
|
||||||
set DIRNAME=%~dp0
|
set DIRNAME=%~dp0
|
||||||
if "%DIRNAME%"=="" set DIRNAME=.
|
if "%DIRNAME%"=="" set DIRNAME=.
|
||||||
|
@rem This is normally unused
|
||||||
set APP_BASE_NAME=%~n0
|
set APP_BASE_NAME=%~n0
|
||||||
set APP_HOME=%DIRNAME%
|
set APP_HOME=%DIRNAME%
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
; SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
|
; SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
|
||||||
;
|
;
|
||||||
; SPDX-License-Identifier: LicenseRef-CCPL
|
; SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
(sources
|
(sources
|
||||||
/doc/
|
/doc/
|
||||||
@@ -10,8 +10,7 @@
|
|||||||
/projects/core/src/main/resources/data/computercraft/lua/bios.lua
|
/projects/core/src/main/resources/data/computercraft/lua/bios.lua
|
||||||
/projects/core/src/main/resources/data/computercraft/lua/rom/
|
/projects/core/src/main/resources/data/computercraft/lua/rom/
|
||||||
/projects/core/src/test/resources/test-rom
|
/projects/core/src/test/resources/test-rom
|
||||||
/projects/web/src/mount)
|
/projects/web/src/frontend/mount)
|
||||||
|
|
||||||
|
|
||||||
(doc
|
(doc
|
||||||
; Also defined in projects/web/build.gradle.kts
|
; Also defined in projects/web/build.gradle.kts
|
||||||
@@ -24,7 +23,7 @@
|
|||||||
(url https://tweaked.cc/)
|
(url https://tweaked.cc/)
|
||||||
(source-link https://github.com/cc-tweaked/CC-Tweaked/blob/${commit}/${path}#L${line})
|
(source-link https://github.com/cc-tweaked/CC-Tweaked/blob/${commit}/${path}#L${line})
|
||||||
|
|
||||||
(styles /projects/web/src/styles.css)
|
(styles /projects/web/build/rollup/index.css)
|
||||||
(scripts /projects/web/build/rollup/index.js)
|
(scripts /projects/web/build/rollup/index.js)
|
||||||
(head doc/head.html))
|
(head doc/head.html))
|
||||||
|
|
||||||
@@ -50,6 +49,8 @@
|
|||||||
(at /
|
(at /
|
||||||
(linters
|
(linters
|
||||||
syntax:string-index
|
syntax:string-index
|
||||||
|
doc:docusaurus-admonition
|
||||||
|
doc:ldoc-reference
|
||||||
|
|
||||||
;; It'd be nice to avoid this, but right now there's a lot of instances of
|
;; It'd be nice to avoid this, but right now there's a lot of instances of
|
||||||
;; it.
|
;; it.
|
||||||
@@ -76,29 +77,24 @@
|
|||||||
(globals
|
(globals
|
||||||
:max
|
:max
|
||||||
_CC_DEFAULT_SETTINGS
|
_CC_DEFAULT_SETTINGS
|
||||||
_CC_DISABLE_LUA51_FEATURES
|
|
||||||
_HOST
|
_HOST
|
||||||
;; Ideally we'd pick these up from bios.lua, but illuaminate currently
|
;; Ideally we'd pick these up from bios.lua, but illuaminate currently
|
||||||
;; isn't smart enough.
|
;; isn't smart enough.
|
||||||
sleep write printError read rs)))
|
sleep write printError read rs)))
|
||||||
|
|
||||||
;; We disable the unused global linter in bios.lua and the APIs. In the future
|
;; We disable the unused global linter in bios.lua, APIs and our documentation
|
||||||
;; hopefully we'll get illuaminate to handle this.
|
;; stubs docs. In the future hopefully we'll get illuaminate to handle this.
|
||||||
(at
|
(at
|
||||||
(/projects/core/src/main/resources/data/computercraft/lua/bios.lua
|
(/doc/stub/
|
||||||
/projects/core/src/main/resources/data/computercraft/lua/rom/apis/)
|
/projects/core/src/main/resources/data/computercraft/lua/bios.lua
|
||||||
(linters -var:unused-global)
|
/projects/core/src/main/resources/data/computercraft/lua/rom/apis/
|
||||||
(lint (allow-toplevel-global true)))
|
/projects/forge/build/docs/luaJavadoc/)
|
||||||
|
|
||||||
;; Silence some variable warnings in documentation stubs.
|
|
||||||
(at (/doc/stub/ /projects/forge/build/docs/luaJavadoc/)
|
|
||||||
(linters -var:unused-global)
|
(linters -var:unused-global)
|
||||||
(lint (allow-toplevel-global true)))
|
(lint (allow-toplevel-global true)))
|
||||||
|
|
||||||
;; Suppress warnings for currently undocumented modules.
|
;; Suppress warnings for currently undocumented modules.
|
||||||
(at
|
(at
|
||||||
(; Lua APIs
|
(; Lua APIs
|
||||||
/projects/core/src/main/resources/data/computercraft/lua/rom/apis/io.lua
|
|
||||||
/projects/core/src/main/resources/data/computercraft/lua/rom/apis/window.lua)
|
/projects/core/src/main/resources/data/computercraft/lua/rom/apis/window.lua)
|
||||||
|
|
||||||
(linters -doc:undocumented -doc:undocumented-arg -doc:undocumented-return))
|
(linters -doc:undocumented -doc:undocumented-arg -doc:undocumented-return))
|
||||||
@@ -118,4 +114,4 @@
|
|||||||
:max sleep write
|
:max sleep write
|
||||||
cct_test describe expect howlci fail it pending stub before_each)))
|
cct_test describe expect howlci fail it pending stub before_each)))
|
||||||
|
|
||||||
(at /projects/web/src/mount/expr_template.lua (lint (globals :max __expr__)))
|
(at /projects/web/src/frontend/mount/expr_template.lua (lint (globals :max __expr__)))
|
||||||
|
|||||||
3351
package-lock.json
generated
3351
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
26
package.json
26
package.json
@@ -6,24 +6,24 @@
|
|||||||
"license": "BSD-3-Clause",
|
"license": "BSD-3-Clause",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@squid-dev/cc-web-term": "^2.0.0",
|
||||||
"preact": "^10.5.5",
|
"preact": "^10.5.5",
|
||||||
|
"setimmediate": "^1.0.5",
|
||||||
"tslib": "^2.0.3"
|
"tslib": "^2.0.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@rollup/plugin-terser": "^0.4.0",
|
"@rollup/plugin-node-resolve": "^15.2.1",
|
||||||
"@rollup/plugin-typescript": "^11.0.0",
|
"@rollup/plugin-typescript": "^11.0.0",
|
||||||
"@rollup/plugin-url": "^8.0.1",
|
"@rollup/plugin-url": "^8.0.1",
|
||||||
"@types/glob": "^8.1.0",
|
"@swc/core": "^1.3.92",
|
||||||
"@types/react-dom": "^18.0.5",
|
"@types/node": "^20.8.3",
|
||||||
"glob": "^9.3.0",
|
"lightningcss": "^1.22.0",
|
||||||
"react-dom": "^18.1.0",
|
"preact-render-to-string": "^6.2.1",
|
||||||
"react": "^18.1.0",
|
"rehype": "^13.0.0",
|
||||||
"rehype-highlight": "^6.0.0",
|
"rehype-highlight": "^7.0.0",
|
||||||
"rehype-react": "^7.1.1",
|
"rehype-react": "^8.0.0",
|
||||||
"rehype": "^12.0.1",
|
"rollup": "^4.0.0",
|
||||||
"requirejs": "^2.3.6",
|
"tsx": "^3.12.10",
|
||||||
"rollup": "^3.19.1",
|
"typescript": "^5.2.2"
|
||||||
"ts-node": "^10.8.0",
|
|
||||||
"typescript": "^4.0.5"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -62,6 +62,9 @@ mentioning:
|
|||||||
- `lints`: This defines an [ErrorProne] plugin which adds a couple of compile-time checks to our code. This is what
|
- `lints`: This defines an [ErrorProne] plugin which adds a couple of compile-time checks to our code. This is what
|
||||||
enforces that no client-specific code is used inside the `main` source set (and a couple of other things!).
|
enforces that no client-specific code is used inside the `main` source set (and a couple of other things!).
|
||||||
|
|
||||||
|
- `standalone`: This contains a standalone UI for computers, allowing debugging and development of CraftOS without
|
||||||
|
launching Minecraft.
|
||||||
|
|
||||||
- `web`: This contains the additional tooling for building [the documentation website][tweaked.cc], such as support for
|
- `web`: This contains the additional tooling for building [the documentation website][tweaked.cc], such as support for
|
||||||
rendering recipes
|
rendering recipes
|
||||||
|
|
||||||
|
|||||||
@@ -11,9 +11,13 @@ import dan200.computercraft.api.turtle.ITurtleUpgrade;
|
|||||||
import dan200.computercraft.api.turtle.TurtleSide;
|
import dan200.computercraft.api.turtle.TurtleSide;
|
||||||
import dan200.computercraft.api.turtle.TurtleUpgradeSerialiser;
|
import dan200.computercraft.api.turtle.TurtleUpgradeSerialiser;
|
||||||
import net.minecraft.client.resources.model.ModelResourceLocation;
|
import net.minecraft.client.resources.model.ModelResourceLocation;
|
||||||
|
import net.minecraft.client.resources.model.UnbakedModel;
|
||||||
|
import net.minecraft.nbt.CompoundTag;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides models for a {@link ITurtleUpgrade}.
|
* Provides models for a {@link ITurtleUpgrade}.
|
||||||
@@ -28,15 +32,45 @@ public interface TurtleUpgradeModeller<T extends ITurtleUpgrade> {
|
|||||||
* When the current turtle is {@literal null}, this function should be constant for a given upgrade and side.
|
* When the current turtle is {@literal null}, this function should be constant for a given upgrade and side.
|
||||||
*
|
*
|
||||||
* @param upgrade The upgrade that you're getting the model for.
|
* @param upgrade The upgrade that you're getting the model for.
|
||||||
* @param turtle Access to the turtle that the upgrade resides on. This will be null when getting item models!
|
* @param turtle Access to the turtle that the upgrade resides on. This will be null when getting item models, unless
|
||||||
|
* {@link #getModel(ITurtleUpgrade, CompoundTag, TurtleSide)} is overriden.
|
||||||
* @param side Which side of the turtle (left or right) the upgrade resides on.
|
* @param side Which side of the turtle (left or right) the upgrade resides on.
|
||||||
* @return The model that you wish to be used to render your upgrade.
|
* @return The model that you wish to be used to render your upgrade.
|
||||||
*/
|
*/
|
||||||
TransformedModel getModel(T upgrade, @Nullable ITurtleAccess turtle, TurtleSide side);
|
TransformedModel getModel(T upgrade, @Nullable ITurtleAccess turtle, TurtleSide side);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A basic {@link TurtleUpgradeModeller} which renders using the upgrade's {@linkplain ITurtleUpgrade#getCraftingItem()
|
* Obtain the model to be used when rendering a turtle peripheral.
|
||||||
* crafting item}.
|
* <p>
|
||||||
|
* This is used when rendering the turtle's item model, and so no {@link ITurtleAccess} is available.
|
||||||
|
*
|
||||||
|
* @param upgrade The upgrade that you're getting the model for.
|
||||||
|
* @param data Upgrade data instance for current turtle side.
|
||||||
|
* @param side Which side of the turtle (left or right) the upgrade resides on.
|
||||||
|
* @return The model that you wish to be used to render your upgrade.
|
||||||
|
*/
|
||||||
|
default TransformedModel getModel(T upgrade, CompoundTag data, TurtleSide side) {
|
||||||
|
return getModel(upgrade, (ITurtleAccess) null, side);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a list of models that this turtle modeller depends on.
|
||||||
|
* <p>
|
||||||
|
* Models included in this list will be loaded and baked alongside item and block models, and so may be referenced
|
||||||
|
* by {@link TransformedModel#of(ResourceLocation)}. You do not need to override this method if you will load models
|
||||||
|
* by other means.
|
||||||
|
*
|
||||||
|
* @return A list of models that this modeller depends on.
|
||||||
|
* @see UnbakedModel#getDependencies()
|
||||||
|
*/
|
||||||
|
default Collection<ResourceLocation> getDependencies() {
|
||||||
|
return List.of();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A basic {@link TurtleUpgradeModeller} which renders using the upgrade's {@linkplain ITurtleUpgrade#getUpgradeItem(CompoundTag)}
|
||||||
|
* upgrade item}.
|
||||||
* <p>
|
* <p>
|
||||||
* This uses appropriate transformations for "flat" items, namely those extending the {@literal minecraft:item/generated}
|
* This uses appropriate transformations for "flat" items, namely those extending the {@literal minecraft:item/generated}
|
||||||
* model type. It will not appear correct for 3D models with additional depth, such as blocks.
|
* model type. It will not appear correct for 3D models with additional depth, such as blocks.
|
||||||
@@ -46,7 +80,7 @@ public interface TurtleUpgradeModeller<T extends ITurtleUpgrade> {
|
|||||||
*/
|
*/
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
static <T extends ITurtleUpgrade> TurtleUpgradeModeller<T> flatItem() {
|
static <T extends ITurtleUpgrade> TurtleUpgradeModeller<T> flatItem() {
|
||||||
return (TurtleUpgradeModeller<T>) TurtleUpgradeModellers.FLAT_ITEM;
|
return (TurtleUpgradeModeller<T>) TurtleUpgradeModellers.UPGRADE_ITEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -58,7 +92,8 @@ public interface TurtleUpgradeModeller<T extends ITurtleUpgrade> {
|
|||||||
* @return The constructed modeller.
|
* @return The constructed modeller.
|
||||||
*/
|
*/
|
||||||
static <T extends ITurtleUpgrade> TurtleUpgradeModeller<T> sided(ModelResourceLocation left, ModelResourceLocation right) {
|
static <T extends ITurtleUpgrade> TurtleUpgradeModeller<T> sided(ModelResourceLocation left, ModelResourceLocation right) {
|
||||||
return (upgrade, turtle, side) -> TransformedModel.of(side == TurtleSide.LEFT ? left : right);
|
// TODO(1.21.0): Remove this.
|
||||||
|
return sided((ResourceLocation) left, right);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -70,6 +105,16 @@ public interface TurtleUpgradeModeller<T extends ITurtleUpgrade> {
|
|||||||
* @return The constructed modeller.
|
* @return The constructed modeller.
|
||||||
*/
|
*/
|
||||||
static <T extends ITurtleUpgrade> TurtleUpgradeModeller<T> sided(ResourceLocation left, ResourceLocation right) {
|
static <T extends ITurtleUpgrade> TurtleUpgradeModeller<T> sided(ResourceLocation left, ResourceLocation right) {
|
||||||
return (upgrade, turtle, side) -> TransformedModel.of(side == TurtleSide.LEFT ? left : right);
|
return new TurtleUpgradeModeller<>() {
|
||||||
|
@Override
|
||||||
|
public TransformedModel getModel(T upgrade, @Nullable ITurtleAccess turtle, TurtleSide side) {
|
||||||
|
return TransformedModel.of(side == TurtleSide.LEFT ? left : right);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<ResourceLocation> getDependencies() {
|
||||||
|
return List.of(left, right);
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,13 +6,20 @@ package dan200.computercraft.api.client.turtle;
|
|||||||
|
|
||||||
import com.mojang.math.Transformation;
|
import com.mojang.math.Transformation;
|
||||||
import dan200.computercraft.api.client.TransformedModel;
|
import dan200.computercraft.api.client.TransformedModel;
|
||||||
|
import dan200.computercraft.api.turtle.ITurtleAccess;
|
||||||
import dan200.computercraft.api.turtle.ITurtleUpgrade;
|
import dan200.computercraft.api.turtle.ITurtleUpgrade;
|
||||||
import dan200.computercraft.api.turtle.TurtleSide;
|
import dan200.computercraft.api.turtle.TurtleSide;
|
||||||
|
import dan200.computercraft.impl.client.ClientPlatformHelper;
|
||||||
|
import net.minecraft.client.Minecraft;
|
||||||
|
import net.minecraft.nbt.CompoundTag;
|
||||||
|
import net.minecraft.world.item.ItemStack;
|
||||||
import org.joml.Matrix4f;
|
import org.joml.Matrix4f;
|
||||||
|
|
||||||
class TurtleUpgradeModellers {
|
import javax.annotation.Nullable;
|
||||||
private static final Transformation leftTransform = getMatrixFor(-0.40625f);
|
|
||||||
private static final Transformation rightTransform = getMatrixFor(0.40625f);
|
final class TurtleUpgradeModellers {
|
||||||
|
private static final Transformation leftTransform = getMatrixFor(-0.4065f);
|
||||||
|
private static final Transformation rightTransform = getMatrixFor(0.4065f);
|
||||||
|
|
||||||
private static Transformation getMatrixFor(float offset) {
|
private static Transformation getMatrixFor(float offset) {
|
||||||
var matrix = new Matrix4f();
|
var matrix = new Matrix4f();
|
||||||
@@ -26,6 +33,23 @@ class TurtleUpgradeModellers {
|
|||||||
return new Transformation(matrix);
|
return new Transformation(matrix);
|
||||||
}
|
}
|
||||||
|
|
||||||
static final TurtleUpgradeModeller<ITurtleUpgrade> FLAT_ITEM = (upgrade, turtle, side) ->
|
static final TurtleUpgradeModeller<ITurtleUpgrade> UPGRADE_ITEM = new UpgradeItemModeller();
|
||||||
TransformedModel.of(upgrade.getCraftingItem(), side == TurtleSide.LEFT ? leftTransform : rightTransform);
|
|
||||||
|
private static final class UpgradeItemModeller implements TurtleUpgradeModeller<ITurtleUpgrade> {
|
||||||
|
@Override
|
||||||
|
public TransformedModel getModel(ITurtleUpgrade upgrade, @Nullable ITurtleAccess turtle, TurtleSide side) {
|
||||||
|
return getModel(turtle == null ? upgrade.getCraftingItem() : upgrade.getUpgradeItem(turtle.getUpgradeNBTData(side)), side);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TransformedModel getModel(ITurtleUpgrade upgrade, CompoundTag data, TurtleSide side) {
|
||||||
|
return getModel(upgrade.getUpgradeItem(data), side);
|
||||||
|
}
|
||||||
|
|
||||||
|
private TransformedModel getModel(ItemStack stack, TurtleSide side) {
|
||||||
|
var model = Minecraft.getInstance().getItemRenderer().getItemModelShaper().getItemModel(stack);
|
||||||
|
if (stack.hasFoil()) model = ClientPlatformHelper.get().createdFoiledModel(model);
|
||||||
|
return new TransformedModel(model, side == TurtleSide.LEFT ? leftTransform : rightTransform);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
package dan200.computercraft.impl.client;
|
package dan200.computercraft.impl.client;
|
||||||
|
|
||||||
import dan200.computercraft.impl.Services;
|
import dan200.computercraft.impl.Services;
|
||||||
|
import net.minecraft.client.renderer.RenderType;
|
||||||
import net.minecraft.client.resources.model.BakedModel;
|
import net.minecraft.client.resources.model.BakedModel;
|
||||||
import net.minecraft.client.resources.model.ModelManager;
|
import net.minecraft.client.resources.model.ModelManager;
|
||||||
import net.minecraft.client.resources.model.ModelResourceLocation;
|
import net.minecraft.client.resources.model.ModelResourceLocation;
|
||||||
@@ -24,6 +25,15 @@ public interface ClientPlatformHelper {
|
|||||||
*/
|
*/
|
||||||
BakedModel getModel(ModelManager manager, ResourceLocation location);
|
BakedModel getModel(ModelManager manager, ResourceLocation location);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wrap this model in a version which renders a foil/enchantment glint.
|
||||||
|
*
|
||||||
|
* @param model The model to wrap.
|
||||||
|
* @return The wrapped model.
|
||||||
|
* @see RenderType#glint()
|
||||||
|
*/
|
||||||
|
BakedModel createdFoiledModel(BakedModel model);
|
||||||
|
|
||||||
static ClientPlatformHelper get() {
|
static ClientPlatformHelper get() {
|
||||||
var instance = Instance.INSTANCE;
|
var instance = Instance.INSTANCE;
|
||||||
return instance == null ? Services.raise(ClientPlatformHelper.class, Instance.ERROR) : instance;
|
return instance == null ? Services.raise(ClientPlatformHelper.class, Instance.ERROR) : instance;
|
||||||
|
|||||||
@@ -46,6 +46,14 @@ public class ComputerCraftTags {
|
|||||||
public static final TagKey<Block> WIRED_MODEM = make("wired_modem");
|
public static final TagKey<Block> WIRED_MODEM = make("wired_modem");
|
||||||
public static final TagKey<Block> MONITOR = make("monitor");
|
public static final TagKey<Block> MONITOR = make("monitor");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Blocks which should be ignored by a {@code peripheral_hub} peripheral.
|
||||||
|
* <p>
|
||||||
|
* This should include blocks which themselves expose a peripheral hub (such as {@linkplain #WIRED_MODEM wired
|
||||||
|
* modems}).
|
||||||
|
*/
|
||||||
|
public static final TagKey<Block> PERIPHERAL_HUB_IGNORE = make("peripheral_hub_ignore");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Blocks which can be broken by any turtle tool.
|
* Blocks which can be broken by any turtle tool.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -5,9 +5,11 @@
|
|||||||
package dan200.computercraft.api.pocket;
|
package dan200.computercraft.api.pocket;
|
||||||
|
|
||||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||||
|
import dan200.computercraft.api.upgrades.UpgradeBase;
|
||||||
import net.minecraft.nbt.CompoundTag;
|
import net.minecraft.nbt.CompoundTag;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
import net.minecraft.world.entity.Entity;
|
import net.minecraft.world.entity.Entity;
|
||||||
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@@ -69,6 +71,8 @@ public interface IPocketAccess {
|
|||||||
*
|
*
|
||||||
* @return The upgrade's NBT.
|
* @return The upgrade's NBT.
|
||||||
* @see #updateUpgradeNBTData()
|
* @see #updateUpgradeNBTData()
|
||||||
|
* @see UpgradeBase#getUpgradeItem(CompoundTag)
|
||||||
|
* @see UpgradeBase#getUpgradeData(ItemStack)
|
||||||
*/
|
*/
|
||||||
CompoundTag getUpgradeNBTData();
|
CompoundTag getUpgradeNBTData();
|
||||||
|
|
||||||
@@ -80,7 +84,10 @@ public interface IPocketAccess {
|
|||||||
void updateUpgradeNBTData();
|
void updateUpgradeNBTData();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove the current peripheral and create a new one. You may wish to do this if the methods available change.
|
* Remove the current peripheral and create a new one.
|
||||||
|
* <p>
|
||||||
|
* You may wish to do this if the methods available change, for instance when the {@linkplain #getEntity() owning
|
||||||
|
* entity} changes.
|
||||||
*/
|
*/
|
||||||
void invalidatePeripheral();
|
void invalidatePeripheral();
|
||||||
|
|
||||||
@@ -88,6 +95,8 @@ public interface IPocketAccess {
|
|||||||
* Get a list of all upgrades for the pocket computer.
|
* Get a list of all upgrades for the pocket computer.
|
||||||
*
|
*
|
||||||
* @return A collection of all upgrade names.
|
* @return A collection of all upgrade names.
|
||||||
|
* @deprecated This is a relic of a previous API, which no longer makes sense with newer versions of ComputerCraft.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated(forRemoval = true)
|
||||||
Map<ResourceLocation, IPeripheral> getUpgrades();
|
Map<ResourceLocation, IPeripheral> getUpgrades();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,10 +8,13 @@ import com.mojang.authlib.GameProfile;
|
|||||||
import dan200.computercraft.api.lua.ILuaCallback;
|
import dan200.computercraft.api.lua.ILuaCallback;
|
||||||
import dan200.computercraft.api.lua.MethodResult;
|
import dan200.computercraft.api.lua.MethodResult;
|
||||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||||
|
import dan200.computercraft.api.upgrades.UpgradeBase;
|
||||||
|
import dan200.computercraft.api.upgrades.UpgradeData;
|
||||||
import net.minecraft.core.BlockPos;
|
import net.minecraft.core.BlockPos;
|
||||||
import net.minecraft.core.Direction;
|
import net.minecraft.core.Direction;
|
||||||
import net.minecraft.nbt.CompoundTag;
|
import net.minecraft.nbt.CompoundTag;
|
||||||
import net.minecraft.world.Container;
|
import net.minecraft.world.Container;
|
||||||
|
import net.minecraft.world.item.ItemStack;
|
||||||
import net.minecraft.world.level.Level;
|
import net.minecraft.world.level.Level;
|
||||||
import org.jetbrains.annotations.ApiStatus;
|
import org.jetbrains.annotations.ApiStatus;
|
||||||
|
|
||||||
@@ -221,23 +224,51 @@ public interface ITurtleAccess {
|
|||||||
void playAnimation(TurtleAnimation animation);
|
void playAnimation(TurtleAnimation animation);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the turtle on the specified side of the turtle, if there is one.
|
* Returns the upgrade on the specified side of the turtle, if there is one.
|
||||||
*
|
*
|
||||||
* @param side The side to get the upgrade from.
|
* @param side The side to get the upgrade from.
|
||||||
* @return The upgrade on the specified side of the turtle, if there is one.
|
* @return The upgrade on the specified side of the turtle, if there is one.
|
||||||
* @see #setUpgrade(TurtleSide, ITurtleUpgrade)
|
* @see #getUpgradeWithData(TurtleSide)
|
||||||
|
* @see #setUpgradeWithData(TurtleSide, UpgradeData)
|
||||||
*/
|
*/
|
||||||
@Nullable
|
@Nullable
|
||||||
ITurtleUpgrade getUpgrade(TurtleSide side);
|
ITurtleUpgrade getUpgrade(TurtleSide side);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the upgrade on the specified side of the turtle, along with its {@linkplain #getUpgradeNBTData(TurtleSide)
|
||||||
|
* update data}.
|
||||||
|
*
|
||||||
|
* @param side The side to get the upgrade from.
|
||||||
|
* @return The upgrade on the specified side of the turtle, along with its upgrade data, if there is one.
|
||||||
|
* @see #getUpgradeWithData(TurtleSide)
|
||||||
|
* @see #setUpgradeWithData(TurtleSide, UpgradeData)
|
||||||
|
*/
|
||||||
|
default @Nullable UpgradeData<ITurtleUpgrade> getUpgradeWithData(TurtleSide side) {
|
||||||
|
var upgrade = getUpgrade(side);
|
||||||
|
return upgrade == null ? null : UpgradeData.of(upgrade, getUpgradeNBTData(side));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the upgrade for a given side, resetting peripherals and clearing upgrade specific data.
|
* Set the upgrade for a given side, resetting peripherals and clearing upgrade specific data.
|
||||||
*
|
*
|
||||||
* @param side The side to set the upgrade on.
|
* @param side The side to set the upgrade on.
|
||||||
* @param upgrade The upgrade to set, may be {@code null} to clear.
|
* @param upgrade The upgrade to set, may be {@code null} to clear.
|
||||||
* @see #getUpgrade(TurtleSide)
|
* @see #getUpgrade(TurtleSide)
|
||||||
|
* @deprecated Use {@link #setUpgradeWithData(TurtleSide, UpgradeData)}
|
||||||
*/
|
*/
|
||||||
void setUpgrade(TurtleSide side, @Nullable ITurtleUpgrade upgrade);
|
@Deprecated
|
||||||
|
default void setUpgrade(TurtleSide side, @Nullable ITurtleUpgrade upgrade) {
|
||||||
|
setUpgradeWithData(side, upgrade == null ? null : UpgradeData.ofDefault(upgrade));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the upgrade for a given side and its upgrade data.
|
||||||
|
*
|
||||||
|
* @param side The side to set the upgrade on.
|
||||||
|
* @param upgrade The upgrade to set, may be {@code null} to clear.
|
||||||
|
* @see #getUpgradeWithData(TurtleSide)
|
||||||
|
*/
|
||||||
|
void setUpgradeWithData(TurtleSide side, @Nullable UpgradeData<ITurtleUpgrade> upgrade);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the peripheral created by the upgrade on the specified side of the turtle, if there is one.
|
* Returns the peripheral created by the upgrade on the specified side of the turtle, if there is one.
|
||||||
@@ -257,6 +288,8 @@ public interface ITurtleAccess {
|
|||||||
* @param side The side to get the upgrade data for.
|
* @param side The side to get the upgrade data for.
|
||||||
* @return The upgrade-specific data.
|
* @return The upgrade-specific data.
|
||||||
* @see #updateUpgradeNBTData(TurtleSide)
|
* @see #updateUpgradeNBTData(TurtleSide)
|
||||||
|
* @see UpgradeBase#getUpgradeItem(CompoundTag)
|
||||||
|
* @see UpgradeBase#getUpgradeData(ItemStack)
|
||||||
*/
|
*/
|
||||||
CompoundTag getUpgradeNBTData(TurtleSide side);
|
CompoundTag getUpgradeNBTData(TurtleSide side);
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ package dan200.computercraft.api.turtle;
|
|||||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||||
import dan200.computercraft.api.upgrades.UpgradeBase;
|
import dan200.computercraft.api.upgrades.UpgradeBase;
|
||||||
import net.minecraft.core.Direction;
|
import net.minecraft.core.Direction;
|
||||||
|
import net.minecraft.nbt.CompoundTag;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
@@ -79,4 +80,17 @@ public interface ITurtleUpgrade extends UpgradeBase {
|
|||||||
*/
|
*/
|
||||||
default void update(ITurtleAccess turtle, TurtleSide side) {
|
default void update(ITurtleAccess turtle, TurtleSide side) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get upgrade data that should be persisted when the turtle was broken.
|
||||||
|
* <p>
|
||||||
|
* This method should be overridden when you don't need to store all upgrade data by default. For instance, if you
|
||||||
|
* store peripheral state in the upgrade data, which should be lost when the turtle is broken.
|
||||||
|
*
|
||||||
|
* @param upgradeData Data that currently stored for this upgrade
|
||||||
|
* @return Filtered version of this data.
|
||||||
|
*/
|
||||||
|
default CompoundTag getPersistedData(CompoundTag upgradeData) {
|
||||||
|
return upgradeData;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,49 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
|
package dan200.computercraft.api.turtle;
|
||||||
|
|
||||||
|
import net.minecraft.util.StringRepresentable;
|
||||||
|
import net.minecraft.world.entity.EquipmentSlot;
|
||||||
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates if an equipped turtle item will consume durability.
|
||||||
|
*
|
||||||
|
* @see TurtleUpgradeDataProvider.ToolBuilder#consumeDurability(TurtleToolDurability)
|
||||||
|
*/
|
||||||
|
public enum TurtleToolDurability implements StringRepresentable {
|
||||||
|
/**
|
||||||
|
* The equipped tool always consumes durability when using.
|
||||||
|
*/
|
||||||
|
ALWAYS("always"),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The equipped tool consumes durability if it is {@linkplain ItemStack#isEnchanted() enchanted} or has
|
||||||
|
* {@linkplain ItemStack#getAttributeModifiers(EquipmentSlot) custom attribute modifiers}.
|
||||||
|
*/
|
||||||
|
WHEN_ENCHANTED("when_enchanted"),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The equipped tool never consumes durability. Tools which have been damaged cannot be used as upgrades.
|
||||||
|
*/
|
||||||
|
NEVER("never");
|
||||||
|
|
||||||
|
private final String serialisedName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The codec which may be used for serialising/deserialising {@link TurtleToolDurability}s.
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
public static final StringRepresentable.EnumCodec<TurtleToolDurability> CODEC = StringRepresentable.fromEnum(TurtleToolDurability::values);
|
||||||
|
|
||||||
|
TurtleToolDurability(String serialisedName) {
|
||||||
|
this.serialisedName = serialisedName;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getSerializedName() {
|
||||||
|
return serialisedName;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -13,8 +13,10 @@ import net.minecraft.data.DataGenerator;
|
|||||||
import net.minecraft.data.PackOutput;
|
import net.minecraft.data.PackOutput;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
import net.minecraft.tags.TagKey;
|
import net.minecraft.tags.TagKey;
|
||||||
|
import net.minecraft.world.entity.EquipmentSlot;
|
||||||
import net.minecraft.world.entity.ai.attributes.Attributes;
|
import net.minecraft.world.entity.ai.attributes.Attributes;
|
||||||
import net.minecraft.world.item.Item;
|
import net.minecraft.world.item.Item;
|
||||||
|
import net.minecraft.world.item.ItemStack;
|
||||||
import net.minecraft.world.level.block.Block;
|
import net.minecraft.world.level.block.Block;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
@@ -61,6 +63,8 @@ public abstract class TurtleUpgradeDataProvider extends UpgradeDataProvider<ITur
|
|||||||
private @Nullable Item craftingItem;
|
private @Nullable Item craftingItem;
|
||||||
private @Nullable Float damageMultiplier = null;
|
private @Nullable Float damageMultiplier = null;
|
||||||
private @Nullable TagKey<Block> breakable;
|
private @Nullable TagKey<Block> breakable;
|
||||||
|
private boolean allowEnchantments = false;
|
||||||
|
private TurtleToolDurability consumeDurability = TurtleToolDurability.NEVER;
|
||||||
|
|
||||||
ToolBuilder(ResourceLocation id, TurtleUpgradeSerialiser<?> serialiser, Item toolItem) {
|
ToolBuilder(ResourceLocation id, TurtleUpgradeSerialiser<?> serialiser, Item toolItem) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
@@ -104,6 +108,28 @@ public abstract class TurtleUpgradeDataProvider extends UpgradeDataProvider<ITur
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicate that this upgrade allows items which have been {@linkplain ItemStack#isEnchanted() enchanted} or have
|
||||||
|
* {@linkplain ItemStack#getAttributeModifiers(EquipmentSlot) custom attribute modifiers}.
|
||||||
|
*
|
||||||
|
* @return The tool builder, for further use.
|
||||||
|
*/
|
||||||
|
public ToolBuilder allowEnchantments() {
|
||||||
|
allowEnchantments = true;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set when the tool will consume durability.
|
||||||
|
*
|
||||||
|
* @param durability The durability predicate.
|
||||||
|
* @return The tool builder, for further use.
|
||||||
|
*/
|
||||||
|
public ToolBuilder consumeDurability(TurtleToolDurability durability) {
|
||||||
|
consumeDurability = durability;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provide a list of breakable blocks. If not given, the tool can break all blocks. If given, only blocks
|
* Provide a list of breakable blocks. If not given, the tool can break all blocks. If given, only blocks
|
||||||
* in this tag, those in {@link ComputerCraftTags.Blocks#TURTLE_ALWAYS_BREAKABLE} and "insta-mine" ones can
|
* in this tag, those in {@link ComputerCraftTags.Blocks#TURTLE_ALWAYS_BREAKABLE} and "insta-mine" ones can
|
||||||
@@ -132,6 +158,10 @@ public abstract class TurtleUpgradeDataProvider extends UpgradeDataProvider<ITur
|
|||||||
}
|
}
|
||||||
if (damageMultiplier != null) s.addProperty("damageMultiplier", damageMultiplier);
|
if (damageMultiplier != null) s.addProperty("damageMultiplier", damageMultiplier);
|
||||||
if (breakable != null) s.addProperty("breakable", breakable.location().toString());
|
if (breakable != null) s.addProperty("breakable", breakable.location().toString());
|
||||||
|
if (allowEnchantments) s.addProperty("allowEnchantments", true);
|
||||||
|
if (consumeDurability != TurtleToolDurability.NEVER) {
|
||||||
|
s.addProperty("consumeDurability", consumeDurability.getSerializedName());
|
||||||
|
}
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,10 +4,14 @@
|
|||||||
|
|
||||||
package dan200.computercraft.api.upgrades;
|
package dan200.computercraft.api.upgrades;
|
||||||
|
|
||||||
|
import dan200.computercraft.api.pocket.IPocketAccess;
|
||||||
import dan200.computercraft.api.pocket.IPocketUpgrade;
|
import dan200.computercraft.api.pocket.IPocketUpgrade;
|
||||||
|
import dan200.computercraft.api.turtle.ITurtleAccess;
|
||||||
import dan200.computercraft.api.turtle.ITurtleUpgrade;
|
import dan200.computercraft.api.turtle.ITurtleUpgrade;
|
||||||
|
import dan200.computercraft.api.turtle.TurtleSide;
|
||||||
import dan200.computercraft.impl.PlatformHelper;
|
import dan200.computercraft.impl.PlatformHelper;
|
||||||
import net.minecraft.Util;
|
import net.minecraft.Util;
|
||||||
|
import net.minecraft.nbt.CompoundTag;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
import net.minecraft.world.item.ItemStack;
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
|
||||||
@@ -50,6 +54,42 @@ public interface UpgradeBase {
|
|||||||
*/
|
*/
|
||||||
ItemStack getCraftingItem();
|
ItemStack getCraftingItem();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the item stack representing a currently equipped turtle upgrade.
|
||||||
|
* <p>
|
||||||
|
* While upgrades can store upgrade data ({@link ITurtleAccess#getUpgradeNBTData(TurtleSide)} and
|
||||||
|
* {@link IPocketAccess#getUpgradeNBTData()}}, by default this data is discarded when an upgrade is unequipped,
|
||||||
|
* and the original item stack is returned.
|
||||||
|
* <p>
|
||||||
|
* By overriding this method, you can create a new {@link ItemStack} which contains enough data to
|
||||||
|
* {@linkplain #getUpgradeData(ItemStack) re-create the upgrade data} if the item is re-equipped.
|
||||||
|
* <p>
|
||||||
|
* When overriding this, you should override {@link #getUpgradeData(ItemStack)} and {@link #isItemSuitable(ItemStack)}
|
||||||
|
* at the same time,
|
||||||
|
*
|
||||||
|
* @param upgradeData The current upgrade data. This should <strong>NOT</strong> be mutated.
|
||||||
|
* @return The item stack returned when unequipping.
|
||||||
|
*/
|
||||||
|
default ItemStack getUpgradeItem(CompoundTag upgradeData) {
|
||||||
|
return getCraftingItem();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extract upgrade data from an {@link ItemStack}.
|
||||||
|
* <p>
|
||||||
|
* This upgrade data will be available with {@link ITurtleAccess#getUpgradeNBTData(TurtleSide)} or
|
||||||
|
* {@link IPocketAccess#getUpgradeNBTData()}.
|
||||||
|
* <p>
|
||||||
|
* This should be an inverse to {@link #getUpgradeItem(CompoundTag)}.
|
||||||
|
*
|
||||||
|
* @param stack The stack that was equipped by the turtle or pocket computer. This will have the same item as
|
||||||
|
* {@link #getCraftingItem()}.
|
||||||
|
* @return The upgrade data that should be set on the turtle or pocket computer.
|
||||||
|
*/
|
||||||
|
default CompoundTag getUpgradeData(ItemStack stack) {
|
||||||
|
return new CompoundTag();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine if an item is suitable for being used for this upgrade.
|
* Determine if an item is suitable for being used for this upgrade.
|
||||||
* <p>
|
* <p>
|
||||||
|
|||||||
@@ -0,0 +1,82 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
|
package dan200.computercraft.api.upgrades;
|
||||||
|
|
||||||
|
import dan200.computercraft.api.pocket.IPocketUpgrade;
|
||||||
|
import dan200.computercraft.api.turtle.ITurtleUpgrade;
|
||||||
|
import net.minecraft.nbt.CompoundTag;
|
||||||
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
import org.jetbrains.annotations.Contract;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An upgrade (i.e. a {@link ITurtleUpgrade}) and its current upgrade data.
|
||||||
|
* <p>
|
||||||
|
* <strong>IMPORTANT:</strong> The {@link #data()} in an upgrade data is often a reference to the original upgrade data.
|
||||||
|
* Be careful to take a {@linkplain #copy() defensive copy} if you plan to use the data in this upgrade.
|
||||||
|
*
|
||||||
|
* @param upgrade The current upgrade.
|
||||||
|
* @param data The upgrade's data.
|
||||||
|
* @param <T> The type of upgrade, either {@link ITurtleUpgrade} or {@link IPocketUpgrade}.
|
||||||
|
*/
|
||||||
|
public record UpgradeData<T extends UpgradeBase>(T upgrade, CompoundTag data) {
|
||||||
|
/**
|
||||||
|
* A utility method to construct a new {@link UpgradeData} instance.
|
||||||
|
*
|
||||||
|
* @param upgrade An upgrade.
|
||||||
|
* @param data The upgrade's data.
|
||||||
|
* @param <T> The type of upgrade.
|
||||||
|
* @return The new {@link UpgradeData} instance.
|
||||||
|
*/
|
||||||
|
public static <T extends UpgradeBase> UpgradeData<T> of(T upgrade, CompoundTag data) {
|
||||||
|
return new UpgradeData<>(upgrade, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an {@link UpgradeData} containing the default {@linkplain #data() data} for an upgrade.
|
||||||
|
*
|
||||||
|
* @param upgrade The upgrade instance.
|
||||||
|
* @param <T> The type of upgrade.
|
||||||
|
* @return The default upgrade data.
|
||||||
|
*/
|
||||||
|
public static <T extends UpgradeBase> UpgradeData<T> ofDefault(T upgrade) {
|
||||||
|
return of(upgrade, upgrade.getUpgradeData(upgrade.getCraftingItem()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Take a copy of a (possibly {@code null}) {@link UpgradeData} instance.
|
||||||
|
*
|
||||||
|
* @param upgrade The copied upgrade data.
|
||||||
|
* @param <T> The type of upgrade.
|
||||||
|
* @return The newly created upgrade data.
|
||||||
|
*/
|
||||||
|
@Contract("!null -> !null; null -> null")
|
||||||
|
public static <T extends UpgradeBase> @Nullable UpgradeData<T> copyOf(@Nullable UpgradeData<T> upgrade) {
|
||||||
|
return upgrade == null ? null : upgrade.copy();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the {@linkplain UpgradeBase#getUpgradeItem(CompoundTag) upgrade item} for this upgrade.
|
||||||
|
* <p>
|
||||||
|
* This returns a defensive copy of the item, to prevent accidental mutation of the upgrade data or original
|
||||||
|
* {@linkplain UpgradeBase#getCraftingItem() upgrade stack}.
|
||||||
|
*
|
||||||
|
* @return This upgrade's item.
|
||||||
|
*/
|
||||||
|
public ItemStack getUpgradeItem() {
|
||||||
|
return upgrade.getUpgradeItem(data).copy();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Take a copy of this {@link UpgradeData}. This returns a new instance with the same upgrade and a fresh copy of
|
||||||
|
* the upgrade data.
|
||||||
|
*
|
||||||
|
* @return A copy of the current instance.
|
||||||
|
*/
|
||||||
|
public UpgradeData<T> copy() {
|
||||||
|
return new UpgradeData<>(upgrade(), data().copy());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -19,14 +19,9 @@ import net.minecraft.data.PackOutput;
|
|||||||
import net.minecraft.resources.ResourceKey;
|
import net.minecraft.resources.ResourceKey;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
import net.minecraft.world.item.Item;
|
import net.minecraft.world.item.Item;
|
||||||
import org.apache.logging.log4j.LogManager;
|
|
||||||
import org.apache.logging.log4j.Logger;
|
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
@@ -39,8 +34,6 @@ import java.util.function.Function;
|
|||||||
* @param <R> The upgrade serialiser to register for.
|
* @param <R> The upgrade serialiser to register for.
|
||||||
*/
|
*/
|
||||||
public abstract class UpgradeDataProvider<T extends UpgradeBase, R extends UpgradeSerialiser<? extends T>> implements DataProvider {
|
public abstract class UpgradeDataProvider<T extends UpgradeBase, R extends UpgradeSerialiser<? extends T>> implements DataProvider {
|
||||||
private static final Logger LOGGER = LogManager.getLogger();
|
|
||||||
|
|
||||||
private final PackOutput output;
|
private final PackOutput output;
|
||||||
private final String name;
|
private final String name;
|
||||||
private final String folder;
|
private final String folder;
|
||||||
@@ -104,7 +97,7 @@ public abstract class UpgradeDataProvider<T extends UpgradeBase, R extends Upgra
|
|||||||
protected abstract void addUpgrades(Consumer<Upgrade<R>> addUpgrade);
|
protected abstract void addUpgrades(Consumer<Upgrade<R>> addUpgrade);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final CompletableFuture<?> run(CachedOutput cache) {
|
public CompletableFuture<?> run(CachedOutput cache) {
|
||||||
var base = output.getOutputFolder().resolve("data");
|
var base = output.getOutputFolder().resolve("data");
|
||||||
|
|
||||||
Set<ResourceLocation> seen = new HashSet<>();
|
Set<ResourceLocation> seen = new HashSet<>();
|
||||||
@@ -127,7 +120,7 @@ public abstract class UpgradeDataProvider<T extends UpgradeBase, R extends Upgra
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
this.upgrades = upgrades;
|
this.upgrades = Collections.unmodifiableList(upgrades);
|
||||||
return Util.sequenceFailFast(futures);
|
return Util.sequenceFailFast(futures);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -166,5 +159,21 @@ public abstract class UpgradeDataProvider<T extends UpgradeBase, R extends Upgra
|
|||||||
public void add(Consumer<Upgrade<R>> add) {
|
public void add(Consumer<Upgrade<R>> add) {
|
||||||
add.accept(this);
|
add.accept(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a new {@link Upgrade} which requires the given mod to be present.
|
||||||
|
* <p>
|
||||||
|
* This uses mod-loader-specific hooks (Forge's crafting conditions and Fabric's resource conditions). If using
|
||||||
|
* this in a multi-loader setup, you must generate resources separately for the two loaders.
|
||||||
|
*
|
||||||
|
* @param modId The id of the mod.
|
||||||
|
* @return A new upgrade instance.
|
||||||
|
*/
|
||||||
|
public Upgrade<R> requireMod(String modId) {
|
||||||
|
return new Upgrade<>(id, serialiser, json -> {
|
||||||
|
PlatformHelper.get().addRequiredModCondition(json, modId);
|
||||||
|
serialise.accept(json);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,8 @@
|
|||||||
|
|
||||||
package dan200.computercraft.impl;
|
package dan200.computercraft.impl;
|
||||||
|
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
import dan200.computercraft.api.upgrades.UpgradeDataProvider;
|
||||||
import net.minecraft.core.Registry;
|
import net.minecraft.core.Registry;
|
||||||
import net.minecraft.nbt.CompoundTag;
|
import net.minecraft.nbt.CompoundTag;
|
||||||
import net.minecraft.resources.ResourceKey;
|
import net.minecraft.resources.ResourceKey;
|
||||||
@@ -63,6 +65,15 @@ public interface PlatformHelper {
|
|||||||
return item.getTag();
|
return item.getTag();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a resource condition which requires a mod to be loaded. This should be used by data providers such as
|
||||||
|
* {@link UpgradeDataProvider}.
|
||||||
|
*
|
||||||
|
* @param object The JSON object we're generating.
|
||||||
|
* @param modId The mod ID that we require.
|
||||||
|
*/
|
||||||
|
void addRequiredModCondition(JsonObject object, String modId);
|
||||||
|
|
||||||
final class Instance {
|
final class Instance {
|
||||||
static final @Nullable PlatformHelper INSTANCE;
|
static final @Nullable PlatformHelper INSTANCE;
|
||||||
static final @Nullable Throwable ERROR;
|
static final @Nullable Throwable ERROR;
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import cc.tweaked.gradle.commonClasses
|
|||||||
plugins {
|
plugins {
|
||||||
id("cc-tweaked.vanilla")
|
id("cc-tweaked.vanilla")
|
||||||
id("cc-tweaked.gametest")
|
id("cc-tweaked.gametest")
|
||||||
|
id("cc-tweaked.publishing")
|
||||||
}
|
}
|
||||||
|
|
||||||
minecraft {
|
minecraft {
|
||||||
@@ -25,6 +26,7 @@ dependencies {
|
|||||||
clientImplementation(clientClasses(project(":common-api")))
|
clientImplementation(clientClasses(project(":common-api")))
|
||||||
|
|
||||||
compileOnly(libs.bundles.externalMods.common)
|
compileOnly(libs.bundles.externalMods.common)
|
||||||
|
clientCompileOnly(variantOf(libs.emi) { classifier("api") })
|
||||||
|
|
||||||
compileOnly(libs.mixin)
|
compileOnly(libs.mixin)
|
||||||
annotationProcessorEverywhere(libs.autoService)
|
annotationProcessorEverywhere(libs.autoService)
|
||||||
@@ -37,4 +39,6 @@ dependencies {
|
|||||||
testModImplementation(testFixtures(project(":core")))
|
testModImplementation(testFixtures(project(":core")))
|
||||||
testModImplementation(testFixtures(project(":common")))
|
testModImplementation(testFixtures(project(":common")))
|
||||||
testModImplementation(libs.bundles.kotlin)
|
testModImplementation(libs.bundles.kotlin)
|
||||||
|
|
||||||
|
testFixturesImplementation(testFixtures(project(":core")))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import dan200.computercraft.client.render.RenderTypes;
|
|||||||
import dan200.computercraft.client.render.TurtleBlockEntityRenderer;
|
import dan200.computercraft.client.render.TurtleBlockEntityRenderer;
|
||||||
import dan200.computercraft.client.render.monitor.MonitorBlockEntityRenderer;
|
import dan200.computercraft.client.render.monitor.MonitorBlockEntityRenderer;
|
||||||
import dan200.computercraft.client.turtle.TurtleModemModeller;
|
import dan200.computercraft.client.turtle.TurtleModemModeller;
|
||||||
|
import dan200.computercraft.client.turtle.TurtleUpgradeModellers;
|
||||||
import dan200.computercraft.core.util.Colour;
|
import dan200.computercraft.core.util.Colour;
|
||||||
import dan200.computercraft.shared.ModRegistry;
|
import dan200.computercraft.shared.ModRegistry;
|
||||||
import dan200.computercraft.shared.common.IColouredItem;
|
import dan200.computercraft.shared.common.IColouredItem;
|
||||||
@@ -20,6 +21,7 @@ import dan200.computercraft.shared.computer.inventory.AbstractComputerMenu;
|
|||||||
import dan200.computercraft.shared.computer.inventory.ViewComputerMenu;
|
import dan200.computercraft.shared.computer.inventory.ViewComputerMenu;
|
||||||
import dan200.computercraft.shared.media.items.DiskItem;
|
import dan200.computercraft.shared.media.items.DiskItem;
|
||||||
import dan200.computercraft.shared.media.items.TreasureDiskItem;
|
import dan200.computercraft.shared.media.items.TreasureDiskItem;
|
||||||
|
import net.minecraft.client.Minecraft;
|
||||||
import net.minecraft.client.color.item.ItemColor;
|
import net.minecraft.client.color.item.ItemColor;
|
||||||
import net.minecraft.client.gui.screens.MenuScreens;
|
import net.minecraft.client.gui.screens.MenuScreens;
|
||||||
import net.minecraft.client.multiplayer.ClientLevel;
|
import net.minecraft.client.multiplayer.ClientLevel;
|
||||||
@@ -29,6 +31,7 @@ import net.minecraft.client.renderer.blockentity.BlockEntityRenderers;
|
|||||||
import net.minecraft.client.renderer.item.ClampedItemPropertyFunction;
|
import net.minecraft.client.renderer.item.ClampedItemPropertyFunction;
|
||||||
import net.minecraft.client.renderer.item.ItemProperties;
|
import net.minecraft.client.renderer.item.ItemProperties;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import net.minecraft.server.packs.resources.PreparableReloadListener;
|
||||||
import net.minecraft.server.packs.resources.ResourceProvider;
|
import net.minecraft.server.packs.resources.ResourceProvider;
|
||||||
import net.minecraft.world.entity.LivingEntity;
|
import net.minecraft.world.entity.LivingEntity;
|
||||||
import net.minecraft.world.item.Item;
|
import net.minecraft.world.item.Item;
|
||||||
@@ -106,25 +109,11 @@ public final class ClientRegistry {
|
|||||||
for (var item : items) ItemProperties.register(item.get(), id, getter);
|
for (var item : items) ItemProperties.register(item.get(), id, getter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void registerReloadListeners(Consumer<PreparableReloadListener> register, Minecraft minecraft) {
|
||||||
|
register.accept(GuiSprites.initialise(minecraft.getTextureManager()));
|
||||||
|
}
|
||||||
|
|
||||||
private static final String[] EXTRA_MODELS = new String[]{
|
private static final String[] EXTRA_MODELS = new String[]{
|
||||||
// Turtle upgrades
|
|
||||||
"block/turtle_modem_normal_off_left",
|
|
||||||
"block/turtle_modem_normal_on_left",
|
|
||||||
"block/turtle_modem_normal_off_right",
|
|
||||||
"block/turtle_modem_normal_on_right",
|
|
||||||
|
|
||||||
"block/turtle_modem_advanced_off_left",
|
|
||||||
"block/turtle_modem_advanced_on_left",
|
|
||||||
"block/turtle_modem_advanced_off_right",
|
|
||||||
"block/turtle_modem_advanced_on_right",
|
|
||||||
|
|
||||||
"block/turtle_crafting_table_left",
|
|
||||||
"block/turtle_crafting_table_right",
|
|
||||||
|
|
||||||
"block/turtle_speaker_left",
|
|
||||||
"block/turtle_speaker_right",
|
|
||||||
|
|
||||||
// Turtle block renderer
|
|
||||||
"block/turtle_colour",
|
"block/turtle_colour",
|
||||||
"block/turtle_elf_overlay",
|
"block/turtle_elf_overlay",
|
||||||
"block/turtle_rainbow_overlay",
|
"block/turtle_rainbow_overlay",
|
||||||
@@ -133,6 +122,7 @@ public final class ClientRegistry {
|
|||||||
|
|
||||||
public static void registerExtraModels(Consumer<ResourceLocation> register) {
|
public static void registerExtraModels(Consumer<ResourceLocation> register) {
|
||||||
for (var model : EXTRA_MODELS) register.accept(new ResourceLocation(ComputerCraftAPI.MOD_ID, model));
|
for (var model : EXTRA_MODELS) register.accept(new ResourceLocation(ComputerCraftAPI.MOD_ID, model));
|
||||||
|
TurtleUpgradeModellers.getDependencies().forEach(register);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void registerItemColours(BiConsumer<ItemColor, ItemLike> register) {
|
public static void registerItemColours(BiConsumer<ItemColor, ItemLike> register) {
|
||||||
|
|||||||
@@ -33,7 +33,6 @@ import java.nio.ByteBuffer;
|
|||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
@@ -219,7 +218,7 @@ public abstract class AbstractComputerScreen<T extends AbstractComputerMenu> ext
|
|||||||
|
|
||||||
private void alert(Component title, Component message) {
|
private void alert(Component title, Component message) {
|
||||||
OptionScreen.show(minecraft, title, message,
|
OptionScreen.show(minecraft, title, message,
|
||||||
Collections.singletonList(OptionScreen.newButton(OK, b -> minecraft.setScreen(this))),
|
List.of(OptionScreen.newButton(OK, b -> minecraft.setScreen(this))),
|
||||||
() -> minecraft.setScreen(this)
|
() -> minecraft.setScreen(this)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,13 +7,14 @@ package dan200.computercraft.client.gui;
|
|||||||
import dan200.computercraft.client.gui.widgets.ComputerSidebar;
|
import dan200.computercraft.client.gui.widgets.ComputerSidebar;
|
||||||
import dan200.computercraft.client.gui.widgets.TerminalWidget;
|
import dan200.computercraft.client.gui.widgets.TerminalWidget;
|
||||||
import dan200.computercraft.client.render.ComputerBorderRenderer;
|
import dan200.computercraft.client.render.ComputerBorderRenderer;
|
||||||
|
import dan200.computercraft.client.render.RenderTypes;
|
||||||
|
import dan200.computercraft.client.render.SpriteRenderer;
|
||||||
import dan200.computercraft.shared.computer.inventory.AbstractComputerMenu;
|
import dan200.computercraft.shared.computer.inventory.AbstractComputerMenu;
|
||||||
import net.minecraft.client.gui.GuiGraphics;
|
import net.minecraft.client.gui.GuiGraphics;
|
||||||
import net.minecraft.network.chat.Component;
|
import net.minecraft.network.chat.Component;
|
||||||
import net.minecraft.world.entity.player.Inventory;
|
import net.minecraft.world.entity.player.Inventory;
|
||||||
|
|
||||||
import static dan200.computercraft.client.render.ComputerBorderRenderer.BORDER;
|
import static dan200.computercraft.client.render.ComputerBorderRenderer.BORDER;
|
||||||
import static dan200.computercraft.client.render.RenderTypes.FULL_BRIGHT_LIGHTMAP;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A GUI for computers which renders the terminal (and border), but with no UI elements.
|
* A GUI for computers which renders the terminal (and border), but with no UI elements.
|
||||||
@@ -39,11 +40,14 @@ public final class ComputerScreen<T extends AbstractComputerMenu> extends Abstra
|
|||||||
public void renderBg(GuiGraphics graphics, float partialTicks, int mouseX, int mouseY) {
|
public void renderBg(GuiGraphics graphics, float partialTicks, int mouseX, int mouseY) {
|
||||||
// Draw a border around the terminal
|
// Draw a border around the terminal
|
||||||
var terminal = getTerminal();
|
var terminal = getTerminal();
|
||||||
var texture = ComputerBorderRenderer.getTexture(family);
|
var spriteRenderer = SpriteRenderer.createForGui(graphics, RenderTypes.GUI_SPRITES);
|
||||||
|
var computerTextures = GuiSprites.getComputerTextures(family);
|
||||||
|
|
||||||
ComputerBorderRenderer.render(
|
ComputerBorderRenderer.render(
|
||||||
graphics.pose().last().pose(), texture, terminal.getX(), terminal.getY(),
|
spriteRenderer, computerTextures,
|
||||||
FULL_BRIGHT_LIGHTMAP, terminal.getWidth(), terminal.getHeight()
|
terminal.getX(), terminal.getY(), terminal.getWidth(), terminal.getHeight(), false
|
||||||
);
|
);
|
||||||
ComputerSidebar.renderBackground(graphics, texture, leftPos, topPos + sidebarYOffset);
|
ComputerSidebar.renderBackground(spriteRenderer, computerTextures, leftPos, topPos + sidebarYOffset);
|
||||||
|
graphics.flush(); // Flush to ensure background textures are drawn before foreground.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,127 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
|
package dan200.computercraft.client.gui;
|
||||||
|
|
||||||
|
import dan200.computercraft.api.ComputerCraftAPI;
|
||||||
|
import dan200.computercraft.client.render.ComputerBorderRenderer;
|
||||||
|
import dan200.computercraft.data.client.ClientDataProviders;
|
||||||
|
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
||||||
|
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
|
||||||
|
import net.minecraft.client.renderer.texture.TextureManager;
|
||||||
|
import net.minecraft.client.resources.TextureAtlasHolder;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sprite sheet for all GUI texutres in the mod.
|
||||||
|
*/
|
||||||
|
public final class GuiSprites extends TextureAtlasHolder {
|
||||||
|
public static final ResourceLocation SPRITE_SHEET = new ResourceLocation(ComputerCraftAPI.MOD_ID, "gui");
|
||||||
|
public static final ResourceLocation TEXTURE = SPRITE_SHEET.withPath(x -> "textures/atlas/" + x + ".png");
|
||||||
|
|
||||||
|
public static final ButtonTextures TURNED_OFF = button("turned_off");
|
||||||
|
public static final ButtonTextures TURNED_ON = button("turned_on");
|
||||||
|
public static final ButtonTextures TERMINATE = button("terminate");
|
||||||
|
|
||||||
|
public static final ComputerTextures COMPUTER_NORMAL = computer("normal", true, true);
|
||||||
|
public static final ComputerTextures COMPUTER_ADVANCED = computer("advanced", true, true);
|
||||||
|
public static final ComputerTextures COMPUTER_COMMAND = computer("command", false, true);
|
||||||
|
public static final ComputerTextures COMPUTER_COLOUR = computer("colour", true, false);
|
||||||
|
|
||||||
|
private static ButtonTextures button(String name) {
|
||||||
|
return new ButtonTextures(
|
||||||
|
new ResourceLocation(ComputerCraftAPI.MOD_ID, "gui/buttons/" + name),
|
||||||
|
new ResourceLocation(ComputerCraftAPI.MOD_ID, "gui/buttons/" + name + "_hover")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ComputerTextures computer(String name, boolean pocket, boolean sidebar) {
|
||||||
|
return new ComputerTextures(
|
||||||
|
new ResourceLocation(ComputerCraftAPI.MOD_ID, "gui/border_" + name),
|
||||||
|
pocket ? new ResourceLocation(ComputerCraftAPI.MOD_ID, "gui/pocket_bottom_" + name) : null,
|
||||||
|
sidebar ? new ResourceLocation(ComputerCraftAPI.MOD_ID, "gui/sidebar_" + name) : null
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static @Nullable GuiSprites instance;
|
||||||
|
|
||||||
|
private GuiSprites(TextureManager textureManager) {
|
||||||
|
super(textureManager, TEXTURE, SPRITE_SHEET);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialise the singleton {@link GuiSprites} instance.
|
||||||
|
*
|
||||||
|
* @param textureManager The current texture manager.
|
||||||
|
* @return The singleton {@link GuiSprites} instance, to register as resource reload listener.
|
||||||
|
*/
|
||||||
|
public static GuiSprites initialise(TextureManager textureManager) {
|
||||||
|
if (instance != null) throw new IllegalStateException("GuiSprites has already been initialised");
|
||||||
|
return instance = new GuiSprites(textureManager);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lookup a texture on the atlas.
|
||||||
|
*
|
||||||
|
* @param texture The texture to find.
|
||||||
|
* @return The sprite on the atlas.
|
||||||
|
*/
|
||||||
|
public static TextureAtlasSprite get(ResourceLocation texture) {
|
||||||
|
if (instance == null) throw new IllegalStateException("GuiSprites has not been initialised");
|
||||||
|
return instance.getSprite(texture);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the appropriate textures to use for a particular computer family.
|
||||||
|
*
|
||||||
|
* @param family The computer family.
|
||||||
|
* @return The family-specific textures.
|
||||||
|
*/
|
||||||
|
public static ComputerTextures getComputerTextures(ComputerFamily family) {
|
||||||
|
return switch (family) {
|
||||||
|
case NORMAL -> COMPUTER_NORMAL;
|
||||||
|
case ADVANCED -> COMPUTER_ADVANCED;
|
||||||
|
case COMMAND -> COMPUTER_COMMAND;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A set of sprites for a button, with both a normal and "active" state.
|
||||||
|
*
|
||||||
|
* @param normal The normal texture for the button.
|
||||||
|
* @param active The texture for the button when it is active (hovered or focused).
|
||||||
|
*/
|
||||||
|
public record ButtonTextures(ResourceLocation normal, ResourceLocation active) {
|
||||||
|
public TextureAtlasSprite get(boolean active) {
|
||||||
|
return GuiSprites.get(active ? this.active : normal);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Stream<ResourceLocation> textures() {
|
||||||
|
return Stream.of(normal, active);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the set of sprites for a computer family.
|
||||||
|
*
|
||||||
|
* @param border The texture for the computer's border.
|
||||||
|
* @param pocketBottom The texture for the bottom of a pocket computer.
|
||||||
|
* @param sidebar The texture for the computer sidebar.
|
||||||
|
* @see ComputerBorderRenderer
|
||||||
|
* @see ClientDataProviders
|
||||||
|
*/
|
||||||
|
public record ComputerTextures(
|
||||||
|
ResourceLocation border,
|
||||||
|
@Nullable ResourceLocation pocketBottom,
|
||||||
|
@Nullable ResourceLocation sidebar
|
||||||
|
) {
|
||||||
|
public Stream<ResourceLocation> textures() {
|
||||||
|
return Stream.of(border, pocketBottom, sidebar).filter(Objects::nonNull);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -100,9 +100,9 @@ public class ItemToast implements Toast {
|
|||||||
graphics.renderFakeItem(stack, MARGIN, MARGIN + height() / 2 - IMAGE_SIZE);
|
graphics.renderFakeItem(stack, MARGIN, MARGIN + height() / 2 - IMAGE_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
graphics.drawString(component.getMinecraft().font, title, textX, MARGIN, 0xff500050);
|
graphics.drawString(component.getMinecraft().font, title, textX, MARGIN, 0xff500050, false);
|
||||||
for (var i = 0; i < message.size(); ++i) {
|
for (var i = 0; i < message.size(); ++i) {
|
||||||
graphics.drawString(component.getMinecraft().font, message.get(i), textX, LINE_SPACING + (i + 1) * LINE_SPACING, 0xff000000);
|
graphics.drawString(component.getMinecraft().font, message.get(i), textX, LINE_SPACING + (i + 1) * LINE_SPACING, 0xff000000, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
return time - firstDisplay < DISPLAY_TIME ? Visibility.SHOW : Visibility.HIDE;
|
return time - firstDisplay < DISPLAY_TIME ? Visibility.SHOW : Visibility.HIDE;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
|
// SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: LicenseRef-CCPL
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
package dan200.computercraft.client.gui;
|
package dan200.computercraft.client.gui;
|
||||||
|
|
||||||
@@ -42,7 +42,6 @@ public class NoTermComputerScreen<T extends AbstractComputerMenu> extends Screen
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void init() {
|
protected void init() {
|
||||||
// FIXME: passEvents = true; // Pass mouse vents through to the game's mouse handler.
|
|
||||||
// First ensure we're still grabbing the mouse, so the user can look around. Then reset bits of state that
|
// First ensure we're still grabbing the mouse, so the user can look around. Then reset bits of state that
|
||||||
// grabbing unsets.
|
// grabbing unsets.
|
||||||
minecraft.mouseHandler.grabMouse();
|
minecraft.mouseHandler.grabMouse();
|
||||||
|
|||||||
@@ -7,7 +7,8 @@ package dan200.computercraft.client.gui;
|
|||||||
import dan200.computercraft.api.ComputerCraftAPI;
|
import dan200.computercraft.api.ComputerCraftAPI;
|
||||||
import dan200.computercraft.client.gui.widgets.ComputerSidebar;
|
import dan200.computercraft.client.gui.widgets.ComputerSidebar;
|
||||||
import dan200.computercraft.client.gui.widgets.TerminalWidget;
|
import dan200.computercraft.client.gui.widgets.TerminalWidget;
|
||||||
import dan200.computercraft.client.render.ComputerBorderRenderer;
|
import dan200.computercraft.client.render.RenderTypes;
|
||||||
|
import dan200.computercraft.client.render.SpriteRenderer;
|
||||||
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
||||||
import dan200.computercraft.shared.computer.inventory.AbstractComputerMenu;
|
import dan200.computercraft.shared.computer.inventory.AbstractComputerMenu;
|
||||||
import dan200.computercraft.shared.turtle.inventory.TurtleMenu;
|
import dan200.computercraft.shared.turtle.inventory.TurtleMenu;
|
||||||
@@ -25,9 +26,11 @@ public class TurtleScreen extends AbstractComputerScreen<TurtleMenu> {
|
|||||||
private static final ResourceLocation BACKGROUND_NORMAL = new ResourceLocation(ComputerCraftAPI.MOD_ID, "textures/gui/turtle_normal.png");
|
private static final ResourceLocation BACKGROUND_NORMAL = new ResourceLocation(ComputerCraftAPI.MOD_ID, "textures/gui/turtle_normal.png");
|
||||||
private static final ResourceLocation BACKGROUND_ADVANCED = new ResourceLocation(ComputerCraftAPI.MOD_ID, "textures/gui/turtle_advanced.png");
|
private static final ResourceLocation BACKGROUND_ADVANCED = new ResourceLocation(ComputerCraftAPI.MOD_ID, "textures/gui/turtle_advanced.png");
|
||||||
|
|
||||||
private static final int TEX_WIDTH = 254;
|
private static final int TEX_WIDTH = 278;
|
||||||
private static final int TEX_HEIGHT = 217;
|
private static final int TEX_HEIGHT = 217;
|
||||||
|
|
||||||
|
private static final int FULL_TEX_SIZE = 512;
|
||||||
|
|
||||||
public TurtleScreen(TurtleMenu container, Inventory player, Component title) {
|
public TurtleScreen(TurtleMenu container, Inventory player, Component title) {
|
||||||
super(container, player, title, BORDER);
|
super(container, player, title, BORDER);
|
||||||
|
|
||||||
@@ -44,18 +47,22 @@ public class TurtleScreen extends AbstractComputerScreen<TurtleMenu> {
|
|||||||
protected void renderBg(GuiGraphics graphics, float partialTicks, int mouseX, int mouseY) {
|
protected void renderBg(GuiGraphics graphics, float partialTicks, int mouseX, int mouseY) {
|
||||||
var advanced = family == ComputerFamily.ADVANCED;
|
var advanced = family == ComputerFamily.ADVANCED;
|
||||||
var texture = advanced ? BACKGROUND_ADVANCED : BACKGROUND_NORMAL;
|
var texture = advanced ? BACKGROUND_ADVANCED : BACKGROUND_NORMAL;
|
||||||
graphics.blit(texture, leftPos + AbstractComputerMenu.SIDEBAR_WIDTH, topPos, 0, 0, TEX_WIDTH, TEX_HEIGHT);
|
graphics.blit(texture, leftPos + AbstractComputerMenu.SIDEBAR_WIDTH, topPos, 0, 0, 0, TEX_WIDTH, TEX_HEIGHT, FULL_TEX_SIZE, FULL_TEX_SIZE);
|
||||||
|
|
||||||
|
// Render selected slot
|
||||||
var slot = getMenu().getSelectedSlot();
|
var slot = getMenu().getSelectedSlot();
|
||||||
if (slot >= 0) {
|
if (slot >= 0) {
|
||||||
var slotX = slot % 4;
|
var slotX = slot % 4;
|
||||||
var slotY = slot / 4;
|
var slotY = slot / 4;
|
||||||
graphics.blit(texture,
|
graphics.blit(texture,
|
||||||
leftPos + TURTLE_START_X - 2 + slotX * 18, topPos + PLAYER_START_Y - 2 + slotY * 18,
|
leftPos + TURTLE_START_X - 2 + slotX * 18, topPos + PLAYER_START_Y - 2 + slotY * 18, 0,
|
||||||
0, 217, 24, 24
|
0, 217, 24, 24, FULL_TEX_SIZE, FULL_TEX_SIZE
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
ComputerSidebar.renderBackground(graphics, ComputerBorderRenderer.getTexture(family), leftPos, topPos + sidebarYOffset);
|
// Render sidebar
|
||||||
|
var spriteRenderer = SpriteRenderer.createForGui(graphics, RenderTypes.GUI_SPRITES);
|
||||||
|
ComputerSidebar.renderBackground(spriteRenderer, GuiSprites.getComputerTextures(family), leftPos, topPos + sidebarYOffset);
|
||||||
|
graphics.flush(); // Flush to ensure background textures are drawn before foreground.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,15 +4,13 @@
|
|||||||
|
|
||||||
package dan200.computercraft.client.gui.widgets;
|
package dan200.computercraft.client.gui.widgets;
|
||||||
|
|
||||||
import dan200.computercraft.api.ComputerCraftAPI;
|
import dan200.computercraft.client.gui.GuiSprites;
|
||||||
import dan200.computercraft.client.gui.widgets.DynamicImageButton.HintedMessage;
|
import dan200.computercraft.client.gui.widgets.DynamicImageButton.HintedMessage;
|
||||||
import dan200.computercraft.client.render.ComputerBorderRenderer;
|
import dan200.computercraft.client.render.SpriteRenderer;
|
||||||
import dan200.computercraft.shared.computer.core.InputHandler;
|
import dan200.computercraft.shared.computer.core.InputHandler;
|
||||||
import dan200.computercraft.shared.computer.inventory.AbstractComputerMenu;
|
import dan200.computercraft.shared.computer.inventory.AbstractComputerMenu;
|
||||||
import net.minecraft.client.gui.GuiGraphics;
|
|
||||||
import net.minecraft.client.gui.components.AbstractWidget;
|
import net.minecraft.client.gui.components.AbstractWidget;
|
||||||
import net.minecraft.network.chat.Component;
|
import net.minecraft.network.chat.Component;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
|
||||||
|
|
||||||
import java.util.function.BooleanSupplier;
|
import java.util.function.BooleanSupplier;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
@@ -21,22 +19,18 @@ import java.util.function.Consumer;
|
|||||||
* Registers buttons to interact with a computer.
|
* Registers buttons to interact with a computer.
|
||||||
*/
|
*/
|
||||||
public final class ComputerSidebar {
|
public final class ComputerSidebar {
|
||||||
private static final ResourceLocation TEXTURE = new ResourceLocation(ComputerCraftAPI.MOD_ID, "textures/gui/buttons.png");
|
|
||||||
|
|
||||||
private static final int TEX_SIZE = 64;
|
|
||||||
|
|
||||||
private static final int ICON_WIDTH = 12;
|
private static final int ICON_WIDTH = 12;
|
||||||
private static final int ICON_HEIGHT = 12;
|
private static final int ICON_HEIGHT = 12;
|
||||||
private static final int ICON_MARGIN = 2;
|
private static final int ICON_MARGIN = 2;
|
||||||
|
|
||||||
private static final int ICON_TEX_Y_DIFF = 14;
|
|
||||||
|
|
||||||
private static final int CORNERS_BORDER = 3;
|
private static final int CORNERS_BORDER = 3;
|
||||||
private static final int FULL_BORDER = CORNERS_BORDER + ICON_MARGIN;
|
private static final int FULL_BORDER = CORNERS_BORDER + ICON_MARGIN;
|
||||||
|
|
||||||
private static final int BUTTONS = 2;
|
private static final int BUTTONS = 2;
|
||||||
private static final int HEIGHT = (ICON_HEIGHT + ICON_MARGIN * 2) * BUTTONS + CORNERS_BORDER * 2;
|
private static final int HEIGHT = (ICON_HEIGHT + ICON_MARGIN * 2) * BUTTONS + CORNERS_BORDER * 2;
|
||||||
|
|
||||||
|
private static final int TEX_HEIGHT = 14;
|
||||||
|
|
||||||
private ComputerSidebar() {
|
private ComputerSidebar() {
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -50,16 +44,18 @@ public final class ComputerSidebar {
|
|||||||
Component.translatable("gui.computercraft.tooltip.turn_off.key")
|
Component.translatable("gui.computercraft.tooltip.turn_off.key")
|
||||||
);
|
);
|
||||||
add.accept(new DynamicImageButton(
|
add.accept(new DynamicImageButton(
|
||||||
x, y, ICON_WIDTH, ICON_HEIGHT, () -> isOn.getAsBoolean() ? 15 : 1, 1, ICON_TEX_Y_DIFF,
|
x, y, ICON_WIDTH, ICON_HEIGHT,
|
||||||
TEXTURE, TEX_SIZE, TEX_SIZE, b -> toggleComputer(isOn, input),
|
h -> isOn.getAsBoolean() ? GuiSprites.TURNED_ON.get(h) : GuiSprites.TURNED_OFF.get(h),
|
||||||
|
b -> toggleComputer(isOn, input),
|
||||||
() -> isOn.getAsBoolean() ? turnOff : turnOn
|
() -> isOn.getAsBoolean() ? turnOff : turnOn
|
||||||
));
|
));
|
||||||
|
|
||||||
y += ICON_HEIGHT + ICON_MARGIN * 2;
|
y += ICON_HEIGHT + ICON_MARGIN * 2;
|
||||||
|
|
||||||
add.accept(new DynamicImageButton(
|
add.accept(new DynamicImageButton(
|
||||||
x, y, ICON_WIDTH, ICON_HEIGHT, 29, 1, ICON_TEX_Y_DIFF,
|
x, y, ICON_WIDTH, ICON_HEIGHT,
|
||||||
TEXTURE, TEX_SIZE, TEX_SIZE, b -> input.queueEvent("terminate"),
|
GuiSprites.TERMINATE::get,
|
||||||
|
b -> input.queueEvent("terminate"),
|
||||||
new HintedMessage(
|
new HintedMessage(
|
||||||
Component.translatable("gui.computercraft.tooltip.terminate"),
|
Component.translatable("gui.computercraft.tooltip.terminate"),
|
||||||
Component.translatable("gui.computercraft.tooltip.terminate.key")
|
Component.translatable("gui.computercraft.tooltip.terminate.key")
|
||||||
@@ -67,22 +63,12 @@ public final class ComputerSidebar {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void renderBackground(GuiGraphics graphics, ResourceLocation texture, int x, int y) {
|
public static void renderBackground(SpriteRenderer renderer, GuiSprites.ComputerTextures textures, int x, int y) {
|
||||||
graphics.blit(texture,
|
var texture = textures.sidebar();
|
||||||
x, y, 0, 102, AbstractComputerMenu.SIDEBAR_WIDTH, FULL_BORDER,
|
if (texture == null) throw new NullPointerException(textures + " has no sidebar texture");
|
||||||
ComputerBorderRenderer.TEX_SIZE, ComputerBorderRenderer.TEX_SIZE
|
var sprite = GuiSprites.get(texture);
|
||||||
);
|
|
||||||
|
|
||||||
graphics.blit(texture,
|
renderer.blitVerticalSliced(sprite, x, y, AbstractComputerMenu.SIDEBAR_WIDTH, HEIGHT, FULL_BORDER, FULL_BORDER, TEX_HEIGHT);
|
||||||
x, y + FULL_BORDER, AbstractComputerMenu.SIDEBAR_WIDTH, HEIGHT - FULL_BORDER * 2,
|
|
||||||
0, 107, AbstractComputerMenu.SIDEBAR_WIDTH, 4,
|
|
||||||
ComputerBorderRenderer.TEX_SIZE, ComputerBorderRenderer.TEX_SIZE
|
|
||||||
);
|
|
||||||
|
|
||||||
graphics.blit(texture,
|
|
||||||
x, y + HEIGHT - FULL_BORDER, 0, 111, AbstractComputerMenu.SIDEBAR_WIDTH, FULL_BORDER,
|
|
||||||
ComputerBorderRenderer.TEX_SIZE, ComputerBorderRenderer.TEX_SIZE
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void toggleComputer(BooleanSupplier isOn, InputHandler input) {
|
private static void toggleComputer(BooleanSupplier isOn, InputHandler input) {
|
||||||
|
|||||||
@@ -5,15 +5,15 @@
|
|||||||
package dan200.computercraft.client.gui.widgets;
|
package dan200.computercraft.client.gui.widgets;
|
||||||
|
|
||||||
import com.mojang.blaze3d.systems.RenderSystem;
|
import com.mojang.blaze3d.systems.RenderSystem;
|
||||||
|
import it.unimi.dsi.fastutil.booleans.Boolean2ObjectFunction;
|
||||||
import net.minecraft.ChatFormatting;
|
import net.minecraft.ChatFormatting;
|
||||||
import net.minecraft.client.gui.GuiGraphics;
|
import net.minecraft.client.gui.GuiGraphics;
|
||||||
import net.minecraft.client.gui.components.Button;
|
import net.minecraft.client.gui.components.Button;
|
||||||
import net.minecraft.client.gui.components.Tooltip;
|
import net.minecraft.client.gui.components.Tooltip;
|
||||||
|
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
|
||||||
import net.minecraft.network.chat.Component;
|
import net.minecraft.network.chat.Component;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import java.util.function.IntSupplier;
|
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -21,60 +21,40 @@ import java.util.function.Supplier;
|
|||||||
* dynamically.
|
* dynamically.
|
||||||
*/
|
*/
|
||||||
public class DynamicImageButton extends Button {
|
public class DynamicImageButton extends Button {
|
||||||
private final ResourceLocation texture;
|
private final Boolean2ObjectFunction<TextureAtlasSprite> texture;
|
||||||
private final IntSupplier xTexStart;
|
|
||||||
private final int yTexStart;
|
|
||||||
private final int yDiffTex;
|
|
||||||
private final int textureWidth;
|
|
||||||
private final int textureHeight;
|
|
||||||
private final Supplier<HintedMessage> message;
|
private final Supplier<HintedMessage> message;
|
||||||
|
|
||||||
public DynamicImageButton(
|
public DynamicImageButton(
|
||||||
int x, int y, int width, int height, int xTexStart, int yTexStart, int yDiffTex,
|
int x, int y, int width, int height, Boolean2ObjectFunction<TextureAtlasSprite> texture, OnPress onPress,
|
||||||
ResourceLocation texture, int textureWidth, int textureHeight,
|
HintedMessage message
|
||||||
OnPress onPress, HintedMessage message
|
|
||||||
) {
|
) {
|
||||||
this(
|
this(x, y, width, height, texture, onPress, () -> message);
|
||||||
x, y, width, height, () -> xTexStart, yTexStart, yDiffTex,
|
|
||||||
texture, textureWidth, textureHeight,
|
|
||||||
onPress, () -> message
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public DynamicImageButton(
|
public DynamicImageButton(
|
||||||
int x, int y, int width, int height, IntSupplier xTexStart, int yTexStart, int yDiffTex,
|
int x, int y, int width, int height,
|
||||||
ResourceLocation texture, int textureWidth, int textureHeight,
|
Boolean2ObjectFunction<TextureAtlasSprite> texture,
|
||||||
OnPress onPress, Supplier<HintedMessage> message
|
OnPress onPress, Supplier<HintedMessage> message
|
||||||
) {
|
) {
|
||||||
super(x, y, width, height, Component.empty(), onPress, DEFAULT_NARRATION);
|
super(x, y, width, height, Component.empty(), onPress, DEFAULT_NARRATION);
|
||||||
this.textureWidth = textureWidth;
|
|
||||||
this.textureHeight = textureHeight;
|
|
||||||
this.xTexStart = xTexStart;
|
|
||||||
this.yTexStart = yTexStart;
|
|
||||||
this.yDiffTex = yDiffTex;
|
|
||||||
this.texture = texture;
|
this.texture = texture;
|
||||||
this.message = message;
|
this.message = message;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void renderWidget(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) {
|
public void renderWidget(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) {
|
||||||
RenderSystem.enableBlend();
|
var texture = this.texture.get(isHoveredOrFocused());
|
||||||
|
|
||||||
|
RenderSystem.disableDepthTest();
|
||||||
|
graphics.blit(getX(), getY(), 0, width, height, texture);
|
||||||
RenderSystem.enableDepthTest();
|
RenderSystem.enableDepthTest();
|
||||||
|
|
||||||
var yTex = yTexStart;
|
|
||||||
if (isHoveredOrFocused()) yTex += yDiffTex;
|
|
||||||
|
|
||||||
graphics.blit(texture, getX(), getY(), xTexStart.getAsInt(), yTex, width, height, textureWidth, textureHeight);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Component getMessage() {
|
|
||||||
return message.get().message;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void render(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) {
|
public void render(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) {
|
||||||
setTooltip(message.get().tooltip());
|
var message = this.message.get();
|
||||||
|
setMessage(message.message());
|
||||||
|
setTooltip(message.tooltip());
|
||||||
super.render(graphics, mouseX, mouseY, partialTicks);
|
super.render(graphics, mouseX, mouseY, partialTicks);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ public class ShaderMod {
|
|||||||
Optional<ShaderMod> get();
|
Optional<ShaderMod> get();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class Storage {
|
private static final class Storage {
|
||||||
static final ShaderMod INSTANCE = ServiceLoader.load(Provider.class)
|
static final ShaderMod INSTANCE = ServiceLoader.load(Provider.class)
|
||||||
.stream()
|
.stream()
|
||||||
.flatMap(x -> x.get().get().stream())
|
.flatMap(x -> x.get().get().stream())
|
||||||
|
|||||||
@@ -0,0 +1,44 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
|
package dan200.computercraft.client.integration.emi;
|
||||||
|
|
||||||
|
import dan200.computercraft.api.turtle.TurtleSide;
|
||||||
|
import dan200.computercraft.shared.ModRegistry;
|
||||||
|
import dan200.computercraft.shared.pocket.items.PocketComputerItem;
|
||||||
|
import dan200.computercraft.shared.turtle.items.TurtleItem;
|
||||||
|
import dev.emi.emi.api.EmiEntrypoint;
|
||||||
|
import dev.emi.emi.api.EmiPlugin;
|
||||||
|
import dev.emi.emi.api.EmiRegistry;
|
||||||
|
import dev.emi.emi.api.stack.Comparison;
|
||||||
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
|
||||||
|
import java.util.function.BiPredicate;
|
||||||
|
|
||||||
|
@EmiEntrypoint
|
||||||
|
public class EMIComputerCraft implements EmiPlugin {
|
||||||
|
@Override
|
||||||
|
public void register(EmiRegistry registry) {
|
||||||
|
registry.setDefaultComparison(ModRegistry.Items.TURTLE_NORMAL.get(), turtleComparison);
|
||||||
|
registry.setDefaultComparison(ModRegistry.Items.TURTLE_ADVANCED.get(), turtleComparison);
|
||||||
|
|
||||||
|
registry.setDefaultComparison(ModRegistry.Items.POCKET_COMPUTER_NORMAL.get(), pocketComparison);
|
||||||
|
registry.setDefaultComparison(ModRegistry.Items.POCKET_COMPUTER_ADVANCED.get(), pocketComparison);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final Comparison turtleComparison = compareStacks((left, right) ->
|
||||||
|
left.getItem() instanceof TurtleItem turtle
|
||||||
|
&& turtle.getUpgrade(left, TurtleSide.LEFT) == turtle.getUpgrade(right, TurtleSide.LEFT)
|
||||||
|
&& turtle.getUpgrade(left, TurtleSide.RIGHT) == turtle.getUpgrade(right, TurtleSide.RIGHT));
|
||||||
|
|
||||||
|
private static final Comparison pocketComparison = compareStacks((left, right) ->
|
||||||
|
left.getItem() instanceof PocketComputerItem && PocketComputerItem.getUpgrade(left) == PocketComputerItem.getUpgrade(right));
|
||||||
|
|
||||||
|
private static Comparison compareStacks(BiPredicate<ItemStack, ItemStack> test) {
|
||||||
|
return Comparison.of((left, right) -> {
|
||||||
|
ItemStack leftStack = left.getItemStack(), rightStack = right.getItemStack();
|
||||||
|
return leftStack.getItem() == rightStack.getItem() && test.test(leftStack, rightStack);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,103 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
|
package dan200.computercraft.client.model.turtle;
|
||||||
|
|
||||||
|
import com.mojang.blaze3d.vertex.DefaultVertexFormat;
|
||||||
|
import com.mojang.blaze3d.vertex.VertexFormat;
|
||||||
|
import com.mojang.blaze3d.vertex.VertexFormatElement;
|
||||||
|
import com.mojang.math.Transformation;
|
||||||
|
import net.minecraft.client.renderer.block.model.BakedQuad;
|
||||||
|
import net.minecraft.client.resources.model.BakedModel;
|
||||||
|
import net.minecraft.core.Direction;
|
||||||
|
import org.joml.Matrix4f;
|
||||||
|
import org.joml.Vector4f;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Applies a {@link Transformation} (or rather a {@link Matrix4f}) to a list of {@link BakedQuad}s.
|
||||||
|
* <p>
|
||||||
|
* This does a little bit of magic compared with other system (i.e. Forge's {@code QuadTransformers}), as it needs to
|
||||||
|
* handle flipping models upside down.
|
||||||
|
* <p>
|
||||||
|
* This is typically used with a {@link BakedModel} subclass - see the loader-specific projects.
|
||||||
|
*/
|
||||||
|
public class ModelTransformer {
|
||||||
|
private static final int[] INVERSE_ORDER = new int[]{ 3, 2, 1, 0 };
|
||||||
|
|
||||||
|
private static final int STRIDE = DefaultVertexFormat.BLOCK.getIntegerSize();
|
||||||
|
private static final int POS_OFFSET = findOffset(DefaultVertexFormat.BLOCK, DefaultVertexFormat.ELEMENT_POSITION);
|
||||||
|
|
||||||
|
protected final Matrix4f transformation;
|
||||||
|
protected final boolean invert;
|
||||||
|
private @Nullable TransformedQuads cache;
|
||||||
|
|
||||||
|
public ModelTransformer(Transformation transformation) {
|
||||||
|
this.transformation = transformation.getMatrix();
|
||||||
|
invert = transformation.getMatrix().determinant() < 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<BakedQuad> transform(List<BakedQuad> quads) {
|
||||||
|
if (quads.isEmpty()) return List.of();
|
||||||
|
|
||||||
|
// We do some basic caching here to avoid recomputing every frame. Most turtle models don't have culled faces,
|
||||||
|
// so it's not worth being smarter here.
|
||||||
|
var cache = this.cache;
|
||||||
|
if (cache != null && quads.equals(cache.original())) return cache.transformed();
|
||||||
|
|
||||||
|
List<BakedQuad> transformed = new ArrayList<>(quads.size());
|
||||||
|
for (var quad : quads) transformed.add(transformQuad(quad));
|
||||||
|
this.cache = new TransformedQuads(quads, transformed);
|
||||||
|
return transformed;
|
||||||
|
}
|
||||||
|
|
||||||
|
private BakedQuad transformQuad(BakedQuad quad) {
|
||||||
|
var inputData = quad.getVertices();
|
||||||
|
var outputData = new int[inputData.length];
|
||||||
|
for (var i = 0; i < 4; i++) {
|
||||||
|
var inStart = STRIDE * i;
|
||||||
|
// Reverse the order of the quads if we're inverting
|
||||||
|
var outStart = getVertexOffset(i, invert);
|
||||||
|
System.arraycopy(inputData, inStart, outputData, outStart, STRIDE);
|
||||||
|
|
||||||
|
// Apply the matrix to our position
|
||||||
|
var inPosStart = inStart + POS_OFFSET;
|
||||||
|
var outPosStart = outStart + POS_OFFSET;
|
||||||
|
|
||||||
|
var x = Float.intBitsToFloat(inputData[inPosStart]);
|
||||||
|
var y = Float.intBitsToFloat(inputData[inPosStart + 1]);
|
||||||
|
var z = Float.intBitsToFloat(inputData[inPosStart + 2]);
|
||||||
|
|
||||||
|
// Transform the position
|
||||||
|
var pos = new Vector4f(x, y, z, 1);
|
||||||
|
transformation.transformProject(pos);
|
||||||
|
|
||||||
|
outputData[outPosStart] = Float.floatToRawIntBits(pos.x());
|
||||||
|
outputData[outPosStart + 1] = Float.floatToRawIntBits(pos.y());
|
||||||
|
outputData[outPosStart + 2] = Float.floatToRawIntBits(pos.z());
|
||||||
|
}
|
||||||
|
|
||||||
|
var direction = Direction.rotate(transformation, quad.getDirection());
|
||||||
|
return new BakedQuad(outputData, quad.getTintIndex(), direction, quad.getSprite(), quad.isShade());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int getVertexOffset(int vertex, boolean invert) {
|
||||||
|
return (invert ? ModelTransformer.INVERSE_ORDER[vertex] : vertex) * ModelTransformer.STRIDE;
|
||||||
|
}
|
||||||
|
|
||||||
|
private record TransformedQuads(List<BakedQuad> original, List<BakedQuad> transformed) {
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int findOffset(VertexFormat format, VertexFormatElement element) {
|
||||||
|
var offset = 0;
|
||||||
|
for (var other : format.getElements()) {
|
||||||
|
if (other == element) return offset / Integer.BYTES;
|
||||||
|
offset += element.getByteSize();
|
||||||
|
}
|
||||||
|
throw new IllegalArgumentException("Cannot find " + element + " in " + format);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,11 +4,13 @@
|
|||||||
|
|
||||||
package dan200.computercraft.client.model.turtle;
|
package dan200.computercraft.client.model.turtle;
|
||||||
|
|
||||||
|
import com.google.common.cache.CacheBuilder;
|
||||||
import com.mojang.blaze3d.vertex.PoseStack;
|
import com.mojang.blaze3d.vertex.PoseStack;
|
||||||
import com.mojang.math.Transformation;
|
import com.mojang.math.Transformation;
|
||||||
import dan200.computercraft.api.client.TransformedModel;
|
import dan200.computercraft.api.client.TransformedModel;
|
||||||
import dan200.computercraft.api.turtle.ITurtleUpgrade;
|
import dan200.computercraft.api.turtle.ITurtleUpgrade;
|
||||||
import dan200.computercraft.api.turtle.TurtleSide;
|
import dan200.computercraft.api.turtle.TurtleSide;
|
||||||
|
import dan200.computercraft.api.upgrades.UpgradeData;
|
||||||
import dan200.computercraft.client.platform.ClientPlatformHelper;
|
import dan200.computercraft.client.platform.ClientPlatformHelper;
|
||||||
import dan200.computercraft.client.render.TurtleBlockEntityRenderer;
|
import dan200.computercraft.client.render.TurtleBlockEntityRenderer;
|
||||||
import dan200.computercraft.client.turtle.TurtleUpgradeModellers;
|
import dan200.computercraft.client.turtle.TurtleUpgradeModellers;
|
||||||
@@ -21,15 +23,17 @@ import net.minecraft.world.item.ItemStack;
|
|||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Combines several individual models together to form a turtle.
|
* Combines several individual models together to form a turtle.
|
||||||
|
*
|
||||||
|
* @param <T> The type of the resulting "baked model".
|
||||||
*/
|
*/
|
||||||
public final class TurtleModelParts {
|
public final class TurtleModelParts<T> {
|
||||||
private static final Transformation identity, flip;
|
private static final Transformation identity, flip;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
@@ -42,33 +46,67 @@ public final class TurtleModelParts {
|
|||||||
flip = new Transformation(stack.last().pose());
|
flip = new Transformation(stack.last().pose());
|
||||||
}
|
}
|
||||||
|
|
||||||
public record Combination(
|
private record Combination(
|
||||||
boolean colour,
|
boolean colour,
|
||||||
@Nullable ITurtleUpgrade leftUpgrade,
|
@Nullable UpgradeData<ITurtleUpgrade> leftUpgrade,
|
||||||
@Nullable ITurtleUpgrade rightUpgrade,
|
@Nullable UpgradeData<ITurtleUpgrade> rightUpgrade,
|
||||||
@Nullable ResourceLocation overlay,
|
@Nullable ResourceLocation overlay,
|
||||||
boolean christmas,
|
boolean christmas,
|
||||||
boolean flip
|
boolean flip
|
||||||
) {
|
) {
|
||||||
|
Combination copy() {
|
||||||
|
if (leftUpgrade == null && rightUpgrade == null) return this;
|
||||||
|
return new Combination(
|
||||||
|
colour, UpgradeData.copyOf(leftUpgrade), UpgradeData.copyOf(rightUpgrade),
|
||||||
|
overlay, christmas, flip
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private final BakedModel familyModel;
|
private final BakedModel familyModel;
|
||||||
private final BakedModel colourModel;
|
private final BakedModel colourModel;
|
||||||
private final Function<TransformedModel, BakedModel> transformer;
|
private final Function<TransformedModel, BakedModel> transformer;
|
||||||
|
private final Function<Combination, T> buildModel;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A cache of {@link TransformedModel} to the transformed {@link BakedModel}. This helps us pool the transformed
|
* A cache of {@link TransformedModel} to the transformed {@link BakedModel}. This helps us pool the transformed
|
||||||
* instances, reducing memory usage and hopefully ensuring their caches are hit more often!
|
* instances, reducing memory usage and hopefully ensuring their caches are hit more often!
|
||||||
*/
|
*/
|
||||||
private final Map<TransformedModel, BakedModel> transformCache = new HashMap<>();
|
private final Map<TransformedModel, BakedModel> transformCache = CacheBuilder.newBuilder()
|
||||||
|
.concurrencyLevel(1)
|
||||||
|
.expireAfterAccess(30, TimeUnit.SECONDS)
|
||||||
|
.<TransformedModel, BakedModel>build()
|
||||||
|
.asMap();
|
||||||
|
|
||||||
public TurtleModelParts(BakedModel familyModel, BakedModel colourModel, ModelTransformer transformer) {
|
/**
|
||||||
|
* A cache of {@link Combination}s to the combined model.
|
||||||
|
*/
|
||||||
|
private final Map<Combination, T> modelCache = CacheBuilder.newBuilder()
|
||||||
|
.concurrencyLevel(1)
|
||||||
|
.expireAfterAccess(30, TimeUnit.SECONDS)
|
||||||
|
.<Combination, T>build()
|
||||||
|
.asMap();
|
||||||
|
|
||||||
|
public TurtleModelParts(BakedModel familyModel, BakedModel colourModel, ModelTransformer transformer, Function<List<BakedModel>, T> combineModel) {
|
||||||
this.familyModel = familyModel;
|
this.familyModel = familyModel;
|
||||||
this.colourModel = colourModel;
|
this.colourModel = colourModel;
|
||||||
this.transformer = x -> transformer.transform(x.getModel(), x.getMatrix());
|
this.transformer = x -> transformer.transform(x.getModel(), x.getMatrix());
|
||||||
|
buildModel = x -> combineModel.apply(buildModel(x));
|
||||||
}
|
}
|
||||||
|
|
||||||
public Combination getCombination(ItemStack stack) {
|
public T getModel(ItemStack stack) {
|
||||||
|
var combination = getCombination(stack);
|
||||||
|
var existing = modelCache.get(combination);
|
||||||
|
if (existing != null) return existing;
|
||||||
|
|
||||||
|
// Take a defensive copy of the upgrade data, and add it to the cache.
|
||||||
|
var newCombination = combination.copy();
|
||||||
|
var newModel = buildModel.apply(newCombination);
|
||||||
|
modelCache.put(newCombination, newModel);
|
||||||
|
return newModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Combination getCombination(ItemStack stack) {
|
||||||
var christmas = Holiday.getCurrent() == Holiday.CHRISTMAS;
|
var christmas = Holiday.getCurrent() == Holiday.CHRISTMAS;
|
||||||
|
|
||||||
if (!(stack.getItem() instanceof TurtleItem turtle)) {
|
if (!(stack.getItem() instanceof TurtleItem turtle)) {
|
||||||
@@ -76,8 +114,8 @@ public final class TurtleModelParts {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var colour = turtle.getColour(stack);
|
var colour = turtle.getColour(stack);
|
||||||
var leftUpgrade = turtle.getUpgrade(stack, TurtleSide.LEFT);
|
var leftUpgrade = turtle.getUpgradeWithData(stack, TurtleSide.LEFT);
|
||||||
var rightUpgrade = turtle.getUpgrade(stack, TurtleSide.RIGHT);
|
var rightUpgrade = turtle.getUpgradeWithData(stack, TurtleSide.RIGHT);
|
||||||
var overlay = turtle.getOverlay(stack);
|
var overlay = turtle.getOverlay(stack);
|
||||||
var label = turtle.getLabel(stack);
|
var label = turtle.getLabel(stack);
|
||||||
var flip = label != null && (label.equals("Dinnerbone") || label.equals("Grumm"));
|
var flip = label != null && (label.equals("Dinnerbone") || label.equals("Grumm"));
|
||||||
@@ -85,7 +123,7 @@ public final class TurtleModelParts {
|
|||||||
return new Combination(colour != -1, leftUpgrade, rightUpgrade, overlay, christmas, flip);
|
return new Combination(colour != -1, leftUpgrade, rightUpgrade, overlay, christmas, flip);
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<BakedModel> buildModel(Combination combo) {
|
private List<BakedModel> buildModel(Combination combo) {
|
||||||
var mc = Minecraft.getInstance();
|
var mc = Minecraft.getInstance();
|
||||||
var modelManager = mc.getItemRenderer().getItemModelShaper().getModelManager();
|
var modelManager = mc.getItemRenderer().getItemModelShaper().getModelManager();
|
||||||
|
|
||||||
@@ -97,19 +135,20 @@ public final class TurtleModelParts {
|
|||||||
if (overlayModelLocation != null) {
|
if (overlayModelLocation != null) {
|
||||||
parts.add(transform(ClientPlatformHelper.get().getModel(modelManager, overlayModelLocation), transformation));
|
parts.add(transform(ClientPlatformHelper.get().getModel(modelManager, overlayModelLocation), transformation));
|
||||||
}
|
}
|
||||||
if (combo.leftUpgrade() != null) {
|
|
||||||
var model = TurtleUpgradeModellers.getModel(combo.leftUpgrade(), null, TurtleSide.LEFT);
|
addUpgrade(parts, transformation, TurtleSide.LEFT, combo.leftUpgrade());
|
||||||
parts.add(transform(model.getModel(), transformation.compose(model.getMatrix())));
|
addUpgrade(parts, transformation, TurtleSide.RIGHT, combo.rightUpgrade());
|
||||||
}
|
|
||||||
if (combo.rightUpgrade() != null) {
|
|
||||||
var model = TurtleUpgradeModellers.getModel(combo.rightUpgrade(), null, TurtleSide.RIGHT);
|
|
||||||
parts.add(transform(model.getModel(), transformation.compose(model.getMatrix())));
|
|
||||||
}
|
|
||||||
|
|
||||||
return parts;
|
return parts;
|
||||||
}
|
}
|
||||||
|
|
||||||
public BakedModel transform(BakedModel model, Transformation transformation) {
|
private void addUpgrade(List<BakedModel> parts, Transformation transformation, TurtleSide side, @Nullable UpgradeData<ITurtleUpgrade> upgrade) {
|
||||||
|
if (upgrade == null) return;
|
||||||
|
var model = TurtleUpgradeModellers.getModel(upgrade.upgrade(), upgrade.data(), side);
|
||||||
|
parts.add(transform(model.getModel(), transformation.compose(model.getMatrix())));
|
||||||
|
}
|
||||||
|
|
||||||
|
private BakedModel transform(BakedModel model, Transformation transformation) {
|
||||||
if (transformation.equals(Transformation.identity())) return model;
|
if (transformation.equals(Transformation.identity())) return model;
|
||||||
return transformCache.computeIfAbsent(new TransformedModel(model, transformation), transformer);
|
return transformCache.computeIfAbsent(new TransformedModel(model, transformation), transformer);
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user