mirror of
https://github.com/SquidDev-CC/CC-Tweaked
synced 2025-10-15 14:07:38 +00:00
Compare commits
142 Commits
v1.19.2-1.
...
v1.19.3-1.
Author | SHA1 | Date | |
---|---|---|---|
![]() |
6a116aadb8 | ||
![]() |
9e6e0c8b88 | ||
![]() |
5502412181 | ||
![]() |
62e3c5f9aa | ||
![]() |
7e54a40fa9 | ||
![]() |
8ac42566ec | ||
![]() |
66b20d2bdb | ||
![]() |
81dad421d5 | ||
![]() |
3075d3cea8 | ||
![]() |
cdab8f429e | ||
![]() |
2e5cd29e12 | ||
![]() |
22cadd6730 | ||
![]() |
366052ec48 | ||
![]() |
3224e0bf8b | ||
![]() |
fb4b097a66 | ||
![]() |
67f3d91850 | ||
![]() |
1e3a930543 | ||
![]() |
b21e2f4e63 | ||
![]() |
a12b405acf | ||
![]() |
e076818b29 | ||
![]() |
1554c7b397 | ||
![]() |
6cd32a6368 | ||
![]() |
7335a892b5 | ||
![]() |
83eddc6636 | ||
![]() |
d066d175bf | ||
![]() |
9873ccfa0d | ||
![]() |
da7a50368d | ||
![]() |
8cfbfe7ceb | ||
![]() |
67244b17af | ||
![]() |
9e1de23f4a | ||
![]() |
86b60855d6 | ||
![]() |
db6b6fd173 | ||
![]() |
2014e9527e | ||
![]() |
f43b839056 | ||
![]() |
f561572509 | ||
![]() |
edb21f33be | ||
![]() |
02b68b259e | ||
![]() |
28a55349a9 | ||
![]() |
2457a31728 | ||
![]() |
cdc91a8e5d | ||
![]() |
8024017f53 | ||
![]() |
592ff84aea | ||
![]() |
4360458416 | ||
![]() |
717e096b94 | ||
![]() |
34a31abd9c | ||
![]() |
bdecb88cca | ||
![]() |
af15030fa4 | ||
![]() |
3a883db49e | ||
![]() |
8ea5b64f64 | ||
![]() |
7b6caf76e4 | ||
![]() |
230c7ee904 | ||
![]() |
aa203802c6 | ||
![]() |
1259e29f21 | ||
![]() |
77f62dac94 | ||
![]() |
7f34aff6bb | ||
![]() |
3047e3cdf4 | ||
![]() |
7a83a403f0 | ||
![]() |
a1d5c76d00 | ||
![]() |
bcdfa7c5ff | ||
![]() |
2c59b9122b | ||
![]() |
d2c7b944ab | ||
![]() |
e241575329 | ||
![]() |
86c4c7483d | ||
![]() |
9010219b9c | ||
![]() |
172d1824fc | ||
![]() |
9d394f44d3 | ||
![]() |
6e5b7243f4 | ||
![]() |
27b732f835 | ||
![]() |
4fa7f50534 | ||
![]() |
eeac86b07c | ||
![]() |
36ce490566 | ||
![]() |
e7fe22d4f8 | ||
![]() |
2b237332ce | ||
![]() |
1276478deb | ||
![]() |
551f6ba60c | ||
![]() |
99a2b26fc5 | ||
![]() |
0787e17ebe | ||
![]() |
06163e4f25 | ||
![]() |
18fbd96c10 | ||
![]() |
367773e173 | ||
![]() |
8007a30849 | ||
![]() |
df38f3e887 | ||
![]() |
c3fe9f00d4 | ||
![]() |
3b42f22a4f | ||
![]() |
9962ce1a5c | ||
![]() |
9f48395596 | ||
![]() |
020c5cd2d3 | ||
![]() |
a9c0b02e3c | ||
![]() |
fc5f296eeb | ||
![]() |
c96172e78d | ||
![]() |
fa122a56cf | ||
![]() |
87c6d3aef6 | ||
![]() |
95c57e843d | ||
![]() |
b13998dd96 | ||
![]() |
47816805fb | ||
![]() |
b8fce1eecc | ||
![]() |
ee2670d53b | ||
![]() |
3a96aea894 | ||
![]() |
0fc78acd49 | ||
![]() |
737d8a2585 | ||
![]() |
e2447bb0fd | ||
![]() |
2255d49d16 | ||
![]() |
3fa39b5f98 | ||
![]() |
08df68dcc0 | ||
![]() |
8f92417a2f | ||
![]() |
b58b9b7df3 | ||
![]() |
8d2e150f05 | ||
![]() |
8152f19b6e | ||
![]() |
b2b58892e3 | ||
![]() |
8360e8234d | ||
![]() |
77624fc6fd | ||
![]() |
1d335f7290 | ||
![]() |
f04acdc199 | ||
![]() |
bdf590fa30 | ||
![]() |
0c4fd2b29e | ||
![]() |
8a7156785d | ||
![]() |
4d50b48ea6 | ||
![]() |
48285404b9 | ||
![]() |
b36b96e0bc | ||
![]() |
34c7fcf750 | ||
![]() |
cc73fcd85d | ||
![]() |
22729f6f16 | ||
![]() |
55494b7671 | ||
![]() |
7d47b219c5 | ||
![]() |
320007dbc6 | ||
![]() |
0908acbe9b | ||
![]() |
e8f9cdd221 | ||
![]() |
53abe5e56e | ||
![]() |
564752c8dd | ||
![]() |
6d665ad841 | ||
![]() |
9cd728fea9 | ||
![]() |
1c890e5a5c | ||
![]() |
955b9c7d28 | ||
![]() |
76710eec9d | ||
![]() |
d8e2161f15 | ||
![]() |
c82f37d3bf | ||
![]() |
c8c128d335 | ||
![]() |
acc254a1ef | ||
![]() |
a17b001950 | ||
![]() |
e4e528e5bf | ||
![]() |
6cc86b0ae5 | ||
![]() |
f478c4ffc4 |
@@ -8,6 +8,17 @@ charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
|
||||
ij_continuation_indent_size = 4
|
||||
ij_any_do_while_brace_force = if_multiline
|
||||
ij_any_if_brace_force = if_multiline
|
||||
ij_any_for_brace_force = if_multiline
|
||||
ij_any_spaces_within_array_initializer_braces = true
|
||||
|
||||
ij_kotlin_allow_trailing_comma = true
|
||||
ij_kotlin_allow_trailing_comma_on_call_site = true
|
||||
ij_kotlin_method_parameters_wrap = off
|
||||
ij_kotlin_call_parameters_wrap = off
|
||||
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
||||
|
||||
|
2
.git-blame-ignore-revs
Normal file
2
.git-blame-ignore-revs
Normal file
@@ -0,0 +1,2 @@
|
||||
# Reformat everything
|
||||
f478c4ffc4fb9fc2200ec9b0bc751d047057ce81
|
4
.gitattributes
vendored
4
.gitattributes
vendored
@@ -1,6 +1,6 @@
|
||||
# Ignore changes in generated files
|
||||
src/generated/** linguist-generated
|
||||
src/testMod/server-files/structures linguist-generated
|
||||
projects/*/src/generated/** linguist-generated
|
||||
projects/common/src/testMod/resources/data/cctest/structures/* linguist-generated
|
||||
|
||||
* text=auto
|
||||
|
||||
|
54
.github/workflows/main-ci.yml
vendored
54
.github/workflows/main-ci.yml
vendored
@@ -33,14 +33,25 @@ jobs:
|
||||
./gradlew downloadAssets || ./gradlew downloadAssets
|
||||
./gradlew build
|
||||
|
||||
- name: Run client tests
|
||||
run: ./gradlew runGametestClient # Not checkClient, as no point running rendering tests.
|
||||
# These are a little flaky on GH actions: its useful to run them, but don't break the build.
|
||||
continue-on-error: true
|
||||
|
||||
- name: Prepare Jars
|
||||
run: |
|
||||
# Find the main jar and append the git hash onto it.
|
||||
mkdir -p jars
|
||||
find projects/forge/build/libs projects/fabric/build/libs -type f -regex '.*[0-9.]+\(-SNAPSHOT\)?\.jar$' -exec bash -c 'cp {} "jars/$(basename {} .jar)-$(git rev-parse HEAD).jar"' \;
|
||||
|
||||
- name: Upload Jar
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: CC-Tweaked
|
||||
path: build/libs
|
||||
path: ./jars
|
||||
|
||||
- name: Upload coverage
|
||||
uses: codecov/codecov-action@v2
|
||||
uses: codecov/codecov-action@v3
|
||||
|
||||
- name: Parse test reports
|
||||
run: ./tools/parse-reports.py
|
||||
@@ -48,3 +59,40 @@ jobs:
|
||||
|
||||
- name: Run linters
|
||||
uses: pre-commit/action@v3.0.0
|
||||
|
||||
build-core:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- name: Windows
|
||||
uses: windows-latest
|
||||
|
||||
- name: macOS
|
||||
uses: macos-latest
|
||||
|
||||
name: Test on ${{ matrix.name }}
|
||||
runs-on: ${{ matrix.uses }}
|
||||
|
||||
steps:
|
||||
- name: Clone repository
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Set up Java
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
java-version: 17
|
||||
distribution: 'temurin'
|
||||
|
||||
- name: Setup Gradle
|
||||
uses: gradle/gradle-build-action@v2
|
||||
with:
|
||||
cache-read-only: ${{ !startsWith(github.ref, 'refs/heads/mc-') }}
|
||||
|
||||
- name: Run tests
|
||||
run: |
|
||||
./gradlew --configure-on-demand :core:test
|
||||
|
||||
- name: Parse test reports
|
||||
run: python3 ./tools/parse-reports.py
|
||||
if: ${{ failure() }}
|
||||
|
4
.github/workflows/make-doc.sh
vendored
4
.github/workflows/make-doc.sh
vendored
@@ -12,8 +12,8 @@ chmod 600 "$HOME/.ssh/key"
|
||||
|
||||
# And upload
|
||||
rsync -avc -e "ssh -i $HOME/.ssh/key -o StrictHostKeyChecking=no -p $SSH_PORT" \
|
||||
"$GITHUB_WORKSPACE/build/docs/site/" \
|
||||
"$GITHUB_WORKSPACE/projects/web/build/site/" \
|
||||
"$SSH_USER@$SSH_HOST:/$DEST"
|
||||
rsync -avc -e "ssh -i $HOME/.ssh/key -o StrictHostKeyChecking=no -p $SSH_PORT" \
|
||||
"$GITHUB_WORKSPACE/build/docs/javadoc/" \
|
||||
"$GITHUB_WORKSPACE/projects/common-api/build/docs/javadoc/" \
|
||||
"$SSH_USER@$SSH_HOST:/$DEST/javadoc"
|
||||
|
4
.github/workflows/make-doc.yml
vendored
4
.github/workflows/make-doc.yml
vendored
@@ -3,7 +3,7 @@ name: Build documentation
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- mc-1.16.x
|
||||
- mc-1.19.x
|
||||
|
||||
jobs:
|
||||
make_doc:
|
||||
@@ -29,7 +29,7 @@ jobs:
|
||||
run: ./gradlew compileJava --no-daemon || ./gradlew compileJava --no-daemon
|
||||
|
||||
- name: Generate documentation
|
||||
run: ./gradlew docWebsite javadoc --no-daemon
|
||||
run: ./gradlew docWebsite :common-api:javadoc --no-daemon
|
||||
|
||||
- name: Upload documentation
|
||||
run: .github/workflows/make-doc.sh 2> /dev/null
|
||||
|
10
.gitignore
vendored
10
.gitignore
vendored
@@ -2,15 +2,17 @@
|
||||
/classes
|
||||
/logs
|
||||
/build
|
||||
/projects/*/logs
|
||||
/projects/*/build
|
||||
/buildSrc/build
|
||||
/out
|
||||
/doc/out/
|
||||
/node_modules
|
||||
/.jqwik-database
|
||||
.jqwik-database
|
||||
|
||||
# Runtime directories
|
||||
/run
|
||||
/run-*
|
||||
/projects/*/run
|
||||
|
||||
*.ipr
|
||||
*.iws
|
||||
@@ -23,8 +25,6 @@
|
||||
/.project
|
||||
/.settings
|
||||
/.vscode
|
||||
bin/
|
||||
*.launch
|
||||
|
||||
/src/generated/resources/.cache
|
||||
/src/web/mount/*.d.ts
|
||||
/projects/*/src/generated/resources/.cache
|
||||
|
@@ -48,9 +48,7 @@ repos:
|
||||
|
||||
exclude: |
|
||||
(?x)^(
|
||||
src/generated|
|
||||
src/test/resources/test-rom/data/json-parsing/|
|
||||
src/testMod/server-files/|
|
||||
config/idea/|
|
||||
projects/[a-z]+/src/generated|
|
||||
projects/core/src/test/resources/test-rom/data/json-parsing/|
|
||||
.*\.dfpwm
|
||||
)
|
||||
|
139
CONTRIBUTING.md
139
CONTRIBUTING.md
@@ -4,96 +4,96 @@ provides an introduction as to how to get started in helping out.
|
||||
|
||||
If you've any other questions, [just ask the community][community] or [open an issue][new-issue].
|
||||
|
||||
## Table of Contents
|
||||
- [Reporting issues](#reporting-issues)
|
||||
- [Translations](#translations)
|
||||
- [Setting up a development environment](#setting-up-a-development-environment)
|
||||
- [Developing CC: Tweaked](#developing-cc-tweaked)
|
||||
- [Writing documentation](#writing-documentation)
|
||||
|
||||
## Reporting issues
|
||||
If you have a bug, suggestion, or other feedback, the best thing to do is [file an issue][new-issue]. When doing so,
|
||||
do use the issue templates - they provide a useful hint on what information to provide.
|
||||
If you have a bug, suggestion, or other feedback, the best thing to do is [file an issue][new-issue]. When doing so, do
|
||||
use the issue templates - they provide a useful hint on what information to provide.
|
||||
|
||||
## Translations
|
||||
Translations are managed through [Weblate], an online interface for managing language strings. This is synced
|
||||
automatically with GitHub, so please don't submit PRs adding/changing translations!
|
||||
|
||||
## Developing
|
||||
In order to develop CC: Tweaked, you'll need to download the source code and then run it. This is a pretty simple
|
||||
process. When building on Windows, Use `gradlew.bat` instead of `./gradlew`.
|
||||
## Setting up a development environment
|
||||
In order to develop CC: Tweaked, you'll need to download the source code and then run it.
|
||||
|
||||
- **Clone the repository:** `git clone https://github.com/cc-tweaked/CC-Tweaked.git && cd CC-Tweaked`
|
||||
- **Setup Forge:** `./gradlew build`
|
||||
- **Run Minecraft:** `./gradlew runClient` (or run the `GradleStart` class from your IDE).
|
||||
- **Optionally:** For small PRs (especially those only touching Lua code), it may be easier to use GitPod, which
|
||||
provides a pre-configured environment: [](https://gitpod.io/#https://github.com/cc-tweaked/CC-Tweaked/)
|
||||
- Make sure you've got the following software instealled:
|
||||
- Java Development Kit (JDK) installed. This can be downloaded from [Adoptium].
|
||||
- [Git](https://git-scm.com/).
|
||||
- If you want to work on documentation, [NodeJS][node].
|
||||
|
||||
Do note you will need to download the mod after compiling to test.
|
||||
- Download CC: Tweaked's source code:
|
||||
```
|
||||
git clone https://github.com/cc-tweaked/CC-Tweaked.git
|
||||
cd CC-Tweaked
|
||||
```
|
||||
|
||||
If you want to run CC:T in a normal Minecraft instance, run `./gradlew build` and copy the `.jar` from `build/libs`.
|
||||
These commands may take a few minutes to run the first time, as the environment is set up, but should be much faster
|
||||
afterwards.
|
||||
- Build CC: Tweaked with `./gradlew build`. This will be very slow the first time it runs, as it needs to download a
|
||||
lot of dependencies (and decompile Minecraft several times). Subsequent runs should be much faster!
|
||||
|
||||
The following sections describe the more niche sections of CC: Tweaked's build system. Some bits of these are
|
||||
quite-complex, and (dare I say) over-engineered, so you may wish to ignore them. Well tested/documented PRs are always
|
||||
preferred (and I'd definitely recommend setting up the tooling if you're doing serious development work), but for
|
||||
small changes it can be a lot.
|
||||
- You're now ready to start developing CC: Tweaked. Running `./gradlew :forge:runClient` or
|
||||
`./gradle :fabric:runClient` will start Minecraft under Forge and Fabric respectively.
|
||||
|
||||
### Code linters
|
||||
CC: Tweaked uses a couple of "linters" on its source code, to enforce a consistent style across the project. While these
|
||||
are run whenever you submit a PR, it's often useful to run this before committing.
|
||||
If you want to run CC:T in a normal Minecraft instance, run `./gradlew assemble` and copy the `.jar` from
|
||||
`projects/forge/build/libs` (for Forge) or `projects/fabric/build/libs` (for Fabric).
|
||||
|
||||
- **[Checkstyle]:** Checks Java code to ensure it is consistently formatted. This can be run with `./gradlew build` or
|
||||
`./gradle check`.
|
||||
- **[illuaminate]:** Checks Lua code for semantic and styleistic issues. This can be run with `./gradlew lintLua`.
|
||||
## Developing CC: Tweaked
|
||||
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
|
||||
looking to make your changes. As always, if you're not sure [do ask the community][community]!
|
||||
|
||||
### Documentation
|
||||
### Testing
|
||||
When making larger changes, it's may be useful to write a test to make sure your code works as expected.
|
||||
|
||||
CC: Tweaked has several test suites, each designed to test something different:
|
||||
|
||||
- In order to test CraftOS and its builtin APIs, we have a test suite written in Lua located at
|
||||
`projects/core/src/test/resources/test-rom/`. These don't rely on any Minecraft code, which means they can run on
|
||||
emulators, acting as a sort of compliance test.
|
||||
|
||||
These tests are written using a test system called "mcfly", heavily inspired by [busted]. Groups of tests go inside
|
||||
`describe` blocks, and a single test goes inside `it`. Assertions are generally written using `expect` (inspired by
|
||||
Hamcrest and the like). For instance, `expect(foo):eq("bar")` asserts that your variable `foo` is equal to the
|
||||
expected value `"bar"`.
|
||||
|
||||
These tests can be run with `./gradlew :core:test`.
|
||||
|
||||
- In-game functionality, such as the behaviour of blocks and items, is tested using [Minecraft's gametest
|
||||
system][mc-test] (`projects/common/src/testMod`). These tests spin up a server, spawn a structure for each test, and
|
||||
then run some code on the blocks defined in that structure.
|
||||
|
||||
These tests can be run with `./gradlew runGametest` (or `./gradle :forge:runGametest`/`./gradlew :fabric:runGametest`
|
||||
for a single loader).
|
||||
|
||||
For more information, [see the architecture document][architecture].
|
||||
|
||||
## Writing documentation
|
||||
When writing documentation for [CC: Tweaked's documentation website][docs], it may be useful to build the documentation
|
||||
and preview it yourself before submitting a PR.
|
||||
|
||||
Our documentation generation pipeline is rather complex, and involves invoking several external tools. Most of this
|
||||
complexity is hidden by Gradle, but you will need to perform some initial setup:
|
||||
You'll first need to [set up a development environment as above](#setting-up-a-development-environment).
|
||||
|
||||
- Install [Node/npm][node].
|
||||
- Run `npm ci` to install our Node dependencies.
|
||||
Once this is set up, you can now run `./gradlew docWebsite`. This generates documentation from our Lua and Java code,
|
||||
writing the resulting HTML into `./projects/web/build/site`, which can then be opened in a browser. When iterating on
|
||||
documentation, you can instead run `./gradlew docWebsite -t`, which will rebuild documentation every time you change a
|
||||
file.
|
||||
|
||||
You can now run `./gradlew docWebsite`. This generates documentation from our Lua and Java code, writing the resulting
|
||||
HTML into `./build/docs/site`.
|
||||
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
|
||||
markdown features - if you can, do check what the documentation looks like locally!
|
||||
|
||||
#### Writing documentation
|
||||
illuaminate's documentation system is not currently documented (somewhat ironic), but is _largely_ the same as
|
||||
[ldoc][ldoc]. Documentation comments are written in Markdown,
|
||||
|
||||
Our markdown engine does _not_ support GitHub flavoured markdown, and so does not support all the features one might
|
||||
expect. It is recommended that you build and preview the docs locally first.
|
||||
|
||||
When iterating on documentation, you can get Gradle to rebuild the website every time a file changes by running
|
||||
`./gradlew docWebsite -t`. This will take a couple of seconds to run, but definitely beats running it manually!
|
||||
|
||||
### Testing
|
||||
Thankfully running tests is much simpler than running the documentation generator! `./gradlew check` will run the
|
||||
entire test suite (and some additional bits of verification).
|
||||
|
||||
Before we get into writing tests, it's worth mentioning the various test suites that CC: Tweaked has:
|
||||
- "Core" Java (`./src/test/java`): These test core bits of the mod which don't require any Minecraft interaction.
|
||||
This includes the `@LuaFunction` system, file system code, etc...
|
||||
|
||||
These tests are run by `./gradlew test`.
|
||||
|
||||
- CraftOS (`./src/test/resources/test-rom/`): These tests are written in Lua, and ensure the Lua environment, libraries
|
||||
and programs work as expected. These are (generally) written to be able to be run on emulators too, to provide some
|
||||
sort of compliance test.
|
||||
|
||||
These tests are run by the '"Core" Java' test suite, and so are also run with `./gradlew test`.
|
||||
|
||||
- In-game (`./src/testMod/java/dan200/computercraft/ingame/`): These tests are run on an actual Minecraft server, using
|
||||
the same system Mojang do][mc-test]. The aim of these is to test in-game behaviour of blocks and peripherals.
|
||||
|
||||
These tests are run with `./gradlew runGametest`.
|
||||
|
||||
## CraftOS tests
|
||||
CraftOS's tests are written using a test system called "mcfly", heavily inspired by [busted] (and thus RSpec). Groups of
|
||||
tests go inside `describe` blocks, and a single test goes inside `it`.
|
||||
|
||||
Assertions are generally written using `expect` (inspired by Hamcrest and the like). For instance, `expect(foo):eq("bar")`
|
||||
asserts that your variable `foo` is equal to the expected value `"bar"`.
|
||||
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
|
||||
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"
|
||||
[community]: README.md#Community "Get in touch with the community."
|
||||
[community]: README.md#community "Get in touch with the community."
|
||||
[Adoptium]: https://adoptium.net/temurin/releases?version=17 "Download OpenJDK 17"
|
||||
[checkstyle]: https://checkstyle.org/
|
||||
[illuaminate]: https://github.com/SquidDev/illuaminate/ "Illuaminate on GitHub"
|
||||
[weblate]: https://i18n.tweaked.cc/projects/cc-tweaked/minecraft/ "CC: Tweaked weblate instance"
|
||||
@@ -102,3 +102,4 @@ asserts that your variable `foo` is equal to the expected value `"bar"`.
|
||||
[mc-test]: https://www.youtube.com/watch?v=vXaWOJTCYNg
|
||||
[busted]: https://github.com/Olivine-Labs/busted "busted: Elegant Lua unit testing."
|
||||
[node]: https://nodejs.org/en/ "Node.js"
|
||||
[architecture]: projects/ARCHITECTURE.md
|
||||
|
18
README.md
18
README.md
@@ -26,16 +26,26 @@ on is present.
|
||||
```groovy
|
||||
repositories {
|
||||
maven {
|
||||
url 'https://squiddev.cc/maven/'
|
||||
url "https://squiddev.cc/maven/"
|
||||
content {
|
||||
includeGroup 'org.squiddev'
|
||||
includeGroup("cc.tweaked")
|
||||
includeModule("org.squiddev", "Cobalt")
|
||||
includeModule("fuzs.forgeconfigapiport", "forgeconfigapiport-fabric")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly fg.deobf("org.squiddev:cc-tweaked-${mc_version}:${cct_version}:api")
|
||||
runtimeOnly fg.deobf("org.squiddev:cc-tweaked-${mc_version}:${cct_version}")
|
||||
// Vanilla (i.e. for multi-loader systems)
|
||||
compileOnly("cc.tweaked:cc-tweaked-$mcVersion-common-api")
|
||||
|
||||
// Forge Gradle
|
||||
compileOnly(fg.deobf("cc.tweaked:cc-tweaked-$mcVersion-forge-api:$cctVersion"))
|
||||
runtimeOnly(fg.deobf("cc.tweaked:cc-tweaked-$mcVersion-forge:$cctVersion"))
|
||||
|
||||
// Fabric Loom
|
||||
modCompileOnly("cc.tweaked:cc-tweaked-$mcVersion-fabric-api:$cctVersion")
|
||||
modRuntimeOnly("cc.tweaked:cc-tweaked-$mcVersion-fabric:$cctVersion")
|
||||
}
|
||||
```
|
||||
|
||||
|
450
build.gradle.kts
450
build.gradle.kts
@@ -1,402 +1,18 @@
|
||||
import cc.tweaked.gradle.*
|
||||
import net.darkhax.curseforgegradle.TaskPublishCurseForge
|
||||
import net.minecraftforge.gradle.common.util.RunConfig
|
||||
import org.jetbrains.gradle.ext.compiler
|
||||
import org.jetbrains.gradle.ext.settings
|
||||
|
||||
plugins {
|
||||
// Build
|
||||
alias(libs.plugins.forgeGradle)
|
||||
alias(libs.plugins.mixinGradle)
|
||||
alias(libs.plugins.librarian)
|
||||
alias(libs.plugins.shadow)
|
||||
// Publishing
|
||||
`maven-publish`
|
||||
alias(libs.plugins.curseForgeGradle)
|
||||
alias(libs.plugins.githubRelease)
|
||||
alias(libs.plugins.minotaur)
|
||||
// Utility
|
||||
publishing
|
||||
alias(libs.plugins.taskTree)
|
||||
|
||||
id("cc-tweaked.illuaminate")
|
||||
id("cc-tweaked.node")
|
||||
id("cc-tweaked.gametest")
|
||||
alias(libs.plugins.githubRelease)
|
||||
id("org.jetbrains.gradle.plugin.idea-ext")
|
||||
id("cc-tweaked")
|
||||
}
|
||||
|
||||
val isStable = true
|
||||
val isUnstable = project.properties["isUnstable"] == "true"
|
||||
val modVersion: String by extra
|
||||
val mcVersion: String by extra
|
||||
|
||||
group = "org.squiddev"
|
||||
version = modVersion
|
||||
base.archivesName.set("cc-tweaked-$mcVersion")
|
||||
|
||||
java.registerFeature("extraMods") { usingSourceSet(sourceSets.main.get()) }
|
||||
|
||||
sourceSets {
|
||||
main {
|
||||
resources.srcDir("src/generated/resources")
|
||||
}
|
||||
}
|
||||
|
||||
minecraft {
|
||||
runs {
|
||||
// configureEach would be better, but we need to eagerly configure configs or otherwise the run task doesn't
|
||||
// get set up properly.
|
||||
all {
|
||||
lazyToken("minecraft_classpath") {
|
||||
configurations["shade"].copyRecursive().resolve().joinToString(File.pathSeparator) { it.absolutePath }
|
||||
}
|
||||
|
||||
property("forge.logging.markers", "REGISTRIES")
|
||||
property("forge.logging.console.level", "debug")
|
||||
|
||||
forceExit = false
|
||||
|
||||
mods.register("computercraft") { source(sourceSets.main.get()) }
|
||||
}
|
||||
|
||||
val client by registering {
|
||||
workingDirectory(file("run"))
|
||||
}
|
||||
|
||||
val server by registering {
|
||||
workingDirectory(file("run/server"))
|
||||
arg("--nogui")
|
||||
}
|
||||
|
||||
val data by registering {
|
||||
workingDirectory(file("run"))
|
||||
args(
|
||||
"--mod",
|
||||
"computercraft",
|
||||
"--all",
|
||||
"--output",
|
||||
file("src/generated/resources/"),
|
||||
"--existing",
|
||||
file("src/main/resources/"),
|
||||
)
|
||||
property("cct.pretty-json", "true")
|
||||
}
|
||||
|
||||
fun RunConfig.configureForGameTest() {
|
||||
val old = lazyTokens.get("minecraft_classpath")
|
||||
lazyToken("minecraft_classpath") {
|
||||
// We do some terrible hacks here to basically find all things not already on the runtime classpath
|
||||
// and add them. /Except/ for our source sets, as those need to load inside the Minecraft classpath.
|
||||
val testMod = configurations["testModRuntimeClasspath"].resolve()
|
||||
val implementation = configurations.runtimeClasspath.get().resolve()
|
||||
val new = (testMod - implementation)
|
||||
.asSequence()
|
||||
.filter { it.isFile && !it.name.endsWith("-test-fixtures.jar") }
|
||||
.map { it.absolutePath }
|
||||
.joinToString(File.pathSeparator)
|
||||
if (old == null) new else old.get() + File.pathSeparator + new
|
||||
}
|
||||
|
||||
property("cctest.sources", file("src/testMod/resources/data/cctest").absolutePath)
|
||||
|
||||
arg("--mixin.config=computercraft-gametest.mixins.json")
|
||||
|
||||
mods.register("cctest") {
|
||||
source(sourceSets["testMod"])
|
||||
source(sourceSets["testFixtures"])
|
||||
}
|
||||
}
|
||||
|
||||
val testClient by registering {
|
||||
workingDirectory(file("run/testClient"))
|
||||
parent(client.get())
|
||||
configureForGameTest()
|
||||
}
|
||||
|
||||
val gameTestServer by registering {
|
||||
workingDirectory(file("run/testServer"))
|
||||
configureForGameTest()
|
||||
|
||||
property("forge.logging.console.level", "info")
|
||||
}
|
||||
}
|
||||
|
||||
mappings("parchment", "${libs.versions.parchmentMc.get()}-${libs.versions.parchment.get()}-$mcVersion")
|
||||
|
||||
accessTransformer(file("src/main/resources/META-INF/accesstransformer.cfg"))
|
||||
}
|
||||
|
||||
mixin {
|
||||
add(sourceSets.main.get(), "computercraft.mixins.refmap.json")
|
||||
config("computercraft.mixins.json")
|
||||
}
|
||||
|
||||
reobf {
|
||||
register("shadowJar")
|
||||
}
|
||||
|
||||
configurations {
|
||||
val shade by registering { isTransitive = false }
|
||||
implementation { extendsFrom(shade.get()) }
|
||||
register("cctJavadoc")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
minecraft("net.minecraftforge:forge:$mcVersion-${libs.versions.forge.get()}")
|
||||
annotationProcessor("org.spongepowered:mixin:0.8.5:processor")
|
||||
|
||||
compileOnly(libs.jetbrainsAnnotations)
|
||||
annotationProcessorEverywhere(libs.autoService)
|
||||
|
||||
"extraModsCompileOnly"(fg.deobf("mezz.jei:jei-1.19.2-forge-api:11.3.0.262"))
|
||||
"extraModsCompileOnly"(fg.deobf("mezz.jei:jei-1.19.2-common-api:11.3.0.262"))
|
||||
"extraModsRuntimeOnly"(fg.deobf("mezz.jei:jei-1.19.2-forge:11.3.0.262"))
|
||||
"extraModsCompileOnly"(fg.deobf("maven.modrinth:oculus:1.2.5"))
|
||||
|
||||
"shade"(libs.cobalt)
|
||||
"shade"("io.netty:netty-codec-http:4.1.76.Final")
|
||||
|
||||
testFixturesApi(libs.bundles.test)
|
||||
testFixturesApi(libs.bundles.kotlin)
|
||||
|
||||
testImplementation(libs.bundles.test)
|
||||
testImplementation(libs.bundles.kotlin)
|
||||
testRuntimeOnly(libs.bundles.testRuntime)
|
||||
|
||||
"cctJavadoc"(libs.cctJavadoc)
|
||||
}
|
||||
|
||||
illuaminate {
|
||||
version.set(libs.versions.illuaminate)
|
||||
}
|
||||
|
||||
// Compile tasks
|
||||
|
||||
tasks.javadoc {
|
||||
include("dan200/computercraft/api/**/*.java")
|
||||
(options as StandardJavadocDocletOptions).links("https://docs.oracle.com/en/java/javase/17/docs/api/")
|
||||
}
|
||||
|
||||
val apiJar by tasks.registering(Jar::class) {
|
||||
archiveClassifier.set("api")
|
||||
from(sourceSets.main.get().output) {
|
||||
include("dan200/computercraft/api/**/*")
|
||||
}
|
||||
}
|
||||
|
||||
tasks.assemble { dependsOn(apiJar) }
|
||||
|
||||
val luaJavadoc by tasks.registering(Javadoc::class) {
|
||||
description = "Generates documentation for Java-side Lua functions."
|
||||
group = JavaBasePlugin.DOCUMENTATION_GROUP
|
||||
|
||||
source(sourceSets.main.get().java)
|
||||
setDestinationDir(buildDir.resolve("docs/luaJavadoc"))
|
||||
classpath = sourceSets.main.get().compileClasspath
|
||||
|
||||
options.docletpath = configurations["cctJavadoc"].files.toList()
|
||||
options.doclet = "cc.tweaked.javadoc.LuaDoclet"
|
||||
(options as StandardJavadocDocletOptions).noTimestamp(false)
|
||||
|
||||
javadocTool.set(
|
||||
javaToolchains.javadocToolFor {
|
||||
languageVersion.set(cc.tweaked.gradle.CCTweakedPlugin.JAVA_VERSION)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
tasks.processResources {
|
||||
inputs.property("modVersion", modVersion)
|
||||
inputs.property("forgeVersion", libs.versions.forge.get())
|
||||
inputs.property("gitHash", cct.gitHash)
|
||||
|
||||
filesMatching("data/computercraft/lua/rom/help/credits.txt") {
|
||||
expand(mapOf("gitContributors" to cct.gitContributors.get().joinToString("\n")))
|
||||
}
|
||||
|
||||
filesMatching("META-INF/mods.toml") {
|
||||
expand(mapOf("forgeVersion" to libs.versions.forge.get(), "file" to mapOf("jarVersion" to modVersion)))
|
||||
}
|
||||
}
|
||||
|
||||
tasks.jar {
|
||||
isReproducibleFileOrder = true
|
||||
isPreserveFileTimestamps = false
|
||||
finalizedBy("reobfJar")
|
||||
archiveClassifier.set("slim")
|
||||
|
||||
manifest {
|
||||
attributes(
|
||||
"Specification-Title" to "computercraft",
|
||||
"Specification-Vendor" to "SquidDev",
|
||||
"Specification-Version" to "1",
|
||||
"Implementation-Title" to "cctweaked",
|
||||
"Implementation-Version" to modVersion,
|
||||
"Implementation-Vendor" to "SquidDev",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
tasks.shadowJar {
|
||||
finalizedBy("reobfShadowJar")
|
||||
|
||||
archiveClassifier.set("")
|
||||
configurations = listOf(project.configurations["shade"])
|
||||
relocate("org.squiddev.cobalt", "cc.tweaked.internal.cobalt")
|
||||
minimize()
|
||||
}
|
||||
|
||||
tasks.assemble { dependsOn("shadowJar") }
|
||||
|
||||
// Web tasks
|
||||
|
||||
val rollup by tasks.registering(NpxExecToDir::class) {
|
||||
group = LifecycleBasePlugin.BUILD_GROUP
|
||||
description = "Bundles JS into rollup"
|
||||
|
||||
// Sources
|
||||
inputs.files(fileTree("src/web")).withPropertyName("sources")
|
||||
// Config files
|
||||
inputs.file("tsconfig.json").withPropertyName("Typescript config")
|
||||
inputs.file("rollup.config.js").withPropertyName("Rollup config")
|
||||
|
||||
// Output directory. Also defined in illuaminate.sexp and rollup.config.js
|
||||
output.set(buildDir.resolve("rollup"))
|
||||
|
||||
args = listOf("rollup", "--config", "rollup.config.js")
|
||||
}
|
||||
|
||||
val illuaminateDocs by tasks.registering(IlluaminateExecToDir::class) {
|
||||
group = JavaBasePlugin.DOCUMENTATION_GROUP
|
||||
description = "Generates docs using Illuaminate"
|
||||
|
||||
// Config files
|
||||
inputs.file("illuaminate.sexp").withPropertyName("illuaminate.sexp")
|
||||
// Sources
|
||||
inputs.files(fileTree("doc")).withPropertyName("docs")
|
||||
inputs.files(fileTree("src/main/resources/data/computercraft/lua")).withPropertyName("lua rom")
|
||||
inputs.files(luaJavadoc)
|
||||
// Additional assets
|
||||
inputs.files(rollup)
|
||||
inputs.file("src/web/styles.css").withPropertyName("styles")
|
||||
|
||||
// Output directory. Also defined in illuaminate.sexp and transform.tsx
|
||||
output.set(buildDir.resolve("illuaminate"))
|
||||
|
||||
args = listOf("doc-gen")
|
||||
}
|
||||
|
||||
val jsxDocs by tasks.registering(NpxExecToDir::class) {
|
||||
group = JavaBasePlugin.DOCUMENTATION_GROUP
|
||||
description = "Post-processes documentation to statically render some dynamic content."
|
||||
|
||||
// Config files
|
||||
inputs.file("tsconfig.json").withPropertyName("Typescript config")
|
||||
// Sources
|
||||
inputs.files(fileTree("src/web")).withPropertyName("sources")
|
||||
inputs.file("src/generated/export/index.json").withPropertyName("export")
|
||||
inputs.files(illuaminateDocs)
|
||||
|
||||
// Output directory. Also defined in src/web/transform.tsx
|
||||
output.set(buildDir.resolve("jsxDocs"))
|
||||
|
||||
args = listOf("ts-node", "-T", "--esm", "src/web/transform.tsx")
|
||||
}
|
||||
|
||||
val docWebsite by tasks.registering(Copy::class) {
|
||||
group = JavaBasePlugin.DOCUMENTATION_GROUP
|
||||
description = "Assemble docs and assets together into the documentation website."
|
||||
|
||||
from(jsxDocs)
|
||||
|
||||
from("doc") {
|
||||
include("logo.png")
|
||||
include("images/**")
|
||||
}
|
||||
from(rollup) { exclude("index.js") }
|
||||
from(illuaminateDocs) { exclude("**/*.html") }
|
||||
from("src/generated/export/items") { into("images/items") }
|
||||
|
||||
into(buildDir.resolve("docs/site"))
|
||||
}
|
||||
|
||||
// Check tasks
|
||||
|
||||
tasks.test {
|
||||
systemProperty("cct.test-files", buildDir.resolve("tmp/testFiles").absolutePath)
|
||||
}
|
||||
|
||||
val lintLua by tasks.registering(IlluaminateExec::class) {
|
||||
group = JavaBasePlugin.VERIFICATION_GROUP
|
||||
description = "Lint Lua (and Lua docs) with illuaminate"
|
||||
|
||||
// Config files
|
||||
inputs.file("illuaminate.sexp").withPropertyName("illuaminate.sexp")
|
||||
// Sources
|
||||
inputs.files(fileTree("doc")).withPropertyName("docs")
|
||||
inputs.files(fileTree("src/main/resources/data/computercraft/lua")).withPropertyName("lua rom")
|
||||
inputs.files(luaJavadoc)
|
||||
|
||||
args = listOf("lint")
|
||||
|
||||
doFirst { if (System.getenv("GITHUB_ACTIONS") != null) println("::add-matcher::.github/matchers/illuaminate.json") }
|
||||
doLast { if (System.getenv("GITHUB_ACTIONS") != null) println("::remove-matcher owner=illuaminate::") }
|
||||
}
|
||||
|
||||
val runGametest by tasks.registering(JavaExec::class) {
|
||||
group = LifecycleBasePlugin.VERIFICATION_GROUP
|
||||
description = "Runs tests on a temporary Minecraft instance."
|
||||
dependsOn("cleanRunGametest")
|
||||
|
||||
// Copy from runGameTestServer. We do it in this slightly odd way as runGameTestServer
|
||||
// isn't created until the task is configured (which is no good for us).
|
||||
val exec = tasks.getByName<JavaExec>("runGameTestServer")
|
||||
dependsOn(exec.dependsOn)
|
||||
exec.copyToFull(this)
|
||||
}
|
||||
|
||||
cct.jacoco(runGametest)
|
||||
|
||||
tasks.check { dependsOn(runGametest) }
|
||||
|
||||
// Upload tasks
|
||||
|
||||
val checkChangelog by tasks.registering(CheckChangelog::class) {
|
||||
version.set(modVersion)
|
||||
whatsNew.set(file("src/main/resources/data/computercraft/lua/rom/help/whatsnew.md"))
|
||||
changelog.set(file("src/main/resources/data/computercraft/lua/rom/help/changelog.md"))
|
||||
}
|
||||
|
||||
tasks.check { dependsOn(checkChangelog) }
|
||||
|
||||
val publishCurseForge by tasks.registering(TaskPublishCurseForge::class) {
|
||||
group = PublishingPlugin.PUBLISH_TASK_GROUP
|
||||
description = "Upload artifacts to CurseForge"
|
||||
|
||||
apiToken = findProperty("curseForgeApiKey") ?: ""
|
||||
enabled = apiToken != ""
|
||||
|
||||
val mainFile = upload("282001", tasks.shadowJar.get().archiveFile)
|
||||
dependsOn(tasks.shadowJar) // Ughr.
|
||||
mainFile.changelog = "Release notes can be found on the [GitHub repository](https://github.com/cc-tweaked/CC-Tweaked/releases/tag/v$mcVersion-$modVersion)."
|
||||
mainFile.changelogType = "markdown"
|
||||
mainFile.releaseType = if (isStable) "release" else "alpha"
|
||||
mainFile.gameVersions.add(mcVersion)
|
||||
}
|
||||
|
||||
tasks.publish { dependsOn(publishCurseForge) }
|
||||
|
||||
modrinth {
|
||||
token.set(findProperty("modrinthApiKey") as String? ?: "")
|
||||
projectId.set("gu7yAYhd")
|
||||
versionNumber.set("$mcVersion-$modVersion")
|
||||
versionName.set(modVersion)
|
||||
versionType.set(if (isStable) "release" else "alpha")
|
||||
uploadFile.set(tasks.shadowJar as Any)
|
||||
gameVersions.add(mcVersion)
|
||||
changelog.set("Release notes can be found on the [GitHub repository](https://github.com/cc-tweaked/CC-Tweaked/releases/tag/v$mcVersion-$modVersion).")
|
||||
|
||||
syncBodyFrom.set(provider { file("doc/mod-page.md").readText() })
|
||||
}
|
||||
|
||||
tasks.publish { dependsOn(tasks.modrinth) }
|
||||
|
||||
githubRelease {
|
||||
token(findProperty("githubApiKey") as String? ?: "")
|
||||
owner.set("cc-tweaked")
|
||||
@@ -407,54 +23,30 @@ githubRelease {
|
||||
releaseName.set("[$mcVersion] $modVersion")
|
||||
body.set(
|
||||
provider {
|
||||
"## " + file("src/main/resources/data/computercraft/lua/rom/help/whatsnew.md")
|
||||
"## " + project(":core").file("src/main/resources/data/computercraft/lua/rom/help/whatsnew.md")
|
||||
.readLines()
|
||||
.takeWhile { it != "Type \"help changelog\" to see the full version history." }
|
||||
.joinToString("\n").trim()
|
||||
},
|
||||
)
|
||||
prerelease.set(!isStable)
|
||||
prerelease.set(isUnstable)
|
||||
}
|
||||
|
||||
tasks.publish { dependsOn(tasks.githubRelease) }
|
||||
|
||||
publishing {
|
||||
publications {
|
||||
register<MavenPublication>("maven") {
|
||||
artifactId = base.archivesName.get()
|
||||
from(components["java"])
|
||||
artifact(apiJar)
|
||||
fg.component(this)
|
||||
|
||||
pom {
|
||||
name.set("CC: Tweaked")
|
||||
description.set("CC: Tweaked is a fork of ComputerCraft, adding programmable computers, turtles and more to Minecraft.")
|
||||
url.set("https://github.com/cc-tweaked/CC-Tweaked")
|
||||
|
||||
scm {
|
||||
url.set("https://github.com/cc-tweaked/CC-Tweaked.git")
|
||||
}
|
||||
|
||||
issueManagement {
|
||||
system.set("github")
|
||||
url.set("https://github.com/cc-tweaked/CC-Tweaked/issues")
|
||||
}
|
||||
|
||||
licenses {
|
||||
license {
|
||||
name.set("ComputerCraft Public License, Version 1.0")
|
||||
url.set("https://github.com/cc-tweaked/CC-Tweaked/blob/HEAD/LICENSE")
|
||||
}
|
||||
}
|
||||
idea.project.settings.compiler.javac {
|
||||
// We want ErrorProne to be present when compiling via IntelliJ, as it offers some helpful warnings
|
||||
// and errors. Loop through our source sets and find the appropriate flags.
|
||||
moduleJavacAdditionalOptions = subprojects
|
||||
.asSequence()
|
||||
.map { evaluationDependsOn(it.path) }
|
||||
.flatMap { project ->
|
||||
val sourceSets = project.extensions.findByType(SourceSetContainer::class) ?: return@flatMap sequenceOf()
|
||||
sourceSets.asSequence().map { sourceSet ->
|
||||
val name = "${idea.project.name}.${project.name}.${sourceSet.name}"
|
||||
val compile = project.tasks.named(sourceSet.compileJavaTaskName, JavaCompile::class).get()
|
||||
name to compile.options.allCompilerArgs.joinToString(" ") { if (it.contains(" ")) "\"$it\"" else it }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
repositories {
|
||||
maven("https://squiddev.cc/maven") {
|
||||
name = "SquidDev"
|
||||
|
||||
credentials(PasswordCredentials::class)
|
||||
}
|
||||
}
|
||||
.toMap()
|
||||
}
|
||||
|
@@ -3,14 +3,51 @@ plugins {
|
||||
`kotlin-dsl`
|
||||
}
|
||||
|
||||
// Duplicated in settings.gradle.kts
|
||||
repositories {
|
||||
mavenCentral()
|
||||
gradlePluginPortal()
|
||||
|
||||
maven("https://maven.minecraftforge.net") {
|
||||
name = "Forge"
|
||||
content {
|
||||
includeGroup("net.minecraftforge")
|
||||
includeGroup("net.minecraftforge.gradle")
|
||||
}
|
||||
}
|
||||
|
||||
maven("https://maven.parchmentmc.org") {
|
||||
name = "Librarian"
|
||||
content {
|
||||
includeGroupByRegex("^org\\.parchmentmc.*")
|
||||
}
|
||||
}
|
||||
|
||||
maven("https://repo.spongepowered.org/repository/maven-public/") {
|
||||
name = "Sponge"
|
||||
content {
|
||||
includeGroup("org.spongepowered")
|
||||
}
|
||||
}
|
||||
|
||||
maven("https://maven.fabricmc.net/") {
|
||||
name = "Fabric"
|
||||
content {
|
||||
includeGroup("net.fabricmc")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(libs.errorProne.plugin)
|
||||
implementation(libs.kotlin.plugin)
|
||||
implementation(libs.spotless)
|
||||
|
||||
implementation(libs.fabric.loom)
|
||||
implementation(libs.forgeGradle)
|
||||
implementation(libs.librarian)
|
||||
implementation(libs.quiltflower)
|
||||
implementation(libs.vanillaGradle)
|
||||
}
|
||||
|
||||
gradlePlugin {
|
||||
|
66
buildSrc/src/main/kotlin/cc-tweaked.fabric.gradle.kts
Normal file
66
buildSrc/src/main/kotlin/cc-tweaked.fabric.gradle.kts
Normal file
@@ -0,0 +1,66 @@
|
||||
/** Default configuration for Fabric projects. */
|
||||
|
||||
import cc.tweaked.gradle.CCTweakedExtension
|
||||
import cc.tweaked.gradle.CCTweakedPlugin
|
||||
import cc.tweaked.gradle.IdeaRunConfigurations
|
||||
import cc.tweaked.gradle.MinecraftConfigurations
|
||||
|
||||
plugins {
|
||||
`java-library`
|
||||
id("fabric-loom")
|
||||
id("io.github.juuxel.loom-quiltflower")
|
||||
id("cc-tweaked.java-convention")
|
||||
}
|
||||
|
||||
plugins.apply(CCTweakedPlugin::class.java)
|
||||
|
||||
val mcVersion: String by extra
|
||||
|
||||
repositories {
|
||||
maven("https://maven.parchmentmc.org/") {
|
||||
name = "Parchment"
|
||||
content {
|
||||
includeGroup("org.parchmentmc.data")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
loom {
|
||||
splitEnvironmentSourceSets()
|
||||
splitModDependencies.set(true)
|
||||
}
|
||||
|
||||
MinecraftConfigurations.setup(project)
|
||||
|
||||
extensions.configure(CCTweakedExtension::class.java) {
|
||||
linters(minecraft = true, loader = "fabric")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
val libs = project.extensions.getByType<VersionCatalogsExtension>().named("libs")
|
||||
|
||||
minecraft("com.mojang:minecraft:$mcVersion")
|
||||
mappings(
|
||||
loom.layered {
|
||||
officialMojangMappings()
|
||||
parchment(
|
||||
project.dependencies.create(
|
||||
group = "org.parchmentmc.data",
|
||||
name = "parchment-${libs.findVersion("parchmentMc").get()}",
|
||||
version = libs.findVersion("parchment").get().toString(),
|
||||
ext = "zip",
|
||||
),
|
||||
)
|
||||
},
|
||||
)
|
||||
|
||||
modImplementation(libs.findLibrary("fabric-loader").get())
|
||||
modImplementation(libs.findLibrary("fabric-api").get())
|
||||
|
||||
// Depend on error prone annotations to silence a lot of compile warnings.
|
||||
compileOnlyApi(libs.findLibrary("errorProne.annotations").get())
|
||||
}
|
||||
|
||||
tasks.ideaSyncTask {
|
||||
doLast { IdeaRunConfigurations(project).patch() }
|
||||
}
|
39
buildSrc/src/main/kotlin/cc-tweaked.forge.gradle.kts
Normal file
39
buildSrc/src/main/kotlin/cc-tweaked.forge.gradle.kts
Normal file
@@ -0,0 +1,39 @@
|
||||
/** Default configuration for Forge projects. */
|
||||
|
||||
import cc.tweaked.gradle.CCTweakedExtension
|
||||
import cc.tweaked.gradle.CCTweakedPlugin
|
||||
import cc.tweaked.gradle.IdeaRunConfigurations
|
||||
import cc.tweaked.gradle.MinecraftConfigurations
|
||||
|
||||
plugins {
|
||||
id("cc-tweaked.java-convention")
|
||||
id("net.minecraftforge.gradle")
|
||||
id("org.parchmentmc.librarian.forgegradle")
|
||||
}
|
||||
|
||||
plugins.apply(CCTweakedPlugin::class.java)
|
||||
|
||||
val mcVersion: String by extra
|
||||
|
||||
minecraft {
|
||||
val libs = project.extensions.getByType<VersionCatalogsExtension>().named("libs")
|
||||
mappings("parchment", "${libs.findVersion("parchmentMc").get()}-${libs.findVersion("parchment").get()}-$mcVersion")
|
||||
|
||||
accessTransformer(project(":forge").file("src/main/resources/META-INF/accesstransformer.cfg"))
|
||||
}
|
||||
|
||||
MinecraftConfigurations.setup(project)
|
||||
|
||||
extensions.configure(CCTweakedExtension::class.java) {
|
||||
linters(minecraft = true, loader = "forge")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
val libs = project.extensions.getByType<VersionCatalogsExtension>().named("libs")
|
||||
"minecraft"("net.minecraftforge:forge:$mcVersion-${libs.findVersion("forge").get()}")
|
||||
}
|
||||
|
||||
tasks.configureEach {
|
||||
// genIntellijRuns isn't registered until much later, so we need this silly hijinks.
|
||||
if (name == "genIntellijRuns") doLast { IdeaRunConfigurations(project).patch() }
|
||||
}
|
@@ -1,4 +1,5 @@
|
||||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||
import cc.tweaked.gradle.clientClasses
|
||||
import cc.tweaked.gradle.commonClasses
|
||||
|
||||
/**
|
||||
* Sets up the configurations for writing game tests.
|
||||
@@ -11,12 +12,13 @@ plugins {
|
||||
id("cc-tweaked.java-convention")
|
||||
}
|
||||
|
||||
val main = sourceSets.main.get()
|
||||
val main = sourceSets["main"]
|
||||
val client = sourceSets["client"]
|
||||
|
||||
// Both testMod and testFixtures inherit from the main classpath, just so we have access to Minecraft classes.
|
||||
// Both testMod and testFixtures inherit from the main and client classpath, just so we have access to Minecraft classes.
|
||||
val testMod by sourceSets.creating {
|
||||
compileClasspath += main.compileClasspath
|
||||
runtimeClasspath += main.runtimeClasspath
|
||||
compileClasspath += main.compileClasspath + client.compileClasspath
|
||||
runtimeClasspath += main.runtimeClasspath + client.runtimeClasspath
|
||||
}
|
||||
|
||||
configurations {
|
||||
@@ -32,12 +34,13 @@ configurations {
|
||||
// Like the main test configurations, we're safe to depend on source set outputs.
|
||||
dependencies {
|
||||
add(testMod.implementationConfigurationName, main.output)
|
||||
add(testMod.implementationConfigurationName, client.output)
|
||||
}
|
||||
|
||||
// Similar to java-test-fixtures, but tries to avoid putting the obfuscated jar on the classpath.
|
||||
|
||||
val testFixtures by sourceSets.creating {
|
||||
compileClasspath += main.compileClasspath
|
||||
compileClasspath += main.compileClasspath + client.compileClasspath
|
||||
}
|
||||
|
||||
java.registerFeature("testFixtures") {
|
||||
@@ -46,8 +49,12 @@ java.registerFeature("testFixtures") {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
add(testFixtures.implementationConfigurationName, main.output)
|
||||
val libs = project.extensions.getByType<VersionCatalogsExtension>().named("libs")
|
||||
add(testFixtures.apiConfigurationName, libs.findBundle("test").get())
|
||||
// Consumers of this project already have the common and client classes on the classpath, so it's fine for these
|
||||
// to be compile-only.
|
||||
add(testFixtures.compileOnlyApiConfigurationName, commonClasses(project))
|
||||
add(testFixtures.compileOnlyApiConfigurationName, clientClasses(project))
|
||||
|
||||
testImplementation(testFixtures(project))
|
||||
add(testMod.implementationConfigurationName, testFixtures(project))
|
||||
}
|
||||
|
@@ -1,23 +1,35 @@
|
||||
import cc.tweaked.gradle.CCTweakedExtension
|
||||
import cc.tweaked.gradle.CCTweakedPlugin
|
||||
import cc.tweaked.gradle.LicenseHeader
|
||||
import com.diffplug.gradle.spotless.FormatExtension
|
||||
import com.diffplug.spotless.LineEnding
|
||||
import net.ltgt.gradle.errorprone.CheckSeverity
|
||||
import net.ltgt.gradle.errorprone.errorprone
|
||||
import java.nio.charset.StandardCharsets
|
||||
|
||||
plugins {
|
||||
`java-library`
|
||||
idea
|
||||
jacoco
|
||||
checkstyle
|
||||
id("com.diffplug.spotless")
|
||||
id("net.ltgt.errorprone")
|
||||
}
|
||||
|
||||
val modVersion: String by extra
|
||||
val mcVersion: String by extra
|
||||
|
||||
group = "cc.tweaked"
|
||||
version = modVersion
|
||||
|
||||
base.archivesName.convention("cc-tweaked-$mcVersion-${project.name}")
|
||||
|
||||
java {
|
||||
toolchain {
|
||||
languageVersion.set(CCTweakedPlugin.JAVA_VERSION)
|
||||
}
|
||||
|
||||
withSourcesJar()
|
||||
withJavadocJar()
|
||||
}
|
||||
|
||||
repositories {
|
||||
@@ -28,10 +40,15 @@ repositories {
|
||||
includeGroup("org.squiddev")
|
||||
includeGroup("cc.tweaked")
|
||||
// Things we mirror
|
||||
includeGroup("com.blamejared.crafttweaker")
|
||||
includeGroup("commoble.morered")
|
||||
includeGroup("dev.architectury")
|
||||
includeGroup("maven.modrinth")
|
||||
includeGroup("me.shedaniel")
|
||||
includeGroup("me.shedaniel.cloth")
|
||||
includeGroup("mezz.jei")
|
||||
includeModule("com.terraformersmc", "modmenu")
|
||||
includeModule("fuzs.forgeconfigapiport", "forgeconfigapiport-fabric")
|
||||
// Until https://github.com/SpongePowered/Mixin/pull/593 is merged
|
||||
includeModule("org.spongepowered", "mixin")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -39,6 +56,9 @@ repositories {
|
||||
dependencies {
|
||||
val libs = project.extensions.getByType<VersionCatalogsExtension>().named("libs")
|
||||
checkstyle(libs.findLibrary("checkstyle").get())
|
||||
|
||||
errorprone(libs.findLibrary("errorProne-core").get())
|
||||
errorprone(libs.findLibrary("nullAway").get())
|
||||
}
|
||||
|
||||
// Configure default JavaCompile tasks with our arguments.
|
||||
@@ -46,13 +66,68 @@ sourceSets.all {
|
||||
tasks.named(compileJavaTaskName, JavaCompile::class.java) {
|
||||
// Processing just gives us "No processor claimed any of these annotations", so skip that!
|
||||
options.compilerArgs.addAll(listOf("-Xlint", "-Xlint:-processing"))
|
||||
|
||||
options.errorprone {
|
||||
check("InvalidBlockTag", CheckSeverity.OFF) // Broken by @cc.xyz
|
||||
check("InvalidParam", CheckSeverity.OFF) // Broken by records.
|
||||
check("InlineMeSuggester", CheckSeverity.OFF) // Minecraft uses @Deprecated liberally
|
||||
// Too many false positives right now. Maybe we need an indirection for it later on.
|
||||
check("ReferenceEquality", CheckSeverity.OFF)
|
||||
check("UnusedVariable", CheckSeverity.OFF) // Too many false positives with records.
|
||||
check("OperatorPrecedence", CheckSeverity.OFF) // For now.
|
||||
check("AlreadyChecked", CheckSeverity.OFF) // Seems to be broken?
|
||||
check("NonOverridingEquals", CheckSeverity.OFF) // Peripheral.equals makes this hard to avoid
|
||||
check("FutureReturnValueIgnored", CheckSeverity.OFF) // Too many false positives with Netty
|
||||
|
||||
check("NullAway", CheckSeverity.ERROR)
|
||||
option("NullAway:AnnotatedPackages", listOf("dan200.computercraft", "net.fabricmc.fabric.api").joinToString(","))
|
||||
option("NullAway:ExcludedFieldAnnotations", listOf("org.spongepowered.asm.mixin.Shadow").joinToString(","))
|
||||
option("NullAway:CastToNonNullMethod", "dan200.computercraft.core.util.Nullability.assertNonNull")
|
||||
option("NullAway:CheckOptionalEmptiness")
|
||||
option("NullAway:AcknowledgeRestrictiveAnnotations")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tasks.compileTestJava {
|
||||
options.errorprone {
|
||||
check("NullAway", CheckSeverity.OFF)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
tasks.withType(JavaCompile::class.java).configureEach {
|
||||
options.encoding = "UTF-8"
|
||||
}
|
||||
|
||||
tasks.withType(AbstractArchiveTask::class.java).configureEach {
|
||||
isPreserveFileTimestamps = false
|
||||
isReproducibleFileOrder = true
|
||||
dirMode = Integer.valueOf("755", 8)
|
||||
fileMode = Integer.valueOf("664", 8)
|
||||
}
|
||||
|
||||
tasks.jar {
|
||||
manifest {
|
||||
attributes(
|
||||
"Specification-Title" to "computercraft",
|
||||
"Specification-Vendor" to "SquidDev",
|
||||
"Specification-Version" to "1",
|
||||
"Implementation-Title" to "cctweaked-${project.name}",
|
||||
"Implementation-Version" to modVersion,
|
||||
"Implementation-Vendor" to "SquidDev",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
tasks.javadoc {
|
||||
options {
|
||||
val stdOptions = this as StandardJavadocDocletOptions
|
||||
stdOptions.addBooleanOption("Xdoclint:all,-missing", true)
|
||||
stdOptions.links("https://docs.oracle.com/en/java/javase/17/docs/api/")
|
||||
}
|
||||
}
|
||||
|
||||
tasks.test {
|
||||
finalizedBy("jacocoTestReport")
|
||||
|
||||
@@ -67,6 +142,14 @@ tasks.withType(JacocoReport::class.java).configureEach {
|
||||
reports.html.required.set(true)
|
||||
}
|
||||
|
||||
project.plugins.withType(CCTweakedPlugin::class.java) {
|
||||
// Set up jacoco to read from /all/ our source directories.
|
||||
val cct = project.extensions.getByType<CCTweakedExtension>()
|
||||
project.tasks.named("jacocoTestReport", JacocoReport::class.java) {
|
||||
for (ref in cct.sourceSets.get()) sourceDirectories.from(ref.allSource.sourceDirectories)
|
||||
}
|
||||
}
|
||||
|
||||
spotless {
|
||||
encoding = StandardCharsets.UTF_8
|
||||
lineEndings = LineEnding.UNIX
|
||||
@@ -78,8 +161,8 @@ spotless {
|
||||
}
|
||||
|
||||
val licenser = LicenseHeader.create(
|
||||
api = file("config/license/api.txt"),
|
||||
main = file("config/license/main.txt"),
|
||||
api = rootProject.file("config/license/api.txt"),
|
||||
main = rootProject.file("config/license/main.txt"),
|
||||
)
|
||||
|
||||
java {
|
||||
@@ -104,3 +187,12 @@ spotless {
|
||||
ktlint().editorConfigOverride(ktlintConfig)
|
||||
}
|
||||
}
|
||||
|
||||
idea.module {
|
||||
excludeDirs.addAll(project.files("run", "out", "logs").files)
|
||||
|
||||
// Force Gradle to write to inherit the output directory from the parent, instead of writing to out/xxx/classes.
|
||||
// This is required for Loom, and we patch Forge's run configurations to work there.
|
||||
// TODO: Submit a patch to Forge to support ProjectRootManager.
|
||||
inheritOutputDirs = true
|
||||
}
|
||||
|
45
buildSrc/src/main/kotlin/cc-tweaked.publishing.gradle.kts
Normal file
45
buildSrc/src/main/kotlin/cc-tweaked.publishing.gradle.kts
Normal file
@@ -0,0 +1,45 @@
|
||||
import org.gradle.kotlin.dsl.`maven-publish`
|
||||
|
||||
plugins {
|
||||
`java-library`
|
||||
`maven-publish`
|
||||
}
|
||||
|
||||
publishing {
|
||||
publications {
|
||||
register<MavenPublication>("maven") {
|
||||
artifactId = base.archivesName.get()
|
||||
from(components["java"])
|
||||
|
||||
pom {
|
||||
name.set("CC: Tweaked")
|
||||
description.set("CC: Tweaked is a fork of ComputerCraft, adding programmable computers, turtles and more to Minecraft.")
|
||||
url.set("https://github.com/cc-tweaked/CC-Tweaked")
|
||||
|
||||
scm {
|
||||
url.set("https://github.com/cc-tweaked/CC-Tweaked.git")
|
||||
}
|
||||
|
||||
issueManagement {
|
||||
system.set("github")
|
||||
url.set("https://github.com/cc-tweaked/CC-Tweaked/issues")
|
||||
}
|
||||
|
||||
licenses {
|
||||
license {
|
||||
name.set("ComputerCraft Public License, Version 1.0")
|
||||
url.set("https://github.com/cc-tweaked/CC-Tweaked/blob/HEAD/LICENSE")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
repositories {
|
||||
maven("https://squiddev.cc/maven") {
|
||||
name = "SquidDev"
|
||||
|
||||
credentials(PasswordCredentials::class)
|
||||
}
|
||||
}
|
||||
}
|
31
buildSrc/src/main/kotlin/cc-tweaked.vanilla.gradle.kts
Normal file
31
buildSrc/src/main/kotlin/cc-tweaked.vanilla.gradle.kts
Normal file
@@ -0,0 +1,31 @@
|
||||
/** Default configuration for non-modloader-specific Minecraft projects. */
|
||||
|
||||
import cc.tweaked.gradle.CCTweakedExtension
|
||||
import cc.tweaked.gradle.CCTweakedPlugin
|
||||
import cc.tweaked.gradle.MinecraftConfigurations
|
||||
|
||||
plugins {
|
||||
id("cc-tweaked.java-convention")
|
||||
id("org.spongepowered.gradle.vanilla")
|
||||
}
|
||||
|
||||
plugins.apply(CCTweakedPlugin::class.java)
|
||||
|
||||
val mcVersion: String by extra
|
||||
|
||||
minecraft {
|
||||
version(mcVersion)
|
||||
}
|
||||
|
||||
dependencies {
|
||||
val libs = project.extensions.getByType<VersionCatalogsExtension>().named("libs")
|
||||
|
||||
// Depend on error prone annotations to silence a lot of compile warnings.
|
||||
compileOnlyApi(libs.findLibrary("errorProne.annotations").get())
|
||||
}
|
||||
|
||||
MinecraftConfigurations.setup(project)
|
||||
|
||||
extensions.configure(CCTweakedExtension::class.java) {
|
||||
linters(minecraft = true, loader = null)
|
||||
}
|
@@ -1,23 +1,35 @@
|
||||
package cc.tweaked.gradle
|
||||
|
||||
import net.ltgt.gradle.errorprone.CheckSeverity
|
||||
import net.ltgt.gradle.errorprone.errorprone
|
||||
import org.gradle.api.GradleException
|
||||
import org.gradle.api.NamedDomainObjectProvider
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.Task
|
||||
import org.gradle.api.attributes.TestSuiteType
|
||||
import org.gradle.api.file.FileSystemOperations
|
||||
import org.gradle.api.plugins.JavaPluginExtension
|
||||
import org.gradle.api.provider.Provider
|
||||
import org.gradle.api.provider.SetProperty
|
||||
import org.gradle.api.reporting.ReportingExtension
|
||||
import org.gradle.api.tasks.JavaExec
|
||||
import org.gradle.api.tasks.SourceSetContainer
|
||||
import org.gradle.api.tasks.SourceSet
|
||||
import org.gradle.api.tasks.bundling.Jar
|
||||
import org.gradle.api.tasks.compile.JavaCompile
|
||||
import org.gradle.api.tasks.javadoc.Javadoc
|
||||
import org.gradle.configurationcache.extensions.capitalized
|
||||
import org.gradle.kotlin.dsl.get
|
||||
import org.gradle.language.base.plugins.LifecycleBasePlugin
|
||||
import org.gradle.language.jvm.tasks.ProcessResources
|
||||
import org.gradle.process.JavaForkOptions
|
||||
import org.gradle.testing.jacoco.plugins.JacocoCoverageReport
|
||||
import org.gradle.testing.jacoco.plugins.JacocoPluginExtension
|
||||
import org.gradle.testing.jacoco.plugins.JacocoTaskExtension
|
||||
import org.gradle.testing.jacoco.tasks.JacocoReport
|
||||
import java.io.BufferedWriter
|
||||
import org.jetbrains.kotlin.gradle.dsl.KotlinProjectExtension
|
||||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
import java.io.OutputStreamWriter
|
||||
import java.net.URI
|
||||
import java.net.URL
|
||||
import java.util.regex.Pattern
|
||||
|
||||
abstract class CCTweakedExtension(
|
||||
@@ -26,45 +38,137 @@ abstract class CCTweakedExtension(
|
||||
) {
|
||||
/** Get the hash of the latest git commit. */
|
||||
val gitHash: Provider<String> = gitProvider(project, "<no git hash>") {
|
||||
ProcessHelpers.captureOut("git", "-C", project.projectDir.absolutePath, "rev-parse", "HEAD").trim()
|
||||
ProcessHelpers.captureOut("git", "-C", project.rootProject.projectDir.absolutePath, "rev-parse", "HEAD").trim()
|
||||
}
|
||||
|
||||
/** Get the current git branch. */
|
||||
val gitBranch: Provider<String> = gitProvider(project, "<no git branch>") {
|
||||
ProcessHelpers.captureOut("git", "-C", project.projectDir.absolutePath, "rev-parse", "--abbrev-ref", "HEAD").trim()
|
||||
ProcessHelpers.captureOut("git", "-C", project.rootProject.projectDir.absolutePath, "rev-parse", "--abbrev-ref", "HEAD")
|
||||
.trim()
|
||||
}
|
||||
|
||||
/** Get a list of all contributors to the project. */
|
||||
val gitContributors: Provider<List<String>> = gitProvider(project, listOf()) {
|
||||
val authors: Set<String> = HashSet(
|
||||
ProcessHelpers.captureLines(
|
||||
"git", "-C", project.projectDir.absolutePath, "log",
|
||||
"--format=tformat:%an <%ae>%n%cn <%ce>%n%(trailers:key=Co-authored-by,valueonly)",
|
||||
),
|
||||
ProcessHelpers.captureLines(
|
||||
"git", "-C", project.rootProject.projectDir.absolutePath, "shortlog", "-ns",
|
||||
"--group=author", "--group=trailer:co-authored-by", "HEAD",
|
||||
)
|
||||
val process = ProcessHelpers.startProcess("git", "check-mailmap", "--stdin")
|
||||
BufferedWriter(OutputStreamWriter(process.outputStream)).use { writer ->
|
||||
for (authorName in authors) {
|
||||
var author = authorName
|
||||
|
||||
if (author.isEmpty()) continue
|
||||
if (!author.endsWith(">")) author += ">" // Some commits have broken Co-Authored-By lines!
|
||||
writer.write(author)
|
||||
writer.newLine()
|
||||
.asSequence()
|
||||
.map {
|
||||
val matcher = COMMIT_COUNTS.matcher(it)
|
||||
matcher.find()
|
||||
matcher.group(1)
|
||||
}
|
||||
}
|
||||
val contributors: MutableSet<String> = HashSet()
|
||||
for (authorLine in ProcessHelpers.captureLines(process)) {
|
||||
val matcher = EMAIL.matcher(authorLine)
|
||||
matcher.find()
|
||||
val name = matcher.group(1)
|
||||
if (!IGNORED_USERS.contains(name)) contributors.add(name)
|
||||
}
|
||||
|
||||
contributors.sortedWith(String.CASE_INSENSITIVE_ORDER)
|
||||
.filter { !IGNORED_USERS.contains(it) }
|
||||
.toList()
|
||||
.sortedWith(String.CASE_INSENSITIVE_ORDER)
|
||||
}
|
||||
|
||||
fun jacoco(task: NamedDomainObjectProvider<JavaExec>) {
|
||||
/**
|
||||
* References to other sources
|
||||
*/
|
||||
val sourceDirectories: SetProperty<SourceSetReference> = project.objects.setProperty(SourceSetReference::class.java)
|
||||
|
||||
/** All source sets referenced by this project. */
|
||||
val sourceSets = sourceDirectories.map { x -> x.map { it.sourceSet } }
|
||||
|
||||
init {
|
||||
sourceDirectories.finalizeValueOnRead()
|
||||
project.afterEvaluate { sourceDirectories.disallowChanges() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark this project as consuming another project. Its [sourceDirectories] are added, allowing easier configuration
|
||||
* of run configurations and other tasks which consume sources/classes.
|
||||
*/
|
||||
fun externalSources(project: Project) {
|
||||
val otherCct = project.extensions.getByType(CCTweakedExtension::class.java)
|
||||
for (sourceSet in otherCct.sourceDirectories.get()) {
|
||||
sourceDirectories.add(SourceSetReference(sourceSet.sourceSet, classes = sourceSet.classes, external = true))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a dependency on another project such that its sources and compiles are processed with this one.
|
||||
*
|
||||
* This is used when importing a common library into a loader-specific one, as we want to compile sources using
|
||||
* the loader-specific sources.
|
||||
*/
|
||||
fun inlineProject(path: String) {
|
||||
val otherProject = project.evaluationDependsOn(path)
|
||||
val otherJava = otherProject.extensions.getByType(JavaPluginExtension::class.java)
|
||||
val main = otherJava.sourceSets.getByName("main")
|
||||
val client = otherJava.sourceSets.getByName("client")
|
||||
val testMod = otherJava.sourceSets.findByName("testMod")
|
||||
val testFixtures = otherJava.sourceSets.findByName("testFixtures")
|
||||
|
||||
// Pull in sources from the other project.
|
||||
extendSourceSet(otherProject, main)
|
||||
extendSourceSet(otherProject, client)
|
||||
if (testMod != null) extendSourceSet(otherProject, testMod)
|
||||
if (testFixtures != null) extendSourceSet(otherProject, testFixtures)
|
||||
|
||||
// The extra source-processing tasks should include these files too.
|
||||
project.tasks.named(main.javadocTaskName, Javadoc::class.java) { source(main.allJava, client.allJava) }
|
||||
project.tasks.named(main.sourcesJarTaskName, Jar::class.java) { from(main.allSource, client.allSource) }
|
||||
sourceDirectories.addAll(SourceSetReference.inline(main), SourceSetReference.inline(client))
|
||||
}
|
||||
|
||||
/**
|
||||
* Extend a source set with files from another project.
|
||||
*
|
||||
* This actually extends the original compile tasks, as extending the source sets does not play well with IDEs.
|
||||
*/
|
||||
private fun extendSourceSet(otherProject: Project, sourceSet: SourceSet) {
|
||||
project.tasks.named(sourceSet.compileJavaTaskName, JavaCompile::class.java) {
|
||||
dependsOn(otherProject.tasks.named(sourceSet.compileJavaTaskName)) // Avoid duplicate compile errors
|
||||
source(sourceSet.allJava)
|
||||
}
|
||||
|
||||
project.tasks.named(sourceSet.processResourcesTaskName, ProcessResources::class.java) {
|
||||
from(sourceSet.resources)
|
||||
}
|
||||
|
||||
// Also try to depend on Kotlin if it exists
|
||||
val kotlin = otherProject.extensions.findByType(KotlinProjectExtension::class.java)
|
||||
if (kotlin != null) {
|
||||
val compileKotlin = sourceSet.getCompileTaskName("kotlin")
|
||||
project.tasks.named(compileKotlin, KotlinCompile::class.java) {
|
||||
dependsOn(otherProject.tasks.named(compileKotlin))
|
||||
source(kotlin.sourceSets.getByName(sourceSet.name).kotlin)
|
||||
}
|
||||
}
|
||||
|
||||
// If we're doing an IDE sync, add a fake dependency to ensure it's on the classpath.
|
||||
if (isIdeSync) project.dependencies.add(sourceSet.apiConfigurationName, sourceSet.output)
|
||||
}
|
||||
|
||||
fun linters(@Suppress("UNUSED_PARAMETER") vararg unused: UseNamedArgs, minecraft: Boolean, loader: String?) {
|
||||
val java = project.extensions.getByType(JavaPluginExtension::class.java)
|
||||
val sourceSets = java.sourceSets
|
||||
|
||||
project.dependencies.run { add("errorprone", project(mapOf("path" to ":lints"))) }
|
||||
sourceSets.all {
|
||||
val name = name
|
||||
project.tasks.named(compileJavaTaskName, JavaCompile::class.java) {
|
||||
options.errorprone {
|
||||
// Only the main source set should run the side checker
|
||||
check("SideChecker", if (minecraft && name == "main") CheckSeverity.DEFAULT else CheckSeverity.OFF)
|
||||
|
||||
// The MissingLoaderOverride check superseeds the MissingOverride one, so disable that.
|
||||
if (loader != null) {
|
||||
check("MissingOverride", CheckSeverity.OFF)
|
||||
option("ModLoader", loader)
|
||||
} else {
|
||||
check("LoaderOverride", CheckSeverity.OFF)
|
||||
check("MissingLoaderOverride", CheckSeverity.OFF)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun <T> jacoco(task: NamedDomainObjectProvider<T>) where T : Task, T : JavaForkOptions {
|
||||
val classDump = project.buildDir.resolve("jacocoClassDump/${task.name}")
|
||||
val reportTaskName = "jacoco${task.name.capitalized()}Report"
|
||||
|
||||
@@ -93,8 +197,7 @@ abstract class CCTweakedExtension(
|
||||
classDirectories.from(classDump)
|
||||
|
||||
// Don't want to use sourceSets(...) here as we have a custom class directory.
|
||||
val sourceSets = project.extensions.getByType(SourceSetContainer::class.java)
|
||||
sourceDirectories.from(sourceSets["main"].allSource.sourceDirectories)
|
||||
for (ref in sourceSets.get()) sourceDirectories.from(ref.allSource.sourceDirectories)
|
||||
}
|
||||
|
||||
project.extensions.configure(ReportingExtension::class.java) {
|
||||
@@ -104,8 +207,43 @@ abstract class CCTweakedExtension(
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Download a file by creating a dummy Ivy repository.
|
||||
*
|
||||
* This should only be used for one-off downloads. Using a more conventional Ivy or Maven repository is preferred
|
||||
* where possible.
|
||||
*/
|
||||
fun downloadFile(label: String, url: String): File {
|
||||
val url = URL(url)
|
||||
val path = File(url.path)
|
||||
|
||||
project.repositories.ivy {
|
||||
name = label
|
||||
setUrl(URI(url.protocol, url.userInfo, url.host, url.port, path.parent, null, null))
|
||||
patternLayout {
|
||||
artifact("[artifact].[ext]")
|
||||
}
|
||||
metadataSources {
|
||||
artifact()
|
||||
}
|
||||
content {
|
||||
includeModule("cc.tweaked.internal", path.nameWithoutExtension)
|
||||
}
|
||||
}
|
||||
|
||||
return project.configurations.detachedConfiguration(
|
||||
project.dependencies.create(
|
||||
mapOf(
|
||||
"group" to "cc.tweaked.internal",
|
||||
"name" to path.nameWithoutExtension,
|
||||
"ext" to path.extension,
|
||||
),
|
||||
),
|
||||
).resolve().single()
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val EMAIL = Pattern.compile("^([^<]+) <.+>$")
|
||||
private val COMMIT_COUNTS = Pattern.compile("""^\s*[0-9]+\s+(.*)$""")
|
||||
private val IGNORED_USERS = setOf(
|
||||
"GitHub", "Daniel Ratcliffe", "Weblate",
|
||||
)
|
||||
@@ -117,8 +255,14 @@ abstract class CCTweakedExtension(
|
||||
} catch (e: IOException) {
|
||||
project.logger.error("Cannot read Git repository: ${e.message}")
|
||||
default
|
||||
} catch (e: GradleException) {
|
||||
project.logger.error("Cannot read Git repository: ${e.message}")
|
||||
default
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val isIdeSync: Boolean
|
||||
get() = java.lang.Boolean.parseBoolean(System.getProperty("idea.sync.active", "false"))
|
||||
}
|
||||
}
|
||||
|
@@ -2,6 +2,8 @@ package cc.tweaked.gradle
|
||||
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.plugins.JavaPlugin
|
||||
import org.gradle.api.plugins.JavaPluginExtension
|
||||
import org.gradle.jvm.toolchain.JavaLanguageVersion
|
||||
|
||||
/**
|
||||
@@ -9,7 +11,12 @@ import org.gradle.jvm.toolchain.JavaLanguageVersion
|
||||
*/
|
||||
class CCTweakedPlugin : Plugin<Project> {
|
||||
override fun apply(project: Project) {
|
||||
project.extensions.create("cct", CCTweakedExtension::class.java)
|
||||
val cct = project.extensions.create("cct", CCTweakedExtension::class.java)
|
||||
|
||||
project.plugins.withType(JavaPlugin::class.java) {
|
||||
val sourceSets = project.extensions.getByType(JavaPluginExtension::class.java).sourceSets
|
||||
cct.sourceDirectories.add(SourceSetReference.internal(sourceSets.getByName("main")))
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
@@ -6,7 +6,6 @@ import org.gradle.api.file.RegularFileProperty
|
||||
import org.gradle.api.provider.Property
|
||||
import org.gradle.api.tasks.*
|
||||
import org.gradle.language.base.plugins.LifecycleBasePlugin
|
||||
import java.nio.charset.StandardCharsets
|
||||
|
||||
/**
|
||||
* Checks the `changelog.md` and `whatsnew.md` files are well-formed.
|
||||
@@ -35,7 +34,7 @@ abstract class CheckChangelog : DefaultTask() {
|
||||
|
||||
var ok = true
|
||||
|
||||
// Check we're targetting the current version
|
||||
// Check we're targeting the current version
|
||||
var whatsNew = whatsNew.get().asFile.readLines()
|
||||
if (whatsNew[0] != "New features in CC: Tweaked $version") {
|
||||
ok = false
|
||||
|
@@ -5,7 +5,6 @@ import com.diffplug.spotless.FormatterStep
|
||||
import com.diffplug.spotless.generic.LicenseHeaderStep
|
||||
import java.io.File
|
||||
import java.io.Serializable
|
||||
import java.nio.charset.StandardCharsets
|
||||
|
||||
/**
|
||||
* Similar to [LicenseHeaderStep], but supports multiple licenses.
|
||||
|
@@ -2,19 +2,116 @@ package cc.tweaked.gradle
|
||||
|
||||
import org.gradle.api.artifacts.dsl.DependencyHandler
|
||||
import org.gradle.api.tasks.JavaExec
|
||||
import org.gradle.process.BaseExecSpec
|
||||
import org.gradle.process.JavaExecSpec
|
||||
import org.gradle.process.ProcessForkOptions
|
||||
|
||||
/**
|
||||
* Add an annotation processor to all source sets.
|
||||
*/
|
||||
fun DependencyHandler.annotationProcessorEverywhere(dep: Any) {
|
||||
add("compileOnly", dep)
|
||||
add("annotationProcessor", dep)
|
||||
|
||||
add("clientCompileOnly", dep)
|
||||
add("clientAnnotationProcessor", dep)
|
||||
|
||||
add("testCompileOnly", dep)
|
||||
add("testAnnotationProcessor", dep)
|
||||
}
|
||||
|
||||
/**
|
||||
* A version of [JavaExecSpec.copyTo] which copies *all* properties.
|
||||
*/
|
||||
fun JavaExec.copyToFull(spec: JavaExec) {
|
||||
copyTo(spec)
|
||||
spec.classpath = classpath
|
||||
spec.mainClass.set(mainClass)
|
||||
spec.javaLauncher.set(javaLauncher)
|
||||
|
||||
// Additional Java options
|
||||
spec.jvmArgs = jvmArgs // Fabric overrides getJvmArgs so copyTo doesn't do the right thing.
|
||||
spec.args = args
|
||||
spec.argumentProviders.addAll(argumentProviders)
|
||||
spec.mainClass.set(mainClass)
|
||||
spec.classpath = classpath
|
||||
spec.javaLauncher.set(javaLauncher)
|
||||
if (executable != null) spec.setExecutable(executable!!)
|
||||
|
||||
// Additional ExecSpec options
|
||||
copyToExec(spec)
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy additional [BaseExecSpec] options which aren't handled by [ProcessForkOptions.copyTo].
|
||||
*/
|
||||
fun BaseExecSpec.copyToExec(spec: BaseExecSpec) {
|
||||
spec.isIgnoreExitValue = isIgnoreExitValue
|
||||
if (standardInput != null) spec.standardInput = standardInput
|
||||
if (standardOutput != null) spec.standardOutput = standardOutput
|
||||
if (errorOutput != null) spec.errorOutput = errorOutput
|
||||
}
|
||||
|
||||
/**
|
||||
* An alternative to [Nothing] with a more descriptive name. Use to enforce calling a function with named arguments:
|
||||
*
|
||||
* ```kotlin
|
||||
* fun f(vararg unused: UseNamedArgs, arg1: Int, arg2: Int) {
|
||||
* // ...
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
class UseNamedArgs private constructor()
|
||||
|
||||
/**
|
||||
* An [AutoCloseable] implementation which can be used to combine other [AutoCloseable] instances.
|
||||
*
|
||||
* Values which implement [AutoCloseable] can be dynamically registered with [CloseScope.add]. When the scope is closed,
|
||||
* each value is closed in the opposite order.
|
||||
*
|
||||
* This is largely intended for cases where it's not appropriate to nest [AutoCloseable.use], for instance when nested
|
||||
* would be too deep.
|
||||
*/
|
||||
class CloseScope : AutoCloseable {
|
||||
private val toClose = ArrayDeque<AutoCloseable>()
|
||||
|
||||
/**
|
||||
* Add a value to be closed when this scope is closed.
|
||||
*/
|
||||
public fun add(value: AutoCloseable) {
|
||||
toClose.addLast(value)
|
||||
}
|
||||
|
||||
override fun close() {
|
||||
close(null)
|
||||
}
|
||||
|
||||
@PublishedApi
|
||||
internal fun close(baseException: Throwable?) {
|
||||
var exception = baseException
|
||||
|
||||
while (true) {
|
||||
var toClose = toClose.removeLastOrNull() ?: break
|
||||
try {
|
||||
toClose.close()
|
||||
} catch (e: Throwable) {
|
||||
if (exception == null) {
|
||||
exception = e
|
||||
} else {
|
||||
exception.addSuppressed(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (exception != null) throw exception
|
||||
}
|
||||
|
||||
inline fun <R> use(block: (CloseScope) -> R): R {
|
||||
var exception: Throwable? = null
|
||||
try {
|
||||
return block(this)
|
||||
} catch (e: Throwable) {
|
||||
exception = e
|
||||
throw e
|
||||
} finally {
|
||||
close(exception)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,170 @@
|
||||
package cc.tweaked.gradle
|
||||
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.logging.Logging
|
||||
import org.w3c.dom.Attr
|
||||
import org.w3c.dom.Document
|
||||
import org.w3c.dom.Node
|
||||
import org.xml.sax.InputSource
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Path
|
||||
import javax.xml.parsers.DocumentBuilderFactory
|
||||
import javax.xml.transform.TransformerFactory
|
||||
import javax.xml.transform.dom.DOMSource
|
||||
import javax.xml.transform.stream.StreamResult
|
||||
import javax.xml.xpath.XPathConstants
|
||||
import javax.xml.xpath.XPathFactory
|
||||
|
||||
/**
|
||||
* Patches up run configurations from ForgeGradle and Loom.
|
||||
*
|
||||
* Would be good to PR some (or all) of these changes upstream at some point.
|
||||
*
|
||||
* @see net.fabricmc.loom.configuration.ide.idea.IdeaSyncTask
|
||||
* @see net.minecraftforge.gradle.common.util.runs.IntellijRunGenerator
|
||||
*/
|
||||
internal class IdeaRunConfigurations(project: Project) {
|
||||
private val rootProject = project.rootProject
|
||||
|
||||
private val documentBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder()
|
||||
private val xpath = XPathFactory.newInstance().newXPath()
|
||||
private val writer = TransformerFactory.newInstance().newTransformer()
|
||||
|
||||
private val ideaDir = rootProject.file(".idea/")
|
||||
private val buildDir: Lazy<String?> = lazy {
|
||||
val ideaMisc = ideaDir.resolve("misc.xml")
|
||||
|
||||
try {
|
||||
val doc = Files.newBufferedReader(ideaMisc.toPath()).use {
|
||||
documentBuilder.parse(InputSource(it))
|
||||
}
|
||||
val node =
|
||||
xpath.evaluate("//component[@name=\"ProjectRootManager\"]/output", doc, XPathConstants.NODE) as Node
|
||||
val attr = node.attributes.getNamedItem("url") as Attr
|
||||
attr.value.removePrefix("file://")
|
||||
} catch (e: Exception) {
|
||||
LOGGER.error("Failed to find root directory", e)
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
fun patch() = synchronized(LOCK) {
|
||||
val runConfigDir = ideaDir.resolve("runConfigurations")
|
||||
if (!runConfigDir.isDirectory) return
|
||||
|
||||
Files.list(runConfigDir.toPath()).use {
|
||||
for (configuration in it) {
|
||||
val filename = configuration.fileName.toString();
|
||||
when {
|
||||
filename.endsWith("_fabric.xml") -> patchFabric(configuration)
|
||||
filename.startsWith("forge_") && filename.endsWith(".xml") -> patchForge(configuration)
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun patchFabric(path: Path) = withXml(path) {
|
||||
setXml("//configuration", "folderName") { "Fabric" }
|
||||
}
|
||||
|
||||
private fun patchForge(path: Path) = withXml(path) {
|
||||
val configId = path.fileName.toString().removePrefix("forge_").removeSuffix(".xml")
|
||||
val sourceSet = forgeConfigs[configId]
|
||||
if (sourceSet == null) {
|
||||
LOGGER.error("[{}] Cannot map run configuration to a known source set", path)
|
||||
return@withXml
|
||||
}
|
||||
|
||||
setXml("//configuration", "folderName") { "Forge" }
|
||||
setXml("//configuration/module", "name") { "${rootProject.name}.forge.$sourceSet" }
|
||||
|
||||
if (buildDir.value == null) return@withXml
|
||||
setXml("//configuration/envs/env[@name=\"MOD_CLASSES\"]", "value") { classpath ->
|
||||
val classes = classpath!!.split(':')
|
||||
val newClasses = mutableListOf<String>()
|
||||
fun appendUnique(x: String) {
|
||||
if (!newClasses.contains(x)) newClasses.add(x)
|
||||
}
|
||||
|
||||
for (entry in classes) {
|
||||
if (!entry.contains("/out/")) {
|
||||
appendUnique(entry)
|
||||
continue
|
||||
}
|
||||
|
||||
val match = CLASSPATH_ENTRY.matchEntire(entry)
|
||||
if (match != null) {
|
||||
val modId = match.groups["modId"]!!.value
|
||||
val proj = match.groups["proj"]!!.value
|
||||
var component = match.groups["component"]!!.value
|
||||
if (component == "production") component = "main"
|
||||
|
||||
appendUnique(forgeModEntry(modId, proj, component))
|
||||
} else {
|
||||
LOGGER.warn("[{}] Unknown classpath entry {}", path, entry)
|
||||
appendUnique(entry)
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure common code is on the classpath
|
||||
for (proj in listOf("common", "common-api")) {
|
||||
for (component in listOf("main", "client")) {
|
||||
appendUnique(forgeModEntry("computercraft", proj, component))
|
||||
}
|
||||
}
|
||||
|
||||
if (newClasses.any { it.startsWith("cctest%%") }) {
|
||||
appendUnique(forgeModEntry("cctest", "core", "testFixtures"))
|
||||
appendUnique(forgeModEntry("cctest", "common", "testFixtures"))
|
||||
appendUnique(forgeModEntry("cctest", "common", "testMod"))
|
||||
}
|
||||
|
||||
newClasses.joinToString(":")
|
||||
}
|
||||
}
|
||||
|
||||
private fun forgeModEntry(mod: String, project: String, component: String) =
|
||||
"$mod%%${buildDir.value}/production/${rootProject.name}.$project.$component"
|
||||
|
||||
private fun LocatedDocument.setXml(xpath: String, attribute: String, value: (String?) -> String) {
|
||||
val node = this@IdeaRunConfigurations.xpath.evaluate(xpath, document, XPathConstants.NODE) as Node?
|
||||
if (node == null) {
|
||||
LOGGER.error("[{}] Cannot find {}", path.fileName, xpath)
|
||||
return
|
||||
}
|
||||
|
||||
val attr = node.attributes.getNamedItem(attribute) as Attr? ?: document.createAttribute(attribute)
|
||||
val oldValue = attr.value
|
||||
attr.value = value(attr.value)
|
||||
node.attributes.setNamedItem(attr)
|
||||
|
||||
if (oldValue != attr.value) {
|
||||
LOGGER.info("[{}] Setting {}@{}:\n Old: {}\n New: {}", path.fileName, xpath, attribute, oldValue, attr.value)
|
||||
}
|
||||
}
|
||||
|
||||
private fun withXml(path: Path, run: LocatedDocument.() -> Unit) {
|
||||
val doc = Files.newBufferedReader(path).use { documentBuilder.parse(InputSource(it)) }
|
||||
run(LocatedDocument(path, doc))
|
||||
Files.newBufferedWriter(path).use { writer.transform(DOMSource(doc), StreamResult(it)) }
|
||||
}
|
||||
|
||||
private class LocatedDocument(val path: Path, val document: Document)
|
||||
|
||||
companion object {
|
||||
private val LOGGER = Logging.getLogger(IdeaRunConfigurations::class.java)
|
||||
private val LOCK = Any()
|
||||
|
||||
private val CLASSPATH_ENTRY =
|
||||
Regex("(?<modId>[a-z]+)%%\\\$PROJECT_DIR\\\$/projects/(?<proj>[a-z-]+)/out/(?<component>\\w+)/(?<type>[a-z]+)\$")
|
||||
|
||||
private val forgeConfigs = mapOf(
|
||||
"runClient" to "client",
|
||||
"runData" to "main",
|
||||
"runGameTestServer" to "testMod",
|
||||
"runServer" to "main",
|
||||
"runTestClient" to "testMod",
|
||||
)
|
||||
}
|
||||
}
|
@@ -66,9 +66,11 @@ class IlluaminatePlugin : Plugin<Project> {
|
||||
|
||||
val osArch = System.getProperty("os.arch").toLowerCase()
|
||||
val arch = when {
|
||||
// On macOS the x86_64 binary will work for both ARM and Intel Macs through Rosetta.
|
||||
os == "macos" -> "x86_64"
|
||||
osArch == "arm" || osArch.startsWith("aarch") -> error("Unsupported architecture '$osArch' for illuaminate")
|
||||
osArch.contains("64") -> "x86_64"
|
||||
else -> error("Unsupported architecture $osArch for illuaminate")
|
||||
else -> error("Unsupported architecture '$osArch' for illuaminate")
|
||||
}
|
||||
|
||||
return project.dependencies.create(
|
||||
|
@@ -0,0 +1,62 @@
|
||||
package cc.tweaked.gradle
|
||||
|
||||
import org.gradle.api.artifacts.Dependency
|
||||
import org.gradle.api.artifacts.MinimalExternalModuleDependency
|
||||
import org.gradle.api.publish.maven.MavenPublication
|
||||
import org.gradle.api.specs.Spec
|
||||
|
||||
/**
|
||||
* A dependency in a POM file.
|
||||
*/
|
||||
data class MavenDependency(val groupId: String?, val artifactId: String?, val version: String?, val scope: String?)
|
||||
|
||||
/**
|
||||
* A spec specifying which dependencies to include/exclude.
|
||||
*/
|
||||
class MavenDependencySpec {
|
||||
private val excludeSpecs = mutableListOf<Spec<MavenDependency>>()
|
||||
|
||||
fun exclude(spec: Spec<MavenDependency>) {
|
||||
excludeSpecs.add(spec)
|
||||
}
|
||||
|
||||
fun exclude(dep: Dependency) {
|
||||
exclude {
|
||||
(dep.group.isNullOrEmpty() || dep.group == it.groupId) &&
|
||||
(dep.name.isNullOrEmpty() || dep.name == it.artifactId) &&
|
||||
(dep.version.isNullOrEmpty() || dep.version == it.version)
|
||||
}
|
||||
}
|
||||
|
||||
fun exclude(dep: MinimalExternalModuleDependency) {
|
||||
exclude {
|
||||
dep.module.group == it.groupId && dep.module.name == it.artifactId
|
||||
}
|
||||
}
|
||||
|
||||
fun isIncluded(dep: MavenDependency) = !excludeSpecs.any { it.isSatisfiedBy(dep) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure dependencies present in this publication's POM file.
|
||||
*
|
||||
* While this approach is very ugly, it's the easiest way to handle it!
|
||||
*/
|
||||
fun MavenPublication.mavenDependencies(action: MavenDependencySpec.() -> Unit) {
|
||||
val spec = MavenDependencySpec()
|
||||
action(spec)
|
||||
|
||||
pom.withXml {
|
||||
val dependencies = XmlUtil.findChild(asNode(), "dependencies") ?: return@withXml
|
||||
dependencies.children().map { it as groovy.util.Node }.forEach {
|
||||
val dep = MavenDependency(
|
||||
groupId = XmlUtil.findChild(it, "groupId")?.text(),
|
||||
artifactId = XmlUtil.findChild(it, "artifactId")?.text(),
|
||||
version = XmlUtil.findChild(it, "version")?.text(),
|
||||
scope = XmlUtil.findChild(it, "scope")?.text(),
|
||||
)
|
||||
|
||||
if (!spec.isIncluded(dep)) it.parent().remove(it)
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,189 @@
|
||||
package cc.tweaked.gradle
|
||||
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.artifacts.Configuration
|
||||
import org.gradle.api.artifacts.ModuleDependency
|
||||
import org.gradle.api.artifacts.dsl.DependencyHandler
|
||||
import org.gradle.api.attributes.Bundling
|
||||
import org.gradle.api.attributes.Category
|
||||
import org.gradle.api.attributes.LibraryElements
|
||||
import org.gradle.api.attributes.Usage
|
||||
import org.gradle.api.attributes.java.TargetJvmVersion
|
||||
import org.gradle.api.capabilities.Capability
|
||||
import org.gradle.api.plugins.BasePlugin
|
||||
import org.gradle.api.plugins.JavaPluginExtension
|
||||
import org.gradle.api.tasks.SourceSet
|
||||
import org.gradle.api.tasks.bundling.Jar
|
||||
import org.gradle.api.tasks.javadoc.Javadoc
|
||||
import org.gradle.kotlin.dsl.get
|
||||
import org.gradle.kotlin.dsl.named
|
||||
|
||||
/**
|
||||
* This sets up a separate client-only source set, and extends that and the main/common source set with additional
|
||||
* metadata, to make it easier to consume jars downstream.
|
||||
*/
|
||||
class MinecraftConfigurations private constructor(private val project: Project) {
|
||||
private val java = project.extensions.getByType(JavaPluginExtension::class.java)
|
||||
private val sourceSets = java.sourceSets
|
||||
private val configurations = project.configurations
|
||||
private val objects = project.objects
|
||||
|
||||
private val main = sourceSets[SourceSet.MAIN_SOURCE_SET_NAME]
|
||||
private val test = sourceSets[SourceSet.TEST_SOURCE_SET_NAME]
|
||||
|
||||
/**
|
||||
* Performs the initial setup of our configurations.
|
||||
*/
|
||||
private fun setup() {
|
||||
// Define a client source set.
|
||||
val client = sourceSets.maybeCreate("client")
|
||||
|
||||
// Ensure the client classpaths behave the same as the main ones.
|
||||
configurations.named(client.compileClasspathConfigurationName) {
|
||||
shouldResolveConsistentlyWith(configurations[main.compileClasspathConfigurationName])
|
||||
}
|
||||
|
||||
configurations.named(client.runtimeClasspathConfigurationName) {
|
||||
shouldResolveConsistentlyWith(configurations[main.runtimeClasspathConfigurationName])
|
||||
}
|
||||
|
||||
// Set up an API configuration for clients (to ensure it's consistent with the main source set).
|
||||
val clientApi = configurations.maybeCreate(client.apiConfigurationName).apply {
|
||||
isVisible = false
|
||||
isCanBeConsumed = false
|
||||
isCanBeResolved = false
|
||||
}
|
||||
configurations.named(client.implementationConfigurationName) { extendsFrom(clientApi) }
|
||||
|
||||
/*
|
||||
Now add outgoing variants for the main and common source sets that we can consume downstream. This is possibly
|
||||
the worst way to do things, but unfortunately the alternatives don't actually work very well:
|
||||
|
||||
- Just using source set outputs: This means dependencies don't propagate, which means when :fabric depends
|
||||
on :fabric-api, we don't inherit the fake :common-api in IDEA.
|
||||
|
||||
- Having separate common/main jars: Nice in principle, but unfortunately Forge needs a separate deobf jar
|
||||
task (as the original jar is obfuscated), and IDEA is not able to map its output back to a source set.
|
||||
|
||||
This works for now, but is incredibly brittle. It's part of the reason we can't use testFixtures inside our
|
||||
MC projects, as that adds a project(self) -> test dependency, which would pull in the jar instead.
|
||||
|
||||
Note we register a fake client jar here. It's not actually needed, but is there to make sure IDEA has
|
||||
a way to tell that client classes are needed at runtime.
|
||||
|
||||
I'm so sorry, deeply aware how cursed this is.
|
||||
*/
|
||||
setupOutgoing(main, "CommonOnly")
|
||||
project.tasks.register(client.jarTaskName, Jar::class.java) {
|
||||
description = "An empty jar standing in for the client classes."
|
||||
group = BasePlugin.BUILD_GROUP
|
||||
archiveClassifier.set("client")
|
||||
}
|
||||
setupOutgoing(client)
|
||||
|
||||
// Reset the client classpath (Loom configures it slightly differently to this) and add a main -> client
|
||||
// dependency. Here we /can/ use source set outputs as we add transitive deps by patching the classpath. Nasty,
|
||||
// but avoids accidentally pulling in Forge's obfuscated jar.
|
||||
client.compileClasspath = client.compileClasspath + main.compileClasspath
|
||||
client.runtimeClasspath = client.runtimeClasspath + main.runtimeClasspath
|
||||
project.dependencies.add(client.apiConfigurationName, main.output)
|
||||
|
||||
// Also add client classes to the test classpath. We do the same nasty tricks as needed for main -> client.
|
||||
test.compileClasspath += client.compileClasspath
|
||||
test.runtimeClasspath += client.runtimeClasspath
|
||||
project.dependencies.add(test.implementationConfigurationName, client.output)
|
||||
|
||||
// Configure some tasks to include our additional files.
|
||||
project.tasks.named("javadoc", Javadoc::class.java) {
|
||||
source(client.allJava)
|
||||
classpath = main.compileClasspath + main.output + client.compileClasspath + client.output
|
||||
}
|
||||
// This are already done by Fabric, but we need it for Forge and vanilla. It shouldn't conflict at all.
|
||||
project.tasks.named("jar", Jar::class.java) { from(client.output) }
|
||||
project.tasks.named("sourcesJar", Jar::class.java) { from(client.allSource) }
|
||||
|
||||
project.extensions.configure(CCTweakedExtension::class.java) {
|
||||
sourceDirectories.add(SourceSetReference.internal(client))
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupOutgoing(sourceSet: SourceSet, suffix: String = "") {
|
||||
setupOutgoing("${sourceSet.apiElementsConfigurationName}$suffix", sourceSet, objects.named(Usage.JAVA_API)) {
|
||||
description = "API elements for ${sourceSet.name}"
|
||||
extendsFrom(configurations[sourceSet.apiConfigurationName])
|
||||
}
|
||||
|
||||
setupOutgoing("${sourceSet.runtimeElementsConfigurationName}$suffix", sourceSet, objects.named(Usage.JAVA_RUNTIME)) {
|
||||
description = "Runtime elements for ${sourceSet.name}"
|
||||
extendsFrom(configurations[sourceSet.implementationConfigurationName], configurations[sourceSet.runtimeOnlyConfigurationName])
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up an outgoing configuration for a specific source set. We set an additional "main" or "client" capability
|
||||
* (depending on the source set name) which allows downstream projects to consume them separately (see
|
||||
* [DependencyHandler.commonClasses] and [DependencyHandler.clientClasses]).
|
||||
*/
|
||||
private fun setupOutgoing(name: String, sourceSet: SourceSet, usage: Usage, configure: Configuration.() -> Unit) {
|
||||
configurations.register(name) {
|
||||
isVisible = false
|
||||
isCanBeConsumed = true
|
||||
isCanBeResolved = false
|
||||
|
||||
configure(this)
|
||||
|
||||
attributes {
|
||||
attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category.LIBRARY))
|
||||
attribute(Usage.USAGE_ATTRIBUTE, usage)
|
||||
attribute(Bundling.BUNDLING_ATTRIBUTE, objects.named(Bundling.EXTERNAL))
|
||||
attributeProvider(
|
||||
TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE,
|
||||
java.toolchain.languageVersion.map { it.asInt() },
|
||||
)
|
||||
attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, objects.named(LibraryElements.JAR))
|
||||
}
|
||||
|
||||
outgoing {
|
||||
capability(BasicOutgoingCapability(project, sourceSet.name))
|
||||
|
||||
// We have two outgoing variants here: the original jar and the classes.
|
||||
artifact(project.tasks.named(sourceSet.jarTaskName))
|
||||
|
||||
variants.create("classes") {
|
||||
attributes.attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, objects.named(LibraryElements.CLASSES))
|
||||
sourceSet.output.classesDirs.forEach { artifact(it) { builtBy(sourceSet.output) } }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun setup(project: Project) {
|
||||
MinecraftConfigurations(project).setup()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class BasicIncomingCapability(private val module: ModuleDependency, private val name: String) : Capability {
|
||||
override fun getGroup(): String = module.group!!
|
||||
override fun getName(): String = "${module.name}-$name"
|
||||
override fun getVersion(): String? = null
|
||||
}
|
||||
|
||||
private class BasicOutgoingCapability(private val project: Project, private val name: String) : Capability {
|
||||
override fun getGroup(): String = project.group.toString()
|
||||
override fun getName(): String = "${project.name}-$name"
|
||||
override fun getVersion(): String = project.version.toString()
|
||||
}
|
||||
|
||||
fun DependencyHandler.clientClasses(notation: Any): ModuleDependency {
|
||||
val dep = create(notation) as ModuleDependency
|
||||
dep.capabilities { requireCapability(BasicIncomingCapability(dep, "client")) }
|
||||
return dep
|
||||
}
|
||||
|
||||
fun DependencyHandler.commonClasses(notation: Any): ModuleDependency {
|
||||
val dep = create(notation) as ModuleDependency
|
||||
dep.capabilities { requireCapability(BasicIncomingCapability(dep, "main")) }
|
||||
return dep
|
||||
}
|
191
buildSrc/src/main/kotlin/cc/tweaked/gradle/MinecraftExec.kt
Normal file
191
buildSrc/src/main/kotlin/cc/tweaked/gradle/MinecraftExec.kt
Normal file
@@ -0,0 +1,191 @@
|
||||
package cc.tweaked.gradle
|
||||
|
||||
import org.gradle.api.GradleException
|
||||
import org.gradle.api.file.FileSystemOperations
|
||||
import org.gradle.api.invocation.Gradle
|
||||
import org.gradle.api.provider.Provider
|
||||
import org.gradle.api.services.BuildService
|
||||
import org.gradle.api.services.BuildServiceParameters
|
||||
import org.gradle.api.tasks.*
|
||||
import org.gradle.kotlin.dsl.getByName
|
||||
import org.gradle.language.base.plugins.LifecycleBasePlugin
|
||||
import java.io.File
|
||||
import java.nio.file.Files
|
||||
import java.util.concurrent.TimeUnit
|
||||
import java.util.function.Supplier
|
||||
import javax.inject.Inject
|
||||
import kotlin.random.Random
|
||||
|
||||
/**
|
||||
* A [JavaExec] task for client-tests. This sets some common setup, and uses [MinecraftRunnerService] to ensure only one
|
||||
* test runs at once.
|
||||
*/
|
||||
abstract class ClientJavaExec : JavaExec() {
|
||||
private val clientRunner: Provider<MinecraftRunnerService> = MinecraftRunnerService.get(project.gradle)
|
||||
|
||||
init {
|
||||
group = LifecycleBasePlugin.VERIFICATION_GROUP
|
||||
usesService(clientRunner)
|
||||
}
|
||||
|
||||
/**
|
||||
* When [false], tests will not be run automatically, allowing the user to debug rendering.
|
||||
*/
|
||||
@get:Input
|
||||
val clientDebug get() = project.hasProperty("clientDebug")
|
||||
|
||||
/**
|
||||
* When [false], tests will not run under a framebuffer.
|
||||
*/
|
||||
@get:Input
|
||||
val useFramebuffer get() = !clientDebug && !project.hasProperty("clientNoFramebuffer")
|
||||
|
||||
/**
|
||||
* The path test results are written to.
|
||||
*/
|
||||
@get:OutputFile
|
||||
val testResults = project.layout.buildDirectory.file("test-results/$name.xml")
|
||||
|
||||
/**
|
||||
* Copy configuration from a task with the given name.
|
||||
*/
|
||||
fun copyFrom(path: String) = copyFrom(project.tasks.getByName(path, JavaExec::class))
|
||||
|
||||
/**
|
||||
* Copy configuration from an existing [JavaExec] task.
|
||||
*/
|
||||
fun copyFrom(task: JavaExec) {
|
||||
for (dep in task.dependsOn) dependsOn(dep)
|
||||
task.copyToFull(this)
|
||||
|
||||
if (!clientDebug) systemProperty("cctest.client", "")
|
||||
systemProperty("cctest.gametest-report", testResults.get().asFile.absoluteFile)
|
||||
workingDir(project.buildDir.resolve("gametest").resolve(name))
|
||||
}
|
||||
|
||||
/**
|
||||
* Only run tests with the given tags.
|
||||
*/
|
||||
fun tags(vararg tags: String) {
|
||||
systemProperty("cctest.tags", tags.joinToString(","))
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a file with the given contents before starting Minecraft. This may be useful for writing config files.
|
||||
*/
|
||||
fun withFileContents(path: Any, contents: Supplier<String>) {
|
||||
val file = project.file(path).toPath()
|
||||
doFirst {
|
||||
Files.createDirectories(file.parent)
|
||||
Files.writeString(file, contents.get())
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy a file to the provided path before starting Minecraft. This copy only occurs if the file does not already
|
||||
* exist.
|
||||
*/
|
||||
fun withFileFrom(path: Any, source: Supplier<File>) {
|
||||
val file = project.file(path).toPath()
|
||||
doFirst {
|
||||
Files.createDirectories(file.parent)
|
||||
if (!Files.exists(file)) Files.copy(source.get().toPath(), file)
|
||||
}
|
||||
}
|
||||
|
||||
@TaskAction
|
||||
override fun exec() {
|
||||
Files.createDirectories(workingDir.toPath())
|
||||
fsOperations.delete { delete(workingDir.resolve("screenshots")) }
|
||||
|
||||
if (useFramebuffer) {
|
||||
clientRunner.get().wrapClient(this) { super.exec() }
|
||||
} else {
|
||||
super.exec()
|
||||
}
|
||||
}
|
||||
|
||||
@get:Inject
|
||||
protected abstract val fsOperations: FileSystemOperations
|
||||
}
|
||||
|
||||
/**
|
||||
* A service for [JavaExec] tasks which start Minecraft.
|
||||
*
|
||||
* Tasks may run `usesService(MinecraftRunnerService.get(gradle))` to ensure that only one Minecraft-related task runs
|
||||
* at once.
|
||||
*/
|
||||
abstract class MinecraftRunnerService : BuildService<BuildServiceParameters.None> {
|
||||
private val hasXvfb = lazy {
|
||||
System.getProperty("os.name", "").equals("linux", ignoreCase = true) && ProcessHelpers.onPath("xvfb-run")
|
||||
}
|
||||
|
||||
internal fun wrapClient(exec: JavaExec, run: () -> Unit) = when {
|
||||
hasXvfb.value -> runXvfb(exec, run)
|
||||
else -> run()
|
||||
}
|
||||
|
||||
/**
|
||||
* Run a program under Xvfb, preventing it spawning a window.
|
||||
*/
|
||||
private fun runXvfb(exec: JavaExec, run: () -> Unit) {
|
||||
fun ProcessBuilder.startVerbose(): Process {
|
||||
exec.logger.info("Running ${this.command()}")
|
||||
return start()
|
||||
}
|
||||
|
||||
CloseScope().use { scope ->
|
||||
val dir = Files.createTempDirectory("cctweaked").toAbsolutePath()
|
||||
scope.add { fsOperations.delete { delete(dir) } }
|
||||
|
||||
val authFile = Files.createTempFile(dir, "Xauthority", "").toAbsolutePath()
|
||||
|
||||
val cookie = StringBuilder().also {
|
||||
for (i in 0..31) it.append("0123456789abcdef"[Random.nextInt(16)])
|
||||
}.toString()
|
||||
|
||||
val xvfb =
|
||||
ProcessBuilder("Xvfb", "-displayfd", "1", "-screen", "0", "640x480x24", "-nolisten", "tcp").also {
|
||||
it.inheritIO()
|
||||
it.environment()["XAUTHORITY"] = authFile.toString()
|
||||
it.redirectOutput(ProcessBuilder.Redirect.PIPE)
|
||||
}.startVerbose()
|
||||
scope.add { xvfb.destroyForcibly().waitFor() }
|
||||
|
||||
val server = xvfb.inputReader().use { it.readLine().trim() }
|
||||
exec.logger.info("Running at :$server (XAUTHORITY=$authFile.toA")
|
||||
|
||||
ProcessBuilder("xauth", "add", ":$server", ".", cookie).also {
|
||||
it.inheritIO()
|
||||
it.environment()["XAUTHORITY"] = authFile.toString()
|
||||
}.startVerbose().waitForOrThrow("Failed to setup XAuthority file")
|
||||
|
||||
scope.add {
|
||||
ProcessBuilder("xauth", "remove", ":$server").also {
|
||||
it.inheritIO()
|
||||
it.environment()["XAUTHORITY"] = authFile.toString()
|
||||
}.startVerbose().waitFor()
|
||||
}
|
||||
|
||||
// Wait a few seconds for Xvfb to start. Ugly, but identical to xvfb-run.
|
||||
if (xvfb.waitFor(3, TimeUnit.SECONDS)) {
|
||||
throw GradleException("Xvfb unexpectedly exited (with status code ${xvfb.exitValue()})")
|
||||
}
|
||||
|
||||
exec.environment("XAUTHORITY", authFile.toString())
|
||||
exec.environment("DISPLAY", ":$server")
|
||||
|
||||
run()
|
||||
}
|
||||
}
|
||||
|
||||
@get:Inject
|
||||
protected abstract val fsOperations: FileSystemOperations
|
||||
|
||||
companion object {
|
||||
fun get(gradle: Gradle): Provider<MinecraftRunnerService> =
|
||||
gradle.sharedServices.registerIfAbsent("cc.tweaked.gradle.ClientJavaExec", MinecraftRunnerService::class.java) {
|
||||
maxParallelUsages.set(1)
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,35 +1,50 @@
|
||||
package cc.tweaked.gradle
|
||||
|
||||
import org.codehaus.groovy.runtime.ProcessGroovyMethods
|
||||
import org.gradle.api.GradleException
|
||||
import java.io.BufferedReader
|
||||
import java.io.IOException
|
||||
import java.io.File
|
||||
import java.io.InputStreamReader
|
||||
import java.util.stream.Collectors
|
||||
|
||||
internal object ProcessHelpers {
|
||||
fun startProcess(vararg command: String): Process {
|
||||
// Something randomly passes in "GIT_DIR=" as an environment variable which clobbers everything else. Don't
|
||||
// inherit the environment array!
|
||||
return Runtime.getRuntime().exec(command, arrayOfNulls(0))
|
||||
return ProcessBuilder()
|
||||
.command(*command)
|
||||
.redirectError(ProcessBuilder.Redirect.INHERIT)
|
||||
.also { it.environment().clear() }
|
||||
.start()
|
||||
}
|
||||
|
||||
fun captureOut(vararg command: String): String {
|
||||
val process = startProcess(*command)
|
||||
process.outputStream.close()
|
||||
|
||||
val result = ProcessGroovyMethods.getText(process)
|
||||
if (process.waitFor() != 0) throw IOException("Command exited with a non-0 status")
|
||||
process.waitForOrThrow("Failed to run command")
|
||||
return result
|
||||
}
|
||||
|
||||
fun captureLines(vararg command: String): List<String> {
|
||||
return captureLines(startProcess(*command))
|
||||
}
|
||||
val process = startProcess(*command)
|
||||
process.outputStream.close()
|
||||
|
||||
fun captureLines(process: Process): List<String> {
|
||||
val out = BufferedReader(InputStreamReader(process.inputStream)).use { reader ->
|
||||
reader.lines().filter { it.isNotEmpty() }.collect(Collectors.toList())
|
||||
reader.lines().filter { it.isNotEmpty() }.toList()
|
||||
}
|
||||
ProcessGroovyMethods.closeStreams(process)
|
||||
if (process.waitFor() != 0) throw IOException("Command exited with a non-0 status")
|
||||
process.waitForOrThrow("Failed to run command")
|
||||
return out
|
||||
}
|
||||
|
||||
fun onPath(name: String): Boolean {
|
||||
val path = System.getenv("PATH") ?: return false
|
||||
return path.splitToSequence(File.pathSeparator).any { File(it, name).exists() }
|
||||
}
|
||||
}
|
||||
|
||||
internal fun Process.waitForOrThrow(message: String) {
|
||||
val ret = waitFor()
|
||||
if (ret != 0) throw GradleException("$message (exited with $ret)")
|
||||
}
|
||||
|
@@ -0,0 +1,20 @@
|
||||
package cc.tweaked.gradle
|
||||
|
||||
import org.gradle.api.tasks.SourceSet
|
||||
|
||||
data class SourceSetReference(
|
||||
val sourceSet: SourceSet,
|
||||
val classes: Boolean,
|
||||
val external: Boolean,
|
||||
) {
|
||||
companion object {
|
||||
/** A source set in the current project. */
|
||||
fun internal(sourceSet: SourceSet) = SourceSetReference(sourceSet, classes = true, external = false)
|
||||
|
||||
/** A source set from another project. */
|
||||
fun external(sourceSet: SourceSet) = SourceSetReference(sourceSet, classes = true, external = true)
|
||||
|
||||
/** A source set which is inlined into the current project. */
|
||||
fun inline(sourceSet: SourceSet) = SourceSetReference(sourceSet, classes = false, external = false)
|
||||
}
|
||||
}
|
12
buildSrc/src/main/kotlin/cc/tweaked/gradle/XmlUtil.kt
Normal file
12
buildSrc/src/main/kotlin/cc/tweaked/gradle/XmlUtil.kt
Normal file
@@ -0,0 +1,12 @@
|
||||
package cc.tweaked.gradle
|
||||
|
||||
import groovy.util.Node
|
||||
import groovy.util.NodeList
|
||||
|
||||
object XmlUtil {
|
||||
fun findChild(node: Node, name: String): Node? = when (val child = node.get(name)) {
|
||||
is Node -> child
|
||||
is NodeList -> child.singleOrNull() as Node?
|
||||
else -> null
|
||||
}
|
||||
}
|
@@ -17,7 +17,10 @@
|
||||
<module name="TreeWalker">
|
||||
<!-- Annotations -->
|
||||
<module name="AnnotationLocation" />
|
||||
<module name="AnnotationUseStyle" />
|
||||
<module name="AnnotationUseStyle">
|
||||
<!-- We want trailing commas on multiline arrays. -->
|
||||
<property name="trailingArrayComma" value="ignore" />
|
||||
</module>
|
||||
<module name="MissingDeprecated" />
|
||||
<module name="MissingOverride" />
|
||||
|
||||
@@ -26,17 +29,11 @@
|
||||
<module name="EmptyCatchBlock">
|
||||
<property name="exceptionVariableName" value="ignored" />
|
||||
</module>
|
||||
<module name="LeftCurly">
|
||||
<property name="option" value="nl" />
|
||||
<!-- The defaults, minus lambdas. -->
|
||||
<property name="tokens" value="ANNOTATION_DEF,CLASS_DEF,CTOR_DEF,ENUM_CONSTANT_DEF,ENUM_DEF,INTERFACE_DEF,LITERAL_CASE,LITERAL_CATCH,LITERAL_DEFAULT,LITERAL_DO,LITERAL_ELSE,LITERAL_FINALLY,LITERAL_FOR,LITERAL_IF,LITERAL_SWITCH,LITERAL_SYNCHRONIZED,LITERAL_TRY,LITERAL_WHILE,METHOD_DEF,OBJBLOCK,STATIC_INIT" />
|
||||
</module>
|
||||
<module name="LeftCurly" />
|
||||
<module name="NeedBraces">
|
||||
<property name="allowSingleLineStatement" value="true"/>
|
||||
</module>
|
||||
<module name="RightCurly">
|
||||
<property name="option" value="alone" />
|
||||
</module>
|
||||
<module name="RightCurly" />
|
||||
|
||||
<!-- Class design. As if we've ever followed good practice here. -->
|
||||
<module name="FinalClass" />
|
||||
@@ -114,7 +111,7 @@
|
||||
</module>
|
||||
<module name="MethodTypeParameterName" />
|
||||
<module name="PackageName">
|
||||
<property name="format" value="^dan200\.computercraft(\.[a-z][a-z0-9]*)*" />
|
||||
<property name="format" value="^(dan200\.computercraft|cc\.tweaked)(\.[a-z][a-z0-9]*)*" />
|
||||
</module>
|
||||
<module name="ParameterName" />
|
||||
<module name="StaticVariableName">
|
||||
@@ -131,18 +128,11 @@
|
||||
<module name="MethodParamPad" />
|
||||
<module name="NoLineWrap" />
|
||||
<module name="NoWhitespaceAfter">
|
||||
<property name="tokens" value="AT,INC,DEC,UNARY_MINUS,UNARY_PLUS,BNOT,LNOT,DOT,ARRAY_DECLARATOR,INDEX_OP" />
|
||||
<property name="tokens" value="AT,INC,DEC,UNARY_MINUS,UNARY_PLUS,BNOT,LNOT,DOT,ARRAY_DECLARATOR,INDEX_OP,METHOD_REF" />
|
||||
</module>
|
||||
<module name="NoWhitespaceBefore" />
|
||||
<!-- TODO: Decide on an OperatorWrap style. -->
|
||||
<module name="ParenPad">
|
||||
<property name="option" value="space" />
|
||||
<property name="tokens" value="ANNOTATION,ANNOTATION_FIELD_DEF,CTOR_CALL,CTOR_DEF,ENUM_CONSTANT_DEF,LITERAL_CATCH,LITERAL_DO,LITERAL_FOR,LITERAL_IF,LITERAL_NEW,LITERAL_SWITCH,LITERAL_SYNCHRONIZED,LITERAL_WHILE,METHOD_CALL,METHOD_DEF,RESOURCE_SPECIFICATION,SUPER_CTOR_CALL,LAMBDA" />
|
||||
</module>
|
||||
<module name="ParenPad">
|
||||
<property name="option" value="nospace" />
|
||||
<property name="tokens" value="DOT,EXPR,QUESTION" />
|
||||
</module>
|
||||
<module name="ParenPad" />
|
||||
<module name="SeparatorWrap">
|
||||
<property name="option" value="eol" />
|
||||
<property name="tokens" value="COMMA,SEMI,ELLIPSIS,ARRAY_DECLARATOR,RBRACK,METHOD_REF" />
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -1,61 +0,0 @@
|
||||
<code_scheme name="Project" version="173">
|
||||
<JSON>
|
||||
<option name="OBJECT_WRAPPING" value="1" />
|
||||
<option name="ARRAY_WRAPPING" value="1" />
|
||||
</JSON>
|
||||
<JavaCodeStyleSettings>
|
||||
<option name="PACKAGES_TO_USE_IMPORT_ON_DEMAND">
|
||||
<value />
|
||||
</option>
|
||||
<option name="JD_P_AT_EMPTY_LINES" value="false" />
|
||||
<option name="JD_PRESERVE_LINE_FEEDS" value="true" />
|
||||
</JavaCodeStyleSettings>
|
||||
<codeStyleSettings language="JAVA">
|
||||
<option name="KEEP_FIRST_COLUMN_COMMENT" value="false" />
|
||||
<option name="BRACE_STYLE" value="2" />
|
||||
<option name="CLASS_BRACE_STYLE" value="2" />
|
||||
<option name="METHOD_BRACE_STYLE" value="2" />
|
||||
<option name="LAMBDA_BRACE_STYLE" value="5" />
|
||||
<option name="ELSE_ON_NEW_LINE" value="true" />
|
||||
<option name="CATCH_ON_NEW_LINE" value="true" />
|
||||
<option name="FINALLY_ON_NEW_LINE" value="true" />
|
||||
<option name="SPACE_WITHIN_METHOD_CALL_PARENTHESES" value="true" />
|
||||
<option name="SPACE_WITHIN_METHOD_PARENTHESES" value="true" />
|
||||
<option name="SPACE_WITHIN_IF_PARENTHESES" value="true" />
|
||||
<option name="SPACE_WITHIN_WHILE_PARENTHESES" value="true" />
|
||||
<option name="SPACE_WITHIN_FOR_PARENTHESES" value="true" />
|
||||
<option name="SPACE_WITHIN_TRY_PARENTHESES" value="true" />
|
||||
<option name="SPACE_WITHIN_CATCH_PARENTHESES" value="true" />
|
||||
<option name="SPACE_WITHIN_SWITCH_PARENTHESES" value="true" />
|
||||
<option name="SPACE_WITHIN_SYNCHRONIZED_PARENTHESES" value="true" />
|
||||
<option name="SPACE_WITHIN_ARRAY_INITIALIZER_BRACES" value="true" />
|
||||
<option name="SPACE_BEFORE_IF_PARENTHESES" value="false" />
|
||||
<option name="SPACE_BEFORE_WHILE_PARENTHESES" value="false" />
|
||||
<option name="SPACE_BEFORE_FOR_PARENTHESES" value="false" />
|
||||
<option name="SPACE_BEFORE_TRY_PARENTHESES" value="false" />
|
||||
<option name="SPACE_BEFORE_CATCH_PARENTHESES" value="false" />
|
||||
<option name="SPACE_BEFORE_SWITCH_PARENTHESES" value="false" />
|
||||
<option name="SPACE_BEFORE_SYNCHRONIZED_PARENTHESES" value="false" />
|
||||
<option name="SPACE_BEFORE_ARRAY_INITIALIZER_LBRACE" value="true" />
|
||||
<option name="KEEP_SIMPLE_METHODS_IN_ONE_LINE" value="true" />
|
||||
<option name="KEEP_SIMPLE_LAMBDAS_IN_ONE_LINE" value="true" />
|
||||
<option name="KEEP_SIMPLE_CLASSES_IN_ONE_LINE" value="true" />
|
||||
<option name="IF_BRACE_FORCE" value="1" />
|
||||
<option name="DOWHILE_BRACE_FORCE" value="1" />
|
||||
<option name="WHILE_BRACE_FORCE" value="1" />
|
||||
<option name="FOR_BRACE_FORCE" value="1" />
|
||||
<option name="SPACE_WITHIN_ANNOTATION_PARENTHESES" value="true" />
|
||||
<indentOptions>
|
||||
<option name="CONTINUATION_INDENT_SIZE" value="4" />
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="JSON">
|
||||
<option name="KEEP_BLANK_LINES_IN_CODE" value="1" />
|
||||
<option name="SPACE_WITHIN_BRACKETS" value="true" />
|
||||
<option name="SPACE_WITHIN_BRACES" value="true" />
|
||||
<indentOptions>
|
||||
<option name="INDENT_SIZE" value="4" />
|
||||
<option name="CONTINUATION_INDENT_SIZE" value="4" />
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
</code_scheme>
|
@@ -3,19 +3,20 @@ module: [kind=event] alarm
|
||||
see: os.setAlarm To start an alarm.
|
||||
---
|
||||
|
||||
The @{timer} event is fired when an alarm started with @{os.setAlarm} completes.
|
||||
The @{alarm} event is fired when an alarm started with @{os.setAlarm} completes.
|
||||
|
||||
## Return Values
|
||||
1. @{string}: The event name.
|
||||
2. @{number}: The ID of the alarm that finished.
|
||||
|
||||
## Example
|
||||
Starts a timer and then prints its ID:
|
||||
Starts a timer and then waits for it to complete.
|
||||
|
||||
```lua
|
||||
local alarmID = os.setAlarm(os.time() + 0.05)
|
||||
local alarm_id = os.setAlarm(os.time() + 0.05)
|
||||
local event, id
|
||||
repeat
|
||||
event, id = os.pullEvent("alarm")
|
||||
until id == alarmID
|
||||
until id == alarm_id
|
||||
print("Alarm with ID " .. id .. " was fired")
|
||||
```
|
||||
|
@@ -3,7 +3,7 @@ module: [kind=event] char
|
||||
see: key To listen to any key press.
|
||||
---
|
||||
|
||||
The @{char} event is fired when a character is _typed_ on the keyboard.
|
||||
The @{char} event is fired when a character is typed on the keyboard.
|
||||
|
||||
The @{char} event is different to a key press. Sometimes multiple key presses may result in one character being
|
||||
typed (for instance, on some European keyboards). Similarly, some keys (e.g. <kbd>Ctrl</kbd>) do not have any
|
||||
@@ -16,9 +16,10 @@ corresponding character. The @{key} should be used if you want to listen to key
|
||||
|
||||
## Example
|
||||
Prints each character the user presses:
|
||||
|
||||
```lua
|
||||
while true do
|
||||
local event, character = os.pullEvent("char")
|
||||
print(character .. " was pressed.")
|
||||
local event, character = os.pullEvent("char")
|
||||
print(character .. " was pressed.")
|
||||
end
|
||||
```
|
||||
|
@@ -6,7 +6,7 @@ The @{computer_command} event is fired when the `/computercraft queue` command i
|
||||
|
||||
## Return Values
|
||||
1. @{string}: The event name.
|
||||
... @{string}: The arguments passed to the command.
|
||||
2. @{string}<abbr title="Variable number of arguments">…</abbr>: The arguments passed to the command.
|
||||
|
||||
## Example
|
||||
Prints the contents of messages sent:
|
||||
|
@@ -5,9 +5,9 @@ since: 1.101.0
|
||||
|
||||
The @{file_transfer} event is queued when a user drags-and-drops a file on an open computer.
|
||||
|
||||
This event contains a single argument, that in turn has a single method @{TransferredFiles.getFiles|getFiles}. This
|
||||
returns the list of files that are being transferred. Each file is a @{fs.BinaryReadHandle|binary file handle} with an
|
||||
additional @{TransferredFile.getName|getName} method.
|
||||
This event contains a single argument of type @{TransferredFiles}, which can be used to @{TransferredFiles.getFiles|get
|
||||
the files to be transferred}. Each file returned is a @{fs.BinaryReadHandle|binary file handle} with an additional
|
||||
@{TransferredFile.getName|getName} method.
|
||||
|
||||
## Return values
|
||||
1. @{string}: The event name
|
||||
|
@@ -11,4 +11,4 @@ This event is normally handled inside @{http.checkURL}, but it can still be seen
|
||||
1. @{string}: The event name.
|
||||
2. @{string}: The URL requested to be checked.
|
||||
3. @{boolean}: Whether the check succeeded.
|
||||
4. @{string|nil}: If the check failed, a reason explaining why the check failed.
|
||||
4. <span class="type">@{string}|@{nil}</span>: If the check failed, a reason explaining why the check failed.
|
||||
|
@@ -11,7 +11,8 @@ This event is normally handled inside @{http.get} and @{http.post}, but it can s
|
||||
1. @{string}: The event name.
|
||||
2. @{string}: The URL of the site requested.
|
||||
3. @{string}: An error describing the failure.
|
||||
4. @{http.Response|nil}: A response handle if the connection succeeded, but the server's response indicated failure.
|
||||
4. <span class="type">@{http.Response}|@{nil}</span>: A response handle if the connection succeeded, but the server's
|
||||
response indicated failure.
|
||||
|
||||
## Example
|
||||
Prints an error why the website cannot be contacted:
|
||||
|
@@ -10,7 +10,7 @@ This event is normally handled inside @{http.get} and @{http.post}, but it can s
|
||||
## Return Values
|
||||
1. @{string}: The event name.
|
||||
2. @{string}: The URL of the site requested.
|
||||
3. @{http.Response}: The handle for the response text.
|
||||
3. @{http.Response}: The successful HTTP response.
|
||||
|
||||
## Example
|
||||
Prints the content of a website (this may fail if the request fails):
|
||||
|
@@ -10,7 +10,7 @@ The @{modem_message} event is fired when a message is received on an open channe
|
||||
3. @{number}: The channel that the message was sent on.
|
||||
4. @{number}: The reply channel set by the sender.
|
||||
5. @{any}: The message as sent by the sender.
|
||||
6. @{number}: The distance between the sender and the receiver, in blocks.
|
||||
6. <span class="type">@{number}|@{nil}</span>: The distance between the sender and the receiver in blocks, or @{nil} if the message was sent between dimensions.
|
||||
|
||||
## Example
|
||||
Wraps a @{modem} peripheral, opens channel 0 for listening, and prints all received messages.
|
||||
@@ -20,7 +20,9 @@ local modem = peripheral.find("modem") or error("No modem attached", 0)
|
||||
modem.open(0)
|
||||
|
||||
while true do
|
||||
local event, side, channel, replyChannel, message, distance = os.pullEvent("modem_message")
|
||||
print(("Message received on side %s on channel %d (reply to %d) from %f blocks away with message %s"):format(side, channel, replyChannel, distance, tostring(message)))
|
||||
local event, side, channel, replyChannel, message, distance = os.pullEvent("modem_message")
|
||||
print(("Message received on side %s on channel %d (reply to %d) from %f blocks away with message %s"):format(
|
||||
side, channel, replyChannel, distance, tostring(message)
|
||||
))
|
||||
end
|
||||
```
|
||||
|
@@ -6,10 +6,11 @@ The @{monitor_resize} event is fired when an adjacent or networked monitor's siz
|
||||
|
||||
## Return Values
|
||||
1. @{string}: The event name.
|
||||
2. @{string}: The side or network ID of the monitor that resized.
|
||||
2. @{string}: The side or network ID of the monitor that was resized.
|
||||
|
||||
## Example
|
||||
Prints a message when a monitor is resized:
|
||||
|
||||
```lua
|
||||
while true do
|
||||
local event, side = os.pullEvent("monitor_resize")
|
||||
|
@@ -14,7 +14,7 @@ This event is usually handled by @{rednet.receive}, but it can also be pulled ma
|
||||
1. @{string}: The event name.
|
||||
2. @{number}: The ID of the sending computer.
|
||||
3. @{any}: The message sent.
|
||||
4. @{string|nil}: The protocol of the message, if provided.
|
||||
4. <span class="type">@{string}|@{nil}</span>: The protocol of the message, if provided.
|
||||
|
||||
## Example
|
||||
Prints a message when one is sent:
|
||||
|
@@ -4,6 +4,9 @@ module: [kind=event] redstone
|
||||
|
||||
The @{event!redstone} event is fired whenever any redstone inputs on the computer change.
|
||||
|
||||
## Return values
|
||||
1. @{string}: The event name.
|
||||
|
||||
## Example
|
||||
Prints a message when a redstone input changes:
|
||||
```lua
|
||||
|
@@ -10,7 +10,7 @@ The @{task_complete} event is fired when an asynchronous task completes. This is
|
||||
2. @{number}: The ID of the task that completed.
|
||||
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.)
|
||||
...: Any parameters returned from the command.
|
||||
5. <abbr title="Variable number of arguments">…</abbr>: Any parameters returned from the command.
|
||||
|
||||
## Example
|
||||
Prints the results of an asynchronous command:
|
||||
|
@@ -9,8 +9,12 @@ The @{term_resize} event is fired when the main terminal is resized. For instanc
|
||||
When this event fires, some parts of the terminal may have been moved or deleted. Simple terminal programs (those
|
||||
not using @{term.setCursorPos}) can ignore this event, but more complex GUI programs should redraw the entire screen.
|
||||
|
||||
## Return values
|
||||
1. @{string}: The event name.
|
||||
|
||||
## Example
|
||||
Prints :
|
||||
Print a message each time the terminal is resized.
|
||||
|
||||
```lua
|
||||
while true do
|
||||
os.pullEvent("term_resize")
|
||||
|
@@ -8,6 +8,9 @@ This event is normally handled by @{os.pullEvent}, and will not be returned. How
|
||||
|
||||
@{terminate} will be sent even when a filter is provided to @{os.pullEventRaw}. When using @{os.pullEventRaw} with a filter, make sure to check that the event is not @{terminate}.
|
||||
|
||||
## Return values
|
||||
1. @{string}: The event name.
|
||||
|
||||
## Example
|
||||
Prints a message when Ctrl-T is held:
|
||||
```lua
|
||||
|
@@ -10,12 +10,12 @@ The @{timer} event is fired when a timer started with @{os.startTimer} completes
|
||||
2. @{number}: The ID of the timer that finished.
|
||||
|
||||
## Example
|
||||
Starts a timer and then prints its ID:
|
||||
Start and wait for a timer to finish.
|
||||
```lua
|
||||
local timerID = os.startTimer(2)
|
||||
local timer_id = os.startTimer(2)
|
||||
local event, id
|
||||
repeat
|
||||
event, id = os.pullEvent("timer")
|
||||
until id == timerID
|
||||
until id == timer_id
|
||||
print("Timer with ID " .. id .. " was fired")
|
||||
```
|
||||
|
@@ -4,6 +4,9 @@ module: [kind=event] turtle_inventory
|
||||
|
||||
The @{turtle_inventory} event is fired when a turtle's inventory is changed.
|
||||
|
||||
## Return values
|
||||
1. @{string}: The event name.
|
||||
|
||||
## Example
|
||||
Prints a message when the inventory is changed:
|
||||
```lua
|
||||
|
@@ -25,7 +25,7 @@ single-player or multiplayer. Look for lines that look like this:
|
||||
```
|
||||
|
||||
On 1.95.0 and later, this will be a single entry with `host = "$private"`. On earlier versions, this will be a number of
|
||||
`[[http.rules]]` with various IP addresses. You will want to remove all of the `[[http.rules]]` entires that have
|
||||
`[[http.rules]]` with various IP addresses. You will want to remove all of the `[[http.rules]]` entries that have
|
||||
`action = "deny"`. Then save the file and relaunch Minecraft (Server).
|
||||
|
||||
Here's what it should look like after removing:
|
||||
@@ -54,7 +54,7 @@ like this:
|
||||
```toml
|
||||
#A list of wildcards for domains or IP ranges that cannot be accessed through the "http" API on Computers.
|
||||
#If this is empty then all whitelisted domains will be accessible. Example: "*.github.com" will block access to all subdomains of github.com.
|
||||
#You can use domain names ("pastebin.com"), wilcards ("*.pastebin.com") or CIDR notation ("127.0.0.0/8").
|
||||
#You can use domain names ("pastebin.com"), wildcards ("*.pastebin.com") or CIDR notation ("127.0.0.0/8").
|
||||
blacklist = ["127.0.0.0/8", "10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16", "fd00::/8"]
|
||||
```
|
||||
|
||||
@@ -65,7 +65,7 @@ Here's what it should look like after removing:
|
||||
```toml
|
||||
#A list of wildcards for domains or IP ranges that cannot be accessed through the "http" API on Computers.
|
||||
#If this is empty then all whitelisted domains will be accessible. Example: "*.github.com" will block access to all subdomains of github.com.
|
||||
#You can use domain names ("pastebin.com"), wilcards ("*.pastebin.com") or CIDR notation ("127.0.0.0/8").
|
||||
#You can use domain names ("pastebin.com"), wildcards ("*.pastebin.com") or CIDR notation ("127.0.0.0/8").
|
||||
blacklist = []
|
||||
```
|
||||
|
||||
|
@@ -1,64 +0,0 @@
|
||||
--- @module fs
|
||||
|
||||
--- Returns true if a path is mounted to the parent filesystem.
|
||||
--
|
||||
-- The root filesystem "/" is considered a mount, along with disk folders and
|
||||
-- the rom folder. Other programs (such as network shares) can exstend this to
|
||||
-- make other mount types by correctly assigning their return value for getDrive.
|
||||
--
|
||||
-- @tparam string path The path to check.
|
||||
-- @treturn boolean If the path is mounted, rather than a normal file/folder.
|
||||
-- @throws If the path does not exist.
|
||||
-- @see getDrive
|
||||
-- @since 1.87.0
|
||||
function isDriveRoot(path) end
|
||||
|
||||
--[[- Provides completion for a file or directory name, suitable for use with
|
||||
@{_G.read}.
|
||||
|
||||
When a directory is a possible candidate for completion, two entries are
|
||||
included - one with a trailing slash (indicating that entries within this
|
||||
directory exist) and one without it (meaning this entry is an immediate
|
||||
completion candidate). `include_dirs` can be set to @{false} to only include
|
||||
those with a trailing slash.
|
||||
|
||||
@tparam[1] string path The path to complete.
|
||||
@tparam[1] string location The location where paths are resolved from.
|
||||
@tparam[1,opt=true] boolean include_files When @{false}, only directories will
|
||||
be included in the returned list.
|
||||
@tparam[1,opt=true] boolean include_dirs When @{false}, "raw" directories will
|
||||
not be included in the returned list.
|
||||
|
||||
@tparam[2] string path The path to complete.
|
||||
@tparam[2] string location The location where paths are resolved from.
|
||||
@tparam[2] {
|
||||
include_dirs? = boolean, include_files? = boolean,
|
||||
include_hidden? = boolean
|
||||
} options
|
||||
This table form is an expanded version of the previous syntax. The
|
||||
`include_files` and `include_dirs` arguments from above are passed in as fields.
|
||||
|
||||
This table also accepts the following options:
|
||||
- `include_hidden`: Whether to include hidden files (those starting with `.`)
|
||||
by default. They will still be shown when typing a `.`.
|
||||
|
||||
@treturn { string... } A list of possible completion candidates.
|
||||
@since 1.74
|
||||
@changed 1.101.0
|
||||
@usage Complete files in the root directory.
|
||||
|
||||
read(nil, nil, function(str)
|
||||
return fs.complete(str, "", true, false)
|
||||
end)
|
||||
|
||||
@usage Complete files in the root directory, hiding hidden files by default.
|
||||
|
||||
read(nil, nil, function(str)
|
||||
return fs.complete(str, "", {
|
||||
include_files = true,
|
||||
include_dirs = false,
|
||||
included_hidden = false,
|
||||
})
|
||||
end)
|
||||
]]
|
||||
function complete(path, location, include_files, include_dirs) end
|
@@ -1,177 +0,0 @@
|
||||
--- Make HTTP requests, sending and receiving data to a remote web server.
|
||||
--
|
||||
-- @module http
|
||||
-- @since 1.1
|
||||
-- @see local_ips To allow accessing servers running on your local network.
|
||||
|
||||
--- Asynchronously make a HTTP request to the given url.
|
||||
--
|
||||
-- This returns immediately, a @{http_success} or @{http_failure} will be queued
|
||||
-- once the request has completed.
|
||||
--
|
||||
-- @tparam string url The url to request
|
||||
-- @tparam[opt] string body An optional string containing the body of the
|
||||
-- request. If specified, a `POST` request will be made instead.
|
||||
-- @tparam[opt] { [string] = string } headers Additional headers to send as part
|
||||
-- of this request.
|
||||
-- @tparam[opt] boolean binary Whether to make a binary HTTP request. If true,
|
||||
-- the body will not be UTF-8 encoded, and the received response will not be
|
||||
-- decoded.
|
||||
--
|
||||
-- @tparam[2] {
|
||||
-- url = string, body? = string, headers? = { [string] = string },
|
||||
-- binary? = boolean, method? = string, redirect? = boolean,
|
||||
-- } request Options for the request.
|
||||
--
|
||||
-- This table form is an expanded version of the previous syntax. All arguments
|
||||
-- from above are passed in as fields instead (for instance,
|
||||
-- `http.request("https://example.com")` becomes `http.request { url =
|
||||
-- "https://example.com" }`).
|
||||
--
|
||||
-- This table also accepts several additional options:
|
||||
--
|
||||
-- - `method`: Which HTTP method to use, for instance `"PATCH"` or `"DELETE"`.
|
||||
-- - `redirect`: Whether to follow HTTP redirects. Defaults to true.
|
||||
--
|
||||
-- @see http.get For a synchronous way to make GET requests.
|
||||
-- @see http.post For a synchronous way to make POST requests.
|
||||
--
|
||||
-- @changed 1.63 Added argument for headers.
|
||||
-- @changed 1.80pr1 Added argument for binary handles.
|
||||
-- @changed 1.80pr1.6 Added support for table argument.
|
||||
-- @changed 1.86.0 Added PATCH and TRACE methods.
|
||||
function request(...) end
|
||||
|
||||
--- Make a HTTP GET request to the given url.
|
||||
--
|
||||
-- @tparam string url The url to request
|
||||
-- @tparam[opt] { [string] = string } headers Additional headers to send as part
|
||||
-- of this request.
|
||||
-- @tparam[opt] boolean binary Whether to make a binary HTTP request. If true,
|
||||
-- the body will not be UTF-8 encoded, and the received response will not be
|
||||
-- decoded.
|
||||
--
|
||||
-- @tparam[2] {
|
||||
-- url = string, headers? = { [string] = string },
|
||||
-- binary? = boolean, method? = string, redirect? = boolean,
|
||||
-- } request Options for the request. See @{http.request} for details on how
|
||||
-- these options behave.
|
||||
--
|
||||
-- @treturn Response The resulting http response, which can be read from.
|
||||
-- @treturn[2] nil When the http request failed, such as in the event of a 404
|
||||
-- error or connection timeout.
|
||||
-- @treturn string A message detailing why the request failed.
|
||||
-- @treturn Response|nil The failing http response, if available.
|
||||
--
|
||||
-- @changed 1.63 Added argument for headers.
|
||||
-- @changed 1.80pr1 Response handles are now returned on error if available.
|
||||
-- @changed 1.80pr1 Added argument for binary handles.
|
||||
-- @changed 1.80pr1.6 Added support for table argument.
|
||||
-- @changed 1.86.0 Added PATCH and TRACE methods.
|
||||
--
|
||||
-- @usage Make a request to [example.tweaked.cc](https://example.tweaked.cc),
|
||||
-- and print the returned page.
|
||||
-- ```lua
|
||||
-- local request = http.get("https://example.tweaked.cc")
|
||||
-- print(request.readAll())
|
||||
-- -- => HTTP is working!
|
||||
-- request.close()
|
||||
-- ```
|
||||
function get(...) end
|
||||
|
||||
--- Make a HTTP POST request to the given url.
|
||||
--
|
||||
-- @tparam string url The url to request
|
||||
-- @tparam string body The body of the POST request.
|
||||
-- @tparam[opt] { [string] = string } headers Additional headers to send as part
|
||||
-- of this request.
|
||||
-- @tparam[opt] boolean binary Whether to make a binary HTTP request. If true,
|
||||
-- the body will not be UTF-8 encoded, and the received response will not be
|
||||
-- decoded.
|
||||
--
|
||||
-- @tparam[2] {
|
||||
-- url = string, body? = string, headers? = { [string] = string },
|
||||
-- binary? = boolean, method? = string, redirect? = boolean,
|
||||
-- } request Options for the request. See @{http.request} for details on how
|
||||
-- these options behave.
|
||||
--
|
||||
-- @treturn Response The resulting http response, which can be read from.
|
||||
-- @treturn[2] nil When the http request failed, such as in the event of a 404
|
||||
-- error or connection timeout.
|
||||
-- @treturn string A message detailing why the request failed.
|
||||
-- @treturn Response|nil The failing http response, if available.
|
||||
--
|
||||
-- @since 1.31
|
||||
-- @changed 1.63 Added argument for headers.
|
||||
-- @changed 1.80pr1 Response handles are now returned on error if available.
|
||||
-- @changed 1.80pr1 Added argument for binary handles.
|
||||
-- @changed 1.80pr1.6 Added support for table argument.
|
||||
-- @changed 1.86.0 Added PATCH and TRACE methods.
|
||||
function post(...) end
|
||||
|
||||
--- Asynchronously determine whether a URL can be requested.
|
||||
--
|
||||
-- If this returns `true`, one should also listen for @{http_check} which will
|
||||
-- container further information about whether the URL is allowed or not.
|
||||
--
|
||||
-- @tparam string url The URL to check.
|
||||
-- @treturn true When this url is not invalid. This does not imply that it is
|
||||
-- allowed - see the comment above.
|
||||
-- @treturn[2] false When this url is invalid.
|
||||
-- @treturn string A reason why this URL is not valid (for instance, if it is
|
||||
-- malformed, or blocked).
|
||||
--
|
||||
-- @see http.checkURL For a synchronous version.
|
||||
function checkURLAsync(url) end
|
||||
|
||||
--- Determine whether a URL can be requested.
|
||||
--
|
||||
-- If this returns `true`, one should also listen for @{http_check} which will
|
||||
-- container further information about whether the URL is allowed or not.
|
||||
--
|
||||
-- @tparam string url The URL to check.
|
||||
-- @treturn true When this url is valid and can be requested via @{http.request}.
|
||||
-- @treturn[2] false When this url is invalid.
|
||||
-- @treturn string A reason why this URL is not valid (for instance, if it is
|
||||
-- malformed, or blocked).
|
||||
--
|
||||
-- @see http.checkURLAsync For an asynchronous version.
|
||||
--
|
||||
-- @usage
|
||||
-- ```lua
|
||||
-- print(http.checkURL("https://example.tweaked.cc/"))
|
||||
-- -- => true
|
||||
-- print(http.checkURL("http://localhost/"))
|
||||
-- -- => false Domain not permitted
|
||||
-- print(http.checkURL("not a url"))
|
||||
-- -- => false URL malformed
|
||||
-- ```
|
||||
function checkURL(url) end
|
||||
|
||||
--- Open a websocket.
|
||||
--
|
||||
-- @tparam string url The websocket url to connect to. This should have the
|
||||
-- `ws://` or `wss://` protocol.
|
||||
-- @tparam[opt] { [string] = string } headers Additional headers to send as part
|
||||
-- of the initial websocket connection.
|
||||
--
|
||||
-- @treturn Websocket The websocket connection.
|
||||
-- @treturn[2] false If the websocket connection failed.
|
||||
-- @treturn string An error message describing why the connection failed.
|
||||
-- @since 1.80pr1.1
|
||||
-- @changed 1.80pr1.3 No longer asynchronous.
|
||||
-- @changed 1.95.3 Added User-Agent to default headers.
|
||||
function websocket(url, headers) end
|
||||
|
||||
--- Asynchronously open a websocket.
|
||||
--
|
||||
-- This returns immediately, a @{websocket_success} or @{websocket_failure}
|
||||
-- will be queued once the request has completed.
|
||||
--
|
||||
-- @tparam string url The websocket url to connect to. This should have the
|
||||
-- `ws://` or `wss://` protocol.
|
||||
-- @tparam[opt] { [string] = string } headers Additional headers to send as part
|
||||
-- of the initial websocket connection.
|
||||
-- @since 1.80pr1.3
|
||||
-- @changed 1.95.3 Added User-Agent to default headers.
|
||||
function websocketAsync(url, headers) end
|
@@ -5,7 +5,8 @@ kotlin.stdlib.default.dependency=false
|
||||
kotlin.jvm.target.validation.mode=error
|
||||
|
||||
# Mod properties
|
||||
modVersion=1.101.1
|
||||
isUnstable=false
|
||||
modVersion=1.103.1
|
||||
|
||||
# Minecraft properties: We want to configure this here so we can read it in settings.gradle
|
||||
mcVersion=1.19.2
|
||||
mcVersion=1.19.3
|
||||
|
@@ -2,70 +2,153 @@
|
||||
|
||||
# Minecraft
|
||||
# MC version is specified in gradle.properties, as we need that in settings.gradle.
|
||||
forge = "43.1.1"
|
||||
parchment = "2022.10.16"
|
||||
fabric-api = "0.68.1+1.19.3"
|
||||
fabric-loader = "0.14.11"
|
||||
forge = "44.1.0"
|
||||
forgeSpi = "6.0.0"
|
||||
mixin = "0.8.5"
|
||||
parchment = "2022.11.27"
|
||||
parchmentMc = "1.19.2"
|
||||
|
||||
# Normal dependencies
|
||||
asm = "9.3"
|
||||
autoService = "1.0.1"
|
||||
cobalt = { strictly = "[0.5.8,0.6.0)", prefer = "0.5.8" }
|
||||
checkerFramework = "3.12.0"
|
||||
cobalt = "0.6.0"
|
||||
fastutil = "8.5.9"
|
||||
guava = "31.1-jre"
|
||||
jetbrainsAnnotations = "23.0.0"
|
||||
kotlin = "1.7.10"
|
||||
kotlin-coroutines = "1.6.0"
|
||||
jsr305 = "3.0.2"
|
||||
kotlin = "1.8.0"
|
||||
kotlin-coroutines = "1.6.4"
|
||||
netty = "4.1.82.Final"
|
||||
nightConfig = "3.6.5"
|
||||
slf4j = "1.7.36"
|
||||
|
||||
# Minecraft mods
|
||||
forgeConfig = "5.0.4"
|
||||
iris = "1.19.3-v1.4.6"
|
||||
jei = "11.3.0.262"
|
||||
modmenu = "5.0.1"
|
||||
oculus = "1.2.5"
|
||||
rei = "10.0.578"
|
||||
rubidium = "0.6.1"
|
||||
sodium = "mc1.19.3-0.4.6"
|
||||
|
||||
# Testing
|
||||
byteBuddy = "1.12.19"
|
||||
hamcrest = "2.2"
|
||||
jqwik = "1.7.0"
|
||||
junit = "5.9.1"
|
||||
|
||||
# Build tools
|
||||
cctJavadoc = "1.5.2"
|
||||
cctJavadoc = "1.6.0"
|
||||
checkstyle = "10.3.4"
|
||||
curseForgeGradle = "1.0.11"
|
||||
errorProne-core = "2.14.0"
|
||||
errorProne-plugin = "2.0.2"
|
||||
fabric-loom = "1.0-SNAPSHOT"
|
||||
forgeGradle = "5.1.+"
|
||||
githubRelease = "2.2.12"
|
||||
illuaminate = "0.1.0-7-g2a5a89c"
|
||||
ideaExt = "1.1.6"
|
||||
illuaminate = "0.1.0-13-g689d73d"
|
||||
librarian = "1.+"
|
||||
minotaur = "2.+"
|
||||
mixinGradle = "0.7.+"
|
||||
nullAway = "0.9.9"
|
||||
quiltflower = "1.7.3"
|
||||
shadow = "7.1.2"
|
||||
spotless = "6.8.0"
|
||||
taskTree = "2.1.0"
|
||||
vanillaGradle = "0.2.1-SNAPSHOT"
|
||||
|
||||
[libraries]
|
||||
# Normal dependencies
|
||||
asm = { module = "org.ow2.asm:asm", version.ref = "asm" }
|
||||
autoService = { module = "com.google.auto.service:auto-service", version.ref = "autoService" }
|
||||
checkerFramework = { module = "org.checkerframework:checker-qual", version.ref = "checkerFramework" }
|
||||
cobalt = { module = "org.squiddev:Cobalt", version.ref = "cobalt" }
|
||||
fastutil = { module = "it.unimi.dsi:fastutil", version.ref = "fastutil" }
|
||||
forgeSpi = { module = "net.minecraftforge:forgespi", version.ref = "forgeSpi" }
|
||||
guava = { module = "com.google.guava:guava", version.ref = "guava" }
|
||||
jetbrainsAnnotations = { module = "org.jetbrains:annotations", version.ref = "jetbrainsAnnotations" }
|
||||
jsr305 = { module = "com.google.code.findbugs:jsr305", version.ref = "jsr305" }
|
||||
kotlin-platform = { module = "org.jetbrains.kotlin:kotlin-bom", version.ref = "kotlin" }
|
||||
kotlin-coroutines = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kotlin-coroutines" }
|
||||
kotlin-stdlib = { module = "org.jetbrains.kotlin:kotlin-stdlib-jdk8", version.ref = "kotlin" }
|
||||
kotlin-stdlib = { module = "org.jetbrains.kotlin:kotlin-stdlib", version.ref = "kotlin" }
|
||||
netty-http = { module = "io.netty:netty-codec-http", version.ref = "netty" }
|
||||
nightConfig-core = { module = "com.electronwill.night-config:core", version.ref = "nightConfig" }
|
||||
nightConfig-toml = { module = "com.electronwill.night-config:toml", version.ref = "nightConfig" }
|
||||
slf4j = { module = "org.slf4j:slf4j-api", version.ref = "slf4j" }
|
||||
|
||||
# Minecraft mods
|
||||
fabric-api = { module = "net.fabricmc.fabric-api:fabric-api", version.ref = "fabric-api" }
|
||||
fabric-loader = { module = "net.fabricmc:fabric-loader", version.ref = "fabric-loader" }
|
||||
forgeConfig = { module = "fuzs.forgeconfigapiport:forgeconfigapiport-fabric", version.ref = "forgeConfig" }
|
||||
iris = { module = "maven.modrinth:iris", version.ref = "iris" }
|
||||
jei-api = { module = "mezz.jei:jei-1.19.2-common-api", version.ref = "jei" }
|
||||
jei-fabric = { module = "mezz.jei:jei-1.19.2-fabric", version.ref = "jei" }
|
||||
jei-forge = { module = "mezz.jei:jei-1.19.2-forge", version.ref = "jei" }
|
||||
mixin = { module = "org.spongepowered:mixin", version.ref = "mixin" }
|
||||
modmenu = { module = "com.terraformersmc:modmenu", version.ref = "modmenu" }
|
||||
oculus = { module = "maven.modrinth:oculus", version.ref = "oculus" }
|
||||
rei-api = { module = "me.shedaniel:RoughlyEnoughItems-api", version.ref = "rei" }
|
||||
rei-builtin = { module = "me.shedaniel:RoughlyEnoughItems-default-plugin", version.ref = "rei" }
|
||||
rei-fabric = { module = "me.shedaniel:RoughlyEnoughItems-fabric", version.ref = "rei" }
|
||||
rubidium = { module = "maven.modrinth:rubidium", version.ref = "rubidium" }
|
||||
sodium = { module = "maven.modrinth:sodium", version.ref = "sodium" }
|
||||
|
||||
# Testing
|
||||
byteBuddyAgent = { module ="net.bytebuddy:byte-buddy-agent", version.ref = "byteBuddy" }
|
||||
byteBuddy = { module ="net.bytebuddy:byte-buddy", version.ref = "byteBuddy" }
|
||||
hamcrest = { module = "org.hamcrest:hamcrest", version.ref = "hamcrest" }
|
||||
jqwik-api = { module = "net.jqwik:jqwik-api", version.ref = "jqwik" }
|
||||
jqwik-engine = { module = "net.jqwik:jqwik-engine", version.ref = "jqwik" }
|
||||
junit-jupiter-api = { module = "org.junit.jupiter:junit-jupiter-api", version.ref = "junit" }
|
||||
junit-jupiter-engine = { module = "org.junit.jupiter:junit-jupiter-engine", version.ref = "junit" }
|
||||
junit-jupiter-params = { module = "org.junit.jupiter:junit-jupiter-params", version.ref = "junit" }
|
||||
slf4j-simple = { module = "org.slf4j:slf4j-simple", version.ref = "slf4j" }
|
||||
|
||||
# Build tools
|
||||
cctJavadoc = { module = "cc.tweaked:cct-javadoc", version.ref = "cctJavadoc" }
|
||||
checkstyle = { module = "com.puppycrawl.tools:checkstyle", version.ref = "checkstyle" }
|
||||
errorProne-annotations = { module = "com.google.errorprone:error_prone_annotations", version.ref = "errorProne-core" }
|
||||
errorProne-api = { module = "com.google.errorprone:error_prone_check_api", version.ref = "errorProne-core" }
|
||||
errorProne-core = { module = "com.google.errorprone:error_prone_core", version.ref = "errorProne-core" }
|
||||
errorProne-plugin = { module = "net.ltgt.gradle:gradle-errorprone-plugin", version.ref = "errorProne-plugin" }
|
||||
errorProne-testHelpers = { module = "com.google.errorprone:error_prone_test_helpers", version.ref = "errorProne-core" }
|
||||
fabric-loom = { module = "net.fabricmc:fabric-loom", version.ref = "fabric-loom" }
|
||||
forgeGradle = { module = "net.minecraftforge.gradle:ForgeGradle", version.ref = "forgeGradle" }
|
||||
kotlin-plugin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" }
|
||||
librarian = { module = "org.parchmentmc:librarian", version.ref = "librarian" }
|
||||
nullAway = { module = "com.uber.nullaway:nullaway", version.ref = "nullAway" }
|
||||
quiltflower = { module = "io.github.juuxel:loom-quiltflower", version.ref = "quiltflower" }
|
||||
spotless = { module = "com.diffplug.spotless:spotless-plugin-gradle", version.ref = "spotless" }
|
||||
vanillaGradle = { module = "org.spongepowered:vanillagradle", version.ref = "vanillaGradle" }
|
||||
|
||||
[plugins]
|
||||
kotlin = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
|
||||
taskTree = { id = "com.dorongold.task-tree", version.ref = "taskTree" }
|
||||
curseForgeGradle = { id = "net.darkhax.curseforgegradle", version.ref = "curseForgeGradle" }
|
||||
mixinGradle = { id = "org.spongepowered.mixin", version.ref = "mixinGradle" }
|
||||
minotaur = { id = "com.modrinth.minotaur", version.ref = "minotaur" }
|
||||
githubRelease = { id = "com.github.breadmoirai.github-release", version.ref = "githubRelease" }
|
||||
forgeGradle = { id = "net.minecraftforge.gradle", version.ref = "forgeGradle" }
|
||||
githubRelease = { id = "com.github.breadmoirai.github-release", version.ref = "githubRelease" }
|
||||
ideaExt = { id = "org.jetbrains.gradle.plugin.idea-ext", version.ref = "ideaExt" }
|
||||
kotlin = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
|
||||
librarian = { id = "org.parchmentmc.librarian.forgegradle", version.ref = "librarian" }
|
||||
minotaur = { id = "com.modrinth.minotaur", version.ref = "minotaur" }
|
||||
mixinGradle = { id = "org.spongepowered.mixin", version.ref = "mixinGradle" }
|
||||
shadow = { id = "com.github.johnrengelman.shadow", version.ref = "shadow" }
|
||||
taskTree = { id = "com.dorongold.task-tree", version.ref = "taskTree" }
|
||||
|
||||
[bundles]
|
||||
kotlin = ["kotlin-stdlib", "kotlin-coroutines"]
|
||||
|
||||
# Minecraft
|
||||
externalMods-common = ["jei-api", "forgeConfig", "nightConfig-core", "nightConfig-toml"]
|
||||
externalMods-forge-compile = ["oculus", "jei-api"]
|
||||
externalMods-forge-runtime = []
|
||||
externalMods-fabric = ["fabric-loader", "fabric-api", "forgeConfig", "nightConfig-core", "nightConfig-toml"]
|
||||
externalMods-fabric-compile = ["iris", "jei-api", "rei-api", "rei-builtin"]
|
||||
externalMods-fabric-runtime = ["modmenu"]
|
||||
|
||||
# Testing
|
||||
test = ["junit-jupiter-api", "junit-jupiter-params", "hamcrest", "jqwik-api"]
|
||||
testRuntime = ["junit-jupiter-engine", "jqwik-engine"]
|
||||
|
@@ -2,25 +2,26 @@
|
||||
|
||||
(sources
|
||||
/doc/
|
||||
/build/docs/luaJavadoc/
|
||||
/src/main/resources/*/computercraft/lua/bios.lua
|
||||
/src/main/resources/*/computercraft/lua/rom/
|
||||
/src/test/resources/test-rom
|
||||
/src/web/mount)
|
||||
/projects/forge/build/docs/luaJavadoc/
|
||||
/projects/core/src/main/resources/data/computercraft/lua/bios.lua
|
||||
/projects/core/src/main/resources/data/computercraft/lua/rom/
|
||||
/projects/core/src/test/resources/test-rom
|
||||
/projects/web/src/mount)
|
||||
|
||||
|
||||
(doc
|
||||
(destination build/illuaminate)
|
||||
; Also defined in projects/web/build.gradle.kts
|
||||
(destination /projects/web/build/illuaminate)
|
||||
(index doc/index.md)
|
||||
|
||||
(site
|
||||
(title "CC: Tweaked")
|
||||
(logo src/main/resources/pack.png)
|
||||
(logo projects/common/src/main/resources/pack.png)
|
||||
(url https://tweaked.cc/)
|
||||
(source-link https://github.com/cc-tweaked/CC-Tweaked/blob/${commit}/${path}#L${line})
|
||||
|
||||
(styles src/web/styles.css)
|
||||
(scripts build/rollup/index.js)
|
||||
(styles /projects/web/src/styles.css)
|
||||
(scripts /projects/web/build/rollup/index.js)
|
||||
(head doc/head.html))
|
||||
|
||||
(module-kinds
|
||||
@@ -32,15 +33,15 @@
|
||||
|
||||
(library-path
|
||||
/doc/stub/
|
||||
/build/docs/luaJavadoc/
|
||||
/projects/forge/build/docs/luaJavadoc/
|
||||
|
||||
/src/main/resources/*/computercraft/lua/rom/apis/
|
||||
/src/main/resources/*/computercraft/lua/rom/apis/command/
|
||||
/src/main/resources/*/computercraft/lua/rom/apis/turtle/
|
||||
/projects/core/src/main/resources/data/computercraft/lua/rom/apis/
|
||||
/projects/core/src/main/resources/data/computercraft/lua/rom/apis/command/
|
||||
/projects/core/src/main/resources/data/computercraft/lua/rom/apis/turtle/
|
||||
|
||||
/src/main/resources/*/computercraft/lua/rom/modules/main/
|
||||
/src/main/resources/*/computercraft/lua/rom/modules/command/
|
||||
/src/main/resources/*/computercraft/lua/rom/modules/turtle/))
|
||||
/projects/core/src/main/resources/data/computercraft/lua/rom/modules/main/
|
||||
/projects/core/src/main/resources/data/computercraft/lua/rom/modules/command/
|
||||
/projects/core/src/main/resources/data/computercraft/lua/rom/modules/turtle/))
|
||||
|
||||
(at /
|
||||
(linters
|
||||
@@ -80,37 +81,37 @@
|
||||
;; We disable the unused global linter in bios.lua and the APIs. In the future
|
||||
;; hopefully we'll get illuaminate to handle this.
|
||||
(at
|
||||
(/src/main/resources/*/computercraft/lua/bios.lua
|
||||
/src/main/resources/*/computercraft/lua/rom/apis/)
|
||||
(/projects/core/src/main/resources/data/computercraft/lua/bios.lua
|
||||
/projects/core/src/main/resources/data/computercraft/lua/rom/apis/)
|
||||
(linters -var:unused-global)
|
||||
(lint (allow-toplevel-global true)))
|
||||
|
||||
;; Silence some variable warnings in documentation stubs.
|
||||
(at (/doc/stub/ /build/docs/luaJavadoc/)
|
||||
(at (/doc/stub/ /projects/forge/build/docs/luaJavadoc/)
|
||||
(linters -var:unused-global)
|
||||
(lint (allow-toplevel-global true)))
|
||||
|
||||
;; Suppress warnings for currently undocumented modules.
|
||||
(at
|
||||
(; Lua APIs
|
||||
/src/main/resources/*/computercraft/lua/rom/apis/io.lua
|
||||
/src/main/resources/*/computercraft/lua/rom/apis/window.lua)
|
||||
/projects/core/src/main/resources/data/computercraft/lua/rom/apis/io.lua
|
||||
/projects/core/src/main/resources/data/computercraft/lua/rom/apis/window.lua)
|
||||
|
||||
(linters -doc:undocumented -doc:undocumented-arg -doc:undocumented-return))
|
||||
|
||||
;; Suppress warnings for various APIs using its own deprecated members.
|
||||
(at
|
||||
(/src/main/resources/*/computercraft/lua/bios.lua
|
||||
/src/main/resources/*/computercraft/lua/rom/apis/turtle/turtle.lua)
|
||||
(/projects/core/src/main/resources/data/computercraft/lua/bios.lua
|
||||
/projects/core/src/main/resources/data/computercraft/lua/rom/apis/turtle/turtle.lua)
|
||||
(linters -var:deprecated))
|
||||
|
||||
(at /src/test/resources/test-rom
|
||||
(at /projects/core/src/test/resources/test-rom
|
||||
; We should still be able to test deprecated members.
|
||||
(linters -var:deprecated)
|
||||
|
||||
(lint
|
||||
(globals
|
||||
:max sleep write
|
||||
cct_test describe expect howlci fail it pending stub)))
|
||||
cct_test describe expect howlci fail it pending stub before_each)))
|
||||
|
||||
(at /src/web/mount/expr_template.lua (lint (globals :max __expr__)))
|
||||
(at /projects/web/src/mount/expr_template.lua (lint (globals :max __expr__)))
|
||||
|
159
projects/ARCHITECTURE.md
Normal file
159
projects/ARCHITECTURE.md
Normal file
@@ -0,0 +1,159 @@
|
||||
# Architecture
|
||||
CC: Tweaked has a rather complex project layout, as there's several use-cases we want to support (multiple mod loaders,
|
||||
usable outside of Minecraft). As such, it can be tricky to understand how the code is structured and how the various
|
||||
sub-projects interact. This document provides a high-level overview of the entire mod.
|
||||
|
||||
## Project Outline
|
||||
CC: Tweaked is split into 4 primary modules (`core`, `common`, `fabric`, `forge`). These themselves are then split into
|
||||
a public API (i.e `core-api`) and the actual implementation (i.e. `core`).
|
||||
|
||||
- `core`: This contains the core "computer" part of ComputerCraft, such as the Lua VM, filesystem and builtin APIs.
|
||||
This is also where the Lua ROM is located (`projects/core/src/main/resources/data/computercraft/lua`). Notably this
|
||||
project does _not_ depend on Minecraft, making it possible to use it in emulators and other tooling.
|
||||
|
||||
- `common`: This contains all non mod-loader-specific Minecraft code. This is where computers, turtles and peripherals
|
||||
are defined (and everything else Minecraft-related!).
|
||||
|
||||
This project is separates client code into its own separate source set (suitably named `client`). This helps us
|
||||
ensure that server code can never reference client-only code (such as LWJGL).
|
||||
|
||||
- `forge` and `fabric`: These contain any mod-loader specific code.
|
||||
|
||||
When we need to call loader-specific code from our own code (for instance, sending network messages or firing
|
||||
loader-specific events), we use a `PlatformHelper` interface (defined in
|
||||
`projects/common/src/main/java/dan200/computercraft/shared/platform/PlatformHelper.java`). This abstracts over most
|
||||
loader-specific code we need to use, and is then implemented by each mod-loader-specific project. The concrete
|
||||
implementation is then loaded with Java's [`ServiceLoader`][ServiceLoader], in a design based on [jaredlll08's
|
||||
multi-loader template][MultiLoader-Template]. We use a similar system for communicating between the API and its
|
||||
implementation.
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
subgraph Common
|
||||
platform(PlatformHelper)
|
||||
impl[AbstractComputerCraftAPI]
|
||||
end
|
||||
subgraph API
|
||||
api(ComputerCraft API) --> impl
|
||||
end
|
||||
subgraph Forge[Forge]
|
||||
platform --> forgePlatform[PlatformHelperImpl]
|
||||
impl -.-> forgeImpl[ComputerCraftAPIImpl]
|
||||
end
|
||||
subgraph Fabric
|
||||
platform --> fabricPlatform[PlatformHelperImpl]
|
||||
impl -.-> fabricImpl[ComputerCraftAPIImpl]
|
||||
end
|
||||
```
|
||||
|
||||
Note the `PlatformHelper` is only used when calling from our code into loader-specific code. While we use this to _fire_
|
||||
events, we do not use it to _subscribe_ to events. For that we just subscribe to the events in the loader-specific
|
||||
project, and then dispatch to the common `CommonHooks` (for shared code) and `ClientHooks` (for client-specific code).
|
||||
|
||||
You may notice there's a couple of other, smaller modules in the codebase. These you can probably ignore, but are worth
|
||||
mentioning:
|
||||
|
||||
- `lints`: This defines an [ErrorProne] plugin which adds a couple of compile-time checks to our code. This is what
|
||||
enforces that no client-specific code is used inside the `main` source set (and a couple of other things!).
|
||||
|
||||
- `web`: This contains the additional tooling for building [the documentation website][tweaked.cc], such as support for
|
||||
rendering recipes
|
||||
|
||||
- `buildSrc` (in the base directory, not in `projects/`): This contains any build logic shared between modules. For
|
||||
instance, `cc-tweaked.java-convention.gradle.kts` sets up the defaults for Java that we use across the whole project.
|
||||
|
||||
> **Note**
|
||||
> The Forge and Fabric modules (and their API counterparts) depend on the common modules. However, in order to correctly
|
||||
> process mixins we need to compile the common code along with the Forge/Fabric code. This leads to a slightly strange
|
||||
> build process:
|
||||
>
|
||||
> - In your IDE, Forge/Fabric depend on the common as normal.
|
||||
> - When building via Gradle, the common code is compiled alongside Forge/Fabric.
|
||||
>
|
||||
> You shouldn't need to worry about this - it should all be set up automatically - but hopefully explains a little bit
|
||||
> why our Gradle scripts are slightly odd!
|
||||
|
||||
## Testing
|
||||
CC: Tweaked has a small (though growing!) test suite to ensure various features behave correctly. Most tests are written
|
||||
in Java using [JUnit], though we also make use of [jqwik] for property testing.
|
||||
|
||||
### Test Fixtures
|
||||
Some projects define an additional `testFixtures` folder alongside their main `test` code (i.e.
|
||||
`projects/core/src/testFixtures`). This source set contains test-related code which might be consumed in dependent
|
||||
projects. For instance, core's test fixtures defines additional [Hamcrest] matchers, which are used in both `core` and
|
||||
`common`'s test suite.
|
||||
|
||||
Test fixtures may also define [Test Interfaces]. This is a pattern for writing tests to ensure that an implementation
|
||||
obeys its interface's contract. For instance, we might have a `ListContract` test, which asserts an abstract list
|
||||
behaves as expected:
|
||||
|
||||
```java
|
||||
interface ListContract<T extends List<Integer>> {
|
||||
T newList();
|
||||
|
||||
@Test
|
||||
default void testAddInsert() {
|
||||
var list = newList();
|
||||
assertTrue(list.add(123));
|
||||
assertTrue(list.contains(123));
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
We can then use this interface to create tests for a specific implementation:
|
||||
|
||||
```java
|
||||
class ArrayListTest implements ListContract<ArrayList<Integer>> {
|
||||
@Override public ArrayList<Integer> newList() { return new ArrayList<>(); }
|
||||
}
|
||||
```
|
||||
|
||||
This is especially useful when testing `PlatformHelper` and other mod loader abstractions.
|
||||
|
||||
### Lua tests
|
||||
While the majority of CC: Tweaked is written in Java, a significant portion of the code is written in Lua. As such, it's
|
||||
also useful to test that.
|
||||
|
||||
This is done by starting a Lua VM with all of ComputerCraft's APIs loaded, then starting a custom test framework
|
||||
(`mcfly.lua`). This test framework discovers tests and sends them back to the Java side. These are turned into JUnit
|
||||
tests which are then in turn run on the computer again. This allows the tests to integrate with existing Java testing
|
||||
tooling (for instance, XML test reports and IDE integration).
|
||||
|
||||
There's a slightly more detailed description of the process at `ComputerTestDelegate.java`.
|
||||
|
||||
### Game tests
|
||||
CC: Tweaked also runs several tests in-game using Minecraft's [gametest framework][mc-test]. These work by starting
|
||||
a Minecraft server and then, for each test, spawning a structure and then interacting with the blocks inside the
|
||||
structure, asserting they behave as expected.
|
||||
|
||||
Unlike most of our other tests, these are written in Kotlin. We make extensive use of [extension methods] to augment
|
||||
vanilla's own test classes, which helps give a more consistent feel to the API.
|
||||
|
||||
Each test works by defining a sequence of steps. Each step can either run an action (`thenExecute`), sleep for a period
|
||||
(`thenIdle`) or sleep until a condition is met (`thenWaitUntil`).
|
||||
|
||||
```kotlin
|
||||
fun Some_test(context: GameTestHelper) = context.sequence {
|
||||
thenExecute { context.setBlock(BlockPos(2, 2, 2), Blocks.AIR) }
|
||||
thenIdle(4)
|
||||
thenExecute { context.assertBlockHas(lamp, RedstoneLampBlock.LIT, false, "Lamp should not be lit") }
|
||||
}
|
||||
```
|
||||
|
||||
Some tests need to use Lua APIs from a computer, such as when testing `turtle.dig`. In order to do this, we install
|
||||
a custom "Lua" runtime (see `ManagedComputers.kt`) which actually runs Java functions. Tests can then enqueue a function
|
||||
to run on a particular computer and then wait for it to finish.
|
||||
|
||||
While the internals of this is quite complex, it ends up being a much nicer workflow than writing parts of the test in
|
||||
Lua. It also ends up being much more efficient, which is important when running a dozen tests at once!
|
||||
|
||||
[MultiLoader-Template]: https://github.com/jaredlll08/MultiLoader-Template/ "MultiLoader-Template - A template for a Forge + Fabric project setup using a Common source set."
|
||||
[ServiceLoader]: https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/ServiceLoader.html "ServiceLoader (Java SE 17 and JDK 17)"
|
||||
[ErrorProne]: https://errorprone.info/ "ErrorProne"
|
||||
[tweaked.cc]: https://tweaked.cc "CC: Tweaked"
|
||||
[JUnit]: https://junit.org/junit5/ "JUnit 5"
|
||||
[jqwik]: https://jqwik.net/
|
||||
[Hamcrest]: https://hamcrest.org/JavaHamcrest/ "Java Hamcrest"
|
||||
[Test Interfaces]: https://junit.org/junit5/docs/current/user-guide/#writing-tests-test-interfaces-and-default-methods
|
||||
[mc-test]: https://www.youtube.com/watch?v=vXaWOJTCYNg "Testing Minecraft in Minecraft on YouTube"
|
||||
[extension methods]: https://kotlinlang.org/docs/extensions.html "Extensions | Kotlin"
|
20
projects/common-api/build.gradle.kts
Normal file
20
projects/common-api/build.gradle.kts
Normal file
@@ -0,0 +1,20 @@
|
||||
plugins {
|
||||
id("cc-tweaked.java-convention")
|
||||
id("cc-tweaked.publishing")
|
||||
id("cc-tweaked.vanilla")
|
||||
}
|
||||
|
||||
java {
|
||||
withJavadocJar()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
api(project(":core-api"))
|
||||
}
|
||||
|
||||
tasks.javadoc {
|
||||
include("dan200/computercraft/api/**/*.java")
|
||||
|
||||
// Include the core-api in our javadoc export. This is wrong, but it means we can export a single javadoc dump.
|
||||
source(project(":core-api").sourceSets.main.map { it.allJava })
|
||||
}
|
@@ -9,33 +9,27 @@ import dan200.computercraft.api.client.turtle.TurtleUpgradeModeller;
|
||||
import dan200.computercraft.api.turtle.ITurtleUpgrade;
|
||||
import dan200.computercraft.api.turtle.TurtleUpgradeSerialiser;
|
||||
import dan200.computercraft.impl.client.ComputerCraftAPIClientService;
|
||||
import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
public final class ComputerCraftAPIClient
|
||||
{
|
||||
private ComputerCraftAPIClient()
|
||||
{
|
||||
public final class ComputerCraftAPIClient {
|
||||
private ComputerCraftAPIClient() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a {@link TurtleUpgradeModeller} for a class of turtle upgrades.
|
||||
* <p>
|
||||
* This may be called at any point after registry creation, though it is recommended to call it within {@link FMLClientSetupEvent}.
|
||||
* This may be called at any point after registry creation, though it is recommended to call it within your client
|
||||
* setup step.
|
||||
*
|
||||
* @param serialiser The turtle upgrade serialiser.
|
||||
* @param modeller The upgrade modeller.
|
||||
* @param <T> The type of the turtle upgrade.
|
||||
*/
|
||||
public static <T extends ITurtleUpgrade> void registerTurtleUpgradeModeller( @Nonnull TurtleUpgradeSerialiser<T> serialiser, @Nonnull TurtleUpgradeModeller<T> modeller )
|
||||
{
|
||||
getInstance().registerTurtleUpgradeModeller( serialiser, modeller );
|
||||
public static <T extends ITurtleUpgrade> void registerTurtleUpgradeModeller(TurtleUpgradeSerialiser<T> serialiser, TurtleUpgradeModeller<T> modeller) {
|
||||
getInstance().registerTurtleUpgradeModeller(serialiser, modeller);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
private static ComputerCraftAPIClientService getInstance()
|
||||
{
|
||||
private static ComputerCraftAPIClientService getInstance() {
|
||||
return ComputerCraftAPIClientService.get();
|
||||
}
|
||||
}
|
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* This file is part of the public ComputerCraft API - http://www.computercraft.info
|
||||
* Copyright Daniel Ratcliffe, 2011-2022. This API may be redistributed unmodified and in full only.
|
||||
* For help using the API, and posting your mods, visit the forums at computercraft.info.
|
||||
*/
|
||||
package dan200.computercraft.api.client;
|
||||
|
||||
import com.mojang.math.Transformation;
|
||||
import dan200.computercraft.impl.client.ClientPlatformHelper;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.resources.model.BakedModel;
|
||||
import net.minecraft.client.resources.model.ModelResourceLocation;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* A model to render, combined with a transformation matrix to apply.
|
||||
*/
|
||||
public final class TransformedModel {
|
||||
private final BakedModel model;
|
||||
private final Transformation matrix;
|
||||
|
||||
public TransformedModel(BakedModel model, Transformation matrix) {
|
||||
this.model = Objects.requireNonNull(model);
|
||||
this.matrix = Objects.requireNonNull(matrix);
|
||||
}
|
||||
|
||||
public TransformedModel(BakedModel model) {
|
||||
this.model = Objects.requireNonNull(model);
|
||||
matrix = Transformation.identity();
|
||||
}
|
||||
|
||||
public static TransformedModel of(ModelResourceLocation location) {
|
||||
var modelManager = Minecraft.getInstance().getModelManager();
|
||||
return new TransformedModel(modelManager.getModel(location));
|
||||
}
|
||||
|
||||
public static TransformedModel of(ResourceLocation location) {
|
||||
var modelManager = Minecraft.getInstance().getModelManager();
|
||||
return new TransformedModel(ClientPlatformHelper.get().getModel(modelManager, location));
|
||||
}
|
||||
|
||||
public static TransformedModel of(ItemStack item, Transformation transform) {
|
||||
var model = Minecraft.getInstance().getItemRenderer().getItemModelShaper().getItemModel(item);
|
||||
return new TransformedModel(model, transform);
|
||||
}
|
||||
|
||||
public BakedModel getModel() {
|
||||
return model;
|
||||
}
|
||||
|
||||
public Transformation getMatrix() {
|
||||
return matrix;
|
||||
}
|
||||
}
|
@@ -14,7 +14,6 @@ import dan200.computercraft.api.turtle.TurtleUpgradeSerialiser;
|
||||
import net.minecraft.client.resources.model.ModelResourceLocation;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
@@ -23,8 +22,7 @@ import javax.annotation.Nullable;
|
||||
* @param <T> The type of turtle upgrade this modeller applies to.
|
||||
* @see ComputerCraftAPIClient#registerTurtleUpgradeModeller(TurtleUpgradeSerialiser, TurtleUpgradeModeller) To register a modeller.
|
||||
*/
|
||||
public interface TurtleUpgradeModeller<T extends ITurtleUpgrade>
|
||||
{
|
||||
public interface TurtleUpgradeModeller<T extends ITurtleUpgrade> {
|
||||
/**
|
||||
* Obtain the model to be used when rendering a turtle peripheral.
|
||||
* <p>
|
||||
@@ -35,8 +33,7 @@ public interface TurtleUpgradeModeller<T extends ITurtleUpgrade>
|
||||
* @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.
|
||||
*/
|
||||
@Nonnull
|
||||
TransformedModel getModel( @Nonnull T upgrade, @Nullable ITurtleAccess turtle, @Nonnull TurtleSide side );
|
||||
TransformedModel getModel(T upgrade, @Nullable ITurtleAccess turtle, TurtleSide side);
|
||||
|
||||
/**
|
||||
* A basic {@link TurtleUpgradeModeller} which renders using the upgrade's {@linkplain ITurtleUpgrade#getCraftingItem()
|
||||
@@ -48,9 +45,8 @@ public interface TurtleUpgradeModeller<T extends ITurtleUpgrade>
|
||||
* @param <T> The type of the turtle upgrade.
|
||||
* @return The constructed modeller.
|
||||
*/
|
||||
@SuppressWarnings( "unchecked" )
|
||||
static <T extends ITurtleUpgrade> TurtleUpgradeModeller<T> flatItem()
|
||||
{
|
||||
@SuppressWarnings("unchecked")
|
||||
static <T extends ITurtleUpgrade> TurtleUpgradeModeller<T> flatItem() {
|
||||
return (TurtleUpgradeModeller<T>) TurtleUpgradeModellers.FLAT_ITEM;
|
||||
}
|
||||
|
||||
@@ -62,9 +58,8 @@ public interface TurtleUpgradeModeller<T extends ITurtleUpgrade>
|
||||
* @param <T> The type of the turtle upgrade.
|
||||
* @return The constructed modeller.
|
||||
*/
|
||||
static <T extends ITurtleUpgrade> TurtleUpgradeModeller<T> sided( ModelResourceLocation left, ModelResourceLocation right )
|
||||
{
|
||||
return ( upgrade, turtle, side ) -> TransformedModel.of( side == TurtleSide.LEFT ? left : right );
|
||||
static <T extends ITurtleUpgrade> TurtleUpgradeModeller<T> sided(ModelResourceLocation left, ModelResourceLocation right) {
|
||||
return (upgrade, turtle, side) -> TransformedModel.of(side == TurtleSide.LEFT ? left : right);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -75,8 +70,7 @@ public interface TurtleUpgradeModeller<T extends ITurtleUpgrade>
|
||||
* @param <T> The type of the turtle upgrade.
|
||||
* @return The constructed modeller.
|
||||
*/
|
||||
static <T extends ITurtleUpgrade> TurtleUpgradeModeller<T> sided( ResourceLocation left, ResourceLocation right )
|
||||
{
|
||||
return ( upgrade, turtle, side ) -> TransformedModel.of( side == TurtleSide.LEFT ? left : right );
|
||||
static <T extends ITurtleUpgrade> TurtleUpgradeModeller<T> sided(ResourceLocation left, ResourceLocation right) {
|
||||
return (upgrade, turtle, side) -> TransformedModel.of(side == TurtleSide.LEFT ? left : right);
|
||||
}
|
||||
}
|
@@ -5,27 +5,28 @@
|
||||
*/
|
||||
package dan200.computercraft.api.client.turtle;
|
||||
|
||||
import com.mojang.math.Matrix4f;
|
||||
import com.mojang.math.Transformation;
|
||||
import dan200.computercraft.api.client.TransformedModel;
|
||||
import dan200.computercraft.api.turtle.ITurtleUpgrade;
|
||||
import dan200.computercraft.api.turtle.TurtleSide;
|
||||
import org.joml.Matrix4f;
|
||||
|
||||
class TurtleUpgradeModellers
|
||||
{
|
||||
private static final Transformation leftTransform = getMatrixFor( -0.40625f );
|
||||
private static final Transformation rightTransform = getMatrixFor( 0.40625f );
|
||||
class TurtleUpgradeModellers {
|
||||
private static final Transformation leftTransform = getMatrixFor(-0.40625f);
|
||||
private static final Transformation rightTransform = getMatrixFor(0.40625f);
|
||||
|
||||
private static Transformation getMatrixFor( float offset )
|
||||
{
|
||||
return new Transformation( new Matrix4f( new float[] {
|
||||
private static Transformation getMatrixFor(float offset) {
|
||||
var matrix = new Matrix4f();
|
||||
matrix.set(new float[]{
|
||||
0.0f, 0.0f, -1.0f, 1.0f + offset,
|
||||
1.0f, 0.0f, 0.0f, 0.0f,
|
||||
0.0f, -1.0f, 0.0f, 1.0f,
|
||||
0.0f, 0.0f, 0.0f, 1.0f,
|
||||
} ) );
|
||||
});
|
||||
matrix.transpose();
|
||||
return new Transformation(matrix);
|
||||
}
|
||||
|
||||
static final TurtleUpgradeModeller<ITurtleUpgrade> FLAT_ITEM = ( upgrade, turtle, side ) ->
|
||||
TransformedModel.of( upgrade.getCraftingItem(), side == TurtleSide.LEFT ? leftTransform : rightTransform );
|
||||
static final TurtleUpgradeModeller<ITurtleUpgrade> FLAT_ITEM = (upgrade, turtle, side) ->
|
||||
TransformedModel.of(upgrade.getCraftingItem(), side == TurtleSide.LEFT ? leftTransform : rightTransform);
|
||||
}
|
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* This file is part of ComputerCraft - http://www.computercraft.info
|
||||
* Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
|
||||
* Send enquiries to dratcliffe@gmail.com
|
||||
*/
|
||||
package dan200.computercraft.impl.client;
|
||||
|
||||
import dan200.computercraft.impl.Services;
|
||||
import net.minecraft.client.resources.model.BakedModel;
|
||||
import net.minecraft.client.resources.model.ModelManager;
|
||||
import net.minecraft.client.resources.model.ModelResourceLocation;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
@ApiStatus.Internal
|
||||
public interface ClientPlatformHelper {
|
||||
/**
|
||||
* Equivalent to {@link ModelManager#getModel(ModelResourceLocation)} but for arbitrary {@link ResourceLocation}s.
|
||||
*
|
||||
* @param manager The model manager.
|
||||
* @param location The model location.
|
||||
* @return The baked model.
|
||||
*/
|
||||
BakedModel getModel(ModelManager manager, ResourceLocation location);
|
||||
|
||||
static ClientPlatformHelper get() {
|
||||
var instance = Instance.INSTANCE;
|
||||
return instance == null ? Services.raise(ClientPlatformHelper.class, Instance.ERROR) : instance;
|
||||
}
|
||||
|
||||
final class Instance {
|
||||
static final @Nullable ClientPlatformHelper INSTANCE;
|
||||
static final @Nullable Throwable ERROR;
|
||||
|
||||
static {
|
||||
var helper = Services.tryLoad(ClientPlatformHelper.class);
|
||||
INSTANCE = helper.instance();
|
||||
ERROR = helper.error();
|
||||
}
|
||||
|
||||
private Instance() {
|
||||
}
|
||||
}
|
||||
}
|
@@ -12,7 +12,6 @@ import dan200.computercraft.api.turtle.TurtleUpgradeSerialiser;
|
||||
import dan200.computercraft.impl.Services;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
@@ -21,30 +20,25 @@ import javax.annotation.Nullable;
|
||||
* Do <strong>NOT</strong> directly reference this class. It exists for internal use by the API.
|
||||
*/
|
||||
@ApiStatus.Internal
|
||||
public interface ComputerCraftAPIClientService
|
||||
{
|
||||
static ComputerCraftAPIClientService get()
|
||||
{
|
||||
ComputerCraftAPIClientService instance = Instance.INSTANCE;
|
||||
return instance == null ? Services.raise( ComputerCraftAPIClientService.class, Instance.ERROR ) : instance;
|
||||
public interface ComputerCraftAPIClientService {
|
||||
static ComputerCraftAPIClientService get() {
|
||||
var instance = Instance.INSTANCE;
|
||||
return instance == null ? Services.raise(ComputerCraftAPIClientService.class, Instance.ERROR) : instance;
|
||||
}
|
||||
|
||||
<T extends ITurtleUpgrade> void registerTurtleUpgradeModeller( @Nonnull TurtleUpgradeSerialiser<T> serialiser, @Nonnull TurtleUpgradeModeller<T> modeller );
|
||||
<T extends ITurtleUpgrade> void registerTurtleUpgradeModeller(TurtleUpgradeSerialiser<T> serialiser, TurtleUpgradeModeller<T> modeller);
|
||||
|
||||
final class Instance
|
||||
{
|
||||
final class Instance {
|
||||
static final @Nullable ComputerCraftAPIClientService INSTANCE;
|
||||
static final @Nullable Throwable ERROR;
|
||||
|
||||
static
|
||||
{
|
||||
Services.LoadedService<ComputerCraftAPIClientService> helper = Services.tryLoad( ComputerCraftAPIClientService.class );
|
||||
static {
|
||||
var helper = Services.tryLoad(ComputerCraftAPIClientService.class);
|
||||
INSTANCE = helper.instance();
|
||||
ERROR = helper.error();
|
||||
}
|
||||
|
||||
private Instance()
|
||||
{
|
||||
private Instance() {
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,203 @@
|
||||
/*
|
||||
* This file is part of the public ComputerCraft API - http://www.computercraft.info
|
||||
* Copyright Daniel Ratcliffe, 2011-2022. This API may be redistributed unmodified and in full only.
|
||||
* For help using the API, and posting your mods, visit the forums at computercraft.info.
|
||||
*/
|
||||
package dan200.computercraft.api;
|
||||
|
||||
import dan200.computercraft.api.filesystem.Mount;
|
||||
import dan200.computercraft.api.filesystem.WritableMount;
|
||||
import dan200.computercraft.api.lua.GenericSource;
|
||||
import dan200.computercraft.api.lua.ILuaAPI;
|
||||
import dan200.computercraft.api.lua.ILuaAPIFactory;
|
||||
import dan200.computercraft.api.media.IMedia;
|
||||
import dan200.computercraft.api.media.MediaProvider;
|
||||
import dan200.computercraft.api.network.PacketNetwork;
|
||||
import dan200.computercraft.api.network.wired.WiredElement;
|
||||
import dan200.computercraft.api.network.wired.WiredNode;
|
||||
import dan200.computercraft.api.peripheral.IComputerAccess;
|
||||
import dan200.computercraft.api.redstone.BundledRedstoneProvider;
|
||||
import dan200.computercraft.api.turtle.ITurtleAccess;
|
||||
import dan200.computercraft.api.turtle.TurtleRefuelHandler;
|
||||
import dan200.computercraft.impl.ComputerCraftAPIService;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.level.Level;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* The static entry point to the ComputerCraft API.
|
||||
* <p>
|
||||
* Members in this class must be called after ComputerCraft has been initialised, but may be called before it is
|
||||
* fully loaded.
|
||||
*/
|
||||
public final class ComputerCraftAPI {
|
||||
public static final String MOD_ID = "computercraft";
|
||||
|
||||
public static String getInstalledVersion() {
|
||||
return getInstance().getInstalledVersion();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a numbered directory in a subfolder of the save directory for a given world, and returns that number.
|
||||
* <p>
|
||||
* Use in conjunction with createSaveDirMount() to create a unique place for your peripherals or media items to store files.
|
||||
*
|
||||
* @param server The server for which the save dir should be created.
|
||||
* @param parentSubPath The folder path within the save directory where the new directory should be created. eg: "computercraft/disk"
|
||||
* @return The numerical value of the name of the new folder, or -1 if the folder could not be created for some reason.
|
||||
* <p>
|
||||
* eg: if createUniqueNumberedSaveDir( world, "computer/disk" ) was called returns 42, then "computer/disk/42" is now
|
||||
* available for writing.
|
||||
* @see #createSaveDirMount(MinecraftServer, String, long)
|
||||
*/
|
||||
public static int createUniqueNumberedSaveDir(MinecraftServer server, String parentSubPath) {
|
||||
return getInstance().createUniqueNumberedSaveDir(server, parentSubPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a file system mount that maps to a subfolder of the save directory for a given world, and returns it.
|
||||
* <p>
|
||||
* Use in conjunction with Use {@link IComputerAccess#mount(String, Mount)} or {@link IComputerAccess#mountWritable(String, WritableMount)}
|
||||
* to mount this on a computer's file system.
|
||||
* <p>
|
||||
* If the same folder may be mounted on multiple computers at once (for instance, if you provide a network file share),
|
||||
* the same mount instance should be used for all computers. You should NOT have multiple mount instances for the
|
||||
* same folder.
|
||||
*
|
||||
* @param server The server which the save dir can be found.
|
||||
* @param subPath The folder path within the save directory that the mount should map to. eg: "disk/42".
|
||||
* Use {@link #createUniqueNumberedSaveDir(MinecraftServer, String)} to create a new numbered folder
|
||||
* to use.
|
||||
* @param capacity The amount of data that can be stored in the directory before it fills up, in bytes.
|
||||
* @return The newly created mount.
|
||||
* @see #createUniqueNumberedSaveDir(MinecraftServer, String)
|
||||
* @see IComputerAccess#mount(String, Mount)
|
||||
* @see IComputerAccess#mountWritable(String, WritableMount)
|
||||
* @see Mount
|
||||
* @see WritableMount
|
||||
*/
|
||||
public static WritableMount createSaveDirMount(MinecraftServer server, String subPath, long capacity) {
|
||||
return getInstance().createSaveDirMount(server, subPath, capacity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a file system mount to a resource folder, and returns it.
|
||||
* <p>
|
||||
* Use in conjunction with {@link IComputerAccess#mount} or {@link IComputerAccess#mountWritable} to mount a
|
||||
* resource folder onto a computer's file system.
|
||||
* <p>
|
||||
* The files in this mount will be a combination of files in all mod jar, and data packs that contain
|
||||
* resources with the same domain and path. For instance, ComputerCraft's resources are stored in
|
||||
* "/data/computercraft/lua/rom". We construct a mount for that with
|
||||
* {@code createResourceMount("computercraft", "lua/rom")}.
|
||||
*
|
||||
* @param server The current Minecraft server, from which to read resources from.
|
||||
* @param domain The domain under which to look for resources. eg: "mymod".
|
||||
* @param subPath The subPath under which to look for resources. eg: "lua/myfiles".
|
||||
* @return The mount, or {@code null} if it could be created for some reason.
|
||||
* @see IComputerAccess#mount(String, Mount)
|
||||
* @see IComputerAccess#mountWritable(String, WritableMount)
|
||||
* @see Mount
|
||||
*/
|
||||
@Nullable
|
||||
public static Mount createResourceMount(MinecraftServer server, String domain, String subPath) {
|
||||
return getInstance().createResourceMount(server, domain, subPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a method source for generic peripherals.
|
||||
*
|
||||
* @param source The method source to register.
|
||||
* @see GenericSource
|
||||
*/
|
||||
public static void registerGenericSource(GenericSource source) {
|
||||
getInstance().registerGenericSource(source);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a bundled redstone provider to provide bundled redstone output for blocks.
|
||||
*
|
||||
* @param provider The bundled redstone provider to register.
|
||||
* @see BundledRedstoneProvider
|
||||
*/
|
||||
public static void registerBundledRedstoneProvider(BundledRedstoneProvider provider) {
|
||||
getInstance().registerBundledRedstoneProvider(provider);
|
||||
}
|
||||
|
||||
/**
|
||||
* If there is a Computer or Turtle at a certain position in the world, get it's bundled redstone output.
|
||||
*
|
||||
* @param world The world this block is in.
|
||||
* @param pos The position this block is at.
|
||||
* @param side The side to extract the bundled redstone output from.
|
||||
* @return If there is a block capable of emitting bundled redstone at the location, it's signal (0-65535) will be returned.
|
||||
* If there is no block capable of emitting bundled redstone at the location, -1 will be returned.
|
||||
* @see BundledRedstoneProvider
|
||||
*/
|
||||
public static int getBundledRedstoneOutput(Level world, BlockPos pos, Direction side) {
|
||||
return getInstance().getBundledRedstoneOutput(world, pos, side);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a media provider to provide {@link IMedia} implementations for Items.
|
||||
*
|
||||
* @param provider The media provider to register.
|
||||
* @see MediaProvider
|
||||
*/
|
||||
public static void registerMediaProvider(MediaProvider provider) {
|
||||
getInstance().registerMediaProvider(provider);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to get the game-wide wireless network.
|
||||
*
|
||||
* @param server The current Minecraft server.
|
||||
* @return The global wireless network, or {@code null} if it could not be fetched.
|
||||
*/
|
||||
public static PacketNetwork getWirelessNetwork(MinecraftServer server) {
|
||||
return getInstance().getWirelessNetwork(server);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a custom {@link ILuaAPI}, which may be added onto all computers without requiring a peripheral.
|
||||
* <p>
|
||||
* Before implementing this interface, consider alternative methods of providing methods. It is generally preferred
|
||||
* to use peripherals to provide functionality to users.
|
||||
*
|
||||
* @param factory The factory for your API subclass.
|
||||
* @see ILuaAPIFactory
|
||||
*/
|
||||
public static void registerAPIFactory(ILuaAPIFactory factory) {
|
||||
getInstance().registerAPIFactory(factory);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new wired node for a given wired element.
|
||||
*
|
||||
* @param element The element to construct it for
|
||||
* @return The element's node
|
||||
* @see WiredElement#getNode()
|
||||
*/
|
||||
public static WiredNode createWiredNodeForElement(WiredElement element) {
|
||||
return getInstance().createWiredNodeForElement(element);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a refuel handler for turtles. This may be used to provide alternative fuel sources, such as consuming RF
|
||||
* batteries.
|
||||
*
|
||||
* @param handler The turtle refuel handler.
|
||||
* @see TurtleRefuelHandler#refuel(ITurtleAccess, ItemStack, int, int)
|
||||
*/
|
||||
public static void registerRefuelHandler(TurtleRefuelHandler handler) {
|
||||
getInstance().registerRefuelHandler(handler);
|
||||
}
|
||||
|
||||
private static ComputerCraftAPIService getInstance() {
|
||||
return ComputerCraftAPIService.get();
|
||||
}
|
||||
}
|
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* This file is part of the public ComputerCraft API - http://www.computercraft.info
|
||||
* Copyright Daniel Ratcliffe, 2011-2022. This API may be redistributed unmodified and in full only.
|
||||
* For help using the API, and posting your mods, visit the forums at computercraft.info.
|
||||
*/
|
||||
package dan200.computercraft.api;
|
||||
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.tags.TagKey;
|
||||
import net.minecraft.world.InteractionHand;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.context.UseOnContext;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
|
||||
/**
|
||||
* Tags provided by ComputerCraft.
|
||||
*/
|
||||
public class ComputerCraftTags {
|
||||
public static class Items {
|
||||
public static final TagKey<Item> COMPUTER = make("computer");
|
||||
public static final TagKey<Item> TURTLE = make("turtle");
|
||||
public static final TagKey<Item> WIRED_MODEM = make("wired_modem");
|
||||
public static final TagKey<Item> MONITOR = make("monitor");
|
||||
|
||||
/**
|
||||
* Items which can be {@linkplain Item#use(Level, Player, InteractionHand) used} when calling
|
||||
* {@code turtle.place()}.
|
||||
* <p>
|
||||
* This does not cover items who handle placing inside {@link Item#useOn(UseOnContext)}, as that is always
|
||||
* called.
|
||||
*/
|
||||
public static final TagKey<Item> TURTLE_CAN_PLACE = make("turtle_can_place");
|
||||
|
||||
private static TagKey<Item> make(String name) {
|
||||
return TagKey.create(Registries.ITEM, new ResourceLocation(ComputerCraftAPI.MOD_ID, name));
|
||||
}
|
||||
}
|
||||
|
||||
public static class Blocks {
|
||||
public static final TagKey<Block> COMPUTER = make("computer");
|
||||
public static final TagKey<Block> TURTLE = make("turtle");
|
||||
public static final TagKey<Block> WIRED_MODEM = make("wired_modem");
|
||||
public static final TagKey<Block> MONITOR = make("monitor");
|
||||
|
||||
/**
|
||||
* Blocks which can be broken by any turtle tool.
|
||||
*/
|
||||
public static final TagKey<Block> TURTLE_ALWAYS_BREAKABLE = make("turtle_always_breakable");
|
||||
|
||||
/**
|
||||
* Blocks which can be broken by the default shovel tool.
|
||||
*/
|
||||
public static final TagKey<Block> TURTLE_SHOVEL_BREAKABLE = make("turtle_shovel_harvestable");
|
||||
|
||||
/**
|
||||
* Blocks which can be broken with the default sword tool.
|
||||
*/
|
||||
public static final TagKey<Block> TURTLE_SWORD_BREAKABLE = make("turtle_sword_harvestable");
|
||||
|
||||
/**
|
||||
* Blocks which can be broken with the default hoe tool.
|
||||
*/
|
||||
public static final TagKey<Block> TURTLE_HOE_BREAKABLE = make("turtle_hoe_harvestable");
|
||||
|
||||
private static TagKey<Block> make(String name) {
|
||||
return TagKey.create(Registries.BLOCK, new ResourceLocation(ComputerCraftAPI.MOD_ID, name));
|
||||
}
|
||||
}
|
||||
}
|
@@ -8,7 +8,7 @@ package dan200.computercraft.api.detail;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
@@ -18,10 +18,9 @@ import java.util.Objects;
|
||||
*
|
||||
* @param <T> The type the stack's item must have.
|
||||
*/
|
||||
public abstract class BasicItemDetailProvider<T> implements IDetailProvider<ItemStack>
|
||||
{
|
||||
public abstract class BasicItemDetailProvider<T> implements DetailProvider<ItemStack> {
|
||||
private final Class<T> itemType;
|
||||
private final String namespace;
|
||||
private final @Nullable String namespace;
|
||||
|
||||
/**
|
||||
* Create a new item detail provider. Meta will be inserted into a new sub-map named as per {@code namespace}.
|
||||
@@ -29,9 +28,8 @@ public abstract class BasicItemDetailProvider<T> implements IDetailProvider<Item
|
||||
* @param itemType The type the stack's item must have.
|
||||
* @param namespace The namespace to use for this provider.
|
||||
*/
|
||||
public BasicItemDetailProvider( String namespace, @Nonnull Class<T> itemType )
|
||||
{
|
||||
Objects.requireNonNull( itemType );
|
||||
public BasicItemDetailProvider(@Nullable String namespace, Class<T> itemType) {
|
||||
Objects.requireNonNull(itemType);
|
||||
this.itemType = itemType;
|
||||
this.namespace = namespace;
|
||||
}
|
||||
@@ -41,9 +39,8 @@ public abstract class BasicItemDetailProvider<T> implements IDetailProvider<Item
|
||||
*
|
||||
* @param itemType The type the stack's item must have.
|
||||
*/
|
||||
public BasicItemDetailProvider( @Nonnull Class<T> itemType )
|
||||
{
|
||||
this( null, itemType );
|
||||
public BasicItemDetailProvider(Class<T> itemType) {
|
||||
this(null, itemType);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -57,23 +54,22 @@ public abstract class BasicItemDetailProvider<T> implements IDetailProvider<Item
|
||||
* @param stack The item stack to provide details for.
|
||||
* @param item The item to provide details for.
|
||||
*/
|
||||
public abstract void provideDetails( @Nonnull Map<? super String, Object> data, @Nonnull ItemStack stack,
|
||||
@Nonnull T item );
|
||||
public abstract void provideDetails(
|
||||
Map<? super String, Object> data, ItemStack stack, T item
|
||||
);
|
||||
|
||||
@Override
|
||||
public void provideDetails( @Nonnull Map<? super String, Object> data, @Nonnull ItemStack stack )
|
||||
{
|
||||
Item item = stack.getItem();
|
||||
if( !itemType.isInstance( item ) ) return;
|
||||
public void provideDetails(Map<? super String, Object> data, ItemStack stack) {
|
||||
var item = stack.getItem();
|
||||
if (!itemType.isInstance(item)) return;
|
||||
|
||||
// If `namespace` is specified, insert into a new data map instead of the existing one.
|
||||
Map<? super String, Object> child = namespace == null ? data : new HashMap<>();
|
||||
|
||||
provideDetails( child, stack, itemType.cast( item ) );
|
||||
provideDetails(child, stack, itemType.cast(item));
|
||||
|
||||
if( namespace != null )
|
||||
{
|
||||
data.put( namespace, child );
|
||||
if (namespace != null) {
|
||||
data.put(namespace, child);
|
||||
}
|
||||
}
|
||||
}
|
@@ -10,7 +10,6 @@ import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
@@ -22,14 +21,12 @@ import javax.annotation.Nullable;
|
||||
* @param blockEntity The block entity at this position, if it exists.
|
||||
*/
|
||||
public record BlockReference(
|
||||
@Nonnull Level level,
|
||||
@Nonnull BlockPos pos,
|
||||
@Nonnull BlockState state,
|
||||
Level level,
|
||||
BlockPos pos,
|
||||
BlockState state,
|
||||
@Nullable BlockEntity blockEntity
|
||||
)
|
||||
{
|
||||
public BlockReference( Level level, BlockPos pos )
|
||||
{
|
||||
this( level, pos, level.getBlockState( pos ), level.getBlockEntity( pos ) );
|
||||
) {
|
||||
public BlockReference(Level level, BlockPos pos) {
|
||||
this(level, pos, level.getBlockState(pos), level.getBlockEntity(pos));
|
||||
}
|
||||
}
|
@@ -5,7 +5,6 @@
|
||||
*/
|
||||
package dan200.computercraft.api.detail;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
@@ -18,8 +17,7 @@ import java.util.Map;
|
||||
* @see DetailRegistry
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface IDetailProvider<T>
|
||||
{
|
||||
public interface DetailProvider<T> {
|
||||
/**
|
||||
* Provide additional details for the given object. This method is called by functions such as
|
||||
* {@code turtle.getItemDetail()} and {@code turtle.inspect()}. New properties should be added to the given
|
||||
@@ -31,5 +29,5 @@ public interface IDetailProvider<T>
|
||||
* @param data The full details to be returned. New properties should be added to this map.
|
||||
* @param object The object to provide details for.
|
||||
*/
|
||||
void provideDetails( @Nonnull Map<? super String, Object> data, @Nonnull T object );
|
||||
void provideDetails(Map<? super String, Object> data, T object);
|
||||
}
|
@@ -20,30 +20,33 @@ import java.util.Map;
|
||||
* @param <T> The type of object that this registry provides details for.
|
||||
*/
|
||||
@ApiStatus.NonExtendable
|
||||
public interface DetailRegistry<T>
|
||||
{
|
||||
public interface DetailRegistry<T> {
|
||||
/**
|
||||
* Registers a detail provider.
|
||||
*
|
||||
* @param provider The detail provider to register.
|
||||
* @see IDetailProvider
|
||||
* @see DetailProvider
|
||||
*/
|
||||
void addProvider( IDetailProvider<T> provider );
|
||||
void addProvider(DetailProvider<T> provider);
|
||||
|
||||
/**
|
||||
* Compute basic details about an object. This is cheaper than computing all details operation, and so is suitable
|
||||
* for when you need to compute the details for a large number of values.
|
||||
* <p>
|
||||
* This method <em>MAY</em> be thread safe: consult the instance's documentation for details.
|
||||
*
|
||||
* @param object The object to get details for.
|
||||
* @return The basic details.
|
||||
*/
|
||||
Map<String, Object> getBasicDetails( T object );
|
||||
Map<String, Object> getBasicDetails(T object);
|
||||
|
||||
/**
|
||||
* Compute all details about an object, using {@link #getBasicDetails(Object)} and any registered providers.
|
||||
* <p>
|
||||
* This method is <em>NOT</em> thread safe. It should only be called from the computer thread.
|
||||
*
|
||||
* @param object The object to get details for.
|
||||
* @return The computed details.
|
||||
*/
|
||||
Map<String, Object> getDetails( T object );
|
||||
Map<String, Object> getDetails(T object);
|
||||
}
|
@@ -12,16 +12,20 @@ import net.minecraft.world.level.block.Block;
|
||||
/**
|
||||
* {@link DetailRegistry}s for built-in Minecraft types.
|
||||
*/
|
||||
public class VanillaDetailRegistries
|
||||
{
|
||||
public class VanillaDetailRegistries {
|
||||
/**
|
||||
* Provides details for {@link ItemStack}s.
|
||||
* <p>
|
||||
* This instance's {@link DetailRegistry#getBasicDetails(Object)} is thread safe (assuming the stack is immutable)
|
||||
* and may be called from the computer thread.
|
||||
*/
|
||||
public static final DetailRegistry<ItemStack> ITEM_STACK = ComputerCraftAPIService.get().getItemStackDetailRegistry();
|
||||
|
||||
/**
|
||||
* Provides details for {@link BlockReference}, a reference to a {@link Block} in the world.
|
||||
* <p>
|
||||
* This instance's {@link DetailRegistry#getBasicDetails(Object)} is thread safe and may be called from the computer
|
||||
* thread.
|
||||
*/
|
||||
public static final DetailRegistry<BlockReference> BLOCK_IN_WORLD = ComputerCraftAPIService.get().getBlockInWorldDetailRegistry();
|
||||
|
||||
}
|
@@ -5,23 +5,24 @@
|
||||
*/
|
||||
package dan200.computercraft.api.media;
|
||||
|
||||
import dan200.computercraft.api.filesystem.IMount;
|
||||
import dan200.computercraft.api.ComputerCraftAPI;
|
||||
import dan200.computercraft.api.filesystem.Mount;
|
||||
import dan200.computercraft.api.filesystem.WritableMount;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.sounds.SoundEvent;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.level.Level;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Represents an item that can be placed in a disk drive and used by a Computer.
|
||||
* <p>
|
||||
* Implement this interface on your {@link Item} class to allow it to be used in the drive. Alternatively, register
|
||||
* a {@link IMediaProvider}.
|
||||
* a {@link MediaProvider}.
|
||||
*/
|
||||
public interface IMedia
|
||||
{
|
||||
public interface IMedia {
|
||||
/**
|
||||
* Get a string representing the label of this item. Will be called via {@code disk.getLabel()} in lua.
|
||||
*
|
||||
@@ -29,7 +30,7 @@ public interface IMedia
|
||||
* @return The label. ie: "Dan's Programs".
|
||||
*/
|
||||
@Nullable
|
||||
String getLabel( @Nonnull ItemStack stack );
|
||||
String getLabel(ItemStack stack);
|
||||
|
||||
/**
|
||||
* Set a string representing the label of this item. Will be called vi {@code disk.setLabel()} in lua.
|
||||
@@ -38,8 +39,7 @@ public interface IMedia
|
||||
* @param label The string to set the label to.
|
||||
* @return true if the label was updated, false if the label may not be modified.
|
||||
*/
|
||||
default boolean setLabel( @Nonnull ItemStack stack, @Nullable String label )
|
||||
{
|
||||
default boolean setLabel(ItemStack stack, @Nullable String label) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -51,8 +51,7 @@ public interface IMedia
|
||||
* @return The name, or null if this item does not represent an item with audio.
|
||||
*/
|
||||
@Nullable
|
||||
default String getAudioTitle( @Nonnull ItemStack stack )
|
||||
{
|
||||
default String getAudioTitle(ItemStack stack) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -63,8 +62,7 @@ public interface IMedia
|
||||
* @return The name, or null if this item does not represent an item with audio.
|
||||
*/
|
||||
@Nullable
|
||||
default SoundEvent getAudio( @Nonnull ItemStack stack )
|
||||
{
|
||||
default SoundEvent getAudio(ItemStack stack) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -73,17 +71,16 @@ public interface IMedia
|
||||
* be mounted onto the filesystem of the computer while the media is in the disk drive.
|
||||
*
|
||||
* @param stack The {@link ItemStack} to modify.
|
||||
* @param world The world in which the item and disk drive reside.
|
||||
* @param level The world in which the item and disk drive reside.
|
||||
* @return The mount, or null if this item does not represent an item with data. If the mount returned also
|
||||
* implements {@link dan200.computercraft.api.filesystem.IWritableMount}, it will mounted using mountWritable()
|
||||
* @see IMount
|
||||
* @see dan200.computercraft.api.filesystem.IWritableMount
|
||||
* @see dan200.computercraft.api.ComputerCraftAPI#createSaveDirMount(Level, String, long)
|
||||
* @see dan200.computercraft.api.ComputerCraftAPI#createResourceMount(String, String)
|
||||
* implements {@link WritableMount}, it will mounted using mountWritable()
|
||||
* @see Mount
|
||||
* @see WritableMount
|
||||
* @see ComputerCraftAPI#createSaveDirMount(MinecraftServer, String, long)
|
||||
* @see ComputerCraftAPI#createResourceMount(MinecraftServer, String, String)
|
||||
*/
|
||||
@Nullable
|
||||
default IMount createDataMount( @Nonnull ItemStack stack, @Nonnull Level world )
|
||||
{
|
||||
default Mount createDataMount(ItemStack stack, ServerLevel level) {
|
||||
return null;
|
||||
}
|
||||
}
|
@@ -7,24 +7,22 @@ package dan200.computercraft.api.media;
|
||||
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* This interface is used to provide {@link IMedia} implementations for {@link ItemStack}.
|
||||
*
|
||||
* @see dan200.computercraft.api.ComputerCraftAPI#registerMediaProvider(IMediaProvider)
|
||||
* @see dan200.computercraft.api.ComputerCraftAPI#registerMediaProvider(MediaProvider)
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface IMediaProvider
|
||||
{
|
||||
public interface MediaProvider {
|
||||
/**
|
||||
* Produce an IMedia implementation from an ItemStack.
|
||||
*
|
||||
* @param stack The stack from which to extract the media information.
|
||||
* @return An {@link IMedia} implementation, or {@code null} if the item is not something you wish to handle
|
||||
* @see dan200.computercraft.api.ComputerCraftAPI#registerMediaProvider(IMediaProvider)
|
||||
* @see dan200.computercraft.api.ComputerCraftAPI#registerMediaProvider(MediaProvider)
|
||||
*/
|
||||
@Nullable
|
||||
IMedia getMedia( @Nonnull ItemStack stack );
|
||||
IMedia getMedia(ItemStack stack);
|
||||
}
|
@@ -6,7 +6,7 @@
|
||||
package dan200.computercraft.api.network;
|
||||
|
||||
/**
|
||||
* Represents a packet which may be sent across a {@link IPacketNetwork}.
|
||||
* Represents a packet which may be sent across a {@link PacketNetwork}.
|
||||
*
|
||||
* @param channel The channel to send the packet along. Receiving devices should only process packets from on
|
||||
* channels they are listening to.
|
||||
@@ -14,17 +14,16 @@ package dan200.computercraft.api.network;
|
||||
* @param payload The contents of this packet. This should be a "valid" Lua object, safe for queuing as an
|
||||
* event or returning from a peripheral call.
|
||||
* @param sender The object which sent this packet.
|
||||
* @see IPacketSender
|
||||
* @see IPacketNetwork#transmitSameDimension(Packet, double)
|
||||
* @see IPacketNetwork#transmitInterdimensional(Packet)
|
||||
* @see IPacketReceiver#receiveDifferentDimension(Packet)
|
||||
* @see IPacketReceiver#receiveSameDimension(Packet, double)
|
||||
* @see PacketSender
|
||||
* @see PacketNetwork#transmitSameDimension(Packet, double)
|
||||
* @see PacketNetwork#transmitInterdimensional(Packet)
|
||||
* @see PacketReceiver#receiveDifferentDimension(Packet)
|
||||
* @see PacketReceiver#receiveSameDimension(Packet, double)
|
||||
*/
|
||||
public record Packet(
|
||||
int channel,
|
||||
int replyChannel,
|
||||
Object payload,
|
||||
IPacketSender sender
|
||||
)
|
||||
{
|
||||
PacketSender sender
|
||||
) {
|
||||
}
|
@@ -5,29 +5,27 @@
|
||||
*/
|
||||
package dan200.computercraft.api.network;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
/**
|
||||
* A packet network represents a collection of devices which can send and receive packets.
|
||||
*
|
||||
* @see Packet
|
||||
* @see IPacketReceiver
|
||||
* @see PacketReceiver
|
||||
*/
|
||||
public interface IPacketNetwork
|
||||
{
|
||||
public interface PacketNetwork {
|
||||
/**
|
||||
* Add a receiver to the network.
|
||||
*
|
||||
* @param receiver The receiver to register to the network.
|
||||
*/
|
||||
void addReceiver( @Nonnull IPacketReceiver receiver );
|
||||
void addReceiver(PacketReceiver receiver);
|
||||
|
||||
/**
|
||||
* Remove a receiver from the network.
|
||||
*
|
||||
* @param receiver The device to remove from the network.
|
||||
*/
|
||||
void removeReceiver( @Nonnull IPacketReceiver receiver );
|
||||
void removeReceiver(PacketReceiver receiver);
|
||||
|
||||
/**
|
||||
* Determine whether this network is wireless.
|
||||
@@ -43,9 +41,9 @@ public interface IPacketNetwork
|
||||
* @param packet The packet to send.
|
||||
* @param range The maximum distance this packet will be sent.
|
||||
* @see #transmitInterdimensional(Packet)
|
||||
* @see IPacketReceiver#receiveSameDimension(Packet, double)
|
||||
* @see PacketReceiver#receiveSameDimension(Packet, double)
|
||||
*/
|
||||
void transmitSameDimension( @Nonnull Packet packet, double range );
|
||||
void transmitSameDimension(Packet packet, double range);
|
||||
|
||||
/**
|
||||
* Submit a packet for transmitting across the network. This will route the packet through the network, sending it
|
||||
@@ -53,7 +51,7 @@ public interface IPacketNetwork
|
||||
*
|
||||
* @param packet The packet to send.
|
||||
* @see #transmitSameDimension(Packet, double)
|
||||
* @see IPacketReceiver#receiveDifferentDimension(Packet)
|
||||
* @see PacketReceiver#receiveDifferentDimension(Packet)
|
||||
*/
|
||||
void transmitInterdimensional( @Nonnull Packet packet );
|
||||
void transmitInterdimensional(Packet packet);
|
||||
}
|
@@ -8,19 +8,16 @@ package dan200.computercraft.api.network;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
/**
|
||||
* An object on an {@link IPacketNetwork}, capable of receiving packets.
|
||||
* An object on an {@link PacketNetwork}, capable of receiving packets.
|
||||
*/
|
||||
public interface IPacketReceiver
|
||||
{
|
||||
public interface PacketReceiver {
|
||||
/**
|
||||
* Get the world in which this packet receiver exists.
|
||||
*
|
||||
* @return The receivers's world.
|
||||
*/
|
||||
@Nonnull
|
||||
Level getLevel();
|
||||
|
||||
/**
|
||||
@@ -28,7 +25,6 @@ public interface IPacketReceiver
|
||||
*
|
||||
* @return The receiver's position.
|
||||
*/
|
||||
@Nonnull
|
||||
Vec3 getPosition();
|
||||
|
||||
/**
|
||||
@@ -41,7 +37,7 @@ public interface IPacketReceiver
|
||||
* @return The maximum distance this device can send and receive messages.
|
||||
* @see #isInterdimensional()
|
||||
* @see #receiveSameDimension(Packet packet, double)
|
||||
* @see IPacketNetwork#transmitInterdimensional(Packet)
|
||||
* @see PacketNetwork#transmitInterdimensional(Packet)
|
||||
*/
|
||||
double getRange();
|
||||
|
||||
@@ -53,7 +49,7 @@ public interface IPacketReceiver
|
||||
* @return Whether this receiver receives packets from other dimensions.
|
||||
* @see #getRange()
|
||||
* @see #receiveDifferentDimension(Packet)
|
||||
* @see IPacketNetwork#transmitInterdimensional(Packet)
|
||||
* @see PacketNetwork#transmitInterdimensional(Packet)
|
||||
*/
|
||||
boolean isInterdimensional();
|
||||
|
||||
@@ -65,10 +61,10 @@ public interface IPacketReceiver
|
||||
* @param distance The distance this packet has travelled from the source.
|
||||
* @see Packet
|
||||
* @see #getRange()
|
||||
* @see IPacketNetwork#transmitSameDimension(Packet, double)
|
||||
* @see IPacketNetwork#transmitInterdimensional(Packet)
|
||||
* @see PacketNetwork#transmitSameDimension(Packet, double)
|
||||
* @see PacketNetwork#transmitInterdimensional(Packet)
|
||||
*/
|
||||
void receiveSameDimension( @Nonnull Packet packet, double distance );
|
||||
void receiveSameDimension(Packet packet, double distance);
|
||||
|
||||
/**
|
||||
* Receive a network packet from a different dimension.
|
||||
@@ -76,9 +72,9 @@ public interface IPacketReceiver
|
||||
* @param packet The packet to receive. Generally you should check that you are listening on the given channel and,
|
||||
* if so, queue the appropriate modem event.
|
||||
* @see Packet
|
||||
* @see IPacketNetwork#transmitInterdimensional(Packet)
|
||||
* @see IPacketNetwork#transmitSameDimension(Packet, double)
|
||||
* @see PacketNetwork#transmitInterdimensional(Packet)
|
||||
* @see PacketNetwork#transmitSameDimension(Packet, double)
|
||||
* @see #isInterdimensional()
|
||||
*/
|
||||
void receiveDifferentDimension( @Nonnull Packet packet );
|
||||
void receiveDifferentDimension(Packet packet);
|
||||
}
|
@@ -8,19 +8,16 @@ package dan200.computercraft.api.network;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
/**
|
||||
* An object on a {@link IPacketNetwork}, capable of sending packets.
|
||||
* An object on a {@link PacketNetwork}, capable of sending packets.
|
||||
*/
|
||||
public interface IPacketSender
|
||||
{
|
||||
public interface PacketSender {
|
||||
/**
|
||||
* Get the world in which this packet sender exists.
|
||||
*
|
||||
* @return The sender's world.
|
||||
*/
|
||||
@Nonnull
|
||||
Level getLevel();
|
||||
|
||||
/**
|
||||
@@ -28,7 +25,6 @@ public interface IPacketSender
|
||||
*
|
||||
* @return The sender's position.
|
||||
*/
|
||||
@Nonnull
|
||||
Vec3 getPosition();
|
||||
|
||||
/**
|
||||
@@ -37,6 +33,5 @@ public interface IPacketSender
|
||||
*
|
||||
* @return This device's id.
|
||||
*/
|
||||
@Nonnull
|
||||
String getSenderID();
|
||||
}
|
@@ -7,28 +7,25 @@ package dan200.computercraft.api.network.wired;
|
||||
|
||||
import dan200.computercraft.api.ComputerCraftAPI;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
/**
|
||||
* An object which may be part of a wired network.
|
||||
* <p>
|
||||
* Elements should construct a node using {@link ComputerCraftAPI#createWiredNodeForElement(IWiredElement)}. This acts
|
||||
* Elements should construct a node using {@link ComputerCraftAPI#createWiredNodeForElement(WiredElement)}. This acts
|
||||
* as a proxy for all network objects. Whilst the node may change networks, an element's node should remain constant
|
||||
* for its lifespan.
|
||||
* <p>
|
||||
* Elements are generally tied to a block or tile entity in world. In such as case, one should provide the
|
||||
* {@link IWiredElement} capability for the appropriate sides.
|
||||
* {@link WiredElement} capability for the appropriate sides.
|
||||
*/
|
||||
public interface IWiredElement extends IWiredSender
|
||||
{
|
||||
public interface WiredElement extends WiredSender {
|
||||
/**
|
||||
* Called when objects on the network change. This may occur when network nodes are added or removed, or when
|
||||
* peripherals change.
|
||||
*
|
||||
* @param change The change which occurred.
|
||||
* @see IWiredNetworkChange
|
||||
* @see WiredNetworkChange
|
||||
*/
|
||||
default void networkChanged( @Nonnull IWiredNetworkChange change )
|
||||
{
|
||||
default void networkChanged(WiredNetworkChange change) {
|
||||
}
|
||||
}
|
@@ -7,25 +7,23 @@ package dan200.computercraft.api.network.wired;
|
||||
|
||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* A wired network is composed of one of more {@link IWiredNode}s, a set of connections between them, and a series
|
||||
* A wired network is composed of one of more {@link WiredNode}s, a set of connections between them, and a series
|
||||
* of peripherals.
|
||||
* <p>
|
||||
* Networks from a connected graph. This means there is some path between all nodes on the network. Further more, if
|
||||
* there is some path between two nodes then they must be on the same network. {@link IWiredNetwork} will automatically
|
||||
* there is some path between two nodes then they must be on the same network. {@link WiredNetwork} will automatically
|
||||
* handle the merging and splitting of networks (and thus changing of available nodes and peripherals) as connections
|
||||
* change.
|
||||
* <p>
|
||||
* This does mean one can not rely on the network remaining consistent between subsequent operations. Consequently,
|
||||
* it is generally preferred to use the methods provided by {@link IWiredNode}.
|
||||
* it is generally preferred to use the methods provided by {@link WiredNode}.
|
||||
*
|
||||
* @see IWiredNode#getNetwork()
|
||||
* @see WiredNode#getNetwork()
|
||||
*/
|
||||
public interface IWiredNetwork
|
||||
{
|
||||
public interface WiredNetwork {
|
||||
/**
|
||||
* Create a connection between two nodes.
|
||||
* <p>
|
||||
@@ -36,10 +34,10 @@ public interface IWiredNetwork
|
||||
* @return {@code true} if a connection was created or {@code false} if the connection already exists.
|
||||
* @throws IllegalStateException If neither node is on the network.
|
||||
* @throws IllegalArgumentException If {@code left} and {@code right} are equal.
|
||||
* @see IWiredNode#connectTo(IWiredNode)
|
||||
* @see IWiredNetwork#connect(IWiredNode, IWiredNode)
|
||||
* @see WiredNode#connectTo(WiredNode)
|
||||
* @see WiredNetwork#connect(WiredNode, WiredNode)
|
||||
*/
|
||||
boolean connect( @Nonnull IWiredNode left, @Nonnull IWiredNode right );
|
||||
boolean connect(WiredNode left, WiredNode right);
|
||||
|
||||
/**
|
||||
* Destroy a connection between this node and another.
|
||||
@@ -51,10 +49,10 @@ public interface IWiredNetwork
|
||||
* @return {@code true} if a connection was destroyed or {@code false} if no connection exists.
|
||||
* @throws IllegalArgumentException If either node is not on the network.
|
||||
* @throws IllegalArgumentException If {@code left} and {@code right} are equal.
|
||||
* @see IWiredNode#disconnectFrom(IWiredNode)
|
||||
* @see IWiredNetwork#connect(IWiredNode, IWiredNode)
|
||||
* @see WiredNode#disconnectFrom(WiredNode)
|
||||
* @see WiredNetwork#connect(WiredNode, WiredNode)
|
||||
*/
|
||||
boolean disconnect( @Nonnull IWiredNode left, @Nonnull IWiredNode right );
|
||||
boolean disconnect(WiredNode left, WiredNode right);
|
||||
|
||||
/**
|
||||
* Sever all connections this node has, removing it from this network.
|
||||
@@ -66,9 +64,9 @@ public interface IWiredNetwork
|
||||
* @return Whether this node was removed from the network. One cannot remove a node from a network where it is the
|
||||
* only element.
|
||||
* @throws IllegalArgumentException If the node is not in the network.
|
||||
* @see IWiredNode#remove()
|
||||
* @see WiredNode#remove()
|
||||
*/
|
||||
boolean remove( @Nonnull IWiredNode node );
|
||||
boolean remove(WiredNode node);
|
||||
|
||||
/**
|
||||
* Update the peripherals a node provides.
|
||||
@@ -79,7 +77,7 @@ public interface IWiredNetwork
|
||||
* @param node The node to attach peripherals for.
|
||||
* @param peripherals The new peripherals for this node.
|
||||
* @throws IllegalArgumentException If the node is not in the network.
|
||||
* @see IWiredNode#updatePeripherals(Map)
|
||||
* @see WiredNode#updatePeripherals(Map)
|
||||
*/
|
||||
void updatePeripherals( @Nonnull IWiredNode node, @Nonnull Map<String, IPeripheral> peripherals );
|
||||
void updatePeripherals(WiredNode node, Map<String, IPeripheral> peripherals);
|
||||
}
|
@@ -7,23 +7,20 @@ package dan200.computercraft.api.network.wired;
|
||||
|
||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Represents a change to the objects on a wired network.
|
||||
*
|
||||
* @see IWiredElement#networkChanged(IWiredNetworkChange)
|
||||
* @see WiredElement#networkChanged(WiredNetworkChange)
|
||||
*/
|
||||
public interface IWiredNetworkChange
|
||||
{
|
||||
public interface WiredNetworkChange {
|
||||
/**
|
||||
* A set of peripherals which have been removed. Note that there may be entries with the same name
|
||||
* in the added and removed set, but with a different peripheral.
|
||||
*
|
||||
* @return The set of removed peripherals.
|
||||
*/
|
||||
@Nonnull
|
||||
Map<String, IPeripheral> peripheralsRemoved();
|
||||
|
||||
/**
|
||||
@@ -32,6 +29,5 @@ public interface IWiredNetworkChange
|
||||
*
|
||||
* @return The set of added peripherals.
|
||||
*/
|
||||
@Nonnull
|
||||
Map<String, IPeripheral> peripheralsAdded();
|
||||
}
|
@@ -5,14 +5,13 @@
|
||||
*/
|
||||
package dan200.computercraft.api.network.wired;
|
||||
|
||||
import dan200.computercraft.api.network.IPacketNetwork;
|
||||
import dan200.computercraft.api.network.PacketNetwork;
|
||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Wired nodes act as a layer between {@link IWiredElement}s and {@link IWiredNetwork}s.
|
||||
* Wired nodes act as a layer between {@link WiredElement}s and {@link WiredNetwork}s.
|
||||
* <p>
|
||||
* Firstly, a node acts as a packet network, capable of sending and receiving modem messages to connected nodes. These
|
||||
* methods may be safely used on any thread.
|
||||
@@ -24,15 +23,13 @@ import java.util.Map;
|
||||
* Wired nodes also provide several convenience methods for interacting with a wired network. These should only ever
|
||||
* be used on the main server thread.
|
||||
*/
|
||||
public interface IWiredNode extends IPacketNetwork
|
||||
{
|
||||
public interface WiredNode extends PacketNetwork {
|
||||
/**
|
||||
* The associated element for this network node.
|
||||
*
|
||||
* @return This node's element.
|
||||
*/
|
||||
@Nonnull
|
||||
IWiredElement getElement();
|
||||
WiredElement getElement();
|
||||
|
||||
/**
|
||||
* The network this node is currently connected to. Note that this may change
|
||||
@@ -42,8 +39,7 @@ public interface IWiredNode extends IPacketNetwork
|
||||
*
|
||||
* @return This node's network.
|
||||
*/
|
||||
@Nonnull
|
||||
IWiredNetwork getNetwork();
|
||||
WiredNetwork getNetwork();
|
||||
|
||||
/**
|
||||
* Create a connection from this node to another.
|
||||
@@ -52,12 +48,11 @@ public interface IWiredNode extends IPacketNetwork
|
||||
*
|
||||
* @param node The other node to connect to.
|
||||
* @return {@code true} if a connection was created or {@code false} if the connection already exists.
|
||||
* @see IWiredNetwork#connect(IWiredNode, IWiredNode)
|
||||
* @see IWiredNode#disconnectFrom(IWiredNode)
|
||||
* @see WiredNetwork#connect(WiredNode, WiredNode)
|
||||
* @see WiredNode#disconnectFrom(WiredNode)
|
||||
*/
|
||||
default boolean connectTo( @Nonnull IWiredNode node )
|
||||
{
|
||||
return getNetwork().connect( this, node );
|
||||
default boolean connectTo(WiredNode node) {
|
||||
return getNetwork().connect(this, node);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -68,12 +63,11 @@ public interface IWiredNode extends IPacketNetwork
|
||||
* @param node The other node to disconnect from.
|
||||
* @return {@code true} if a connection was destroyed or {@code false} if no connection exists.
|
||||
* @throws IllegalArgumentException If {@code node} is not on the same network.
|
||||
* @see IWiredNetwork#disconnect(IWiredNode, IWiredNode)
|
||||
* @see IWiredNode#connectTo(IWiredNode)
|
||||
* @see WiredNetwork#disconnect(WiredNode, WiredNode)
|
||||
* @see WiredNode#connectTo(WiredNode)
|
||||
*/
|
||||
default boolean disconnectFrom( @Nonnull IWiredNode node )
|
||||
{
|
||||
return getNetwork().disconnect( this, node );
|
||||
default boolean disconnectFrom(WiredNode node) {
|
||||
return getNetwork().disconnect(this, node);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -85,11 +79,10 @@ public interface IWiredNode extends IPacketNetwork
|
||||
* @return Whether this node was removed from the network. One cannot remove a node from a network where it is the
|
||||
* only element.
|
||||
* @throws IllegalArgumentException If the node is not in the network.
|
||||
* @see IWiredNetwork#remove(IWiredNode)
|
||||
* @see WiredNetwork#remove(WiredNode)
|
||||
*/
|
||||
default boolean remove()
|
||||
{
|
||||
return getNetwork().remove( this );
|
||||
default boolean remove() {
|
||||
return getNetwork().remove(this);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -99,10 +92,9 @@ public interface IWiredNode extends IPacketNetwork
|
||||
* that your network element owns.
|
||||
*
|
||||
* @param peripherals The new peripherals for this node.
|
||||
* @see IWiredNetwork#updatePeripherals(IWiredNode, Map)
|
||||
* @see WiredNetwork#updatePeripherals(WiredNode, Map)
|
||||
*/
|
||||
default void updatePeripherals( @Nonnull Map<String, IPeripheral> peripherals )
|
||||
{
|
||||
getNetwork().updatePeripherals( this, peripherals );
|
||||
default void updatePeripherals(Map<String, IPeripheral> peripherals) {
|
||||
getNetwork().updatePeripherals(this, peripherals);
|
||||
}
|
||||
}
|
@@ -5,18 +5,16 @@
|
||||
*/
|
||||
package dan200.computercraft.api.network.wired;
|
||||
|
||||
import dan200.computercraft.api.network.IPacketSender;
|
||||
import dan200.computercraft.api.network.PacketSender;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
/**
|
||||
* An object on a {@link IWiredNetwork} capable of sending packets.
|
||||
* An object on a {@link WiredNetwork} capable of sending packets.
|
||||
* <p>
|
||||
* Unlike a regular {@link IPacketSender}, this must be associated with the node you are attempting to
|
||||
* Unlike a regular {@link PacketSender}, this must be associated with the node you are attempting to
|
||||
* to send the packet from.
|
||||
*/
|
||||
public interface IWiredSender extends IPacketSender
|
||||
{
|
||||
public interface WiredSender extends PacketSender {
|
||||
/**
|
||||
* The node in the network representing this object.
|
||||
* <p>
|
||||
@@ -25,6 +23,5 @@ public interface IWiredSender extends IPacketSender
|
||||
*
|
||||
* @return The node for this element.
|
||||
*/
|
||||
@Nonnull
|
||||
IWiredNode getNode();
|
||||
WiredNode getNode();
|
||||
}
|
@@ -5,53 +5,43 @@
|
||||
*/
|
||||
package dan200.computercraft.api.pocket;
|
||||
|
||||
import dan200.computercraft.api.upgrades.IUpgradeBase;
|
||||
import dan200.computercraft.api.upgrades.UpgradeBase;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
/**
|
||||
* A base class for {@link IPocketUpgrade}s.
|
||||
* <p>
|
||||
* One does not have to use this, but it does provide a convenient template.
|
||||
*/
|
||||
public abstract class AbstractPocketUpgrade implements IPocketUpgrade
|
||||
{
|
||||
public abstract class AbstractPocketUpgrade implements IPocketUpgrade {
|
||||
private final ResourceLocation id;
|
||||
private final String adjective;
|
||||
private final ItemStack stack;
|
||||
|
||||
protected AbstractPocketUpgrade( ResourceLocation id, String adjective, ItemStack stack )
|
||||
{
|
||||
protected AbstractPocketUpgrade(ResourceLocation id, String adjective, ItemStack stack) {
|
||||
this.id = id;
|
||||
this.adjective = adjective;
|
||||
this.stack = stack;
|
||||
}
|
||||
|
||||
protected AbstractPocketUpgrade( ResourceLocation id, ItemStack stack )
|
||||
{
|
||||
this( id, IUpgradeBase.getDefaultAdjective( id ), stack );
|
||||
protected AbstractPocketUpgrade(ResourceLocation id, ItemStack stack) {
|
||||
this(id, UpgradeBase.getDefaultAdjective(id), stack);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public final ResourceLocation getUpgradeID()
|
||||
{
|
||||
public final ResourceLocation getUpgradeID() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public final String getUnlocalisedAdjective()
|
||||
{
|
||||
public final String getUnlocalisedAdjective() {
|
||||
return adjective;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public final ItemStack getCraftingItem()
|
||||
{
|
||||
public final ItemStack getCraftingItem() {
|
||||
return stack;
|
||||
}
|
||||
}
|
@@ -10,15 +10,13 @@ import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Wrapper class for pocket computers.
|
||||
*/
|
||||
public interface IPocketAccess
|
||||
{
|
||||
public interface IPocketAccess {
|
||||
/**
|
||||
* Gets the entity holding this item.
|
||||
* <p>
|
||||
@@ -45,7 +43,7 @@ public interface IPocketAccess
|
||||
* {@code 0x000000} and {@code 0xFFFFFF} or -1 to reset to the default colour.
|
||||
* @see #getColour()
|
||||
*/
|
||||
void setColour( int colour );
|
||||
void setColour(int colour);
|
||||
|
||||
/**
|
||||
* Get the colour of this pocket computer's light as a RGB number.
|
||||
@@ -63,7 +61,7 @@ public interface IPocketAccess
|
||||
* {@code 0x000000} and {@code 0xFFFFFF} or -1 to reset to the default colour.
|
||||
* @see #getLight()
|
||||
*/
|
||||
void setLight( int colour );
|
||||
void setLight(int colour);
|
||||
|
||||
/**
|
||||
* Get the upgrade-specific NBT.
|
||||
@@ -73,7 +71,6 @@ public interface IPocketAccess
|
||||
* @return The upgrade's NBT.
|
||||
* @see #updateUpgradeNBTData()
|
||||
*/
|
||||
@Nonnull
|
||||
CompoundTag getUpgradeNBTData();
|
||||
|
||||
/**
|
||||
@@ -93,6 +90,5 @@ public interface IPocketAccess
|
||||
*
|
||||
* @return A collection of all upgrade names.
|
||||
*/
|
||||
@Nonnull
|
||||
Map<ResourceLocation, IPeripheral> getUpgrades();
|
||||
}
|
@@ -6,10 +6,9 @@
|
||||
package dan200.computercraft.api.pocket;
|
||||
|
||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||
import dan200.computercraft.api.upgrades.IUpgradeBase;
|
||||
import dan200.computercraft.api.upgrades.UpgradeBase;
|
||||
import net.minecraft.world.level.Level;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
@@ -24,8 +23,7 @@ import javax.annotation.Nullable;
|
||||
*
|
||||
* @see PocketUpgradeSerialiser For how to register a pocket computer upgrade.
|
||||
*/
|
||||
public interface IPocketUpgrade extends IUpgradeBase
|
||||
{
|
||||
public interface IPocketUpgrade extends UpgradeBase {
|
||||
/**
|
||||
* Creates a peripheral for the pocket computer.
|
||||
* <p>
|
||||
@@ -38,7 +36,7 @@ public interface IPocketUpgrade extends IUpgradeBase
|
||||
* @see #update(IPocketAccess, IPeripheral)
|
||||
*/
|
||||
@Nullable
|
||||
IPeripheral createPeripheral( @Nonnull IPocketAccess access );
|
||||
IPeripheral createPeripheral(IPocketAccess access);
|
||||
|
||||
/**
|
||||
* Called when the pocket computer item stack updates.
|
||||
@@ -47,8 +45,7 @@ public interface IPocketUpgrade extends IUpgradeBase
|
||||
* @param peripheral The peripheral for this upgrade.
|
||||
* @see #createPeripheral(IPocketAccess)
|
||||
*/
|
||||
default void update( @Nonnull IPocketAccess access, @Nullable IPeripheral peripheral )
|
||||
{
|
||||
default void update(IPocketAccess access, @Nullable IPeripheral peripheral) {
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -62,8 +59,7 @@ public interface IPocketUpgrade extends IUpgradeBase
|
||||
* access the GUI.
|
||||
* @see #createPeripheral(IPocketAccess)
|
||||
*/
|
||||
default boolean onRightClick( @Nonnull Level world, @Nonnull IPocketAccess access, @Nullable IPeripheral peripheral )
|
||||
{
|
||||
default boolean onRightClick(Level world, IPocketAccess access, @Nullable IPeripheral peripheral) {
|
||||
return false;
|
||||
}
|
||||
}
|
@@ -7,24 +7,21 @@ package dan200.computercraft.api.pocket;
|
||||
|
||||
import dan200.computercraft.api.upgrades.UpgradeDataProvider;
|
||||
import net.minecraft.data.DataGenerator;
|
||||
import net.minecraftforge.data.event.GatherDataEvent;
|
||||
import net.minecraft.data.PackOutput;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* A data provider to generate pocket computer upgrades.
|
||||
* <p>
|
||||
* This should be subclassed and registered to a {@link DataGenerator}. Override the {@link #addUpgrades(Consumer)} function,
|
||||
* construct each upgrade, and pass them off to the provided consumer to generate them.
|
||||
* This should be subclassed and registered to a {@link DataGenerator.PackGenerator}. Override the
|
||||
* {@link #addUpgrades(Consumer)} function, construct each upgrade, and pass them off to the provided consumer to
|
||||
* generate them.
|
||||
*
|
||||
* @see GatherDataEvent To register your data provider
|
||||
* @see PocketUpgradeSerialiser
|
||||
*/
|
||||
public abstract class PocketUpgradeDataProvider extends UpgradeDataProvider<IPocketUpgrade, PocketUpgradeSerialiser<?>>
|
||||
{
|
||||
public PocketUpgradeDataProvider( @Nonnull DataGenerator generator )
|
||||
{
|
||||
super( generator, "Pocket Computer Upgrades", "computercraft/pocket_upgrades", PocketUpgradeSerialiser.REGISTRY_ID );
|
||||
public abstract class PocketUpgradeDataProvider extends UpgradeDataProvider<IPocketUpgrade, PocketUpgradeSerialiser<?>> {
|
||||
public PocketUpgradeDataProvider(PackOutput output) {
|
||||
super(output, "Pocket Computer Upgrades", "computercraft/pocket_upgrades", PocketUpgradeSerialiser.REGISTRY_ID);
|
||||
}
|
||||
}
|
@@ -5,21 +5,17 @@
|
||||
*/
|
||||
package dan200.computercraft.api.pocket;
|
||||
|
||||
import dan200.computercraft.ComputerCraft;
|
||||
import dan200.computercraft.api.upgrades.IUpgradeBase;
|
||||
import dan200.computercraft.api.ComputerCraftAPI;
|
||||
import dan200.computercraft.api.upgrades.UpgradeBase;
|
||||
import dan200.computercraft.api.upgrades.UpgradeSerialiser;
|
||||
import dan200.computercraft.internal.upgrades.SerialiserWithCraftingItem;
|
||||
import dan200.computercraft.internal.upgrades.SimpleSerialiser;
|
||||
import dan200.computercraft.impl.upgrades.SerialiserWithCraftingItem;
|
||||
import dan200.computercraft.impl.upgrades.SimpleSerialiser;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.crafting.SimpleRecipeSerializer;
|
||||
import net.minecraftforge.registries.DeferredRegister;
|
||||
import net.minecraftforge.registries.IForgeRegistry;
|
||||
import net.minecraftforge.registries.RegistryManager;
|
||||
import net.minecraft.world.item.crafting.SimpleCraftingRecipeSerializer;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Function;
|
||||
|
||||
@@ -33,32 +29,15 @@ import java.util.function.Function;
|
||||
* @see IPocketUpgrade
|
||||
* @see PocketUpgradeDataProvider
|
||||
*/
|
||||
public interface PocketUpgradeSerialiser<T extends IPocketUpgrade> extends UpgradeSerialiser<T>
|
||||
{
|
||||
public interface PocketUpgradeSerialiser<T extends IPocketUpgrade> extends UpgradeSerialiser<T> {
|
||||
/**
|
||||
* The ID for the associated registry.
|
||||
* <p>
|
||||
* This is largely intended for use with Forge Registry methods/classes, such as {@link DeferredRegister} and
|
||||
* {@link RegistryManager#getRegistry(ResourceKey)}.
|
||||
*/
|
||||
ResourceKey<Registry<PocketUpgradeSerialiser<?>>> REGISTRY_ID = ResourceKey.createRegistryKey( new ResourceLocation( ComputerCraft.MOD_ID, "pocket_upgrade_serialiser" ) );
|
||||
ResourceKey<Registry<PocketUpgradeSerialiser<?>>> REGISTRY_ID = ResourceKey.createRegistryKey(new ResourceLocation(ComputerCraftAPI.MOD_ID, "pocket_upgrade_serialiser"));
|
||||
|
||||
/**
|
||||
* The associated registry.
|
||||
*
|
||||
* @return The registry for pocket upgrade serialisers.
|
||||
* @see #REGISTRY_ID
|
||||
* @deprecated Use {@link #REGISTRY_ID} directly.
|
||||
*/
|
||||
@Deprecated( forRemoval = true )
|
||||
static IForgeRegistry<PocketUpgradeSerialiser<?>> registry()
|
||||
{
|
||||
return RegistryManager.ACTIVE.getRegistry( REGISTRY_ID );
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an upgrade serialiser for a simple upgrade. This is similar to a {@link SimpleRecipeSerializer}, but for
|
||||
* upgrades.
|
||||
* Create an upgrade serialiser for a simple upgrade. This is similar to a {@link SimpleCraftingRecipeSerializer},
|
||||
* but for upgrades.
|
||||
* <p>
|
||||
* If you might want to vary the item, it's suggested you use {@link #simpleWithCustomItem(BiFunction)} instead.
|
||||
*
|
||||
@@ -66,40 +45,32 @@ public interface PocketUpgradeSerialiser<T extends IPocketUpgrade> extends Upgra
|
||||
* @param <T> The type of the generated upgrade.
|
||||
* @return The serialiser for this upgrade
|
||||
*/
|
||||
@Nonnull
|
||||
static <T extends IPocketUpgrade> PocketUpgradeSerialiser<T> simple( @Nonnull Function<ResourceLocation, T> factory )
|
||||
{
|
||||
final class Impl extends SimpleSerialiser<T> implements PocketUpgradeSerialiser<T>
|
||||
{
|
||||
private Impl( Function<ResourceLocation, T> constructor )
|
||||
{
|
||||
super( constructor );
|
||||
static <T extends IPocketUpgrade> PocketUpgradeSerialiser<T> simple(Function<ResourceLocation, T> factory) {
|
||||
final class Impl extends SimpleSerialiser<T> implements PocketUpgradeSerialiser<T> {
|
||||
private Impl(Function<ResourceLocation, T> constructor) {
|
||||
super(constructor);
|
||||
}
|
||||
}
|
||||
|
||||
return new Impl( factory );
|
||||
return new Impl(factory);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an upgrade serialiser for a simple upgrade whose crafting item can be specified.
|
||||
*
|
||||
* @param factory Generate a new upgrade with a specific ID and crafting item. The returned upgrade's
|
||||
* {@link IUpgradeBase#getCraftingItem()} <strong>MUST</strong> equal the provided item.
|
||||
* {@link UpgradeBase#getCraftingItem()} <strong>MUST</strong> equal the provided item.
|
||||
* @param <T> The type of the generated upgrade.
|
||||
* @return The serialiser for this upgrade.
|
||||
* @see #simple(Function) For upgrades whose crafting stack should not vary.
|
||||
*/
|
||||
@Nonnull
|
||||
static <T extends IPocketUpgrade> PocketUpgradeSerialiser<T> simpleWithCustomItem( @Nonnull BiFunction<ResourceLocation, ItemStack, T> factory )
|
||||
{
|
||||
final class Impl extends SerialiserWithCraftingItem<T> implements PocketUpgradeSerialiser<T>
|
||||
{
|
||||
private Impl( BiFunction<ResourceLocation, ItemStack, T> factory )
|
||||
{
|
||||
super( factory );
|
||||
static <T extends IPocketUpgrade> PocketUpgradeSerialiser<T> simpleWithCustomItem(BiFunction<ResourceLocation, ItemStack, T> factory) {
|
||||
final class Impl extends SerialiserWithCraftingItem<T> implements PocketUpgradeSerialiser<T> {
|
||||
private Impl(BiFunction<ResourceLocation, ItemStack, T> factory) {
|
||||
super(factory);
|
||||
}
|
||||
}
|
||||
|
||||
return new Impl( factory );
|
||||
return new Impl(factory);
|
||||
}
|
||||
}
|
@@ -5,20 +5,18 @@
|
||||
*/
|
||||
package dan200.computercraft.api.redstone;
|
||||
|
||||
import dan200.computercraft.api.ComputerCraftAPI;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.world.level.Level;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
/**
|
||||
* This interface is used to provide bundled redstone output for blocks.
|
||||
*
|
||||
* @see dan200.computercraft.api.ComputerCraftAPI#registerBundledRedstoneProvider(IBundledRedstoneProvider)
|
||||
* @see ComputerCraftAPI#registerBundledRedstoneProvider(BundledRedstoneProvider)
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface IBundledRedstoneProvider
|
||||
{
|
||||
public interface BundledRedstoneProvider {
|
||||
/**
|
||||
* Produce an bundled redstone output from a block location.
|
||||
*
|
||||
@@ -27,7 +25,7 @@ public interface IBundledRedstoneProvider
|
||||
* @param side The side to extract the bundled redstone output from.
|
||||
* @return A number in the range 0-65535 to indicate this block is providing output, or -1 if you do not wish to
|
||||
* handle this block.
|
||||
* @see dan200.computercraft.api.ComputerCraftAPI#registerBundledRedstoneProvider(IBundledRedstoneProvider)
|
||||
* @see ComputerCraftAPI#registerBundledRedstoneProvider(BundledRedstoneProvider)
|
||||
*/
|
||||
int getBundledRedstoneOutput( @Nonnull Level world, @Nonnull BlockPos pos, @Nonnull Direction side );
|
||||
int getBundledRedstoneOutput(Level world, BlockPos pos, Direction side);
|
||||
}
|
@@ -5,62 +5,50 @@
|
||||
*/
|
||||
package dan200.computercraft.api.turtle;
|
||||
|
||||
import dan200.computercraft.api.upgrades.IUpgradeBase;
|
||||
import dan200.computercraft.api.upgrades.UpgradeBase;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
/**
|
||||
* A base class for {@link ITurtleUpgrade}s.
|
||||
* <p>
|
||||
* One does not have to use this, but it does provide a convenient template.
|
||||
*/
|
||||
public abstract class AbstractTurtleUpgrade implements ITurtleUpgrade
|
||||
{
|
||||
public abstract class AbstractTurtleUpgrade implements ITurtleUpgrade {
|
||||
private final ResourceLocation id;
|
||||
private final TurtleUpgradeType type;
|
||||
private final String adjective;
|
||||
private final ItemStack stack;
|
||||
|
||||
protected AbstractTurtleUpgrade( ResourceLocation id, TurtleUpgradeType type, String adjective, ItemStack stack )
|
||||
{
|
||||
protected AbstractTurtleUpgrade(ResourceLocation id, TurtleUpgradeType type, String adjective, ItemStack stack) {
|
||||
this.id = id;
|
||||
this.type = type;
|
||||
this.adjective = adjective;
|
||||
this.stack = stack;
|
||||
}
|
||||
|
||||
protected AbstractTurtleUpgrade( ResourceLocation id, TurtleUpgradeType type, ItemStack stack )
|
||||
{
|
||||
this( id, type, IUpgradeBase.getDefaultAdjective( id ), stack );
|
||||
protected AbstractTurtleUpgrade(ResourceLocation id, TurtleUpgradeType type, ItemStack stack) {
|
||||
this(id, type, UpgradeBase.getDefaultAdjective(id), stack);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public final ResourceLocation getUpgradeID()
|
||||
{
|
||||
public final ResourceLocation getUpgradeID() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public final String getUnlocalisedAdjective()
|
||||
{
|
||||
public final String getUnlocalisedAdjective() {
|
||||
return adjective;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public final TurtleUpgradeType getType()
|
||||
{
|
||||
public final TurtleUpgradeType getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public final ItemStack getCraftingItem()
|
||||
{
|
||||
public final ItemStack getCraftingItem() {
|
||||
return stack;
|
||||
}
|
||||
}
|
@@ -15,9 +15,8 @@ import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.world.Container;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import net.minecraftforge.items.IItemHandlerModifiable;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
@@ -26,14 +25,13 @@ import javax.annotation.Nullable;
|
||||
* This should not be implemented by your classes. Do not interact with turtles except via this interface and
|
||||
* {@link ITurtleUpgrade}.
|
||||
*/
|
||||
public interface ITurtleAccess
|
||||
{
|
||||
@ApiStatus.NonExtendable
|
||||
public interface ITurtleAccess {
|
||||
/**
|
||||
* Returns the world in which the turtle resides.
|
||||
*
|
||||
* @return the world in which the turtle resides.
|
||||
*/
|
||||
@Nonnull
|
||||
Level getLevel();
|
||||
|
||||
/**
|
||||
@@ -41,13 +39,12 @@ public interface ITurtleAccess
|
||||
*
|
||||
* @return a vector containing the integer co-ordinates at which the turtle resides.
|
||||
*/
|
||||
@Nonnull
|
||||
BlockPos getPosition();
|
||||
|
||||
/**
|
||||
* Determine if this turtle has been removed.
|
||||
* <p>
|
||||
* It's possible for a turtle to be removed while a {@link ITurtleCommand} is executed, for instance if interacting
|
||||
* It's possible for a turtle to be removed while a {@link TurtleCommand} is executed, for instance if interacting
|
||||
* with a block causes the turtle to be blown up. It's recommended you check the turtle is still present before
|
||||
* trying to interact with it again.
|
||||
* <p>
|
||||
@@ -70,7 +67,7 @@ public interface ITurtleAccess
|
||||
* was cancelled.
|
||||
* @throws UnsupportedOperationException When attempting to teleport on the client side.
|
||||
*/
|
||||
boolean teleportTo( @Nonnull Level world, @Nonnull BlockPos pos );
|
||||
boolean teleportTo(Level world, BlockPos pos);
|
||||
|
||||
/**
|
||||
* Returns a vector containing the floating point co-ordinates at which the turtle is rendered.
|
||||
@@ -80,8 +77,7 @@ public interface ITurtleAccess
|
||||
* @return A vector containing the floating point co-ordinates at which the turtle resides.
|
||||
* @see #getVisualYaw(float)
|
||||
*/
|
||||
@Nonnull
|
||||
Vec3 getVisualPosition( float f );
|
||||
Vec3 getVisualPosition(float f);
|
||||
|
||||
/**
|
||||
* Returns the yaw the turtle is facing when it is rendered.
|
||||
@@ -90,7 +86,7 @@ public interface ITurtleAccess
|
||||
* @return The yaw the turtle is facing.
|
||||
* @see #getVisualPosition(float)
|
||||
*/
|
||||
float getVisualYaw( float f );
|
||||
float getVisualYaw(float f);
|
||||
|
||||
/**
|
||||
* Returns the world direction the turtle is currently facing.
|
||||
@@ -98,7 +94,6 @@ public interface ITurtleAccess
|
||||
* @return The world direction the turtle is currently facing.
|
||||
* @see #setDirection(Direction)
|
||||
*/
|
||||
@Nonnull
|
||||
Direction getDirection();
|
||||
|
||||
/**
|
||||
@@ -108,7 +103,7 @@ public interface ITurtleAccess
|
||||
* @param dir The new direction to set. This should be on either the x or z axis (so north, south, east or west).
|
||||
* @see #getDirection()
|
||||
*/
|
||||
void setDirection( @Nonnull Direction dir );
|
||||
void setDirection(Direction dir);
|
||||
|
||||
/**
|
||||
* Get the currently selected slot in the turtle's inventory.
|
||||
@@ -128,7 +123,7 @@ public interface ITurtleAccess
|
||||
* @see #getInventory()
|
||||
* @see #getSelectedSlot()
|
||||
*/
|
||||
void setSelectedSlot( int slot );
|
||||
void setSelectedSlot(int slot);
|
||||
|
||||
/**
|
||||
* Set the colour of the turtle to a RGB number.
|
||||
@@ -137,7 +132,7 @@ public interface ITurtleAccess
|
||||
* and {@code 0xFFFFFF} or -1 to reset to the default colour.
|
||||
* @see #getColour()
|
||||
*/
|
||||
void setColour( int colour );
|
||||
void setColour(int colour);
|
||||
|
||||
/**
|
||||
* Get the colour of this turtle as a RGB number.
|
||||
@@ -162,25 +157,9 @@ public interface ITurtleAccess
|
||||
* Note: this inventory should only be accessed and modified on the server thread.
|
||||
*
|
||||
* @return This turtle's inventory
|
||||
* @see #getItemHandler()
|
||||
*/
|
||||
@Nonnull
|
||||
Container getInventory();
|
||||
|
||||
/**
|
||||
* Get the inventory of this turtle as an {@link IItemHandlerModifiable}.
|
||||
* <p>
|
||||
* Note: this inventory should only be accessed and modified on the server thread.
|
||||
*
|
||||
* @return This turtle's inventory
|
||||
* @see #getInventory()
|
||||
* @see IItemHandlerModifiable
|
||||
* @deprecated Use {@link #getInventory()} directly.
|
||||
*/
|
||||
@Nonnull
|
||||
@Deprecated( forRemoval = true )
|
||||
IItemHandlerModifiable getItemHandler();
|
||||
|
||||
/**
|
||||
* Determine whether this turtle will require fuel when performing actions.
|
||||
*
|
||||
@@ -209,7 +188,7 @@ public interface ITurtleAccess
|
||||
* @see #addFuel(int)
|
||||
* @see #consumeFuel(int)
|
||||
*/
|
||||
void setFuelLevel( int fuel );
|
||||
void setFuelLevel(int fuel);
|
||||
|
||||
/**
|
||||
* Get the maximum amount of fuel a turtle can hold.
|
||||
@@ -226,7 +205,7 @@ public interface ITurtleAccess
|
||||
* greater than the current fuel level of the turtle. No fuel will be consumed if {@code false} is returned.
|
||||
* @throws UnsupportedOperationException When attempting to consume fuel on the client side.
|
||||
*/
|
||||
boolean consumeFuel( int fuel );
|
||||
boolean consumeFuel(int fuel);
|
||||
|
||||
/**
|
||||
* Increase the turtle's fuel level by the given amount.
|
||||
@@ -234,7 +213,7 @@ public interface ITurtleAccess
|
||||
* @param fuel The amount to refuel with.
|
||||
* @throws UnsupportedOperationException When attempting to refuel on the client side.
|
||||
*/
|
||||
void addFuel( int fuel );
|
||||
void addFuel(int fuel);
|
||||
|
||||
/**
|
||||
* Adds a custom command to the turtles command queue. Unlike peripheral methods, these custom commands will be executed
|
||||
@@ -247,11 +226,10 @@ public interface ITurtleAccess
|
||||
* @return The objects the command returned when executed. you should probably return these to the player
|
||||
* unchanged if called from a peripheral method.
|
||||
* @throws UnsupportedOperationException When attempting to execute a command on the client side.
|
||||
* @see ITurtleCommand
|
||||
* @see TurtleCommand
|
||||
* @see MethodResult#pullEvent(String, ILuaCallback)
|
||||
*/
|
||||
@Nonnull
|
||||
MethodResult executeCommand( @Nonnull ITurtleCommand command );
|
||||
MethodResult executeCommand(TurtleCommand command);
|
||||
|
||||
/**
|
||||
* Start playing a specific animation. This will prevent other turtle commands from executing until
|
||||
@@ -261,7 +239,7 @@ public interface ITurtleAccess
|
||||
* @throws UnsupportedOperationException When attempting to execute play an animation on the client side.
|
||||
* @see TurtleAnimation
|
||||
*/
|
||||
void playAnimation( @Nonnull TurtleAnimation animation );
|
||||
void playAnimation(TurtleAnimation animation);
|
||||
|
||||
/**
|
||||
* Returns the turtle on the specified side of the turtle, if there is one.
|
||||
@@ -271,7 +249,7 @@ public interface ITurtleAccess
|
||||
* @see #setUpgrade(TurtleSide, ITurtleUpgrade)
|
||||
*/
|
||||
@Nullable
|
||||
ITurtleUpgrade getUpgrade( @Nonnull TurtleSide side );
|
||||
ITurtleUpgrade getUpgrade(TurtleSide side);
|
||||
|
||||
/**
|
||||
* Set the upgrade for a given side, resetting peripherals and clearing upgrade specific data.
|
||||
@@ -280,7 +258,7 @@ public interface ITurtleAccess
|
||||
* @param upgrade The upgrade to set, may be {@code null} to clear.
|
||||
* @see #getUpgrade(TurtleSide)
|
||||
*/
|
||||
void setUpgrade( @Nonnull TurtleSide side, @Nullable ITurtleUpgrade upgrade );
|
||||
void setUpgrade(TurtleSide side, @Nullable ITurtleUpgrade upgrade);
|
||||
|
||||
/**
|
||||
* Returns the peripheral created by the upgrade on the specified side of the turtle, if there is one.
|
||||
@@ -289,7 +267,7 @@ public interface ITurtleAccess
|
||||
* @return The peripheral created by the upgrade on the specified side of the turtle, {@code null} if none exists.
|
||||
*/
|
||||
@Nullable
|
||||
IPeripheral getPeripheral( @Nonnull TurtleSide side );
|
||||
IPeripheral getPeripheral(TurtleSide side);
|
||||
|
||||
/**
|
||||
* Get an upgrade-specific NBT compound, which can be used to store arbitrary data.
|
||||
@@ -301,8 +279,7 @@ public interface ITurtleAccess
|
||||
* @return The upgrade-specific data.
|
||||
* @see #updateUpgradeNBTData(TurtleSide)
|
||||
*/
|
||||
@Nonnull
|
||||
CompoundTag getUpgradeNBTData( @Nullable TurtleSide side );
|
||||
CompoundTag getUpgradeNBTData(TurtleSide side);
|
||||
|
||||
/**
|
||||
* Mark the upgrade-specific data as dirty on a specific side. This is required for the data to be synced to the
|
||||
@@ -311,5 +288,5 @@ public interface ITurtleAccess
|
||||
* @param side The side to mark dirty.
|
||||
* @see #updateUpgradeNBTData(TurtleSide)
|
||||
*/
|
||||
void updateUpgradeNBTData( @Nonnull TurtleSide side );
|
||||
void updateUpgradeNBTData(TurtleSide side);
|
||||
}
|
@@ -6,12 +6,9 @@
|
||||
package dan200.computercraft.api.turtle;
|
||||
|
||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||
import dan200.computercraft.api.upgrades.IUpgradeBase;
|
||||
import dan200.computercraft.api.upgrades.UpgradeBase;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraftforge.event.entity.player.AttackEntityEvent;
|
||||
import net.minecraftforge.event.level.BlockEvent;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
@@ -27,15 +24,13 @@ import javax.annotation.Nullable;
|
||||
*
|
||||
* @see TurtleUpgradeSerialiser For how to register a turtle upgrade.
|
||||
*/
|
||||
public interface ITurtleUpgrade extends IUpgradeBase
|
||||
{
|
||||
public interface ITurtleUpgrade extends UpgradeBase {
|
||||
/**
|
||||
* Return whether this turtle adds a tool or a peripheral to the turtle.
|
||||
*
|
||||
* @return The type of upgrade this is.
|
||||
* @see TurtleUpgradeType for the differences between them.
|
||||
*/
|
||||
@Nonnull
|
||||
TurtleUpgradeType getType();
|
||||
|
||||
/**
|
||||
@@ -51,8 +46,7 @@ public interface ITurtleUpgrade extends IUpgradeBase
|
||||
* and this method is not expected to be called.
|
||||
*/
|
||||
@Nullable
|
||||
default IPeripheral createPeripheral( @Nonnull ITurtleAccess turtle, @Nonnull TurtleSide side )
|
||||
{
|
||||
default IPeripheral createPeripheral(ITurtleAccess turtle, TurtleSide side) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -60,8 +54,8 @@ public interface ITurtleUpgrade extends IUpgradeBase
|
||||
* Will only be called for Tool turtle. Called when turtle.dig() or turtle.attack() is called
|
||||
* by the turtle, and the tool is required to do some work.
|
||||
* <p>
|
||||
* Conforming implementations should fire {@link BlockEvent.BreakEvent} for digging {@link AttackEntityEvent}
|
||||
* for attacking.
|
||||
* Conforming implementations should fire loader-specific events when using the tool, for instance Forge's
|
||||
* {@code AttackEntityEvent}.
|
||||
*
|
||||
* @param turtle Access to the turtle that the tool resides on.
|
||||
* @param side Which side of the turtle (left or right) the tool resides on.
|
||||
@@ -74,9 +68,7 @@ public interface ITurtleUpgrade extends IUpgradeBase
|
||||
* a swinging animation. You may return {@code null} if this turtle is a Peripheral and this method is not expected
|
||||
* to be called.
|
||||
*/
|
||||
@Nonnull
|
||||
default TurtleCommandResult useTool( @Nonnull ITurtleAccess turtle, @Nonnull TurtleSide side, @Nonnull TurtleVerb verb, @Nonnull Direction direction )
|
||||
{
|
||||
default TurtleCommandResult useTool(ITurtleAccess turtle, TurtleSide side, TurtleVerb verb, Direction direction) {
|
||||
return TurtleCommandResult.failure();
|
||||
}
|
||||
|
||||
@@ -86,7 +78,6 @@ public interface ITurtleUpgrade extends IUpgradeBase
|
||||
* @param turtle Access to the turtle that the upgrade resides on.
|
||||
* @param side Which side of the turtle (left or right) the upgrade resides on.
|
||||
*/
|
||||
default void update( @Nonnull ITurtleAccess turtle, @Nonnull TurtleSide side )
|
||||
{
|
||||
default void update(ITurtleAccess turtle, TurtleSide side) {
|
||||
}
|
||||
}
|
@@ -12,8 +12,7 @@ package dan200.computercraft.api.turtle;
|
||||
*
|
||||
* @see ITurtleAccess#playAnimation(TurtleAnimation)
|
||||
*/
|
||||
public enum TurtleAnimation
|
||||
{
|
||||
public enum TurtleAnimation {
|
||||
/**
|
||||
* An animation which does nothing. This takes no time to complete.
|
||||
*
|
@@ -5,16 +5,14 @@
|
||||
*/
|
||||
package dan200.computercraft.api.turtle;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
/**
|
||||
* An interface for objects executing custom turtle commands, used with {@link ITurtleAccess#executeCommand(ITurtleCommand)}.
|
||||
* An interface for objects executing custom turtle commands, used with {@link ITurtleAccess#executeCommand(TurtleCommand)}.
|
||||
*
|
||||
* @see ITurtleAccess#executeCommand(ITurtleCommand)
|
||||
* @see ITurtleAccess#executeCommand(TurtleCommand)
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface ITurtleCommand
|
||||
{
|
||||
public interface TurtleCommand {
|
||||
/**
|
||||
* Will be called by the turtle on the main thread when it is time to execute the custom command.
|
||||
* <p>
|
||||
@@ -23,11 +21,10 @@ public interface ITurtleCommand
|
||||
*
|
||||
* @param turtle Access to the turtle for whom the command was issued.
|
||||
* @return A result, indicating whether this action succeeded or not.
|
||||
* @see ITurtleAccess#executeCommand(ITurtleCommand)
|
||||
* @see ITurtleAccess#executeCommand(TurtleCommand)
|
||||
* @see TurtleCommandResult#success()
|
||||
* @see TurtleCommandResult#failure(String)
|
||||
* @see TurtleCommandResult
|
||||
*/
|
||||
@Nonnull
|
||||
TurtleCommandResult execute( @Nonnull ITurtleAccess turtle );
|
||||
TurtleCommandResult execute(ITurtleAccess turtle);
|
||||
}
|
@@ -7,28 +7,24 @@ package dan200.computercraft.api.turtle;
|
||||
|
||||
import net.minecraft.core.Direction;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Used to indicate the result of executing a turtle command.
|
||||
*
|
||||
* @see ITurtleCommand#execute(ITurtleAccess)
|
||||
* @see TurtleCommand#execute(ITurtleAccess)
|
||||
* @see ITurtleUpgrade#useTool(ITurtleAccess, TurtleSide, TurtleVerb, Direction)
|
||||
*/
|
||||
public final class TurtleCommandResult
|
||||
{
|
||||
private static final TurtleCommandResult EMPTY_SUCCESS = new TurtleCommandResult( true, null, null );
|
||||
private static final TurtleCommandResult EMPTY_FAILURE = new TurtleCommandResult( false, null, null );
|
||||
public final class TurtleCommandResult {
|
||||
private static final TurtleCommandResult EMPTY_SUCCESS = new TurtleCommandResult(true, null, null);
|
||||
private static final TurtleCommandResult EMPTY_FAILURE = new TurtleCommandResult(false, null, null);
|
||||
|
||||
/**
|
||||
* Create a successful command result with no result.
|
||||
*
|
||||
* @return A successful command result with no values.
|
||||
*/
|
||||
@Nonnull
|
||||
public static TurtleCommandResult success()
|
||||
{
|
||||
public static TurtleCommandResult success() {
|
||||
return EMPTY_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -38,11 +34,9 @@ public final class TurtleCommandResult
|
||||
* @param results The results of executing this command.
|
||||
* @return A successful command result with the given values.
|
||||
*/
|
||||
@Nonnull
|
||||
public static TurtleCommandResult success( @Nullable Object[] results )
|
||||
{
|
||||
if( results == null || results.length == 0 ) return EMPTY_SUCCESS;
|
||||
return new TurtleCommandResult( true, null, results );
|
||||
public static TurtleCommandResult success(@Nullable Object[] results) {
|
||||
if (results == null || results.length == 0) return EMPTY_SUCCESS;
|
||||
return new TurtleCommandResult(true, null, results);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -50,9 +44,7 @@ public final class TurtleCommandResult
|
||||
*
|
||||
* @return A failed command result with no message.
|
||||
*/
|
||||
@Nonnull
|
||||
public static TurtleCommandResult failure()
|
||||
{
|
||||
public static TurtleCommandResult failure() {
|
||||
return EMPTY_FAILURE;
|
||||
}
|
||||
|
||||
@@ -62,19 +54,16 @@ public final class TurtleCommandResult
|
||||
* @param errorMessage The error message to provide.
|
||||
* @return A failed command result with a message.
|
||||
*/
|
||||
@Nonnull
|
||||
public static TurtleCommandResult failure( @Nullable String errorMessage )
|
||||
{
|
||||
if( errorMessage == null ) return EMPTY_FAILURE;
|
||||
return new TurtleCommandResult( false, errorMessage, null );
|
||||
public static TurtleCommandResult failure(@Nullable String errorMessage) {
|
||||
if (errorMessage == null) return EMPTY_FAILURE;
|
||||
return new TurtleCommandResult(false, errorMessage, null);
|
||||
}
|
||||
|
||||
private final boolean success;
|
||||
private final String errorMessage;
|
||||
private final Object[] results;
|
||||
private final @Nullable String errorMessage;
|
||||
private final @Nullable Object[] results;
|
||||
|
||||
private TurtleCommandResult( boolean success, String errorMessage, Object[] results )
|
||||
{
|
||||
private TurtleCommandResult(boolean success, @Nullable String errorMessage, @Nullable Object[] results) {
|
||||
this.success = success;
|
||||
this.errorMessage = errorMessage;
|
||||
this.results = results;
|
||||
@@ -85,8 +74,7 @@ public final class TurtleCommandResult
|
||||
*
|
||||
* @return If the command was successful.
|
||||
*/
|
||||
public boolean isSuccess()
|
||||
{
|
||||
public boolean isSuccess() {
|
||||
return success;
|
||||
}
|
||||
|
||||
@@ -96,8 +84,7 @@ public final class TurtleCommandResult
|
||||
* @return The command's error message, or {@code null} if it was a success.
|
||||
*/
|
||||
@Nullable
|
||||
public String getErrorMessage()
|
||||
{
|
||||
public String getErrorMessage() {
|
||||
return errorMessage;
|
||||
}
|
||||
|
||||
@@ -107,8 +94,7 @@ public final class TurtleCommandResult
|
||||
* @return The command's result, or {@code null} if it was a failure.
|
||||
*/
|
||||
@Nullable
|
||||
public Object[] getResults()
|
||||
{
|
||||
public Object[] getResults() {
|
||||
return results;
|
||||
}
|
||||
}
|
@@ -16,8 +16,7 @@ import java.util.OptionalInt;
|
||||
*
|
||||
* @see ComputerCraftAPI#registerRefuelHandler(TurtleRefuelHandler)
|
||||
*/
|
||||
public interface TurtleRefuelHandler
|
||||
{
|
||||
public interface TurtleRefuelHandler {
|
||||
/**
|
||||
* Refuel a turtle using an item.
|
||||
*
|
||||
@@ -31,5 +30,5 @@ public interface TurtleRefuelHandler
|
||||
* {@code OptionalInt#of(0)} if so), but should <em>NOT</em> modify the stack or inventory.
|
||||
* @return The amount of fuel gained, or {@link OptionalInt#empty()} if this handler does not accept the given item.
|
||||
*/
|
||||
OptionalInt refuel( ITurtleAccess turtle, ItemStack stack, int slot, int limit );
|
||||
OptionalInt refuel(ITurtleAccess turtle, ItemStack stack, int slot, int limit);
|
||||
}
|
@@ -8,8 +8,7 @@ package dan200.computercraft.api.turtle;
|
||||
/**
|
||||
* An enum representing the two sides of the turtle that a turtle turtle might reside.
|
||||
*/
|
||||
public enum TurtleSide
|
||||
{
|
||||
public enum TurtleSide {
|
||||
/**
|
||||
* The turtle's left side (where the pickaxe usually is on a Wireless Mining Turtle).
|
||||
*/
|
@@ -8,34 +8,33 @@ package dan200.computercraft.api.turtle;
|
||||
import dan200.computercraft.api.ComputerCraftAPI;
|
||||
import dan200.computercraft.api.ComputerCraftTags;
|
||||
import dan200.computercraft.api.upgrades.UpgradeDataProvider;
|
||||
import dan200.computercraft.impl.PlatformHelper;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.data.DataGenerator;
|
||||
import net.minecraft.data.PackOutput;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.tags.TagKey;
|
||||
import net.minecraft.world.entity.ai.attributes.Attributes;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraftforge.data.event.GatherDataEvent;
|
||||
import net.minecraftforge.registries.ForgeRegistries;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* A data provider to generate turtle upgrades.
|
||||
* <p>
|
||||
* This should be subclassed and registered to a {@link DataGenerator}. Override the {@link #addUpgrades(Consumer)} function,
|
||||
* construct each upgrade, and pass them off to the provided consumer to generate them.
|
||||
* This should be subclassed and registered to a {@link DataGenerator.PackGenerator}. Override the
|
||||
* {@link #addUpgrades(Consumer)} function, construct each upgrade, and pass them off to the provided consumer to
|
||||
* generate them.
|
||||
*
|
||||
* @see GatherDataEvent To register your data provider
|
||||
* @see TurtleUpgradeSerialiser
|
||||
*/
|
||||
public abstract class TurtleUpgradeDataProvider extends UpgradeDataProvider<ITurtleUpgrade, TurtleUpgradeSerialiser<?>>
|
||||
{
|
||||
private static final ResourceLocation TOOL_ID = new ResourceLocation( ComputerCraftAPI.MOD_ID, "tool" );
|
||||
public abstract class TurtleUpgradeDataProvider extends UpgradeDataProvider<ITurtleUpgrade, TurtleUpgradeSerialiser<?>> {
|
||||
private static final ResourceLocation TOOL_ID = new ResourceLocation(ComputerCraftAPI.MOD_ID, "tool");
|
||||
|
||||
public TurtleUpgradeDataProvider( DataGenerator generator )
|
||||
{
|
||||
super( generator, "Turtle Upgrades", "computercraft/turtle_upgrades", TurtleUpgradeSerialiser.REGISTRY_ID );
|
||||
public TurtleUpgradeDataProvider(PackOutput output) {
|
||||
super(output, "Turtle Upgrades", "computercraft/turtle_upgrades", TurtleUpgradeSerialiser.REGISTRY_ID);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -46,10 +45,8 @@ public abstract class TurtleUpgradeDataProvider extends UpgradeDataProvider<ITur
|
||||
* to specify {@link ToolBuilder#damageMultiplier(float)} and {@link ToolBuilder#breakable(TagKey)}.
|
||||
* @return A tool builder,
|
||||
*/
|
||||
@Nonnull
|
||||
public final ToolBuilder tool( @Nonnull ResourceLocation id, @Nonnull Item item )
|
||||
{
|
||||
return new ToolBuilder( id, existingSerialiser( TOOL_ID ), item );
|
||||
public final ToolBuilder tool(ResourceLocation id, Item item) {
|
||||
return new ToolBuilder(id, existingSerialiser(TOOL_ID), item);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -57,18 +54,16 @@ public abstract class TurtleUpgradeDataProvider extends UpgradeDataProvider<ITur
|
||||
*
|
||||
* @see #tool(ResourceLocation, Item)
|
||||
*/
|
||||
public static class ToolBuilder
|
||||
{
|
||||
public static class ToolBuilder {
|
||||
private final ResourceLocation id;
|
||||
private final TurtleUpgradeSerialiser<?> serialiser;
|
||||
private final Item toolItem;
|
||||
private String adjective;
|
||||
private Item craftingItem;
|
||||
private Float damageMultiplier = null;
|
||||
private TagKey<Block> breakable;
|
||||
private @Nullable String adjective;
|
||||
private @Nullable Item craftingItem;
|
||||
private @Nullable Float damageMultiplier = null;
|
||||
private @Nullable TagKey<Block> breakable;
|
||||
|
||||
ToolBuilder( ResourceLocation id, TurtleUpgradeSerialiser<?> serialiser, Item toolItem )
|
||||
{
|
||||
ToolBuilder(ResourceLocation id, TurtleUpgradeSerialiser<?> serialiser, Item toolItem) {
|
||||
this.id = id;
|
||||
this.serialiser = serialiser;
|
||||
this.toolItem = toolItem;
|
||||
@@ -81,9 +76,7 @@ public abstract class TurtleUpgradeDataProvider extends UpgradeDataProvider<ITur
|
||||
* @param adjective The new adjective to use.
|
||||
* @return The tool builder, for further use.
|
||||
*/
|
||||
@Nonnull
|
||||
public ToolBuilder adjective( @Nonnull String adjective )
|
||||
{
|
||||
public ToolBuilder adjective(String adjective) {
|
||||
this.adjective = adjective;
|
||||
return this;
|
||||
}
|
||||
@@ -95,9 +88,7 @@ public abstract class TurtleUpgradeDataProvider extends UpgradeDataProvider<ITur
|
||||
* @param craftingItem The item used to craft this upgrade.
|
||||
* @return The tool builder, for further use.
|
||||
*/
|
||||
@Nonnull
|
||||
public ToolBuilder craftingItem( @Nonnull Item craftingItem )
|
||||
{
|
||||
public ToolBuilder craftingItem(Item craftingItem) {
|
||||
this.craftingItem = craftingItem;
|
||||
return this;
|
||||
}
|
||||
@@ -107,10 +98,9 @@ public abstract class TurtleUpgradeDataProvider extends UpgradeDataProvider<ITur
|
||||
* get the final damage.
|
||||
*
|
||||
* @param damageMultiplier The damage multiplier.
|
||||
* @return The tool builder, for futher use.
|
||||
* @return The tool builder, for further use.
|
||||
*/
|
||||
public ToolBuilder damageMultiplier( float damageMultiplier )
|
||||
{
|
||||
public ToolBuilder damageMultiplier(float damageMultiplier) {
|
||||
this.damageMultiplier = damageMultiplier;
|
||||
return this;
|
||||
}
|
||||
@@ -124,8 +114,7 @@ public abstract class TurtleUpgradeDataProvider extends UpgradeDataProvider<ITur
|
||||
* @return The tool builder, for further use.
|
||||
* @see ComputerCraftTags.Blocks
|
||||
*/
|
||||
public ToolBuilder breakable( @Nonnull TagKey<Block> breakable )
|
||||
{
|
||||
public ToolBuilder breakable(TagKey<Block> breakable) {
|
||||
this.breakable = breakable;
|
||||
return this;
|
||||
}
|
||||
@@ -135,18 +124,16 @@ public abstract class TurtleUpgradeDataProvider extends UpgradeDataProvider<ITur
|
||||
*
|
||||
* @param add The callback given to {@link #addUpgrades(Consumer)}.
|
||||
*/
|
||||
public void add( @Nonnull Consumer<Upgrade<TurtleUpgradeSerialiser<?>>> add )
|
||||
{
|
||||
add.accept( new Upgrade<>( id, serialiser, s -> {
|
||||
s.addProperty( "item", ForgeRegistries.ITEMS.getKey( toolItem ).toString() );
|
||||
if( adjective != null ) s.addProperty( "adjective", adjective );
|
||||
if( craftingItem != null )
|
||||
{
|
||||
s.addProperty( "craftItem", ForgeRegistries.ITEMS.getKey( craftingItem ).toString() );
|
||||
public void add(Consumer<Upgrade<TurtleUpgradeSerialiser<?>>> add) {
|
||||
add.accept(new Upgrade<>(id, serialiser, s -> {
|
||||
s.addProperty("item", PlatformHelper.get().getRegistryKey(Registries.ITEM, toolItem).toString());
|
||||
if (adjective != null) s.addProperty("adjective", adjective);
|
||||
if (craftingItem != null) {
|
||||
s.addProperty("craftItem", PlatformHelper.get().getRegistryKey(Registries.ITEM, craftingItem).toString());
|
||||
}
|
||||
if( damageMultiplier != null ) s.addProperty( "damageMultiplier", damageMultiplier );
|
||||
if( breakable != null ) s.addProperty( "breakable", breakable.location().toString() );
|
||||
} ) );
|
||||
if (damageMultiplier != null) s.addProperty("damageMultiplier", damageMultiplier);
|
||||
if (breakable != null) s.addProperty("breakable", breakable.location().toString());
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
@@ -5,37 +5,30 @@
|
||||
*/
|
||||
package dan200.computercraft.api.turtle;
|
||||
|
||||
import dan200.computercraft.ComputerCraft;
|
||||
import dan200.computercraft.api.client.ComputerCraftAPIClient;
|
||||
import dan200.computercraft.api.client.turtle.TurtleUpgradeModeller;
|
||||
import dan200.computercraft.api.upgrades.IUpgradeBase;
|
||||
import dan200.computercraft.api.ComputerCraftAPI;
|
||||
import dan200.computercraft.api.upgrades.UpgradeBase;
|
||||
import dan200.computercraft.api.upgrades.UpgradeSerialiser;
|
||||
import dan200.computercraft.internal.upgrades.SerialiserWithCraftingItem;
|
||||
import dan200.computercraft.internal.upgrades.SimpleSerialiser;
|
||||
import dan200.computercraft.impl.upgrades.SerialiserWithCraftingItem;
|
||||
import dan200.computercraft.impl.upgrades.SimpleSerialiser;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.crafting.RecipeSerializer;
|
||||
import net.minecraft.world.item.crafting.SimpleRecipeSerializer;
|
||||
import net.minecraftforge.registries.DeferredRegister;
|
||||
import net.minecraftforge.registries.IForgeRegistry;
|
||||
import net.minecraftforge.registries.RegistryManager;
|
||||
import net.minecraft.world.item.crafting.SimpleCraftingRecipeSerializer;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* Reads a {@link ITurtleUpgrade} from disk and reads/writes it to a network packet.
|
||||
* <p>
|
||||
* These should be registered in a {@link IForgeRegistry} while the game is loading, much like {@link RecipeSerializer}s.
|
||||
* It is suggested you use a {@link DeferredRegister}.
|
||||
* These should be registered in a {@link Registry} while the game is loading, much like {@link RecipeSerializer}s.
|
||||
* <p>
|
||||
* If your turtle upgrade doesn't have any associated configurable parameters (like most upgrades), you can use
|
||||
* {@link #simple(Function)} or {@link #simpleWithCustomItem(BiFunction)} to create a basic upgrade serialiser.
|
||||
*
|
||||
* <h2>Example</h2>
|
||||
* <h2>Example (Forge)</h2>
|
||||
* <pre>{@code
|
||||
* static final DeferredRegister<TurtleUpgradeSerialiser<?>> SERIALISERS = DeferredRegister.create( TurtleUpgradeSerialiser.TYPE, "my_mod" );
|
||||
*
|
||||
@@ -57,7 +50,7 @@ import java.util.function.Function;
|
||||
* }</pre>
|
||||
* <p>
|
||||
* Finally, we need to register a model for our upgrade. This is done with
|
||||
* {@link ComputerCraftAPIClient#registerTurtleUpgradeModeller(TurtleUpgradeSerialiser, TurtleUpgradeModeller)}:
|
||||
* {@link dan200.computercraft.api.client.ComputerCraftAPIClient#registerTurtleUpgradeModeller}:
|
||||
*
|
||||
* <pre>{@code
|
||||
* // Register our model inside FMLClientSetupEvent
|
||||
@@ -69,34 +62,17 @@ import java.util.function.Function;
|
||||
* @param <T> The type of turtle upgrade this is responsible for serialising.
|
||||
* @see ITurtleUpgrade
|
||||
* @see TurtleUpgradeDataProvider
|
||||
* @see TurtleUpgradeModeller
|
||||
* @see dan200.computercraft.api.client.turtle.TurtleUpgradeModeller
|
||||
*/
|
||||
public interface TurtleUpgradeSerialiser<T extends ITurtleUpgrade> extends UpgradeSerialiser<T>
|
||||
{
|
||||
public interface TurtleUpgradeSerialiser<T extends ITurtleUpgrade> extends UpgradeSerialiser<T> {
|
||||
/**
|
||||
* The ID for the associated registry.
|
||||
* <p>
|
||||
* This is largely intended for use with Forge Registry methods/classes, such as {@link DeferredRegister} and
|
||||
* {@link RegistryManager#getRegistry(ResourceKey)}.
|
||||
*/
|
||||
ResourceKey<Registry<TurtleUpgradeSerialiser<?>>> REGISTRY_ID = ResourceKey.createRegistryKey( new ResourceLocation( ComputerCraft.MOD_ID, "turtle_upgrade_serialiser" ) );
|
||||
ResourceKey<Registry<TurtleUpgradeSerialiser<?>>> REGISTRY_ID = ResourceKey.createRegistryKey(new ResourceLocation(ComputerCraftAPI.MOD_ID, "turtle_upgrade_serialiser"));
|
||||
|
||||
/**
|
||||
* The associated registry.
|
||||
*
|
||||
* @return The registry for pocket upgrade serialisers.
|
||||
* @see #REGISTRY_ID
|
||||
* @deprecated Use {@link #REGISTRY_ID} directly.
|
||||
*/
|
||||
@Deprecated( forRemoval = true )
|
||||
static IForgeRegistry<TurtleUpgradeSerialiser<?>> registry()
|
||||
{
|
||||
return RegistryManager.ACTIVE.getRegistry( REGISTRY_ID );
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an upgrade serialiser for a simple upgrade. This is similar to a {@link SimpleRecipeSerializer}, but for
|
||||
* upgrades.
|
||||
* Create an upgrade serialiser for a simple upgrade. This is similar to a {@link SimpleCraftingRecipeSerializer},
|
||||
* but for upgrades.
|
||||
* <p>
|
||||
* If you might want to vary the item, it's suggested you use {@link #simpleWithCustomItem(BiFunction)} instead.
|
||||
*
|
||||
@@ -104,40 +80,32 @@ public interface TurtleUpgradeSerialiser<T extends ITurtleUpgrade> extends Upgra
|
||||
* @param <T> The type of the generated upgrade.
|
||||
* @return The serialiser for this upgrade
|
||||
*/
|
||||
@Nonnull
|
||||
static <T extends ITurtleUpgrade> TurtleUpgradeSerialiser<T> simple( @Nonnull Function<ResourceLocation, T> factory )
|
||||
{
|
||||
final class Impl extends SimpleSerialiser<T> implements TurtleUpgradeSerialiser<T>
|
||||
{
|
||||
private Impl( Function<ResourceLocation, T> constructor )
|
||||
{
|
||||
super( constructor );
|
||||
static <T extends ITurtleUpgrade> TurtleUpgradeSerialiser<T> simple(Function<ResourceLocation, T> factory) {
|
||||
final class Impl extends SimpleSerialiser<T> implements TurtleUpgradeSerialiser<T> {
|
||||
private Impl(Function<ResourceLocation, T> constructor) {
|
||||
super(constructor);
|
||||
}
|
||||
}
|
||||
|
||||
return new Impl( factory );
|
||||
return new Impl(factory);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an upgrade serialiser for a simple upgrade whose crafting item can be specified.
|
||||
*
|
||||
* @param factory Generate a new upgrade with a specific ID and crafting item. The returned upgrade's
|
||||
* {@link IUpgradeBase#getCraftingItem()} <strong>MUST</strong> equal the provided item.
|
||||
* {@link UpgradeBase#getCraftingItem()} <strong>MUST</strong> equal the provided item.
|
||||
* @param <T> The type of the generated upgrade.
|
||||
* @return The serialiser for this upgrade.
|
||||
* @see #simple(Function) For upgrades whose crafting stack should not vary.
|
||||
*/
|
||||
@Nonnull
|
||||
static <T extends ITurtleUpgrade> TurtleUpgradeSerialiser<T> simpleWithCustomItem( @Nonnull BiFunction<ResourceLocation, ItemStack, T> factory )
|
||||
{
|
||||
final class Impl extends SerialiserWithCraftingItem<T> implements TurtleUpgradeSerialiser<T>
|
||||
{
|
||||
private Impl( BiFunction<ResourceLocation, ItemStack, T> factory )
|
||||
{
|
||||
super( factory );
|
||||
static <T extends ITurtleUpgrade> TurtleUpgradeSerialiser<T> simpleWithCustomItem(BiFunction<ResourceLocation, ItemStack, T> factory) {
|
||||
final class Impl extends SerialiserWithCraftingItem<T> implements TurtleUpgradeSerialiser<T> {
|
||||
private Impl(BiFunction<ResourceLocation, ItemStack, T> factory) {
|
||||
super(factory);
|
||||
}
|
||||
}
|
||||
|
||||
return new Impl( factory );
|
||||
return new Impl(factory);
|
||||
}
|
||||
}
|
@@ -10,8 +10,7 @@ package dan200.computercraft.api.turtle;
|
||||
*
|
||||
* @see ITurtleUpgrade#getType()
|
||||
*/
|
||||
public enum TurtleUpgradeType
|
||||
{
|
||||
public enum TurtleUpgradeType {
|
||||
/**
|
||||
* A tool is rendered as an item on the side of the turtle, and responds to the {@code turtle.dig()}
|
||||
* and {@code turtle.attack()} methods (Such as pickaxe or sword on Mining and Melee turtles).
|
||||
@@ -31,13 +30,11 @@ public enum TurtleUpgradeType
|
||||
*/
|
||||
BOTH;
|
||||
|
||||
public boolean isTool()
|
||||
{
|
||||
public boolean isTool() {
|
||||
return this == TOOL || this == BOTH;
|
||||
}
|
||||
|
||||
public boolean isPeripheral()
|
||||
{
|
||||
public boolean isPeripheral() {
|
||||
return this == PERIPHERAL || this == BOTH;
|
||||
}
|
||||
}
|
@@ -14,8 +14,7 @@ import net.minecraft.core.Direction;
|
||||
* @see ITurtleUpgrade#getType()
|
||||
* @see ITurtleUpgrade#useTool(ITurtleAccess, TurtleSide, TurtleVerb, Direction)
|
||||
*/
|
||||
public enum TurtleVerb
|
||||
{
|
||||
public enum TurtleVerb {
|
||||
/**
|
||||
* The turtle called {@code turtle.dig()}, {@code turtle.digUp()} or {@code turtle.digDown()}.
|
||||
*/
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user