mirror of
https://github.com/SquidDev-CC/CC-Tweaked
synced 2025-10-16 06:27:39 +00:00
Compare commits
92 Commits
v1.19.2-1.
...
hotfix/ite
Author | SHA1 | Date | |
---|---|---|---|
![]() |
1215d93645 | ||
![]() |
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
|
trim_trailing_whitespace = true
|
||||||
insert_final_newline = true
|
insert_final_newline = true
|
||||||
|
|
||||||
|
ij_continuation_indent_size = 4
|
||||||
|
ij_any_do_while_brace_force = if_multiline
|
||||||
|
ij_any_if_brace_force = if_multiline
|
||||||
|
ij_any_for_brace_force = if_multiline
|
||||||
|
ij_any_spaces_within_array_initializer_braces = true
|
||||||
|
|
||||||
|
ij_kotlin_allow_trailing_comma = true
|
||||||
|
ij_kotlin_allow_trailing_comma_on_call_site = true
|
||||||
|
ij_kotlin_method_parameters_wrap = off
|
||||||
|
ij_kotlin_call_parameters_wrap = off
|
||||||
|
|
||||||
[*.md]
|
[*.md]
|
||||||
trim_trailing_whitespace = false
|
trim_trailing_whitespace = false
|
||||||
|
|
||||||
|
2
.git-blame-ignore-revs
Normal file
2
.git-blame-ignore-revs
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
# Reformat everything
|
||||||
|
f478c4ffc4fb9fc2200ec9b0bc751d047057ce81
|
4
.gitattributes
vendored
4
.gitattributes
vendored
@@ -1,6 +1,6 @@
|
|||||||
# Ignore changes in generated files
|
# Ignore changes in generated files
|
||||||
src/generated/** linguist-generated
|
projects/*/src/generated/** linguist-generated
|
||||||
src/testMod/server-files/structures linguist-generated
|
projects/common/src/testMod/resources/data/cctest/structures/* linguist-generated
|
||||||
|
|
||||||
* text=auto
|
* text=auto
|
||||||
|
|
||||||
|
48
.github/workflows/main-ci.yml
vendored
48
.github/workflows/main-ci.yml
vendored
@@ -33,14 +33,19 @@ jobs:
|
|||||||
./gradlew downloadAssets || ./gradlew downloadAssets
|
./gradlew downloadAssets || ./gradlew downloadAssets
|
||||||
./gradlew build
|
./gradlew build
|
||||||
|
|
||||||
|
- name: Run client tests
|
||||||
|
run: ./gradlew runGametestClient # Not checkClient, as no point running rendering tests.
|
||||||
|
# These are a little flaky on GH actions: its useful to run them, but don't break the build.
|
||||||
|
continue-on-error: true
|
||||||
|
|
||||||
- name: Upload Jar
|
- name: Upload Jar
|
||||||
uses: actions/upload-artifact@v2
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: CC-Tweaked
|
name: CC-Tweaked
|
||||||
path: build/libs
|
path: projects/forge/build/libs
|
||||||
|
|
||||||
- name: Upload coverage
|
- name: Upload coverage
|
||||||
uses: codecov/codecov-action@v2
|
uses: codecov/codecov-action@v3
|
||||||
|
|
||||||
- name: Parse test reports
|
- name: Parse test reports
|
||||||
run: ./tools/parse-reports.py
|
run: ./tools/parse-reports.py
|
||||||
@@ -48,3 +53,40 @@ jobs:
|
|||||||
|
|
||||||
- name: Run linters
|
- name: Run linters
|
||||||
uses: pre-commit/action@v3.0.0
|
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
|
# And upload
|
||||||
rsync -avc -e "ssh -i $HOME/.ssh/key -o StrictHostKeyChecking=no -p $SSH_PORT" \
|
rsync -avc -e "ssh -i $HOME/.ssh/key -o StrictHostKeyChecking=no -p $SSH_PORT" \
|
||||||
"$GITHUB_WORKSPACE/build/docs/site/" \
|
"$GITHUB_WORKSPACE/projects/web/build/site/" \
|
||||||
"$SSH_USER@$SSH_HOST:/$DEST"
|
"$SSH_USER@$SSH_HOST:/$DEST"
|
||||||
rsync -avc -e "ssh -i $HOME/.ssh/key -o StrictHostKeyChecking=no -p $SSH_PORT" \
|
rsync -avc -e "ssh -i $HOME/.ssh/key -o StrictHostKeyChecking=no -p $SSH_PORT" \
|
||||||
"$GITHUB_WORKSPACE/build/docs/javadoc/" \
|
"$GITHUB_WORKSPACE/projects/common-api/build/docs/javadoc/" \
|
||||||
"$SSH_USER@$SSH_HOST:/$DEST/javadoc"
|
"$SSH_USER@$SSH_HOST:/$DEST/javadoc"
|
||||||
|
4
.github/workflows/make-doc.yml
vendored
4
.github/workflows/make-doc.yml
vendored
@@ -3,7 +3,7 @@ name: Build documentation
|
|||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- mc-1.16.x
|
- mc-1.19.x
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
make_doc:
|
make_doc:
|
||||||
@@ -29,7 +29,7 @@ jobs:
|
|||||||
run: ./gradlew compileJava --no-daemon || ./gradlew compileJava --no-daemon
|
run: ./gradlew compileJava --no-daemon || ./gradlew compileJava --no-daemon
|
||||||
|
|
||||||
- name: Generate documentation
|
- name: Generate documentation
|
||||||
run: ./gradlew docWebsite javadoc --no-daemon
|
run: ./gradlew docWebsite :common-api:javadoc --no-daemon
|
||||||
|
|
||||||
- name: Upload documentation
|
- name: Upload documentation
|
||||||
run: .github/workflows/make-doc.sh 2> /dev/null
|
run: .github/workflows/make-doc.sh 2> /dev/null
|
||||||
|
10
.gitignore
vendored
10
.gitignore
vendored
@@ -2,15 +2,17 @@
|
|||||||
/classes
|
/classes
|
||||||
/logs
|
/logs
|
||||||
/build
|
/build
|
||||||
|
/projects/*/logs
|
||||||
|
/projects/*/build
|
||||||
/buildSrc/build
|
/buildSrc/build
|
||||||
/out
|
/out
|
||||||
/doc/out/
|
/doc/out/
|
||||||
/node_modules
|
/node_modules
|
||||||
/.jqwik-database
|
.jqwik-database
|
||||||
|
|
||||||
# Runtime directories
|
# Runtime directories
|
||||||
/run
|
/run
|
||||||
/run-*
|
/projects/*/run
|
||||||
|
|
||||||
*.ipr
|
*.ipr
|
||||||
*.iws
|
*.iws
|
||||||
@@ -23,8 +25,6 @@
|
|||||||
/.project
|
/.project
|
||||||
/.settings
|
/.settings
|
||||||
/.vscode
|
/.vscode
|
||||||
bin/
|
|
||||||
*.launch
|
*.launch
|
||||||
|
|
||||||
/src/generated/resources/.cache
|
/projects/*/src/generated/resources/.cache
|
||||||
/src/web/mount/*.d.ts
|
|
||||||
|
@@ -48,9 +48,7 @@ repos:
|
|||||||
|
|
||||||
exclude: |
|
exclude: |
|
||||||
(?x)^(
|
(?x)^(
|
||||||
src/generated|
|
projects/[a-z]+/src/generated|
|
||||||
src/test/resources/test-rom/data/json-parsing/|
|
projects/core/src/test/resources/test-rom/data/json-parsing/|
|
||||||
src/testMod/server-files/|
|
|
||||||
config/idea/|
|
|
||||||
.*\.dfpwm
|
.*\.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].
|
If you've any other questions, [just ask the community][community] or [open an issue][new-issue].
|
||||||
|
|
||||||
|
## Table of Contents
|
||||||
|
- [Reporting issues](#reporting-issues)
|
||||||
|
- [Translations](#translations)
|
||||||
|
- [Setting up a development environment](#setting-up-a-development-environment)
|
||||||
|
- [Developing CC: Tweaked](#developing-cc-tweaked)
|
||||||
|
- [Writing documentation](#writing-documentation)
|
||||||
|
|
||||||
## Reporting issues
|
## Reporting issues
|
||||||
If you have a bug, suggestion, or other feedback, the best thing to do is [file an issue][new-issue]. When doing so,
|
If you have a bug, suggestion, or other feedback, the best thing to do is [file an issue][new-issue]. When doing so, do
|
||||||
do use the issue templates - they provide a useful hint on what information to provide.
|
use the issue templates - they provide a useful hint on what information to provide.
|
||||||
|
|
||||||
## Translations
|
## Translations
|
||||||
Translations are managed through [Weblate], an online interface for managing language strings. This is synced
|
Translations are managed through [Weblate], an online interface for managing language strings. This is synced
|
||||||
automatically with GitHub, so please don't submit PRs adding/changing translations!
|
automatically with GitHub, so please don't submit PRs adding/changing translations!
|
||||||
|
|
||||||
## Developing
|
## Setting up a development environment
|
||||||
In order to develop CC: Tweaked, you'll need to download the source code and then run it. This is a pretty simple
|
In order to develop CC: Tweaked, you'll need to download the source code and then run it.
|
||||||
process. When building on Windows, Use `gradlew.bat` instead of `./gradlew`.
|
|
||||||
|
|
||||||
- **Clone the repository:** `git clone https://github.com/cc-tweaked/CC-Tweaked.git && cd CC-Tweaked`
|
- Make sure you've got the following software instealled:
|
||||||
- **Setup Forge:** `./gradlew build`
|
- Java Development Kit (JDK) installed. This can be downloaded from [Adoptium].
|
||||||
- **Run Minecraft:** `./gradlew runClient` (or run the `GradleStart` class from your IDE).
|
- [Git](https://git-scm.com/).
|
||||||
- **Optionally:** For small PRs (especially those only touching Lua code), it may be easier to use GitPod, which
|
- If you want to work on documentation, [NodeJS][node].
|
||||||
provides a pre-configured environment: [](https://gitpod.io/#https://github.com/cc-tweaked/CC-Tweaked/)
|
|
||||||
|
|
||||||
Do note you will need to download the mod after compiling to test.
|
- Download CC: Tweaked's source code:
|
||||||
|
```
|
||||||
|
git clone https://github.com/cc-tweaked/CC-Tweaked.git
|
||||||
|
cd CC-Tweaked
|
||||||
|
```
|
||||||
|
|
||||||
If you want to run CC:T in a normal Minecraft instance, run `./gradlew build` and copy the `.jar` from `build/libs`.
|
- Build CC: Tweaked with `./gradlew build`. This will be very slow the first time it runs, as it needs to download a
|
||||||
These commands may take a few minutes to run the first time, as the environment is set up, but should be much faster
|
lot of dependencies (and decompile Minecraft several times). Subsequent runs should be much faster!
|
||||||
afterwards.
|
|
||||||
|
|
||||||
The following sections describe the more niche sections of CC: Tweaked's build system. Some bits of these are
|
- You're now ready to start developing CC: Tweaked. Running `./gradlew :forge:runClient` or
|
||||||
quite-complex, and (dare I say) over-engineered, so you may wish to ignore them. Well tested/documented PRs are always
|
`./gradle :fabric:runClient` will start Minecraft under Forge and Fabric respectively.
|
||||||
preferred (and I'd definitely recommend setting up the tooling if you're doing serious development work), but for
|
|
||||||
small changes it can be a lot.
|
|
||||||
|
|
||||||
### Code linters
|
If you want to run CC:T in a normal Minecraft instance, run `./gradlew assemble` and copy the `.jar` from
|
||||||
CC: Tweaked uses a couple of "linters" on its source code, to enforce a consistent style across the project. While these
|
`projects/forge/build/libs` (for Forge) or `projects/fabric/build/libs` (for Fabric).
|
||||||
are run whenever you submit a PR, it's often useful to run this before committing.
|
|
||||||
|
|
||||||
- **[Checkstyle]:** Checks Java code to ensure it is consistently formatted. This can be run with `./gradlew build` or
|
## Developing CC: Tweaked
|
||||||
`./gradle check`.
|
Before making any major changes to CC: Tweaked, I'd recommend you have a read of the [the architecture
|
||||||
- **[illuaminate]:** Checks Lua code for semantic and styleistic issues. This can be run with `./gradlew lintLua`.
|
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
|
When writing documentation for [CC: Tweaked's documentation website][docs], it may be useful to build the documentation
|
||||||
and preview it yourself before submitting a PR.
|
and preview it yourself before submitting a PR.
|
||||||
|
|
||||||
Our documentation generation pipeline is rather complex, and involves invoking several external tools. Most of this
|
You'll first need to [set up a development environment as above](#setting-up-a-development-environment).
|
||||||
complexity is hidden by Gradle, but you will need to perform some initial setup:
|
|
||||||
|
|
||||||
- Install [Node/npm][node].
|
Once this is set up, you can now run `./gradlew docWebsite`. This generates documentation from our Lua and Java code,
|
||||||
- Run `npm ci` to install our Node dependencies.
|
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
|
Documentation is built using [illuaminate] which, while not currently documented (somewhat ironic), is largely the same
|
||||||
HTML into `./build/docs/site`.
|
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
|
When writing long-form documentation (such as the guides in [doc/guides](doc/guides)), I find it useful to tell a
|
||||||
illuaminate's documentation system is not currently documented (somewhat ironic), but is _largely_ the same as
|
narrative. Think of what you want the user to learn or achieve, then start introducing a simple concept and then talk
|
||||||
[ldoc][ldoc]. Documentation comments are written in Markdown,
|
about how you can build on that, until you've covered everything!
|
||||||
|
|
||||||
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"`.
|
|
||||||
|
|
||||||
[new-issue]: https://github.com/cc-tweaked/CC-Tweaked/issues/new/choose "Create a new issue"
|
[new-issue]: https://github.com/cc-tweaked/CC-Tweaked/issues/new/choose "Create a new issue"
|
||||||
[community]: README.md#Community "Get in touch with the community."
|
[community]: README.md#community "Get in touch with the community."
|
||||||
|
[Adoptium]: https://adoptium.net/temurin/releases?version=17 "Download OpenJDK 17"
|
||||||
[checkstyle]: https://checkstyle.org/
|
[checkstyle]: https://checkstyle.org/
|
||||||
[illuaminate]: https://github.com/SquidDev/illuaminate/ "Illuaminate on GitHub"
|
[illuaminate]: https://github.com/SquidDev/illuaminate/ "Illuaminate on GitHub"
|
||||||
[weblate]: https://i18n.tweaked.cc/projects/cc-tweaked/minecraft/ "CC: Tweaked weblate instance"
|
[weblate]: https://i18n.tweaked.cc/projects/cc-tweaked/minecraft/ "CC: Tweaked weblate instance"
|
||||||
@@ -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
|
[mc-test]: https://www.youtube.com/watch?v=vXaWOJTCYNg
|
||||||
[busted]: https://github.com/Olivine-Labs/busted "busted: Elegant Lua unit testing."
|
[busted]: https://github.com/Olivine-Labs/busted "busted: Elegant Lua unit testing."
|
||||||
[node]: https://nodejs.org/en/ "Node.js"
|
[node]: https://nodejs.org/en/ "Node.js"
|
||||||
|
[architecture]: projects/ARCHITECTURE.md
|
||||||
|
18
README.md
18
README.md
@@ -26,16 +26,26 @@ on is present.
|
|||||||
```groovy
|
```groovy
|
||||||
repositories {
|
repositories {
|
||||||
maven {
|
maven {
|
||||||
url 'https://squiddev.cc/maven/'
|
url "https://squiddev.cc/maven/"
|
||||||
content {
|
content {
|
||||||
includeGroup 'org.squiddev'
|
includeGroup("cc.tweaked")
|
||||||
|
includeModule("org.squiddev", "Cobalt")
|
||||||
|
includeModule("fuzs.forgeconfigapiport", "forgeconfigapiport-fabric")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
compileOnly fg.deobf("org.squiddev:cc-tweaked-${mc_version}:${cct_version}:api")
|
// Vanilla (i.e. for multi-loader systems)
|
||||||
runtimeOnly fg.deobf("org.squiddev:cc-tweaked-${mc_version}:${cct_version}")
|
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 org.jetbrains.gradle.ext.compiler
|
||||||
import net.darkhax.curseforgegradle.TaskPublishCurseForge
|
import org.jetbrains.gradle.ext.settings
|
||||||
import net.minecraftforge.gradle.common.util.RunConfig
|
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
// Build
|
publishing
|
||||||
alias(libs.plugins.forgeGradle)
|
|
||||||
alias(libs.plugins.mixinGradle)
|
|
||||||
alias(libs.plugins.librarian)
|
|
||||||
alias(libs.plugins.shadow)
|
|
||||||
// Publishing
|
|
||||||
`maven-publish`
|
|
||||||
alias(libs.plugins.curseForgeGradle)
|
|
||||||
alias(libs.plugins.githubRelease)
|
|
||||||
alias(libs.plugins.minotaur)
|
|
||||||
// Utility
|
|
||||||
alias(libs.plugins.taskTree)
|
alias(libs.plugins.taskTree)
|
||||||
|
alias(libs.plugins.githubRelease)
|
||||||
id("cc-tweaked.illuaminate")
|
id("org.jetbrains.gradle.plugin.idea-ext")
|
||||||
id("cc-tweaked.node")
|
|
||||||
id("cc-tweaked.gametest")
|
|
||||||
id("cc-tweaked")
|
id("cc-tweaked")
|
||||||
}
|
}
|
||||||
|
|
||||||
val isStable = true
|
val isUnstable = project.properties["isUnstable"] == "true"
|
||||||
val modVersion: String by extra
|
val modVersion: String by extra
|
||||||
val mcVersion: String by extra
|
val mcVersion: String by extra
|
||||||
|
|
||||||
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 {
|
githubRelease {
|
||||||
token(findProperty("githubApiKey") as String? ?: "")
|
token(findProperty("githubApiKey") as String? ?: "")
|
||||||
owner.set("cc-tweaked")
|
owner.set("cc-tweaked")
|
||||||
@@ -407,54 +23,30 @@ githubRelease {
|
|||||||
releaseName.set("[$mcVersion] $modVersion")
|
releaseName.set("[$mcVersion] $modVersion")
|
||||||
body.set(
|
body.set(
|
||||||
provider {
|
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()
|
.readLines()
|
||||||
.takeWhile { it != "Type \"help changelog\" to see the full version history." }
|
.takeWhile { it != "Type \"help changelog\" to see the full version history." }
|
||||||
.joinToString("\n").trim()
|
.joinToString("\n").trim()
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
prerelease.set(!isStable)
|
prerelease.set(isUnstable)
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.publish { dependsOn(tasks.githubRelease) }
|
tasks.publish { dependsOn(tasks.githubRelease) }
|
||||||
|
|
||||||
publishing {
|
idea.project.settings.compiler.javac {
|
||||||
publications {
|
// We want ErrorProne to be present when compiling via IntelliJ, as it offers some helpful warnings
|
||||||
register<MavenPublication>("maven") {
|
// and errors. Loop through our source sets and find the appropriate flags.
|
||||||
artifactId = base.archivesName.get()
|
moduleJavacAdditionalOptions = subprojects
|
||||||
from(components["java"])
|
.asSequence()
|
||||||
artifact(apiJar)
|
.map { evaluationDependsOn(it.path) }
|
||||||
fg.component(this)
|
.flatMap { project ->
|
||||||
|
val sourceSets = project.extensions.findByType(SourceSetContainer::class) ?: return@flatMap sequenceOf()
|
||||||
pom {
|
sourceSets.asSequence().map { sourceSet ->
|
||||||
name.set("CC: Tweaked")
|
val name = "${idea.project.name}.${project.name}.${sourceSet.name}"
|
||||||
description.set("CC: Tweaked is a fork of ComputerCraft, adding programmable computers, turtles and more to Minecraft.")
|
val compile = project.tasks.named(sourceSet.compileJavaTaskName, JavaCompile::class).get()
|
||||||
url.set("https://github.com/cc-tweaked/CC-Tweaked")
|
name to compile.options.allCompilerArgs.joinToString(" ") { if (it.contains(" ")) "\"$it\"" else it }
|
||||||
|
|
||||||
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")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
.toMap()
|
||||||
|
|
||||||
repositories {
|
|
||||||
maven("https://squiddev.cc/maven") {
|
|
||||||
name = "SquidDev"
|
|
||||||
|
|
||||||
credentials(PasswordCredentials::class)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@@ -3,14 +3,51 @@ plugins {
|
|||||||
`kotlin-dsl`
|
`kotlin-dsl`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Duplicated in settings.gradle.kts
|
||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
gradlePluginPortal()
|
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 {
|
dependencies {
|
||||||
|
implementation(libs.errorProne.plugin)
|
||||||
implementation(libs.kotlin.plugin)
|
implementation(libs.kotlin.plugin)
|
||||||
implementation(libs.spotless)
|
implementation(libs.spotless)
|
||||||
|
|
||||||
|
implementation(libs.fabric.loom)
|
||||||
|
implementation(libs.forgeGradle)
|
||||||
|
implementation(libs.librarian)
|
||||||
|
implementation(libs.quiltflower)
|
||||||
|
implementation(libs.vanillaGradle)
|
||||||
}
|
}
|
||||||
|
|
||||||
gradlePlugin {
|
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.
|
* Sets up the configurations for writing game tests.
|
||||||
@@ -11,12 +12,13 @@ plugins {
|
|||||||
id("cc-tweaked.java-convention")
|
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 {
|
val testMod by sourceSets.creating {
|
||||||
compileClasspath += main.compileClasspath
|
compileClasspath += main.compileClasspath + client.compileClasspath
|
||||||
runtimeClasspath += main.runtimeClasspath
|
runtimeClasspath += main.runtimeClasspath + client.runtimeClasspath
|
||||||
}
|
}
|
||||||
|
|
||||||
configurations {
|
configurations {
|
||||||
@@ -32,12 +34,13 @@ configurations {
|
|||||||
// Like the main test configurations, we're safe to depend on source set outputs.
|
// Like the main test configurations, we're safe to depend on source set outputs.
|
||||||
dependencies {
|
dependencies {
|
||||||
add(testMod.implementationConfigurationName, main.output)
|
add(testMod.implementationConfigurationName, main.output)
|
||||||
|
add(testMod.implementationConfigurationName, client.output)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Similar to java-test-fixtures, but tries to avoid putting the obfuscated jar on the classpath.
|
// Similar to java-test-fixtures, but tries to avoid putting the obfuscated jar on the classpath.
|
||||||
|
|
||||||
val testFixtures by sourceSets.creating {
|
val testFixtures by sourceSets.creating {
|
||||||
compileClasspath += main.compileClasspath
|
compileClasspath += main.compileClasspath + client.compileClasspath
|
||||||
}
|
}
|
||||||
|
|
||||||
java.registerFeature("testFixtures") {
|
java.registerFeature("testFixtures") {
|
||||||
@@ -46,8 +49,12 @@ java.registerFeature("testFixtures") {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
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))
|
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.CCTweakedPlugin
|
||||||
import cc.tweaked.gradle.LicenseHeader
|
import cc.tweaked.gradle.LicenseHeader
|
||||||
import com.diffplug.gradle.spotless.FormatExtension
|
import com.diffplug.gradle.spotless.FormatExtension
|
||||||
import com.diffplug.spotless.LineEnding
|
import com.diffplug.spotless.LineEnding
|
||||||
|
import net.ltgt.gradle.errorprone.CheckSeverity
|
||||||
|
import net.ltgt.gradle.errorprone.errorprone
|
||||||
import java.nio.charset.StandardCharsets
|
import java.nio.charset.StandardCharsets
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
`java-library`
|
`java-library`
|
||||||
|
idea
|
||||||
jacoco
|
jacoco
|
||||||
checkstyle
|
checkstyle
|
||||||
id("com.diffplug.spotless")
|
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 {
|
java {
|
||||||
toolchain {
|
toolchain {
|
||||||
languageVersion.set(CCTweakedPlugin.JAVA_VERSION)
|
languageVersion.set(CCTweakedPlugin.JAVA_VERSION)
|
||||||
}
|
}
|
||||||
|
|
||||||
withSourcesJar()
|
withSourcesJar()
|
||||||
withJavadocJar()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
@@ -28,10 +40,15 @@ repositories {
|
|||||||
includeGroup("org.squiddev")
|
includeGroup("org.squiddev")
|
||||||
includeGroup("cc.tweaked")
|
includeGroup("cc.tweaked")
|
||||||
// Things we mirror
|
// Things we mirror
|
||||||
includeGroup("com.blamejared.crafttweaker")
|
includeGroup("dev.architectury")
|
||||||
includeGroup("commoble.morered")
|
|
||||||
includeGroup("maven.modrinth")
|
includeGroup("maven.modrinth")
|
||||||
|
includeGroup("me.shedaniel")
|
||||||
|
includeGroup("me.shedaniel.cloth")
|
||||||
includeGroup("mezz.jei")
|
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 {
|
dependencies {
|
||||||
val libs = project.extensions.getByType<VersionCatalogsExtension>().named("libs")
|
val libs = project.extensions.getByType<VersionCatalogsExtension>().named("libs")
|
||||||
checkstyle(libs.findLibrary("checkstyle").get())
|
checkstyle(libs.findLibrary("checkstyle").get())
|
||||||
|
|
||||||
|
errorprone(libs.findLibrary("errorProne-core").get())
|
||||||
|
errorprone(libs.findLibrary("nullAway").get())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Configure default JavaCompile tasks with our arguments.
|
// Configure default JavaCompile tasks with our arguments.
|
||||||
@@ -46,13 +66,68 @@ sourceSets.all {
|
|||||||
tasks.named(compileJavaTaskName, JavaCompile::class.java) {
|
tasks.named(compileJavaTaskName, JavaCompile::class.java) {
|
||||||
// Processing just gives us "No processor claimed any of these annotations", so skip that!
|
// Processing just gives us "No processor claimed any of these annotations", so skip that!
|
||||||
options.compilerArgs.addAll(listOf("-Xlint", "-Xlint:-processing"))
|
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 {
|
tasks.withType(JavaCompile::class.java).configureEach {
|
||||||
options.encoding = "UTF-8"
|
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 {
|
tasks.test {
|
||||||
finalizedBy("jacocoTestReport")
|
finalizedBy("jacocoTestReport")
|
||||||
|
|
||||||
@@ -67,6 +142,14 @@ tasks.withType(JacocoReport::class.java).configureEach {
|
|||||||
reports.html.required.set(true)
|
reports.html.required.set(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
project.plugins.withType(CCTweakedPlugin::class.java) {
|
||||||
|
// Set up jacoco to read from /all/ our source directories.
|
||||||
|
val cct = project.extensions.getByType<CCTweakedExtension>()
|
||||||
|
project.tasks.named("jacocoTestReport", JacocoReport::class.java) {
|
||||||
|
for (ref in cct.sourceSets.get()) sourceDirectories.from(ref.allSource.sourceDirectories)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
spotless {
|
spotless {
|
||||||
encoding = StandardCharsets.UTF_8
|
encoding = StandardCharsets.UTF_8
|
||||||
lineEndings = LineEnding.UNIX
|
lineEndings = LineEnding.UNIX
|
||||||
@@ -78,8 +161,8 @@ spotless {
|
|||||||
}
|
}
|
||||||
|
|
||||||
val licenser = LicenseHeader.create(
|
val licenser = LicenseHeader.create(
|
||||||
api = file("config/license/api.txt"),
|
api = rootProject.file("config/license/api.txt"),
|
||||||
main = file("config/license/main.txt"),
|
main = rootProject.file("config/license/main.txt"),
|
||||||
)
|
)
|
||||||
|
|
||||||
java {
|
java {
|
||||||
@@ -104,3 +187,12 @@ spotless {
|
|||||||
ktlint().editorConfigOverride(ktlintConfig)
|
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
|
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.NamedDomainObjectProvider
|
||||||
import org.gradle.api.Project
|
import org.gradle.api.Project
|
||||||
|
import org.gradle.api.Task
|
||||||
import org.gradle.api.attributes.TestSuiteType
|
import org.gradle.api.attributes.TestSuiteType
|
||||||
import org.gradle.api.file.FileSystemOperations
|
import org.gradle.api.file.FileSystemOperations
|
||||||
|
import org.gradle.api.plugins.JavaPluginExtension
|
||||||
import org.gradle.api.provider.Provider
|
import org.gradle.api.provider.Provider
|
||||||
|
import org.gradle.api.provider.SetProperty
|
||||||
import org.gradle.api.reporting.ReportingExtension
|
import org.gradle.api.reporting.ReportingExtension
|
||||||
import org.gradle.api.tasks.JavaExec
|
import org.gradle.api.tasks.SourceSet
|
||||||
import org.gradle.api.tasks.SourceSetContainer
|
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.configurationcache.extensions.capitalized
|
||||||
import org.gradle.kotlin.dsl.get
|
|
||||||
import org.gradle.language.base.plugins.LifecycleBasePlugin
|
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.JacocoCoverageReport
|
||||||
import org.gradle.testing.jacoco.plugins.JacocoPluginExtension
|
import org.gradle.testing.jacoco.plugins.JacocoPluginExtension
|
||||||
import org.gradle.testing.jacoco.plugins.JacocoTaskExtension
|
import org.gradle.testing.jacoco.plugins.JacocoTaskExtension
|
||||||
import org.gradle.testing.jacoco.tasks.JacocoReport
|
import org.gradle.testing.jacoco.tasks.JacocoReport
|
||||||
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.IOException
|
||||||
import java.io.OutputStreamWriter
|
import java.net.URI
|
||||||
|
import java.net.URL
|
||||||
import java.util.regex.Pattern
|
import java.util.regex.Pattern
|
||||||
|
|
||||||
abstract class CCTweakedExtension(
|
abstract class CCTweakedExtension(
|
||||||
@@ -26,45 +38,137 @@ abstract class CCTweakedExtension(
|
|||||||
) {
|
) {
|
||||||
/** Get the hash of the latest git commit. */
|
/** Get the hash of the latest git commit. */
|
||||||
val gitHash: Provider<String> = gitProvider(project, "<no git hash>") {
|
val gitHash: Provider<String> = 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. */
|
/** Get the current git branch. */
|
||||||
val gitBranch: Provider<String> = gitProvider(project, "<no 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. */
|
/** Get a list of all contributors to the project. */
|
||||||
val gitContributors: Provider<List<String>> = gitProvider(project, listOf()) {
|
val gitContributors: Provider<List<String>> = gitProvider(project, listOf()) {
|
||||||
val authors: Set<String> = HashSet(
|
ProcessHelpers.captureLines(
|
||||||
ProcessHelpers.captureLines(
|
"git", "-C", project.rootProject.projectDir.absolutePath, "shortlog", "-ns",
|
||||||
"git", "-C", project.projectDir.absolutePath, "log",
|
"--group=author", "--group=trailer:co-authored-by", "HEAD",
|
||||||
"--format=tformat:%an <%ae>%n%cn <%ce>%n%(trailers:key=Co-authored-by,valueonly)",
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
val process = ProcessHelpers.startProcess("git", "check-mailmap", "--stdin")
|
.asSequence()
|
||||||
BufferedWriter(OutputStreamWriter(process.outputStream)).use { writer ->
|
.map {
|
||||||
for (authorName in authors) {
|
val matcher = COMMIT_COUNTS.matcher(it)
|
||||||
var author = authorName
|
matcher.find()
|
||||||
|
matcher.group(1)
|
||||||
if (author.isEmpty()) continue
|
|
||||||
if (!author.endsWith(">")) author += ">" // Some commits have broken Co-Authored-By lines!
|
|
||||||
writer.write(author)
|
|
||||||
writer.newLine()
|
|
||||||
}
|
}
|
||||||
}
|
.filter { !IGNORED_USERS.contains(it) }
|
||||||
val contributors: MutableSet<String> = HashSet()
|
.toList()
|
||||||
for (authorLine in ProcessHelpers.captureLines(process)) {
|
.sortedWith(String.CASE_INSENSITIVE_ORDER)
|
||||||
val matcher = EMAIL.matcher(authorLine)
|
|
||||||
matcher.find()
|
|
||||||
val name = matcher.group(1)
|
|
||||||
if (!IGNORED_USERS.contains(name)) contributors.add(name)
|
|
||||||
}
|
|
||||||
|
|
||||||
contributors.sortedWith(String.CASE_INSENSITIVE_ORDER)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun jacoco(task: NamedDomainObjectProvider<JavaExec>) {
|
/**
|
||||||
|
* 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 classDump = project.buildDir.resolve("jacocoClassDump/${task.name}")
|
||||||
val reportTaskName = "jacoco${task.name.capitalized()}Report"
|
val reportTaskName = "jacoco${task.name.capitalized()}Report"
|
||||||
|
|
||||||
@@ -93,8 +197,7 @@ abstract class CCTweakedExtension(
|
|||||||
classDirectories.from(classDump)
|
classDirectories.from(classDump)
|
||||||
|
|
||||||
// Don't want to use sourceSets(...) here as we have a custom class directory.
|
// Don't want to use sourceSets(...) here as we have a custom class directory.
|
||||||
val sourceSets = project.extensions.getByType(SourceSetContainer::class.java)
|
for (ref in sourceSets.get()) sourceDirectories.from(ref.allSource.sourceDirectories)
|
||||||
sourceDirectories.from(sourceSets["main"].allSource.sourceDirectories)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
project.extensions.configure(ReportingExtension::class.java) {
|
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 {
|
companion object {
|
||||||
private val EMAIL = Pattern.compile("^([^<]+) <.+>$")
|
private val COMMIT_COUNTS = Pattern.compile("""^\s*[0-9]+\s+(.*)$""")
|
||||||
private val IGNORED_USERS = setOf(
|
private val IGNORED_USERS = setOf(
|
||||||
"GitHub", "Daniel Ratcliffe", "Weblate",
|
"GitHub", "Daniel Ratcliffe", "Weblate",
|
||||||
)
|
)
|
||||||
@@ -117,8 +255,14 @@ abstract class CCTweakedExtension(
|
|||||||
} catch (e: IOException) {
|
} catch (e: IOException) {
|
||||||
project.logger.error("Cannot read Git repository: ${e.message}")
|
project.logger.error("Cannot read Git repository: ${e.message}")
|
||||||
default
|
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.Plugin
|
||||||
import org.gradle.api.Project
|
import org.gradle.api.Project
|
||||||
|
import org.gradle.api.plugins.JavaPlugin
|
||||||
|
import org.gradle.api.plugins.JavaPluginExtension
|
||||||
import org.gradle.jvm.toolchain.JavaLanguageVersion
|
import org.gradle.jvm.toolchain.JavaLanguageVersion
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -9,7 +11,12 @@ import org.gradle.jvm.toolchain.JavaLanguageVersion
|
|||||||
*/
|
*/
|
||||||
class CCTweakedPlugin : Plugin<Project> {
|
class CCTweakedPlugin : Plugin<Project> {
|
||||||
override fun apply(project: 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 {
|
companion object {
|
||||||
|
@@ -6,7 +6,6 @@ import org.gradle.api.file.RegularFileProperty
|
|||||||
import org.gradle.api.provider.Property
|
import org.gradle.api.provider.Property
|
||||||
import org.gradle.api.tasks.*
|
import org.gradle.api.tasks.*
|
||||||
import org.gradle.language.base.plugins.LifecycleBasePlugin
|
import org.gradle.language.base.plugins.LifecycleBasePlugin
|
||||||
import java.nio.charset.StandardCharsets
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks the `changelog.md` and `whatsnew.md` files are well-formed.
|
* Checks the `changelog.md` and `whatsnew.md` files are well-formed.
|
||||||
|
@@ -5,7 +5,6 @@ import com.diffplug.spotless.FormatterStep
|
|||||||
import com.diffplug.spotless.generic.LicenseHeaderStep
|
import com.diffplug.spotless.generic.LicenseHeaderStep
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.Serializable
|
import java.io.Serializable
|
||||||
import java.nio.charset.StandardCharsets
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Similar to [LicenseHeaderStep], but supports multiple licenses.
|
* 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.artifacts.dsl.DependencyHandler
|
||||||
import org.gradle.api.tasks.JavaExec
|
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) {
|
fun DependencyHandler.annotationProcessorEverywhere(dep: Any) {
|
||||||
add("compileOnly", dep)
|
add("compileOnly", dep)
|
||||||
add("annotationProcessor", dep)
|
add("annotationProcessor", dep)
|
||||||
|
|
||||||
|
add("clientCompileOnly", dep)
|
||||||
|
add("clientAnnotationProcessor", dep)
|
||||||
|
|
||||||
add("testCompileOnly", dep)
|
add("testCompileOnly", dep)
|
||||||
add("testAnnotationProcessor", dep)
|
add("testAnnotationProcessor", dep)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A version of [JavaExecSpec.copyTo] which copies *all* properties.
|
||||||
|
*/
|
||||||
fun JavaExec.copyToFull(spec: JavaExec) {
|
fun JavaExec.copyToFull(spec: JavaExec) {
|
||||||
copyTo(spec)
|
copyTo(spec)
|
||||||
spec.classpath = classpath
|
|
||||||
spec.mainClass.set(mainClass)
|
// Additional Java options
|
||||||
spec.javaLauncher.set(javaLauncher)
|
spec.jvmArgs = jvmArgs // Fabric overrides getJvmArgs so copyTo doesn't do the right thing.
|
||||||
spec.args = args
|
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 osArch = System.getProperty("os.arch").toLowerCase()
|
||||||
val arch = when {
|
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 == "arm" || osArch.startsWith("aarch") -> error("Unsupported architecture '$osArch' for illuaminate")
|
||||||
osArch.contains("64") -> "x86_64"
|
osArch.contains("64") -> "x86_64"
|
||||||
else -> error("Unsupported architecture $osArch for illuaminate")
|
else -> error("Unsupported architecture '$osArch' for illuaminate")
|
||||||
}
|
}
|
||||||
|
|
||||||
return project.dependencies.create(
|
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
|
package cc.tweaked.gradle
|
||||||
|
|
||||||
import org.codehaus.groovy.runtime.ProcessGroovyMethods
|
import org.codehaus.groovy.runtime.ProcessGroovyMethods
|
||||||
|
import org.gradle.api.GradleException
|
||||||
import java.io.BufferedReader
|
import java.io.BufferedReader
|
||||||
import java.io.IOException
|
import java.io.File
|
||||||
import java.io.InputStreamReader
|
import java.io.InputStreamReader
|
||||||
import java.util.stream.Collectors
|
|
||||||
|
|
||||||
internal object ProcessHelpers {
|
internal object ProcessHelpers {
|
||||||
fun startProcess(vararg command: String): Process {
|
fun startProcess(vararg command: String): Process {
|
||||||
// Something randomly passes in "GIT_DIR=" as an environment variable which clobbers everything else. Don't
|
// Something randomly passes in "GIT_DIR=" as an environment variable which clobbers everything else. Don't
|
||||||
// inherit the environment array!
|
// 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 {
|
fun captureOut(vararg command: String): String {
|
||||||
val process = startProcess(*command)
|
val process = startProcess(*command)
|
||||||
|
process.outputStream.close()
|
||||||
|
|
||||||
val result = ProcessGroovyMethods.getText(process)
|
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
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
fun captureLines(vararg command: String): List<String> {
|
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 ->
|
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)
|
ProcessGroovyMethods.closeStreams(process)
|
||||||
if (process.waitFor() != 0) throw IOException("Command exited with a non-0 status")
|
process.waitForOrThrow("Failed to run command")
|
||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun onPath(name: String): Boolean {
|
||||||
|
val path = System.getenv("PATH") ?: return false
|
||||||
|
return path.splitToSequence(File.pathSeparator).any { File(it, name).exists() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun Process.waitForOrThrow(message: String) {
|
||||||
|
val ret = waitFor()
|
||||||
|
if (ret != 0) throw GradleException("$message (exited with $ret)")
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,20 @@
|
|||||||
|
package cc.tweaked.gradle
|
||||||
|
|
||||||
|
import org.gradle.api.tasks.SourceSet
|
||||||
|
|
||||||
|
data class SourceSetReference(
|
||||||
|
val sourceSet: SourceSet,
|
||||||
|
val classes: Boolean,
|
||||||
|
val external: Boolean,
|
||||||
|
) {
|
||||||
|
companion object {
|
||||||
|
/** A source set in the current project. */
|
||||||
|
fun internal(sourceSet: SourceSet) = SourceSetReference(sourceSet, classes = true, external = false)
|
||||||
|
|
||||||
|
/** A source set from another project. */
|
||||||
|
fun external(sourceSet: SourceSet) = SourceSetReference(sourceSet, classes = true, external = true)
|
||||||
|
|
||||||
|
/** A source set which is inlined into the current project. */
|
||||||
|
fun inline(sourceSet: SourceSet) = SourceSetReference(sourceSet, classes = false, external = false)
|
||||||
|
}
|
||||||
|
}
|
12
buildSrc/src/main/kotlin/cc/tweaked/gradle/XmlUtil.kt
Normal file
12
buildSrc/src/main/kotlin/cc/tweaked/gradle/XmlUtil.kt
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
package cc.tweaked.gradle
|
||||||
|
|
||||||
|
import groovy.util.Node
|
||||||
|
import groovy.util.NodeList
|
||||||
|
|
||||||
|
object XmlUtil {
|
||||||
|
fun findChild(node: Node, name: String): Node? = when (val child = node.get(name)) {
|
||||||
|
is Node -> child
|
||||||
|
is NodeList -> child.singleOrNull() as Node?
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
}
|
@@ -17,7 +17,10 @@
|
|||||||
<module name="TreeWalker">
|
<module name="TreeWalker">
|
||||||
<!-- Annotations -->
|
<!-- Annotations -->
|
||||||
<module name="AnnotationLocation" />
|
<module name="AnnotationLocation" />
|
||||||
<module name="AnnotationUseStyle" />
|
<module name="AnnotationUseStyle">
|
||||||
|
<!-- We want trailing commas on multiline arrays. -->
|
||||||
|
<property name="trailingArrayComma" value="ignore" />
|
||||||
|
</module>
|
||||||
<module name="MissingDeprecated" />
|
<module name="MissingDeprecated" />
|
||||||
<module name="MissingOverride" />
|
<module name="MissingOverride" />
|
||||||
|
|
||||||
@@ -26,17 +29,11 @@
|
|||||||
<module name="EmptyCatchBlock">
|
<module name="EmptyCatchBlock">
|
||||||
<property name="exceptionVariableName" value="ignored" />
|
<property name="exceptionVariableName" value="ignored" />
|
||||||
</module>
|
</module>
|
||||||
<module name="LeftCurly">
|
<module name="LeftCurly" />
|
||||||
<property name="option" value="nl" />
|
|
||||||
<!-- The defaults, minus lambdas. -->
|
|
||||||
<property name="tokens" value="ANNOTATION_DEF,CLASS_DEF,CTOR_DEF,ENUM_CONSTANT_DEF,ENUM_DEF,INTERFACE_DEF,LITERAL_CASE,LITERAL_CATCH,LITERAL_DEFAULT,LITERAL_DO,LITERAL_ELSE,LITERAL_FINALLY,LITERAL_FOR,LITERAL_IF,LITERAL_SWITCH,LITERAL_SYNCHRONIZED,LITERAL_TRY,LITERAL_WHILE,METHOD_DEF,OBJBLOCK,STATIC_INIT" />
|
|
||||||
</module>
|
|
||||||
<module name="NeedBraces">
|
<module name="NeedBraces">
|
||||||
<property name="allowSingleLineStatement" value="true"/>
|
<property name="allowSingleLineStatement" value="true"/>
|
||||||
</module>
|
</module>
|
||||||
<module name="RightCurly">
|
<module name="RightCurly" />
|
||||||
<property name="option" value="alone" />
|
|
||||||
</module>
|
|
||||||
|
|
||||||
<!-- Class design. As if we've ever followed good practice here. -->
|
<!-- Class design. As if we've ever followed good practice here. -->
|
||||||
<module name="FinalClass" />
|
<module name="FinalClass" />
|
||||||
@@ -114,7 +111,7 @@
|
|||||||
</module>
|
</module>
|
||||||
<module name="MethodTypeParameterName" />
|
<module name="MethodTypeParameterName" />
|
||||||
<module name="PackageName">
|
<module name="PackageName">
|
||||||
<property name="format" value="^dan200\.computercraft(\.[a-z][a-z0-9]*)*" />
|
<property name="format" value="^(dan200\.computercraft|cc\.tweaked)(\.[a-z][a-z0-9]*)*" />
|
||||||
</module>
|
</module>
|
||||||
<module name="ParameterName" />
|
<module name="ParameterName" />
|
||||||
<module name="StaticVariableName">
|
<module name="StaticVariableName">
|
||||||
@@ -131,18 +128,11 @@
|
|||||||
<module name="MethodParamPad" />
|
<module name="MethodParamPad" />
|
||||||
<module name="NoLineWrap" />
|
<module name="NoLineWrap" />
|
||||||
<module name="NoWhitespaceAfter">
|
<module name="NoWhitespaceAfter">
|
||||||
<property name="tokens" value="AT,INC,DEC,UNARY_MINUS,UNARY_PLUS,BNOT,LNOT,DOT,ARRAY_DECLARATOR,INDEX_OP" />
|
<property name="tokens" value="AT,INC,DEC,UNARY_MINUS,UNARY_PLUS,BNOT,LNOT,DOT,ARRAY_DECLARATOR,INDEX_OP,METHOD_REF" />
|
||||||
</module>
|
</module>
|
||||||
<module name="NoWhitespaceBefore" />
|
<module name="NoWhitespaceBefore" />
|
||||||
<!-- TODO: Decide on an OperatorWrap style. -->
|
<!-- TODO: Decide on an OperatorWrap style. -->
|
||||||
<module name="ParenPad">
|
<module name="ParenPad" />
|
||||||
<property name="option" value="space" />
|
|
||||||
<property name="tokens" value="ANNOTATION,ANNOTATION_FIELD_DEF,CTOR_CALL,CTOR_DEF,ENUM_CONSTANT_DEF,LITERAL_CATCH,LITERAL_DO,LITERAL_FOR,LITERAL_IF,LITERAL_NEW,LITERAL_SWITCH,LITERAL_SYNCHRONIZED,LITERAL_WHILE,METHOD_CALL,METHOD_DEF,RESOURCE_SPECIFICATION,SUPER_CTOR_CALL,LAMBDA" />
|
|
||||||
</module>
|
|
||||||
<module name="ParenPad">
|
|
||||||
<property name="option" value="nospace" />
|
|
||||||
<property name="tokens" value="DOT,EXPR,QUESTION" />
|
|
||||||
</module>
|
|
||||||
<module name="SeparatorWrap">
|
<module name="SeparatorWrap">
|
||||||
<property name="option" value="eol" />
|
<property name="option" value="eol" />
|
||||||
<property name="tokens" value="COMMA,SEMI,ELLIPSIS,ARRAY_DECLARATOR,RBRACK,METHOD_REF" />
|
<property name="tokens" value="COMMA,SEMI,ELLIPSIS,ARRAY_DECLARATOR,RBRACK,METHOD_REF" />
|
||||||
|
File diff suppressed because it is too large
Load Diff
@@ -1,61 +0,0 @@
|
|||||||
<code_scheme name="Project" version="173">
|
|
||||||
<JSON>
|
|
||||||
<option name="OBJECT_WRAPPING" value="1" />
|
|
||||||
<option name="ARRAY_WRAPPING" value="1" />
|
|
||||||
</JSON>
|
|
||||||
<JavaCodeStyleSettings>
|
|
||||||
<option name="PACKAGES_TO_USE_IMPORT_ON_DEMAND">
|
|
||||||
<value />
|
|
||||||
</option>
|
|
||||||
<option name="JD_P_AT_EMPTY_LINES" value="false" />
|
|
||||||
<option name="JD_PRESERVE_LINE_FEEDS" value="true" />
|
|
||||||
</JavaCodeStyleSettings>
|
|
||||||
<codeStyleSettings language="JAVA">
|
|
||||||
<option name="KEEP_FIRST_COLUMN_COMMENT" value="false" />
|
|
||||||
<option name="BRACE_STYLE" value="2" />
|
|
||||||
<option name="CLASS_BRACE_STYLE" value="2" />
|
|
||||||
<option name="METHOD_BRACE_STYLE" value="2" />
|
|
||||||
<option name="LAMBDA_BRACE_STYLE" value="5" />
|
|
||||||
<option name="ELSE_ON_NEW_LINE" value="true" />
|
|
||||||
<option name="CATCH_ON_NEW_LINE" value="true" />
|
|
||||||
<option name="FINALLY_ON_NEW_LINE" value="true" />
|
|
||||||
<option name="SPACE_WITHIN_METHOD_CALL_PARENTHESES" value="true" />
|
|
||||||
<option name="SPACE_WITHIN_METHOD_PARENTHESES" value="true" />
|
|
||||||
<option name="SPACE_WITHIN_IF_PARENTHESES" value="true" />
|
|
||||||
<option name="SPACE_WITHIN_WHILE_PARENTHESES" value="true" />
|
|
||||||
<option name="SPACE_WITHIN_FOR_PARENTHESES" value="true" />
|
|
||||||
<option name="SPACE_WITHIN_TRY_PARENTHESES" value="true" />
|
|
||||||
<option name="SPACE_WITHIN_CATCH_PARENTHESES" value="true" />
|
|
||||||
<option name="SPACE_WITHIN_SWITCH_PARENTHESES" value="true" />
|
|
||||||
<option name="SPACE_WITHIN_SYNCHRONIZED_PARENTHESES" value="true" />
|
|
||||||
<option name="SPACE_WITHIN_ARRAY_INITIALIZER_BRACES" value="true" />
|
|
||||||
<option name="SPACE_BEFORE_IF_PARENTHESES" value="false" />
|
|
||||||
<option name="SPACE_BEFORE_WHILE_PARENTHESES" value="false" />
|
|
||||||
<option name="SPACE_BEFORE_FOR_PARENTHESES" value="false" />
|
|
||||||
<option name="SPACE_BEFORE_TRY_PARENTHESES" value="false" />
|
|
||||||
<option name="SPACE_BEFORE_CATCH_PARENTHESES" value="false" />
|
|
||||||
<option name="SPACE_BEFORE_SWITCH_PARENTHESES" value="false" />
|
|
||||||
<option name="SPACE_BEFORE_SYNCHRONIZED_PARENTHESES" value="false" />
|
|
||||||
<option name="SPACE_BEFORE_ARRAY_INITIALIZER_LBRACE" value="true" />
|
|
||||||
<option name="KEEP_SIMPLE_METHODS_IN_ONE_LINE" value="true" />
|
|
||||||
<option name="KEEP_SIMPLE_LAMBDAS_IN_ONE_LINE" value="true" />
|
|
||||||
<option name="KEEP_SIMPLE_CLASSES_IN_ONE_LINE" value="true" />
|
|
||||||
<option name="IF_BRACE_FORCE" value="1" />
|
|
||||||
<option name="DOWHILE_BRACE_FORCE" value="1" />
|
|
||||||
<option name="WHILE_BRACE_FORCE" value="1" />
|
|
||||||
<option name="FOR_BRACE_FORCE" value="1" />
|
|
||||||
<option name="SPACE_WITHIN_ANNOTATION_PARENTHESES" value="true" />
|
|
||||||
<indentOptions>
|
|
||||||
<option name="CONTINUATION_INDENT_SIZE" value="4" />
|
|
||||||
</indentOptions>
|
|
||||||
</codeStyleSettings>
|
|
||||||
<codeStyleSettings language="JSON">
|
|
||||||
<option name="KEEP_BLANK_LINES_IN_CODE" value="1" />
|
|
||||||
<option name="SPACE_WITHIN_BRACKETS" value="true" />
|
|
||||||
<option name="SPACE_WITHIN_BRACES" value="true" />
|
|
||||||
<indentOptions>
|
|
||||||
<option name="INDENT_SIZE" value="4" />
|
|
||||||
<option name="CONTINUATION_INDENT_SIZE" value="4" />
|
|
||||||
</indentOptions>
|
|
||||||
</codeStyleSettings>
|
|
||||||
</code_scheme>
|
|
@@ -1,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
|
kotlin.jvm.target.validation.mode=error
|
||||||
|
|
||||||
# Mod properties
|
# Mod properties
|
||||||
modVersion=1.101.1
|
isUnstable=true
|
||||||
|
modVersion=1.102.0
|
||||||
|
|
||||||
# Minecraft properties: We want to configure this here so we can read it in settings.gradle
|
# Minecraft properties: We want to configure this here so we can read it in settings.gradle
|
||||||
mcVersion=1.19.2
|
mcVersion=1.19.3
|
||||||
|
@@ -2,70 +2,152 @@
|
|||||||
|
|
||||||
# Minecraft
|
# Minecraft
|
||||||
# MC version is specified in gradle.properties, as we need that in settings.gradle.
|
# MC version is specified in gradle.properties, as we need that in settings.gradle.
|
||||||
forge = "43.1.1"
|
fabric-api = "0.68.1+1.19.3"
|
||||||
parchment = "2022.10.16"
|
fabric-loader = "0.14.11"
|
||||||
|
forge = "44.0.18"
|
||||||
|
forgeSpi = "6.0.0"
|
||||||
|
mixin = "0.8.5"
|
||||||
|
parchment = "2022.11.27"
|
||||||
parchmentMc = "1.19.2"
|
parchmentMc = "1.19.2"
|
||||||
|
|
||||||
|
# Normal dependencies
|
||||||
|
asm = "9.3"
|
||||||
autoService = "1.0.1"
|
autoService = "1.0.1"
|
||||||
cobalt = { strictly = "[0.5.8,0.6.0)", prefer = "0.5.8" }
|
checkerFramework = "3.12.0"
|
||||||
|
cobalt = "0.5.9"
|
||||||
|
fastutil = "8.5.9"
|
||||||
|
guava = "31.1-jre"
|
||||||
jetbrainsAnnotations = "23.0.0"
|
jetbrainsAnnotations = "23.0.0"
|
||||||
|
jsr305 = "3.0.2"
|
||||||
kotlin = "1.7.10"
|
kotlin = "1.7.10"
|
||||||
kotlin-coroutines = "1.6.0"
|
kotlin-coroutines = "1.6.0"
|
||||||
|
netty = "4.1.82.Final"
|
||||||
|
nightConfig = "3.6.5"
|
||||||
|
slf4j = "1.7.36"
|
||||||
|
|
||||||
|
# Minecraft mods
|
||||||
|
forgeConfig = "5.0.3"
|
||||||
|
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
|
# Testing
|
||||||
|
byteBuddy = "1.12.19"
|
||||||
hamcrest = "2.2"
|
hamcrest = "2.2"
|
||||||
jqwik = "1.7.0"
|
jqwik = "1.7.0"
|
||||||
junit = "5.9.1"
|
junit = "5.9.1"
|
||||||
|
|
||||||
# Build tools
|
# Build tools
|
||||||
cctJavadoc = "1.5.2"
|
cctJavadoc = "1.5.3"
|
||||||
checkstyle = "10.3.4"
|
checkstyle = "10.3.4"
|
||||||
curseForgeGradle = "1.0.11"
|
curseForgeGradle = "1.0.11"
|
||||||
|
errorProne-core = "2.14.0"
|
||||||
|
errorProne-plugin = "2.0.2"
|
||||||
|
fabric-loom = "1.0-SNAPSHOT"
|
||||||
forgeGradle = "5.1.+"
|
forgeGradle = "5.1.+"
|
||||||
githubRelease = "2.2.12"
|
githubRelease = "2.2.12"
|
||||||
|
ideaExt = "1.1.6"
|
||||||
illuaminate = "0.1.0-7-g2a5a89c"
|
illuaminate = "0.1.0-7-g2a5a89c"
|
||||||
librarian = "1.+"
|
librarian = "1.+"
|
||||||
minotaur = "2.+"
|
minotaur = "2.+"
|
||||||
mixinGradle = "0.7.+"
|
mixinGradle = "0.7.+"
|
||||||
|
nullAway = "0.9.9"
|
||||||
|
quiltflower = "1.7.3"
|
||||||
shadow = "7.1.2"
|
shadow = "7.1.2"
|
||||||
spotless = "6.8.0"
|
spotless = "6.8.0"
|
||||||
taskTree = "2.1.0"
|
taskTree = "2.1.0"
|
||||||
|
vanillaGradle = "0.2.1-SNAPSHOT"
|
||||||
|
|
||||||
[libraries]
|
[libraries]
|
||||||
|
# Normal dependencies
|
||||||
|
asm = { module = "org.ow2.asm:asm", version.ref = "asm" }
|
||||||
autoService = { module = "com.google.auto.service:auto-service", version.ref = "autoService" }
|
autoService = { module = "com.google.auto.service:auto-service", version.ref = "autoService" }
|
||||||
|
checkerFramework = { module = "org.checkerframework:checker-qual", version.ref = "checkerFramework" }
|
||||||
cobalt = { module = "org.squiddev:Cobalt", version.ref = "cobalt" }
|
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" }
|
jetbrainsAnnotations = { module = "org.jetbrains:annotations", version.ref = "jetbrainsAnnotations" }
|
||||||
|
jsr305 = { module = "com.google.code.findbugs:jsr305", version.ref = "jsr305" }
|
||||||
kotlin-coroutines = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kotlin-coroutines" }
|
kotlin-coroutines = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kotlin-coroutines" }
|
||||||
kotlin-stdlib = { module = "org.jetbrains.kotlin:kotlin-stdlib-jdk8", version.ref = "kotlin" }
|
kotlin-stdlib = { module = "org.jetbrains.kotlin:kotlin-stdlib-jdk8", 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
|
# Testing
|
||||||
|
byteBuddyAgent = { module ="net.bytebuddy:byte-buddy-agent", version.ref = "byteBuddy" }
|
||||||
|
byteBuddy = { module ="net.bytebuddy:byte-buddy", version.ref = "byteBuddy" }
|
||||||
hamcrest = { module = "org.hamcrest:hamcrest", version.ref = "hamcrest" }
|
hamcrest = { module = "org.hamcrest:hamcrest", version.ref = "hamcrest" }
|
||||||
jqwik-api = { module = "net.jqwik:jqwik-api", version.ref = "jqwik" }
|
jqwik-api = { module = "net.jqwik:jqwik-api", version.ref = "jqwik" }
|
||||||
jqwik-engine = { module = "net.jqwik:jqwik-engine", version.ref = "jqwik" }
|
jqwik-engine = { module = "net.jqwik:jqwik-engine", version.ref = "jqwik" }
|
||||||
junit-jupiter-api = { module = "org.junit.jupiter:junit-jupiter-api", version.ref = "junit" }
|
junit-jupiter-api = { module = "org.junit.jupiter:junit-jupiter-api", version.ref = "junit" }
|
||||||
junit-jupiter-engine = { module = "org.junit.jupiter:junit-jupiter-engine", version.ref = "junit" }
|
junit-jupiter-engine = { module = "org.junit.jupiter:junit-jupiter-engine", version.ref = "junit" }
|
||||||
junit-jupiter-params = { module = "org.junit.jupiter:junit-jupiter-params", version.ref = "junit" }
|
junit-jupiter-params = { module = "org.junit.jupiter:junit-jupiter-params", version.ref = "junit" }
|
||||||
|
slf4j-simple = { module = "org.slf4j:slf4j-simple", version.ref = "slf4j" }
|
||||||
|
|
||||||
# Build tools
|
# Build tools
|
||||||
cctJavadoc = { module = "cc.tweaked:cct-javadoc", version.ref = "cctJavadoc" }
|
cctJavadoc = { module = "cc.tweaked:cct-javadoc", version.ref = "cctJavadoc" }
|
||||||
checkstyle = { module = "com.puppycrawl.tools:checkstyle", version.ref = "checkstyle" }
|
checkstyle = { module = "com.puppycrawl.tools:checkstyle", version.ref = "checkstyle" }
|
||||||
|
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" }
|
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" }
|
spotless = { module = "com.diffplug.spotless:spotless-plugin-gradle", version.ref = "spotless" }
|
||||||
|
vanillaGradle = { module = "org.spongepowered:vanillagradle", version.ref = "vanillaGradle" }
|
||||||
|
|
||||||
[plugins]
|
[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" }
|
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" }
|
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" }
|
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" }
|
shadow = { id = "com.github.johnrengelman.shadow", version.ref = "shadow" }
|
||||||
|
taskTree = { id = "com.dorongold.task-tree", version.ref = "taskTree" }
|
||||||
|
|
||||||
[bundles]
|
[bundles]
|
||||||
kotlin = ["kotlin-stdlib", "kotlin-coroutines"]
|
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
|
# Testing
|
||||||
test = ["junit-jupiter-api", "junit-jupiter-params", "hamcrest", "jqwik-api"]
|
test = ["junit-jupiter-api", "junit-jupiter-params", "hamcrest", "jqwik-api"]
|
||||||
testRuntime = ["junit-jupiter-engine", "jqwik-engine"]
|
testRuntime = ["junit-jupiter-engine", "jqwik-engine"]
|
||||||
|
@@ -2,25 +2,26 @@
|
|||||||
|
|
||||||
(sources
|
(sources
|
||||||
/doc/
|
/doc/
|
||||||
/build/docs/luaJavadoc/
|
/projects/forge/build/docs/luaJavadoc/
|
||||||
/src/main/resources/*/computercraft/lua/bios.lua
|
/projects/core/src/main/resources/data/computercraft/lua/bios.lua
|
||||||
/src/main/resources/*/computercraft/lua/rom/
|
/projects/core/src/main/resources/data/computercraft/lua/rom/
|
||||||
/src/test/resources/test-rom
|
/projects/core/src/test/resources/test-rom
|
||||||
/src/web/mount)
|
/projects/web/src/mount)
|
||||||
|
|
||||||
|
|
||||||
(doc
|
(doc
|
||||||
(destination build/illuaminate)
|
; Also defined in projects/web/build.gradle.kts
|
||||||
|
(destination /projects/web/build/illuaminate)
|
||||||
(index doc/index.md)
|
(index doc/index.md)
|
||||||
|
|
||||||
(site
|
(site
|
||||||
(title "CC: Tweaked")
|
(title "CC: Tweaked")
|
||||||
(logo src/main/resources/pack.png)
|
(logo projects/common/src/main/resources/pack.png)
|
||||||
(url https://tweaked.cc/)
|
(url https://tweaked.cc/)
|
||||||
(source-link https://github.com/cc-tweaked/CC-Tweaked/blob/${commit}/${path}#L${line})
|
(source-link https://github.com/cc-tweaked/CC-Tweaked/blob/${commit}/${path}#L${line})
|
||||||
|
|
||||||
(styles src/web/styles.css)
|
(styles /projects/web/src/styles.css)
|
||||||
(scripts build/rollup/index.js)
|
(scripts /projects/web/build/rollup/index.js)
|
||||||
(head doc/head.html))
|
(head doc/head.html))
|
||||||
|
|
||||||
(module-kinds
|
(module-kinds
|
||||||
@@ -32,15 +33,15 @@
|
|||||||
|
|
||||||
(library-path
|
(library-path
|
||||||
/doc/stub/
|
/doc/stub/
|
||||||
/build/docs/luaJavadoc/
|
/projects/forge/build/docs/luaJavadoc/
|
||||||
|
|
||||||
/src/main/resources/*/computercraft/lua/rom/apis/
|
/projects/core/src/main/resources/data/computercraft/lua/rom/apis/
|
||||||
/src/main/resources/*/computercraft/lua/rom/apis/command/
|
/projects/core/src/main/resources/data/computercraft/lua/rom/apis/command/
|
||||||
/src/main/resources/*/computercraft/lua/rom/apis/turtle/
|
/projects/core/src/main/resources/data/computercraft/lua/rom/apis/turtle/
|
||||||
|
|
||||||
/src/main/resources/*/computercraft/lua/rom/modules/main/
|
/projects/core/src/main/resources/data/computercraft/lua/rom/modules/main/
|
||||||
/src/main/resources/*/computercraft/lua/rom/modules/command/
|
/projects/core/src/main/resources/data/computercraft/lua/rom/modules/command/
|
||||||
/src/main/resources/*/computercraft/lua/rom/modules/turtle/))
|
/projects/core/src/main/resources/data/computercraft/lua/rom/modules/turtle/))
|
||||||
|
|
||||||
(at /
|
(at /
|
||||||
(linters
|
(linters
|
||||||
@@ -80,37 +81,37 @@
|
|||||||
;; We disable the unused global linter in bios.lua and the APIs. In the future
|
;; We disable the unused global linter in bios.lua and the APIs. In the future
|
||||||
;; hopefully we'll get illuaminate to handle this.
|
;; hopefully we'll get illuaminate to handle this.
|
||||||
(at
|
(at
|
||||||
(/src/main/resources/*/computercraft/lua/bios.lua
|
(/projects/core/src/main/resources/data/computercraft/lua/bios.lua
|
||||||
/src/main/resources/*/computercraft/lua/rom/apis/)
|
/projects/core/src/main/resources/data/computercraft/lua/rom/apis/)
|
||||||
(linters -var:unused-global)
|
(linters -var:unused-global)
|
||||||
(lint (allow-toplevel-global true)))
|
(lint (allow-toplevel-global true)))
|
||||||
|
|
||||||
;; Silence some variable warnings in documentation stubs.
|
;; Silence some variable warnings in documentation stubs.
|
||||||
(at (/doc/stub/ /build/docs/luaJavadoc/)
|
(at (/doc/stub/ /projects/forge/build/docs/luaJavadoc/)
|
||||||
(linters -var:unused-global)
|
(linters -var:unused-global)
|
||||||
(lint (allow-toplevel-global true)))
|
(lint (allow-toplevel-global true)))
|
||||||
|
|
||||||
;; Suppress warnings for currently undocumented modules.
|
;; Suppress warnings for currently undocumented modules.
|
||||||
(at
|
(at
|
||||||
(; Lua APIs
|
(; Lua APIs
|
||||||
/src/main/resources/*/computercraft/lua/rom/apis/io.lua
|
/projects/core/src/main/resources/data/computercraft/lua/rom/apis/io.lua
|
||||||
/src/main/resources/*/computercraft/lua/rom/apis/window.lua)
|
/projects/core/src/main/resources/data/computercraft/lua/rom/apis/window.lua)
|
||||||
|
|
||||||
(linters -doc:undocumented -doc:undocumented-arg -doc:undocumented-return))
|
(linters -doc:undocumented -doc:undocumented-arg -doc:undocumented-return))
|
||||||
|
|
||||||
;; Suppress warnings for various APIs using its own deprecated members.
|
;; Suppress warnings for various APIs using its own deprecated members.
|
||||||
(at
|
(at
|
||||||
(/src/main/resources/*/computercraft/lua/bios.lua
|
(/projects/core/src/main/resources/data/computercraft/lua/bios.lua
|
||||||
/src/main/resources/*/computercraft/lua/rom/apis/turtle/turtle.lua)
|
/projects/core/src/main/resources/data/computercraft/lua/rom/apis/turtle/turtle.lua)
|
||||||
(linters -var:deprecated))
|
(linters -var:deprecated))
|
||||||
|
|
||||||
(at /src/test/resources/test-rom
|
(at /projects/core/src/test/resources/test-rom
|
||||||
; We should still be able to test deprecated members.
|
; We should still be able to test deprecated members.
|
||||||
(linters -var:deprecated)
|
(linters -var:deprecated)
|
||||||
|
|
||||||
(lint
|
(lint
|
||||||
(globals
|
(globals
|
||||||
:max sleep write
|
:max sleep write
|
||||||
cct_test describe expect howlci fail it pending stub)))
|
cct_test describe expect howlci fail it pending stub before_each)))
|
||||||
|
|
||||||
(at /src/web/mount/expr_template.lua (lint (globals :max __expr__)))
|
(at /projects/web/src/mount/expr_template.lua (lint (globals :max __expr__)))
|
||||||
|
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.ITurtleUpgrade;
|
||||||
import dan200.computercraft.api.turtle.TurtleUpgradeSerialiser;
|
import dan200.computercraft.api.turtle.TurtleUpgradeSerialiser;
|
||||||
import dan200.computercraft.impl.client.ComputerCraftAPIClientService;
|
import dan200.computercraft.impl.client.ComputerCraftAPIClientService;
|
||||||
import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent;
|
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
|
||||||
|
|
||||||
public final class ComputerCraftAPIClient
|
public final class ComputerCraftAPIClient {
|
||||||
{
|
private ComputerCraftAPIClient() {
|
||||||
private ComputerCraftAPIClient()
|
|
||||||
{
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Register a {@link TurtleUpgradeModeller} for a class of turtle upgrades.
|
* Register a {@link TurtleUpgradeModeller} for a class of turtle upgrades.
|
||||||
* <p>
|
* <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 serialiser The turtle upgrade serialiser.
|
||||||
* @param modeller The upgrade modeller.
|
* @param modeller The upgrade modeller.
|
||||||
* @param <T> The type of the turtle upgrade.
|
* @param <T> The type of the turtle upgrade.
|
||||||
*/
|
*/
|
||||||
public static <T extends ITurtleUpgrade> void registerTurtleUpgradeModeller( @Nonnull TurtleUpgradeSerialiser<T> serialiser, @Nonnull TurtleUpgradeModeller<T> modeller )
|
public static <T extends ITurtleUpgrade> void registerTurtleUpgradeModeller(TurtleUpgradeSerialiser<T> serialiser, TurtleUpgradeModeller<T> modeller) {
|
||||||
{
|
getInstance().registerTurtleUpgradeModeller(serialiser, modeller);
|
||||||
getInstance().registerTurtleUpgradeModeller( serialiser, modeller );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
private static ComputerCraftAPIClientService getInstance() {
|
||||||
private static ComputerCraftAPIClientService getInstance()
|
|
||||||
{
|
|
||||||
return ComputerCraftAPIClientService.get();
|
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.client.resources.model.ModelResourceLocation;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -23,8 +22,7 @@ import javax.annotation.Nullable;
|
|||||||
* @param <T> The type of turtle upgrade this modeller applies to.
|
* @param <T> The type of turtle upgrade this modeller applies to.
|
||||||
* @see ComputerCraftAPIClient#registerTurtleUpgradeModeller(TurtleUpgradeSerialiser, TurtleUpgradeModeller) To register a modeller.
|
* @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.
|
* Obtain the model to be used when rendering a turtle peripheral.
|
||||||
* <p>
|
* <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.
|
* @param side Which side of the turtle (left or right) the upgrade resides on.
|
||||||
* @return The model that you wish to be used to render your upgrade.
|
* @return The model that you wish to be used to render your upgrade.
|
||||||
*/
|
*/
|
||||||
@Nonnull
|
TransformedModel getModel(T upgrade, @Nullable ITurtleAccess turtle, TurtleSide side);
|
||||||
TransformedModel getModel( @Nonnull T upgrade, @Nullable ITurtleAccess turtle, @Nonnull TurtleSide side );
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A basic {@link TurtleUpgradeModeller} which renders using the upgrade's {@linkplain ITurtleUpgrade#getCraftingItem()
|
* 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.
|
* @param <T> The type of the turtle upgrade.
|
||||||
* @return The constructed modeller.
|
* @return The constructed modeller.
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings( "unchecked" )
|
@SuppressWarnings("unchecked")
|
||||||
static <T extends ITurtleUpgrade> TurtleUpgradeModeller<T> flatItem()
|
static <T extends ITurtleUpgrade> TurtleUpgradeModeller<T> flatItem() {
|
||||||
{
|
|
||||||
return (TurtleUpgradeModeller<T>) TurtleUpgradeModellers.FLAT_ITEM;
|
return (TurtleUpgradeModeller<T>) TurtleUpgradeModellers.FLAT_ITEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -62,9 +58,8 @@ public interface TurtleUpgradeModeller<T extends ITurtleUpgrade>
|
|||||||
* @param <T> The type of the turtle upgrade.
|
* @param <T> The type of the turtle upgrade.
|
||||||
* @return The constructed modeller.
|
* @return The constructed modeller.
|
||||||
*/
|
*/
|
||||||
static <T extends ITurtleUpgrade> TurtleUpgradeModeller<T> sided( ModelResourceLocation left, ModelResourceLocation right )
|
static <T extends ITurtleUpgrade> TurtleUpgradeModeller<T> sided(ModelResourceLocation left, ModelResourceLocation right) {
|
||||||
{
|
return (upgrade, turtle, side) -> TransformedModel.of(side == TurtleSide.LEFT ? left : right);
|
||||||
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.
|
* @param <T> The type of the turtle upgrade.
|
||||||
* @return The constructed modeller.
|
* @return The constructed modeller.
|
||||||
*/
|
*/
|
||||||
static <T extends ITurtleUpgrade> TurtleUpgradeModeller<T> sided( ResourceLocation left, ResourceLocation right )
|
static <T extends ITurtleUpgrade> TurtleUpgradeModeller<T> sided(ResourceLocation left, ResourceLocation right) {
|
||||||
{
|
return (upgrade, turtle, side) -> TransformedModel.of(side == TurtleSide.LEFT ? left : right);
|
||||||
return ( upgrade, turtle, side ) -> TransformedModel.of( side == TurtleSide.LEFT ? left : right );
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -5,27 +5,28 @@
|
|||||||
*/
|
*/
|
||||||
package dan200.computercraft.api.client.turtle;
|
package dan200.computercraft.api.client.turtle;
|
||||||
|
|
||||||
import com.mojang.math.Matrix4f;
|
|
||||||
import com.mojang.math.Transformation;
|
import com.mojang.math.Transformation;
|
||||||
import dan200.computercraft.api.client.TransformedModel;
|
import dan200.computercraft.api.client.TransformedModel;
|
||||||
import dan200.computercraft.api.turtle.ITurtleUpgrade;
|
import dan200.computercraft.api.turtle.ITurtleUpgrade;
|
||||||
import dan200.computercraft.api.turtle.TurtleSide;
|
import dan200.computercraft.api.turtle.TurtleSide;
|
||||||
|
import org.joml.Matrix4f;
|
||||||
|
|
||||||
class TurtleUpgradeModellers
|
class TurtleUpgradeModellers {
|
||||||
{
|
private static final Transformation leftTransform = getMatrixFor(-0.40625f);
|
||||||
private static final Transformation leftTransform = getMatrixFor( -0.40625f );
|
private static final Transformation rightTransform = getMatrixFor(0.40625f);
|
||||||
private static final Transformation rightTransform = getMatrixFor( 0.40625f );
|
|
||||||
|
|
||||||
private static Transformation getMatrixFor( float offset )
|
private static Transformation getMatrixFor(float offset) {
|
||||||
{
|
var matrix = new Matrix4f();
|
||||||
return new Transformation( new Matrix4f( new float[] {
|
matrix.set(new float[]{
|
||||||
0.0f, 0.0f, -1.0f, 1.0f + offset,
|
0.0f, 0.0f, -1.0f, 1.0f + offset,
|
||||||
1.0f, 0.0f, 0.0f, 0.0f,
|
1.0f, 0.0f, 0.0f, 0.0f,
|
||||||
0.0f, -1.0f, 0.0f, 1.0f,
|
0.0f, -1.0f, 0.0f, 1.0f,
|
||||||
0.0f, 0.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 ) ->
|
static final TurtleUpgradeModeller<ITurtleUpgrade> FLAT_ITEM = (upgrade, turtle, side) ->
|
||||||
TransformedModel.of( upgrade.getCraftingItem(), side == TurtleSide.LEFT ? leftTransform : rightTransform );
|
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 dan200.computercraft.impl.Services;
|
||||||
import org.jetbrains.annotations.ApiStatus;
|
import org.jetbrains.annotations.ApiStatus;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
|
||||||
import javax.annotation.Nullable;
|
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.
|
* Do <strong>NOT</strong> directly reference this class. It exists for internal use by the API.
|
||||||
*/
|
*/
|
||||||
@ApiStatus.Internal
|
@ApiStatus.Internal
|
||||||
public interface ComputerCraftAPIClientService
|
public interface ComputerCraftAPIClientService {
|
||||||
{
|
static ComputerCraftAPIClientService get() {
|
||||||
static ComputerCraftAPIClientService get()
|
var instance = Instance.INSTANCE;
|
||||||
{
|
return instance == null ? Services.raise(ComputerCraftAPIClientService.class, Instance.ERROR) : instance;
|
||||||
ComputerCraftAPIClientService 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 ComputerCraftAPIClientService INSTANCE;
|
||||||
static final @Nullable Throwable ERROR;
|
static final @Nullable Throwable ERROR;
|
||||||
|
|
||||||
static
|
static {
|
||||||
{
|
var helper = Services.tryLoad(ComputerCraftAPIClientService.class);
|
||||||
Services.LoadedService<ComputerCraftAPIClientService> helper = Services.tryLoad( ComputerCraftAPIClientService.class );
|
|
||||||
INSTANCE = helper.instance();
|
INSTANCE = helper.instance();
|
||||||
ERROR = helper.error();
|
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,59 @@
|
|||||||
|
/*
|
||||||
|
* 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.item.Item;
|
||||||
|
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");
|
||||||
|
|
||||||
|
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.Item;
|
||||||
import net.minecraft.world.item.ItemStack;
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nullable;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
@@ -18,10 +18,9 @@ import java.util.Objects;
|
|||||||
*
|
*
|
||||||
* @param <T> The type the stack's item must have.
|
* @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 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}.
|
* 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 itemType The type the stack's item must have.
|
||||||
* @param namespace The namespace to use for this provider.
|
* @param namespace The namespace to use for this provider.
|
||||||
*/
|
*/
|
||||||
public BasicItemDetailProvider( String namespace, @Nonnull Class<T> itemType )
|
public BasicItemDetailProvider(@Nullable String namespace, Class<T> itemType) {
|
||||||
{
|
Objects.requireNonNull(itemType);
|
||||||
Objects.requireNonNull( itemType );
|
|
||||||
this.itemType = itemType;
|
this.itemType = itemType;
|
||||||
this.namespace = namespace;
|
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.
|
* @param itemType The type the stack's item must have.
|
||||||
*/
|
*/
|
||||||
public BasicItemDetailProvider( @Nonnull Class<T> itemType )
|
public BasicItemDetailProvider(Class<T> itemType) {
|
||||||
{
|
this(null, 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 stack The item stack to provide details for.
|
||||||
* @param item The item to provide details for.
|
* @param item The item to provide details for.
|
||||||
*/
|
*/
|
||||||
public abstract void provideDetails( @Nonnull Map<? super String, Object> data, @Nonnull ItemStack stack,
|
public abstract void provideDetails(
|
||||||
@Nonnull T item );
|
Map<? super String, Object> data, ItemStack stack, T item
|
||||||
|
);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void provideDetails( @Nonnull Map<? super String, Object> data, @Nonnull ItemStack stack )
|
public void provideDetails(Map<? super String, Object> data, ItemStack stack) {
|
||||||
{
|
var item = stack.getItem();
|
||||||
Item item = stack.getItem();
|
if (!itemType.isInstance(item)) return;
|
||||||
if( !itemType.isInstance( item ) ) return;
|
|
||||||
|
|
||||||
// If `namespace` is specified, insert into a new data map instead of the existing one.
|
// If `namespace` is specified, insert into a new data map instead of the existing one.
|
||||||
Map<? super String, Object> child = namespace == null ? data : new HashMap<>();
|
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 )
|
if (namespace != null) {
|
||||||
{
|
data.put(namespace, child);
|
||||||
data.put( namespace, child );
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -10,7 +10,6 @@ import net.minecraft.world.level.Level;
|
|||||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||||
import net.minecraft.world.level.block.state.BlockState;
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -22,14 +21,12 @@ import javax.annotation.Nullable;
|
|||||||
* @param blockEntity The block entity at this position, if it exists.
|
* @param blockEntity The block entity at this position, if it exists.
|
||||||
*/
|
*/
|
||||||
public record BlockReference(
|
public record BlockReference(
|
||||||
@Nonnull Level level,
|
Level level,
|
||||||
@Nonnull BlockPos pos,
|
BlockPos pos,
|
||||||
@Nonnull BlockState state,
|
BlockState state,
|
||||||
@Nullable BlockEntity blockEntity
|
@Nullable BlockEntity blockEntity
|
||||||
)
|
) {
|
||||||
{
|
public BlockReference(Level level, BlockPos pos) {
|
||||||
public BlockReference( Level level, BlockPos pos )
|
this(level, pos, level.getBlockState(pos), level.getBlockEntity(pos));
|
||||||
{
|
|
||||||
this( level, pos, level.getBlockState( pos ), level.getBlockEntity( pos ) );
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -5,7 +5,6 @@
|
|||||||
*/
|
*/
|
||||||
package dan200.computercraft.api.detail;
|
package dan200.computercraft.api.detail;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -18,8 +17,7 @@ import java.util.Map;
|
|||||||
* @see DetailRegistry
|
* @see DetailRegistry
|
||||||
*/
|
*/
|
||||||
@FunctionalInterface
|
@FunctionalInterface
|
||||||
public interface IDetailProvider<T>
|
public interface DetailProvider<T> {
|
||||||
{
|
|
||||||
/**
|
/**
|
||||||
* Provide additional details for the given object. This method is called by functions such as
|
* 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
|
* {@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 data The full details to be returned. New properties should be added to this map.
|
||||||
* @param object The object to provide details for.
|
* @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,15 +20,14 @@ import java.util.Map;
|
|||||||
* @param <T> The type of object that this registry provides details for.
|
* @param <T> The type of object that this registry provides details for.
|
||||||
*/
|
*/
|
||||||
@ApiStatus.NonExtendable
|
@ApiStatus.NonExtendable
|
||||||
public interface DetailRegistry<T>
|
public interface DetailRegistry<T> {
|
||||||
{
|
|
||||||
/**
|
/**
|
||||||
* Registers a detail provider.
|
* Registers a detail provider.
|
||||||
*
|
*
|
||||||
* @param provider The detail provider to register.
|
* @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
|
* Compute basic details about an object. This is cheaper than computing all details operation, and so is suitable
|
||||||
@@ -37,7 +36,7 @@ public interface DetailRegistry<T>
|
|||||||
* @param object The object to get details for.
|
* @param object The object to get details for.
|
||||||
* @return The basic details.
|
* @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.
|
* Compute all details about an object, using {@link #getBasicDetails(Object)} and any registered providers.
|
||||||
@@ -45,5 +44,5 @@ public interface DetailRegistry<T>
|
|||||||
* @param object The object to get details for.
|
* @param object The object to get details for.
|
||||||
* @return The computed details.
|
* @return The computed details.
|
||||||
*/
|
*/
|
||||||
Map<String, Object> getDetails( T object );
|
Map<String, Object> getDetails(T object);
|
||||||
}
|
}
|
@@ -12,8 +12,7 @@ import net.minecraft.world.level.block.Block;
|
|||||||
/**
|
/**
|
||||||
* {@link DetailRegistry}s for built-in Minecraft types.
|
* {@link DetailRegistry}s for built-in Minecraft types.
|
||||||
*/
|
*/
|
||||||
public class VanillaDetailRegistries
|
public class VanillaDetailRegistries {
|
||||||
{
|
|
||||||
/**
|
/**
|
||||||
* Provides details for {@link ItemStack}s.
|
* Provides details for {@link ItemStack}s.
|
||||||
*/
|
*/
|
@@ -5,23 +5,24 @@
|
|||||||
*/
|
*/
|
||||||
package dan200.computercraft.api.media;
|
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.sounds.SoundEvent;
|
||||||
import net.minecraft.world.item.Item;
|
import net.minecraft.world.item.Item;
|
||||||
import net.minecraft.world.item.ItemStack;
|
import net.minecraft.world.item.ItemStack;
|
||||||
import net.minecraft.world.level.Level;
|
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents an item that can be placed in a disk drive and used by a Computer.
|
* Represents an item that can be placed in a disk drive and used by a Computer.
|
||||||
* <p>
|
* <p>
|
||||||
* Implement this interface on your {@link Item} class to allow it to be used in the drive. Alternatively, register
|
* Implement this interface on your {@link Item} class to allow it to be used in the drive. 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.
|
* 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".
|
* @return The label. ie: "Dan's Programs".
|
||||||
*/
|
*/
|
||||||
@Nullable
|
@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.
|
* 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.
|
* @param label The string to set the label to.
|
||||||
* @return true if the label was updated, false if the label may not be modified.
|
* @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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -51,8 +51,7 @@ public interface IMedia
|
|||||||
* @return The name, or null if this item does not represent an item with audio.
|
* @return The name, or null if this item does not represent an item with audio.
|
||||||
*/
|
*/
|
||||||
@Nullable
|
@Nullable
|
||||||
default String getAudioTitle( @Nonnull ItemStack stack )
|
default String getAudioTitle(ItemStack stack) {
|
||||||
{
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,8 +62,7 @@ public interface IMedia
|
|||||||
* @return The name, or null if this item does not represent an item with audio.
|
* @return The name, or null if this item does not represent an item with audio.
|
||||||
*/
|
*/
|
||||||
@Nullable
|
@Nullable
|
||||||
default SoundEvent getAudio( @Nonnull ItemStack stack )
|
default SoundEvent getAudio(ItemStack stack) {
|
||||||
{
|
|
||||||
return null;
|
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.
|
* be mounted onto the filesystem of the computer while the media is in the disk drive.
|
||||||
*
|
*
|
||||||
* @param stack The {@link ItemStack} to modify.
|
* @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
|
* @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()
|
* implements {@link WritableMount}, it will mounted using mountWritable()
|
||||||
* @see IMount
|
* @see Mount
|
||||||
* @see dan200.computercraft.api.filesystem.IWritableMount
|
* @see WritableMount
|
||||||
* @see dan200.computercraft.api.ComputerCraftAPI#createSaveDirMount(Level, String, long)
|
* @see ComputerCraftAPI#createSaveDirMount(MinecraftServer, String, long)
|
||||||
* @see dan200.computercraft.api.ComputerCraftAPI#createResourceMount(String, String)
|
* @see ComputerCraftAPI#createResourceMount(MinecraftServer, String, String)
|
||||||
*/
|
*/
|
||||||
@Nullable
|
@Nullable
|
||||||
default IMount createDataMount( @Nonnull ItemStack stack, @Nonnull Level world )
|
default Mount createDataMount(ItemStack stack, ServerLevel level) {
|
||||||
{
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -7,24 +7,22 @@ package dan200.computercraft.api.media;
|
|||||||
|
|
||||||
import net.minecraft.world.item.ItemStack;
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This interface is used to provide {@link IMedia} implementations for {@link ItemStack}.
|
* 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
|
@FunctionalInterface
|
||||||
public interface IMediaProvider
|
public interface MediaProvider {
|
||||||
{
|
|
||||||
/**
|
/**
|
||||||
* Produce an IMedia implementation from an ItemStack.
|
* Produce an IMedia implementation from an ItemStack.
|
||||||
*
|
*
|
||||||
* @param stack The stack from which to extract the media information.
|
* @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
|
* @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
|
@Nullable
|
||||||
IMedia getMedia( @Nonnull ItemStack stack );
|
IMedia getMedia(ItemStack stack);
|
||||||
}
|
}
|
@@ -6,7 +6,7 @@
|
|||||||
package dan200.computercraft.api.network;
|
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
|
* @param channel The channel to send the packet along. Receiving devices should only process packets from on
|
||||||
* channels they are listening to.
|
* 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
|
* @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.
|
* event or returning from a peripheral call.
|
||||||
* @param sender The object which sent this packet.
|
* @param sender The object which sent this packet.
|
||||||
* @see IPacketSender
|
* @see PacketSender
|
||||||
* @see IPacketNetwork#transmitSameDimension(Packet, double)
|
* @see PacketNetwork#transmitSameDimension(Packet, double)
|
||||||
* @see IPacketNetwork#transmitInterdimensional(Packet)
|
* @see PacketNetwork#transmitInterdimensional(Packet)
|
||||||
* @see IPacketReceiver#receiveDifferentDimension(Packet)
|
* @see PacketReceiver#receiveDifferentDimension(Packet)
|
||||||
* @see IPacketReceiver#receiveSameDimension(Packet, double)
|
* @see PacketReceiver#receiveSameDimension(Packet, double)
|
||||||
*/
|
*/
|
||||||
public record Packet(
|
public record Packet(
|
||||||
int channel,
|
int channel,
|
||||||
int replyChannel,
|
int replyChannel,
|
||||||
Object payload,
|
Object payload,
|
||||||
IPacketSender sender
|
PacketSender sender
|
||||||
)
|
) {
|
||||||
{
|
|
||||||
}
|
}
|
@@ -5,29 +5,27 @@
|
|||||||
*/
|
*/
|
||||||
package dan200.computercraft.api.network;
|
package dan200.computercraft.api.network;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A packet network represents a collection of devices which can send and receive packets.
|
* A packet network represents a collection of devices which can send and receive packets.
|
||||||
*
|
*
|
||||||
* @see Packet
|
* @see Packet
|
||||||
* @see IPacketReceiver
|
* @see PacketReceiver
|
||||||
*/
|
*/
|
||||||
public interface IPacketNetwork
|
public interface PacketNetwork {
|
||||||
{
|
|
||||||
/**
|
/**
|
||||||
* Add a receiver to the network.
|
* Add a receiver to the network.
|
||||||
*
|
*
|
||||||
* @param receiver The receiver to register 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.
|
* Remove a receiver from the network.
|
||||||
*
|
*
|
||||||
* @param receiver The device to remove 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.
|
* Determine whether this network is wireless.
|
||||||
@@ -43,9 +41,9 @@ public interface IPacketNetwork
|
|||||||
* @param packet The packet to send.
|
* @param packet The packet to send.
|
||||||
* @param range The maximum distance this packet will be sent.
|
* @param range The maximum distance this packet will be sent.
|
||||||
* @see #transmitInterdimensional(Packet)
|
* @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
|
* 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.
|
* @param packet The packet to send.
|
||||||
* @see #transmitSameDimension(Packet, double)
|
* @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.level.Level;
|
||||||
import net.minecraft.world.phys.Vec3;
|
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.
|
* Get the world in which this packet receiver exists.
|
||||||
*
|
*
|
||||||
* @return The receivers's world.
|
* @return The receivers's world.
|
||||||
*/
|
*/
|
||||||
@Nonnull
|
|
||||||
Level getLevel();
|
Level getLevel();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -28,7 +25,6 @@ public interface IPacketReceiver
|
|||||||
*
|
*
|
||||||
* @return The receiver's position.
|
* @return The receiver's position.
|
||||||
*/
|
*/
|
||||||
@Nonnull
|
|
||||||
Vec3 getPosition();
|
Vec3 getPosition();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -41,7 +37,7 @@ public interface IPacketReceiver
|
|||||||
* @return The maximum distance this device can send and receive messages.
|
* @return The maximum distance this device can send and receive messages.
|
||||||
* @see #isInterdimensional()
|
* @see #isInterdimensional()
|
||||||
* @see #receiveSameDimension(Packet packet, double)
|
* @see #receiveSameDimension(Packet packet, double)
|
||||||
* @see IPacketNetwork#transmitInterdimensional(Packet)
|
* @see PacketNetwork#transmitInterdimensional(Packet)
|
||||||
*/
|
*/
|
||||||
double getRange();
|
double getRange();
|
||||||
|
|
||||||
@@ -53,7 +49,7 @@ public interface IPacketReceiver
|
|||||||
* @return Whether this receiver receives packets from other dimensions.
|
* @return Whether this receiver receives packets from other dimensions.
|
||||||
* @see #getRange()
|
* @see #getRange()
|
||||||
* @see #receiveDifferentDimension(Packet)
|
* @see #receiveDifferentDimension(Packet)
|
||||||
* @see IPacketNetwork#transmitInterdimensional(Packet)
|
* @see PacketNetwork#transmitInterdimensional(Packet)
|
||||||
*/
|
*/
|
||||||
boolean isInterdimensional();
|
boolean isInterdimensional();
|
||||||
|
|
||||||
@@ -65,10 +61,10 @@ public interface IPacketReceiver
|
|||||||
* @param distance The distance this packet has travelled from the source.
|
* @param distance The distance this packet has travelled from the source.
|
||||||
* @see Packet
|
* @see Packet
|
||||||
* @see #getRange()
|
* @see #getRange()
|
||||||
* @see IPacketNetwork#transmitSameDimension(Packet, double)
|
* @see PacketNetwork#transmitSameDimension(Packet, double)
|
||||||
* @see IPacketNetwork#transmitInterdimensional(Packet)
|
* @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.
|
* 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,
|
* @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.
|
* if so, queue the appropriate modem event.
|
||||||
* @see Packet
|
* @see Packet
|
||||||
* @see IPacketNetwork#transmitInterdimensional(Packet)
|
* @see PacketNetwork#transmitInterdimensional(Packet)
|
||||||
* @see IPacketNetwork#transmitSameDimension(Packet, double)
|
* @see PacketNetwork#transmitSameDimension(Packet, double)
|
||||||
* @see #isInterdimensional()
|
* @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.level.Level;
|
||||||
import net.minecraft.world.phys.Vec3;
|
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.
|
* Get the world in which this packet sender exists.
|
||||||
*
|
*
|
||||||
* @return The sender's world.
|
* @return The sender's world.
|
||||||
*/
|
*/
|
||||||
@Nonnull
|
|
||||||
Level getLevel();
|
Level getLevel();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -28,7 +25,6 @@ public interface IPacketSender
|
|||||||
*
|
*
|
||||||
* @return The sender's position.
|
* @return The sender's position.
|
||||||
*/
|
*/
|
||||||
@Nonnull
|
|
||||||
Vec3 getPosition();
|
Vec3 getPosition();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -37,6 +33,5 @@ public interface IPacketSender
|
|||||||
*
|
*
|
||||||
* @return This device's id.
|
* @return This device's id.
|
||||||
*/
|
*/
|
||||||
@Nonnull
|
|
||||||
String getSenderID();
|
String getSenderID();
|
||||||
}
|
}
|
@@ -7,28 +7,25 @@ package dan200.computercraft.api.network.wired;
|
|||||||
|
|
||||||
import dan200.computercraft.api.ComputerCraftAPI;
|
import dan200.computercraft.api.ComputerCraftAPI;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An object which may be part of a wired network.
|
* An object which may be part of a wired network.
|
||||||
* <p>
|
* <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
|
* as a proxy for all network objects. Whilst the node may change networks, an element's node should remain constant
|
||||||
* for its lifespan.
|
* for its lifespan.
|
||||||
* <p>
|
* <p>
|
||||||
* Elements are generally tied to a block or tile entity in world. In such as case, one should provide the
|
* Elements are generally tied to a block or 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
|
* Called when objects on the network change. This may occur when network nodes are added or removed, or when
|
||||||
* peripherals change.
|
* peripherals change.
|
||||||
*
|
*
|
||||||
* @param change The change which occurred.
|
* @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 dan200.computercraft.api.peripheral.IPeripheral;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
|
||||||
import java.util.Map;
|
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.
|
* of peripherals.
|
||||||
* <p>
|
* <p>
|
||||||
* Networks from a connected graph. This means there is some path between all nodes on the network. Further more, if
|
* 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
|
* handle the merging and splitting of networks (and thus changing of available nodes and peripherals) as connections
|
||||||
* change.
|
* change.
|
||||||
* <p>
|
* <p>
|
||||||
* This does mean one can not rely on the network remaining consistent between subsequent operations. Consequently,
|
* 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.
|
* Create a connection between two nodes.
|
||||||
* <p>
|
* <p>
|
||||||
@@ -36,10 +34,10 @@ public interface IWiredNetwork
|
|||||||
* @return {@code true} if a connection was created or {@code false} if the connection already exists.
|
* @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 IllegalStateException If neither node is on the network.
|
||||||
* @throws IllegalArgumentException If {@code left} and {@code right} are equal.
|
* @throws IllegalArgumentException If {@code left} and {@code right} are equal.
|
||||||
* @see IWiredNode#connectTo(IWiredNode)
|
* @see WiredNode#connectTo(WiredNode)
|
||||||
* @see IWiredNetwork#connect(IWiredNode, IWiredNode)
|
* @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.
|
* 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.
|
* @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 either node is not on the network.
|
||||||
* @throws IllegalArgumentException If {@code left} and {@code right} are equal.
|
* @throws IllegalArgumentException If {@code left} and {@code right} are equal.
|
||||||
* @see IWiredNode#disconnectFrom(IWiredNode)
|
* @see WiredNode#disconnectFrom(WiredNode)
|
||||||
* @see IWiredNetwork#connect(IWiredNode, IWiredNode)
|
* @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.
|
* 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
|
* @return Whether this node was removed from the network. One cannot remove a node from a network where it is the
|
||||||
* only element.
|
* only element.
|
||||||
* @throws IllegalArgumentException If the node is not in the network.
|
* @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.
|
* Update the peripherals a node provides.
|
||||||
@@ -79,7 +77,7 @@ public interface IWiredNetwork
|
|||||||
* @param node The node to attach peripherals for.
|
* @param node The node to attach peripherals for.
|
||||||
* @param peripherals The new peripherals for this node.
|
* @param peripherals The new peripherals for this node.
|
||||||
* @throws IllegalArgumentException If the node is not in the network.
|
* @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 dan200.computercraft.api.peripheral.IPeripheral;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a change to the objects on a wired network.
|
* 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
|
* 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.
|
* in the added and removed set, but with a different peripheral.
|
||||||
*
|
*
|
||||||
* @return The set of removed peripherals.
|
* @return The set of removed peripherals.
|
||||||
*/
|
*/
|
||||||
@Nonnull
|
|
||||||
Map<String, IPeripheral> peripheralsRemoved();
|
Map<String, IPeripheral> peripheralsRemoved();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -32,6 +29,5 @@ public interface IWiredNetworkChange
|
|||||||
*
|
*
|
||||||
* @return The set of added peripherals.
|
* @return The set of added peripherals.
|
||||||
*/
|
*/
|
||||||
@Nonnull
|
|
||||||
Map<String, IPeripheral> peripheralsAdded();
|
Map<String, IPeripheral> peripheralsAdded();
|
||||||
}
|
}
|
@@ -5,14 +5,13 @@
|
|||||||
*/
|
*/
|
||||||
package dan200.computercraft.api.network.wired;
|
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 dan200.computercraft.api.peripheral.IPeripheral;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
|
||||||
import java.util.Map;
|
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>
|
* <p>
|
||||||
* Firstly, a node acts as a packet network, capable of sending and receiving modem messages to connected nodes. These
|
* Firstly, a node acts as a packet network, capable of sending and receiving modem messages to connected nodes. These
|
||||||
* methods may be safely used on any thread.
|
* methods may be safely used on any thread.
|
||||||
@@ -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
|
* Wired nodes also provide several convenience methods for interacting with a wired network. These should only ever
|
||||||
* be used on the main server thread.
|
* be used on the main server thread.
|
||||||
*/
|
*/
|
||||||
public interface IWiredNode extends IPacketNetwork
|
public interface WiredNode extends PacketNetwork {
|
||||||
{
|
|
||||||
/**
|
/**
|
||||||
* The associated element for this network node.
|
* The associated element for this network node.
|
||||||
*
|
*
|
||||||
* @return This node's element.
|
* @return This node's element.
|
||||||
*/
|
*/
|
||||||
@Nonnull
|
WiredElement getElement();
|
||||||
IWiredElement getElement();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The network this node is currently connected to. Note that this may change
|
* 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.
|
* @return This node's network.
|
||||||
*/
|
*/
|
||||||
@Nonnull
|
WiredNetwork getNetwork();
|
||||||
IWiredNetwork getNetwork();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a connection from this node to another.
|
* 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.
|
* @param node The other node to connect to.
|
||||||
* @return {@code true} if a connection was created or {@code false} if the connection already exists.
|
* @return {@code true} if a connection was created or {@code false} if the connection already exists.
|
||||||
* @see IWiredNetwork#connect(IWiredNode, IWiredNode)
|
* @see WiredNetwork#connect(WiredNode, WiredNode)
|
||||||
* @see IWiredNode#disconnectFrom(IWiredNode)
|
* @see WiredNode#disconnectFrom(WiredNode)
|
||||||
*/
|
*/
|
||||||
default boolean connectTo( @Nonnull IWiredNode node )
|
default boolean connectTo(WiredNode node) {
|
||||||
{
|
return getNetwork().connect(this, node);
|
||||||
return getNetwork().connect( this, node );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -68,12 +63,11 @@ public interface IWiredNode extends IPacketNetwork
|
|||||||
* @param node The other node to disconnect from.
|
* @param node The other node to disconnect from.
|
||||||
* @return {@code true} if a connection was destroyed or {@code false} if no connection exists.
|
* @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.
|
* @throws IllegalArgumentException If {@code node} is not on the same network.
|
||||||
* @see IWiredNetwork#disconnect(IWiredNode, IWiredNode)
|
* @see WiredNetwork#disconnect(WiredNode, WiredNode)
|
||||||
* @see IWiredNode#connectTo(IWiredNode)
|
* @see WiredNode#connectTo(WiredNode)
|
||||||
*/
|
*/
|
||||||
default boolean disconnectFrom( @Nonnull IWiredNode node )
|
default boolean disconnectFrom(WiredNode node) {
|
||||||
{
|
return getNetwork().disconnect(this, 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
|
* @return Whether this node was removed from the network. One cannot remove a node from a network where it is the
|
||||||
* only element.
|
* only element.
|
||||||
* @throws IllegalArgumentException If the node is not in the network.
|
* @throws IllegalArgumentException If the node is not in the network.
|
||||||
* @see IWiredNetwork#remove(IWiredNode)
|
* @see WiredNetwork#remove(WiredNode)
|
||||||
*/
|
*/
|
||||||
default boolean remove()
|
default boolean remove() {
|
||||||
{
|
return getNetwork().remove(this);
|
||||||
return getNetwork().remove( this );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -99,10 +92,9 @@ public interface IWiredNode extends IPacketNetwork
|
|||||||
* that your network element owns.
|
* that your network element owns.
|
||||||
*
|
*
|
||||||
* @param peripherals The new peripherals for this node.
|
* @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 )
|
default void updatePeripherals(Map<String, IPeripheral> peripherals) {
|
||||||
{
|
getNetwork().updatePeripherals(this, peripherals);
|
||||||
getNetwork().updatePeripherals( this, peripherals );
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -5,18 +5,16 @@
|
|||||||
*/
|
*/
|
||||||
package dan200.computercraft.api.network.wired;
|
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>
|
* <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.
|
* to send the packet from.
|
||||||
*/
|
*/
|
||||||
public interface IWiredSender extends IPacketSender
|
public interface WiredSender extends PacketSender {
|
||||||
{
|
|
||||||
/**
|
/**
|
||||||
* The node in the network representing this object.
|
* The node in the network representing this object.
|
||||||
* <p>
|
* <p>
|
||||||
@@ -25,6 +23,5 @@ public interface IWiredSender extends IPacketSender
|
|||||||
*
|
*
|
||||||
* @return The node for this element.
|
* @return The node for this element.
|
||||||
*/
|
*/
|
||||||
@Nonnull
|
WiredNode getNode();
|
||||||
IWiredNode getNode();
|
|
||||||
}
|
}
|
@@ -5,53 +5,43 @@
|
|||||||
*/
|
*/
|
||||||
package dan200.computercraft.api.pocket;
|
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.resources.ResourceLocation;
|
||||||
import net.minecraft.world.item.ItemStack;
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A base class for {@link IPocketUpgrade}s.
|
* A base class for {@link IPocketUpgrade}s.
|
||||||
* <p>
|
* <p>
|
||||||
* One does not have to use this, but it does provide a convenient template.
|
* One does not have to use this, but it does provide a convenient template.
|
||||||
*/
|
*/
|
||||||
public abstract class AbstractPocketUpgrade implements IPocketUpgrade
|
public abstract class AbstractPocketUpgrade implements IPocketUpgrade {
|
||||||
{
|
|
||||||
private final ResourceLocation id;
|
private final ResourceLocation id;
|
||||||
private final String adjective;
|
private final String adjective;
|
||||||
private final ItemStack stack;
|
private final ItemStack stack;
|
||||||
|
|
||||||
protected AbstractPocketUpgrade( ResourceLocation id, String adjective, ItemStack stack )
|
protected AbstractPocketUpgrade(ResourceLocation id, String adjective, ItemStack stack) {
|
||||||
{
|
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.adjective = adjective;
|
this.adjective = adjective;
|
||||||
this.stack = stack;
|
this.stack = stack;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected AbstractPocketUpgrade( ResourceLocation id, ItemStack stack )
|
protected AbstractPocketUpgrade(ResourceLocation id, ItemStack stack) {
|
||||||
{
|
this(id, UpgradeBase.getDefaultAdjective(id), stack);
|
||||||
this( id, IUpgradeBase.getDefaultAdjective( id ), stack );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
|
||||||
@Override
|
@Override
|
||||||
public final ResourceLocation getUpgradeID()
|
public final ResourceLocation getUpgradeID() {
|
||||||
{
|
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
|
||||||
@Override
|
@Override
|
||||||
public final String getUnlocalisedAdjective()
|
public final String getUnlocalisedAdjective() {
|
||||||
{
|
|
||||||
return adjective;
|
return adjective;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
|
||||||
@Override
|
@Override
|
||||||
public final ItemStack getCraftingItem()
|
public final ItemStack getCraftingItem() {
|
||||||
{
|
|
||||||
return stack;
|
return stack;
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -10,15 +10,13 @@ import net.minecraft.nbt.CompoundTag;
|
|||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
import net.minecraft.world.entity.Entity;
|
import net.minecraft.world.entity.Entity;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wrapper class for pocket computers.
|
* Wrapper class for pocket computers.
|
||||||
*/
|
*/
|
||||||
public interface IPocketAccess
|
public interface IPocketAccess {
|
||||||
{
|
|
||||||
/**
|
/**
|
||||||
* Gets the entity holding this item.
|
* Gets the entity holding this item.
|
||||||
* <p>
|
* <p>
|
||||||
@@ -45,7 +43,7 @@ public interface IPocketAccess
|
|||||||
* {@code 0x000000} and {@code 0xFFFFFF} or -1 to reset to the default colour.
|
* {@code 0x000000} and {@code 0xFFFFFF} or -1 to reset to the default colour.
|
||||||
* @see #getColour()
|
* @see #getColour()
|
||||||
*/
|
*/
|
||||||
void setColour( int colour );
|
void setColour(int colour);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the colour of this pocket computer's light as a RGB number.
|
* Get the colour of this pocket computer's light as a RGB number.
|
||||||
@@ -63,7 +61,7 @@ public interface IPocketAccess
|
|||||||
* {@code 0x000000} and {@code 0xFFFFFF} or -1 to reset to the default colour.
|
* {@code 0x000000} and {@code 0xFFFFFF} or -1 to reset to the default colour.
|
||||||
* @see #getLight()
|
* @see #getLight()
|
||||||
*/
|
*/
|
||||||
void setLight( int colour );
|
void setLight(int colour);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the upgrade-specific NBT.
|
* Get the upgrade-specific NBT.
|
||||||
@@ -73,7 +71,6 @@ public interface IPocketAccess
|
|||||||
* @return The upgrade's NBT.
|
* @return The upgrade's NBT.
|
||||||
* @see #updateUpgradeNBTData()
|
* @see #updateUpgradeNBTData()
|
||||||
*/
|
*/
|
||||||
@Nonnull
|
|
||||||
CompoundTag getUpgradeNBTData();
|
CompoundTag getUpgradeNBTData();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -93,6 +90,5 @@ public interface IPocketAccess
|
|||||||
*
|
*
|
||||||
* @return A collection of all upgrade names.
|
* @return A collection of all upgrade names.
|
||||||
*/
|
*/
|
||||||
@Nonnull
|
|
||||||
Map<ResourceLocation, IPeripheral> getUpgrades();
|
Map<ResourceLocation, IPeripheral> getUpgrades();
|
||||||
}
|
}
|
@@ -6,10 +6,9 @@
|
|||||||
package dan200.computercraft.api.pocket;
|
package dan200.computercraft.api.pocket;
|
||||||
|
|
||||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||||
import dan200.computercraft.api.upgrades.IUpgradeBase;
|
import dan200.computercraft.api.upgrades.UpgradeBase;
|
||||||
import net.minecraft.world.level.Level;
|
import net.minecraft.world.level.Level;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -24,8 +23,7 @@ import javax.annotation.Nullable;
|
|||||||
*
|
*
|
||||||
* @see PocketUpgradeSerialiser For how to register a pocket computer upgrade.
|
* @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.
|
* Creates a peripheral for the pocket computer.
|
||||||
* <p>
|
* <p>
|
||||||
@@ -38,7 +36,7 @@ public interface IPocketUpgrade extends IUpgradeBase
|
|||||||
* @see #update(IPocketAccess, IPeripheral)
|
* @see #update(IPocketAccess, IPeripheral)
|
||||||
*/
|
*/
|
||||||
@Nullable
|
@Nullable
|
||||||
IPeripheral createPeripheral( @Nonnull IPocketAccess access );
|
IPeripheral createPeripheral(IPocketAccess access);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when the pocket computer item stack updates.
|
* Called when the pocket computer item stack updates.
|
||||||
@@ -47,8 +45,7 @@ public interface IPocketUpgrade extends IUpgradeBase
|
|||||||
* @param peripheral The peripheral for this upgrade.
|
* @param peripheral The peripheral for this upgrade.
|
||||||
* @see #createPeripheral(IPocketAccess)
|
* @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.
|
* access the GUI.
|
||||||
* @see #createPeripheral(IPocketAccess)
|
* @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;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -7,24 +7,21 @@ package dan200.computercraft.api.pocket;
|
|||||||
|
|
||||||
import dan200.computercraft.api.upgrades.UpgradeDataProvider;
|
import dan200.computercraft.api.upgrades.UpgradeDataProvider;
|
||||||
import net.minecraft.data.DataGenerator;
|
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;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A data provider to generate pocket computer upgrades.
|
* A data provider to generate pocket computer upgrades.
|
||||||
* <p>
|
* <p>
|
||||||
* This should be subclassed and registered to a {@link DataGenerator}. Override the {@link #addUpgrades(Consumer)} function,
|
* This should be subclassed and registered to a {@link DataGenerator.PackGenerator}. Override the
|
||||||
* construct each upgrade, and pass them off to the provided consumer to generate them.
|
* {@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
|
* @see PocketUpgradeSerialiser
|
||||||
*/
|
*/
|
||||||
public abstract class PocketUpgradeDataProvider extends UpgradeDataProvider<IPocketUpgrade, PocketUpgradeSerialiser<?>>
|
public abstract class PocketUpgradeDataProvider extends UpgradeDataProvider<IPocketUpgrade, PocketUpgradeSerialiser<?>> {
|
||||||
{
|
public PocketUpgradeDataProvider(PackOutput output) {
|
||||||
public PocketUpgradeDataProvider( @Nonnull DataGenerator generator )
|
super(output, "Pocket Computer Upgrades", "computercraft/pocket_upgrades", PocketUpgradeSerialiser.REGISTRY_ID);
|
||||||
{
|
|
||||||
super( generator, "Pocket Computer Upgrades", "computercraft/pocket_upgrades", PocketUpgradeSerialiser.REGISTRY_ID );
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -5,21 +5,17 @@
|
|||||||
*/
|
*/
|
||||||
package dan200.computercraft.api.pocket;
|
package dan200.computercraft.api.pocket;
|
||||||
|
|
||||||
import dan200.computercraft.ComputerCraft;
|
import dan200.computercraft.api.ComputerCraftAPI;
|
||||||
import dan200.computercraft.api.upgrades.IUpgradeBase;
|
import dan200.computercraft.api.upgrades.UpgradeBase;
|
||||||
import dan200.computercraft.api.upgrades.UpgradeSerialiser;
|
import dan200.computercraft.api.upgrades.UpgradeSerialiser;
|
||||||
import dan200.computercraft.internal.upgrades.SerialiserWithCraftingItem;
|
import dan200.computercraft.impl.upgrades.SerialiserWithCraftingItem;
|
||||||
import dan200.computercraft.internal.upgrades.SimpleSerialiser;
|
import dan200.computercraft.impl.upgrades.SimpleSerialiser;
|
||||||
import net.minecraft.core.Registry;
|
import net.minecraft.core.Registry;
|
||||||
import net.minecraft.resources.ResourceKey;
|
import net.minecraft.resources.ResourceKey;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
import net.minecraft.world.item.ItemStack;
|
import net.minecraft.world.item.ItemStack;
|
||||||
import net.minecraft.world.item.crafting.SimpleRecipeSerializer;
|
import net.minecraft.world.item.crafting.SimpleCraftingRecipeSerializer;
|
||||||
import net.minecraftforge.registries.DeferredRegister;
|
|
||||||
import net.minecraftforge.registries.IForgeRegistry;
|
|
||||||
import net.minecraftforge.registries.RegistryManager;
|
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
|
||||||
import java.util.function.BiFunction;
|
import java.util.function.BiFunction;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
@@ -33,32 +29,15 @@ import java.util.function.Function;
|
|||||||
* @see IPocketUpgrade
|
* @see IPocketUpgrade
|
||||||
* @see PocketUpgradeDataProvider
|
* @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.
|
* 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.
|
* Create an upgrade serialiser for a simple upgrade. This is similar to a {@link SimpleCraftingRecipeSerializer},
|
||||||
*
|
* but for upgrades.
|
||||||
* @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.
|
|
||||||
* <p>
|
* <p>
|
||||||
* If you might want to vary the item, it's suggested you use {@link #simpleWithCustomItem(BiFunction)} instead.
|
* 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.
|
* @param <T> The type of the generated upgrade.
|
||||||
* @return The serialiser for this upgrade
|
* @return The serialiser for this upgrade
|
||||||
*/
|
*/
|
||||||
@Nonnull
|
static <T extends IPocketUpgrade> PocketUpgradeSerialiser<T> simple(Function<ResourceLocation, T> factory) {
|
||||||
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) {
|
||||||
final class Impl extends SimpleSerialiser<T> implements PocketUpgradeSerialiser<T>
|
super(constructor);
|
||||||
{
|
|
||||||
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.
|
* 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
|
* @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.
|
* @param <T> The type of the generated upgrade.
|
||||||
* @return The serialiser for this upgrade.
|
* @return The serialiser for this upgrade.
|
||||||
* @see #simple(Function) For upgrades whose crafting stack should not vary.
|
* @see #simple(Function) For upgrades whose crafting stack should not vary.
|
||||||
*/
|
*/
|
||||||
@Nonnull
|
static <T extends IPocketUpgrade> PocketUpgradeSerialiser<T> simpleWithCustomItem(BiFunction<ResourceLocation, ItemStack, T> factory) {
|
||||||
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) {
|
||||||
final class Impl extends SerialiserWithCraftingItem<T> implements PocketUpgradeSerialiser<T>
|
super(factory);
|
||||||
{
|
|
||||||
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;
|
package dan200.computercraft.api.redstone;
|
||||||
|
|
||||||
|
import dan200.computercraft.api.ComputerCraftAPI;
|
||||||
import net.minecraft.core.BlockPos;
|
import net.minecraft.core.BlockPos;
|
||||||
import net.minecraft.core.Direction;
|
import net.minecraft.core.Direction;
|
||||||
import net.minecraft.world.level.Level;
|
import net.minecraft.world.level.Level;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This interface is used to provide bundled redstone output for blocks.
|
* This interface is used to provide bundled redstone output for blocks.
|
||||||
*
|
*
|
||||||
* @see dan200.computercraft.api.ComputerCraftAPI#registerBundledRedstoneProvider(IBundledRedstoneProvider)
|
* @see ComputerCraftAPI#registerBundledRedstoneProvider(BundledRedstoneProvider)
|
||||||
*/
|
*/
|
||||||
@FunctionalInterface
|
@FunctionalInterface
|
||||||
public interface IBundledRedstoneProvider
|
public interface BundledRedstoneProvider {
|
||||||
{
|
|
||||||
/**
|
/**
|
||||||
* Produce an bundled redstone output from a block location.
|
* 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.
|
* @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
|
* @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.
|
* 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;
|
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.resources.ResourceLocation;
|
||||||
import net.minecraft.world.item.ItemStack;
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A base class for {@link ITurtleUpgrade}s.
|
* A base class for {@link ITurtleUpgrade}s.
|
||||||
* <p>
|
* <p>
|
||||||
* One does not have to use this, but it does provide a convenient template.
|
* One does not have to use this, but it does provide a convenient template.
|
||||||
*/
|
*/
|
||||||
public abstract class AbstractTurtleUpgrade implements ITurtleUpgrade
|
public abstract class AbstractTurtleUpgrade implements ITurtleUpgrade {
|
||||||
{
|
|
||||||
private final ResourceLocation id;
|
private final ResourceLocation id;
|
||||||
private final TurtleUpgradeType type;
|
private final TurtleUpgradeType type;
|
||||||
private final String adjective;
|
private final String adjective;
|
||||||
private final ItemStack stack;
|
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.id = id;
|
||||||
this.type = type;
|
this.type = type;
|
||||||
this.adjective = adjective;
|
this.adjective = adjective;
|
||||||
this.stack = stack;
|
this.stack = stack;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected AbstractTurtleUpgrade( ResourceLocation id, TurtleUpgradeType type, ItemStack stack )
|
protected AbstractTurtleUpgrade(ResourceLocation id, TurtleUpgradeType type, ItemStack stack) {
|
||||||
{
|
this(id, type, UpgradeBase.getDefaultAdjective(id), stack);
|
||||||
this( id, type, IUpgradeBase.getDefaultAdjective( id ), stack );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
|
||||||
@Override
|
@Override
|
||||||
public final ResourceLocation getUpgradeID()
|
public final ResourceLocation getUpgradeID() {
|
||||||
{
|
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
|
||||||
@Override
|
@Override
|
||||||
public final String getUnlocalisedAdjective()
|
public final String getUnlocalisedAdjective() {
|
||||||
{
|
|
||||||
return adjective;
|
return adjective;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
|
||||||
@Override
|
@Override
|
||||||
public final TurtleUpgradeType getType()
|
public final TurtleUpgradeType getType() {
|
||||||
{
|
|
||||||
return type;
|
return type;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
|
||||||
@Override
|
@Override
|
||||||
public final ItemStack getCraftingItem()
|
public final ItemStack getCraftingItem() {
|
||||||
{
|
|
||||||
return stack;
|
return stack;
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -15,9 +15,7 @@ import net.minecraft.nbt.CompoundTag;
|
|||||||
import net.minecraft.world.Container;
|
import net.minecraft.world.Container;
|
||||||
import net.minecraft.world.level.Level;
|
import net.minecraft.world.level.Level;
|
||||||
import net.minecraft.world.phys.Vec3;
|
import net.minecraft.world.phys.Vec3;
|
||||||
import net.minecraftforge.items.IItemHandlerModifiable;
|
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -26,14 +24,12 @@ import javax.annotation.Nullable;
|
|||||||
* This should not be implemented by your classes. Do not interact with turtles except via this interface and
|
* This should not be implemented by your classes. Do not interact with turtles except via this interface and
|
||||||
* {@link ITurtleUpgrade}.
|
* {@link ITurtleUpgrade}.
|
||||||
*/
|
*/
|
||||||
public interface ITurtleAccess
|
public interface ITurtleAccess {
|
||||||
{
|
|
||||||
/**
|
/**
|
||||||
* Returns the world in which the turtle resides.
|
* Returns the world in which the turtle resides.
|
||||||
*
|
*
|
||||||
* @return the world in which the turtle resides.
|
* @return the world in which the turtle resides.
|
||||||
*/
|
*/
|
||||||
@Nonnull
|
|
||||||
Level getLevel();
|
Level getLevel();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -41,13 +37,12 @@ public interface ITurtleAccess
|
|||||||
*
|
*
|
||||||
* @return a vector containing the integer co-ordinates at which the turtle resides.
|
* @return a vector containing the integer co-ordinates at which the turtle resides.
|
||||||
*/
|
*/
|
||||||
@Nonnull
|
|
||||||
BlockPos getPosition();
|
BlockPos getPosition();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine if this turtle has been removed.
|
* Determine if this turtle has been removed.
|
||||||
* <p>
|
* <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
|
* 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.
|
* trying to interact with it again.
|
||||||
* <p>
|
* <p>
|
||||||
@@ -70,7 +65,7 @@ public interface ITurtleAccess
|
|||||||
* was cancelled.
|
* was cancelled.
|
||||||
* @throws UnsupportedOperationException When attempting to teleport on the client side.
|
* @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.
|
* Returns a vector containing the floating point co-ordinates at which the turtle is rendered.
|
||||||
@@ -80,8 +75,7 @@ public interface ITurtleAccess
|
|||||||
* @return A vector containing the floating point co-ordinates at which the turtle resides.
|
* @return A vector containing the floating point co-ordinates at which the turtle resides.
|
||||||
* @see #getVisualYaw(float)
|
* @see #getVisualYaw(float)
|
||||||
*/
|
*/
|
||||||
@Nonnull
|
Vec3 getVisualPosition(float f);
|
||||||
Vec3 getVisualPosition( float f );
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the yaw the turtle is facing when it is rendered.
|
* Returns the yaw the turtle is facing when it is rendered.
|
||||||
@@ -90,7 +84,7 @@ public interface ITurtleAccess
|
|||||||
* @return The yaw the turtle is facing.
|
* @return The yaw the turtle is facing.
|
||||||
* @see #getVisualPosition(float)
|
* @see #getVisualPosition(float)
|
||||||
*/
|
*/
|
||||||
float getVisualYaw( float f );
|
float getVisualYaw(float f);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the world direction the turtle is currently facing.
|
* Returns the world direction the turtle is currently facing.
|
||||||
@@ -98,7 +92,6 @@ public interface ITurtleAccess
|
|||||||
* @return The world direction the turtle is currently facing.
|
* @return The world direction the turtle is currently facing.
|
||||||
* @see #setDirection(Direction)
|
* @see #setDirection(Direction)
|
||||||
*/
|
*/
|
||||||
@Nonnull
|
|
||||||
Direction getDirection();
|
Direction getDirection();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -108,7 +101,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).
|
* @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()
|
* @see #getDirection()
|
||||||
*/
|
*/
|
||||||
void setDirection( @Nonnull Direction dir );
|
void setDirection(Direction dir);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the currently selected slot in the turtle's inventory.
|
* Get the currently selected slot in the turtle's inventory.
|
||||||
@@ -128,7 +121,7 @@ public interface ITurtleAccess
|
|||||||
* @see #getInventory()
|
* @see #getInventory()
|
||||||
* @see #getSelectedSlot()
|
* @see #getSelectedSlot()
|
||||||
*/
|
*/
|
||||||
void setSelectedSlot( int slot );
|
void setSelectedSlot(int slot);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the colour of the turtle to a RGB number.
|
* Set the colour of the turtle to a RGB number.
|
||||||
@@ -137,7 +130,7 @@ public interface ITurtleAccess
|
|||||||
* and {@code 0xFFFFFF} or -1 to reset to the default colour.
|
* and {@code 0xFFFFFF} or -1 to reset to the default colour.
|
||||||
* @see #getColour()
|
* @see #getColour()
|
||||||
*/
|
*/
|
||||||
void setColour( int colour );
|
void setColour(int colour);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the colour of this turtle as a RGB number.
|
* Get the colour of this turtle as a RGB number.
|
||||||
@@ -162,25 +155,9 @@ public interface ITurtleAccess
|
|||||||
* Note: this inventory should only be accessed and modified on the server thread.
|
* Note: this inventory should only be accessed and modified on the server thread.
|
||||||
*
|
*
|
||||||
* @return This turtle's inventory
|
* @return This turtle's inventory
|
||||||
* @see #getItemHandler()
|
|
||||||
*/
|
*/
|
||||||
@Nonnull
|
|
||||||
Container getInventory();
|
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.
|
* Determine whether this turtle will require fuel when performing actions.
|
||||||
*
|
*
|
||||||
@@ -209,7 +186,7 @@ public interface ITurtleAccess
|
|||||||
* @see #addFuel(int)
|
* @see #addFuel(int)
|
||||||
* @see #consumeFuel(int)
|
* @see #consumeFuel(int)
|
||||||
*/
|
*/
|
||||||
void setFuelLevel( int fuel );
|
void setFuelLevel(int fuel);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the maximum amount of fuel a turtle can hold.
|
* Get the maximum amount of fuel a turtle can hold.
|
||||||
@@ -226,7 +203,7 @@ public interface ITurtleAccess
|
|||||||
* greater than the current fuel level of the turtle. No fuel will be consumed if {@code false} is returned.
|
* 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.
|
* @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.
|
* Increase the turtle's fuel level by the given amount.
|
||||||
@@ -234,7 +211,7 @@ public interface ITurtleAccess
|
|||||||
* @param fuel The amount to refuel with.
|
* @param fuel The amount to refuel with.
|
||||||
* @throws UnsupportedOperationException When attempting to refuel on the client side.
|
* @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
|
* Adds a custom command to the turtles command queue. Unlike peripheral methods, these custom commands will be executed
|
||||||
@@ -247,11 +224,10 @@ public interface ITurtleAccess
|
|||||||
* @return The objects the command returned when executed. you should probably return these to the player
|
* @return The objects the command returned when executed. you should probably return these to the player
|
||||||
* unchanged if called from a peripheral method.
|
* unchanged if called from a peripheral method.
|
||||||
* @throws UnsupportedOperationException When attempting to execute a command on the client side.
|
* @throws UnsupportedOperationException When attempting to execute a command on the client side.
|
||||||
* @see ITurtleCommand
|
* @see TurtleCommand
|
||||||
* @see MethodResult#pullEvent(String, ILuaCallback)
|
* @see MethodResult#pullEvent(String, ILuaCallback)
|
||||||
*/
|
*/
|
||||||
@Nonnull
|
MethodResult executeCommand(TurtleCommand command);
|
||||||
MethodResult executeCommand( @Nonnull ITurtleCommand command );
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Start playing a specific animation. This will prevent other turtle commands from executing until
|
* Start playing a specific animation. This will prevent other turtle commands from executing until
|
||||||
@@ -261,7 +237,7 @@ public interface ITurtleAccess
|
|||||||
* @throws UnsupportedOperationException When attempting to execute play an animation on the client side.
|
* @throws UnsupportedOperationException When attempting to execute play an animation on the client side.
|
||||||
* @see TurtleAnimation
|
* @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.
|
* Returns the turtle on the specified side of the turtle, if there is one.
|
||||||
@@ -271,7 +247,7 @@ public interface ITurtleAccess
|
|||||||
* @see #setUpgrade(TurtleSide, ITurtleUpgrade)
|
* @see #setUpgrade(TurtleSide, ITurtleUpgrade)
|
||||||
*/
|
*/
|
||||||
@Nullable
|
@Nullable
|
||||||
ITurtleUpgrade getUpgrade( @Nonnull TurtleSide side );
|
ITurtleUpgrade getUpgrade(TurtleSide side);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the upgrade for a given side, resetting peripherals and clearing upgrade specific data.
|
* Set the upgrade for a given side, resetting peripherals and clearing upgrade specific data.
|
||||||
@@ -280,7 +256,7 @@ public interface ITurtleAccess
|
|||||||
* @param upgrade The upgrade to set, may be {@code null} to clear.
|
* @param upgrade The upgrade to set, may be {@code null} to clear.
|
||||||
* @see #getUpgrade(TurtleSide)
|
* @see #getUpgrade(TurtleSide)
|
||||||
*/
|
*/
|
||||||
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.
|
* Returns the peripheral created by the upgrade on the specified side of the turtle, if there is one.
|
||||||
@@ -289,7 +265,7 @@ public interface ITurtleAccess
|
|||||||
* @return The peripheral created by the upgrade on the specified side of the turtle, {@code null} if none exists.
|
* @return The peripheral created by the upgrade on the specified side of the turtle, {@code null} if none exists.
|
||||||
*/
|
*/
|
||||||
@Nullable
|
@Nullable
|
||||||
IPeripheral getPeripheral( @Nonnull TurtleSide side );
|
IPeripheral getPeripheral(TurtleSide side);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get an upgrade-specific NBT compound, which can be used to store arbitrary data.
|
* Get an upgrade-specific NBT compound, which can be used to store arbitrary data.
|
||||||
@@ -301,8 +277,7 @@ public interface ITurtleAccess
|
|||||||
* @return The upgrade-specific data.
|
* @return The upgrade-specific data.
|
||||||
* @see #updateUpgradeNBTData(TurtleSide)
|
* @see #updateUpgradeNBTData(TurtleSide)
|
||||||
*/
|
*/
|
||||||
@Nonnull
|
CompoundTag getUpgradeNBTData(TurtleSide side);
|
||||||
CompoundTag getUpgradeNBTData( @Nullable TurtleSide side );
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mark the upgrade-specific data as dirty on a specific side. This is required for the data to be synced to the
|
* Mark the upgrade-specific data as dirty on a specific side. This is required for the data to be synced to the
|
||||||
@@ -311,5 +286,5 @@ public interface ITurtleAccess
|
|||||||
* @param side The side to mark dirty.
|
* @param side The side to mark dirty.
|
||||||
* @see #updateUpgradeNBTData(TurtleSide)
|
* @see #updateUpgradeNBTData(TurtleSide)
|
||||||
*/
|
*/
|
||||||
void updateUpgradeNBTData( @Nonnull TurtleSide side );
|
void updateUpgradeNBTData(TurtleSide side);
|
||||||
}
|
}
|
@@ -6,12 +6,9 @@
|
|||||||
package dan200.computercraft.api.turtle;
|
package dan200.computercraft.api.turtle;
|
||||||
|
|
||||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
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.minecraft.core.Direction;
|
||||||
import net.minecraftforge.event.entity.player.AttackEntityEvent;
|
|
||||||
import net.minecraftforge.event.level.BlockEvent;
|
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -27,15 +24,13 @@ import javax.annotation.Nullable;
|
|||||||
*
|
*
|
||||||
* @see TurtleUpgradeSerialiser For how to register a turtle upgrade.
|
* @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 whether this turtle adds a tool or a peripheral to the turtle.
|
||||||
*
|
*
|
||||||
* @return The type of upgrade this is.
|
* @return The type of upgrade this is.
|
||||||
* @see TurtleUpgradeType for the differences between them.
|
* @see TurtleUpgradeType for the differences between them.
|
||||||
*/
|
*/
|
||||||
@Nonnull
|
|
||||||
TurtleUpgradeType getType();
|
TurtleUpgradeType getType();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -51,8 +46,7 @@ public interface ITurtleUpgrade extends IUpgradeBase
|
|||||||
* and this method is not expected to be called.
|
* and this method is not expected to be called.
|
||||||
*/
|
*/
|
||||||
@Nullable
|
@Nullable
|
||||||
default IPeripheral createPeripheral( @Nonnull ITurtleAccess turtle, @Nonnull TurtleSide side )
|
default IPeripheral createPeripheral(ITurtleAccess turtle, TurtleSide side) {
|
||||||
{
|
|
||||||
return null;
|
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
|
* 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.
|
* by the turtle, and the tool is required to do some work.
|
||||||
* <p>
|
* <p>
|
||||||
* Conforming implementations should fire {@link BlockEvent.BreakEvent} for digging {@link AttackEntityEvent}
|
* Conforming implementations should fire loader-specific events when using the tool, for instance Forge's
|
||||||
* for attacking.
|
* {@code AttackEntityEvent}.
|
||||||
*
|
*
|
||||||
* @param turtle Access to the turtle that the tool resides on.
|
* @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.
|
* @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
|
* a swinging animation. You may return {@code null} if this turtle is a Peripheral and this method is not expected
|
||||||
* to be called.
|
* to be called.
|
||||||
*/
|
*/
|
||||||
@Nonnull
|
default TurtleCommandResult useTool(ITurtleAccess turtle, TurtleSide side, TurtleVerb verb, Direction direction) {
|
||||||
default TurtleCommandResult useTool( @Nonnull ITurtleAccess turtle, @Nonnull TurtleSide side, @Nonnull TurtleVerb verb, @Nonnull Direction direction )
|
|
||||||
{
|
|
||||||
return TurtleCommandResult.failure();
|
return TurtleCommandResult.failure();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -86,7 +78,6 @@ public interface ITurtleUpgrade extends IUpgradeBase
|
|||||||
* @param turtle Access to the turtle that the upgrade resides on.
|
* @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.
|
* @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)
|
* @see ITurtleAccess#playAnimation(TurtleAnimation)
|
||||||
*/
|
*/
|
||||||
public enum TurtleAnimation
|
public enum TurtleAnimation {
|
||||||
{
|
|
||||||
/**
|
/**
|
||||||
* An animation which does nothing. This takes no time to complete.
|
* An animation which does nothing. This takes no time to complete.
|
||||||
*
|
*
|
@@ -5,16 +5,14 @@
|
|||||||
*/
|
*/
|
||||||
package dan200.computercraft.api.turtle;
|
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
|
@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.
|
* Will be called by the turtle on the main thread when it is time to execute the custom command.
|
||||||
* <p>
|
* <p>
|
||||||
@@ -23,11 +21,10 @@ public interface ITurtleCommand
|
|||||||
*
|
*
|
||||||
* @param turtle Access to the turtle for whom the command was issued.
|
* @param turtle Access to the turtle for whom the command was issued.
|
||||||
* @return A result, indicating whether this action succeeded or not.
|
* @return A result, indicating whether this action succeeded or not.
|
||||||
* @see ITurtleAccess#executeCommand(ITurtleCommand)
|
* @see ITurtleAccess#executeCommand(TurtleCommand)
|
||||||
* @see TurtleCommandResult#success()
|
* @see TurtleCommandResult#success()
|
||||||
* @see TurtleCommandResult#failure(String)
|
* @see TurtleCommandResult#failure(String)
|
||||||
* @see TurtleCommandResult
|
* @see TurtleCommandResult
|
||||||
*/
|
*/
|
||||||
@Nonnull
|
TurtleCommandResult execute(ITurtleAccess turtle);
|
||||||
TurtleCommandResult execute( @Nonnull ITurtleAccess turtle );
|
|
||||||
}
|
}
|
@@ -7,28 +7,24 @@ package dan200.computercraft.api.turtle;
|
|||||||
|
|
||||||
import net.minecraft.core.Direction;
|
import net.minecraft.core.Direction;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used to indicate the result of executing a turtle command.
|
* 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)
|
* @see ITurtleUpgrade#useTool(ITurtleAccess, TurtleSide, TurtleVerb, Direction)
|
||||||
*/
|
*/
|
||||||
public final class TurtleCommandResult
|
public final class TurtleCommandResult {
|
||||||
{
|
private static final TurtleCommandResult EMPTY_SUCCESS = new TurtleCommandResult(true, null, null);
|
||||||
private static final TurtleCommandResult EMPTY_SUCCESS = new TurtleCommandResult( true, null, null );
|
private static final TurtleCommandResult EMPTY_FAILURE = new TurtleCommandResult(false, null, null);
|
||||||
private static final TurtleCommandResult EMPTY_FAILURE = new TurtleCommandResult( false, null, null );
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a successful command result with no result.
|
* Create a successful command result with no result.
|
||||||
*
|
*
|
||||||
* @return A successful command result with no values.
|
* @return A successful command result with no values.
|
||||||
*/
|
*/
|
||||||
@Nonnull
|
public static TurtleCommandResult success() {
|
||||||
public static TurtleCommandResult success()
|
|
||||||
{
|
|
||||||
return EMPTY_SUCCESS;
|
return EMPTY_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -38,11 +34,9 @@ public final class TurtleCommandResult
|
|||||||
* @param results The results of executing this command.
|
* @param results The results of executing this command.
|
||||||
* @return A successful command result with the given values.
|
* @return A successful command result with the given values.
|
||||||
*/
|
*/
|
||||||
@Nonnull
|
public static TurtleCommandResult success(@Nullable Object[] results) {
|
||||||
public static TurtleCommandResult success( @Nullable Object[] results )
|
if (results == null || results.length == 0) return EMPTY_SUCCESS;
|
||||||
{
|
return new TurtleCommandResult(true, null, 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.
|
* @return A failed command result with no message.
|
||||||
*/
|
*/
|
||||||
@Nonnull
|
public static TurtleCommandResult failure() {
|
||||||
public static TurtleCommandResult failure()
|
|
||||||
{
|
|
||||||
return EMPTY_FAILURE;
|
return EMPTY_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -62,19 +54,16 @@ public final class TurtleCommandResult
|
|||||||
* @param errorMessage The error message to provide.
|
* @param errorMessage The error message to provide.
|
||||||
* @return A failed command result with a message.
|
* @return A failed command result with a message.
|
||||||
*/
|
*/
|
||||||
@Nonnull
|
public static TurtleCommandResult failure(@Nullable String errorMessage) {
|
||||||
public static TurtleCommandResult failure( @Nullable String errorMessage )
|
if (errorMessage == null) return EMPTY_FAILURE;
|
||||||
{
|
return new TurtleCommandResult(false, errorMessage, null);
|
||||||
if( errorMessage == null ) return EMPTY_FAILURE;
|
|
||||||
return new TurtleCommandResult( false, errorMessage, null );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private final boolean success;
|
private final boolean success;
|
||||||
private final String errorMessage;
|
private final @Nullable String errorMessage;
|
||||||
private final Object[] results;
|
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.success = success;
|
||||||
this.errorMessage = errorMessage;
|
this.errorMessage = errorMessage;
|
||||||
this.results = results;
|
this.results = results;
|
||||||
@@ -85,8 +74,7 @@ public final class TurtleCommandResult
|
|||||||
*
|
*
|
||||||
* @return If the command was successful.
|
* @return If the command was successful.
|
||||||
*/
|
*/
|
||||||
public boolean isSuccess()
|
public boolean isSuccess() {
|
||||||
{
|
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -96,8 +84,7 @@ public final class TurtleCommandResult
|
|||||||
* @return The command's error message, or {@code null} if it was a success.
|
* @return The command's error message, or {@code null} if it was a success.
|
||||||
*/
|
*/
|
||||||
@Nullable
|
@Nullable
|
||||||
public String getErrorMessage()
|
public String getErrorMessage() {
|
||||||
{
|
|
||||||
return errorMessage;
|
return errorMessage;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -107,8 +94,7 @@ public final class TurtleCommandResult
|
|||||||
* @return The command's result, or {@code null} if it was a failure.
|
* @return The command's result, or {@code null} if it was a failure.
|
||||||
*/
|
*/
|
||||||
@Nullable
|
@Nullable
|
||||||
public Object[] getResults()
|
public Object[] getResults() {
|
||||||
{
|
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -16,8 +16,7 @@ import java.util.OptionalInt;
|
|||||||
*
|
*
|
||||||
* @see ComputerCraftAPI#registerRefuelHandler(TurtleRefuelHandler)
|
* @see ComputerCraftAPI#registerRefuelHandler(TurtleRefuelHandler)
|
||||||
*/
|
*/
|
||||||
public interface TurtleRefuelHandler
|
public interface TurtleRefuelHandler {
|
||||||
{
|
|
||||||
/**
|
/**
|
||||||
* Refuel a turtle using an item.
|
* 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.
|
* {@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.
|
* @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.
|
* 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).
|
* 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.ComputerCraftAPI;
|
||||||
import dan200.computercraft.api.ComputerCraftTags;
|
import dan200.computercraft.api.ComputerCraftTags;
|
||||||
import dan200.computercraft.api.upgrades.UpgradeDataProvider;
|
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.DataGenerator;
|
||||||
|
import net.minecraft.data.PackOutput;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
import net.minecraft.tags.TagKey;
|
import net.minecraft.tags.TagKey;
|
||||||
import net.minecraft.world.entity.ai.attributes.Attributes;
|
import net.minecraft.world.entity.ai.attributes.Attributes;
|
||||||
import net.minecraft.world.item.Item;
|
import net.minecraft.world.item.Item;
|
||||||
import net.minecraft.world.level.block.Block;
|
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;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A data provider to generate turtle upgrades.
|
* A data provider to generate turtle upgrades.
|
||||||
* <p>
|
* <p>
|
||||||
* This should be subclassed and registered to a {@link DataGenerator}. Override the {@link #addUpgrades(Consumer)} function,
|
* This should be subclassed and registered to a {@link DataGenerator.PackGenerator}. Override the
|
||||||
* construct each upgrade, and pass them off to the provided consumer to generate them.
|
* {@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
|
* @see TurtleUpgradeSerialiser
|
||||||
*/
|
*/
|
||||||
public abstract class TurtleUpgradeDataProvider extends UpgradeDataProvider<ITurtleUpgrade, TurtleUpgradeSerialiser<?>>
|
public abstract class TurtleUpgradeDataProvider extends UpgradeDataProvider<ITurtleUpgrade, TurtleUpgradeSerialiser<?>> {
|
||||||
{
|
private static final ResourceLocation TOOL_ID = new ResourceLocation(ComputerCraftAPI.MOD_ID, "tool");
|
||||||
private static final ResourceLocation TOOL_ID = new ResourceLocation( ComputerCraftAPI.MOD_ID, "tool" );
|
|
||||||
|
|
||||||
public TurtleUpgradeDataProvider( DataGenerator generator )
|
public TurtleUpgradeDataProvider(PackOutput output) {
|
||||||
{
|
super(output, "Turtle Upgrades", "computercraft/turtle_upgrades", TurtleUpgradeSerialiser.REGISTRY_ID);
|
||||||
super( generator, "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)}.
|
* to specify {@link ToolBuilder#damageMultiplier(float)} and {@link ToolBuilder#breakable(TagKey)}.
|
||||||
* @return A tool builder,
|
* @return A tool builder,
|
||||||
*/
|
*/
|
||||||
@Nonnull
|
public final ToolBuilder tool(ResourceLocation id, Item item) {
|
||||||
public final ToolBuilder tool( @Nonnull ResourceLocation id, @Nonnull Item item )
|
return new ToolBuilder(id, existingSerialiser(TOOL_ID), item);
|
||||||
{
|
|
||||||
return new ToolBuilder( id, existingSerialiser( TOOL_ID ), item );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -57,18 +54,16 @@ public abstract class TurtleUpgradeDataProvider extends UpgradeDataProvider<ITur
|
|||||||
*
|
*
|
||||||
* @see #tool(ResourceLocation, Item)
|
* @see #tool(ResourceLocation, Item)
|
||||||
*/
|
*/
|
||||||
public static class ToolBuilder
|
public static class ToolBuilder {
|
||||||
{
|
|
||||||
private final ResourceLocation id;
|
private final ResourceLocation id;
|
||||||
private final TurtleUpgradeSerialiser<?> serialiser;
|
private final TurtleUpgradeSerialiser<?> serialiser;
|
||||||
private final Item toolItem;
|
private final Item toolItem;
|
||||||
private String adjective;
|
private @Nullable String adjective;
|
||||||
private Item craftingItem;
|
private @Nullable Item craftingItem;
|
||||||
private Float damageMultiplier = null;
|
private @Nullable Float damageMultiplier = null;
|
||||||
private TagKey<Block> breakable;
|
private @Nullable TagKey<Block> breakable;
|
||||||
|
|
||||||
ToolBuilder( ResourceLocation id, TurtleUpgradeSerialiser<?> serialiser, Item toolItem )
|
ToolBuilder(ResourceLocation id, TurtleUpgradeSerialiser<?> serialiser, Item toolItem) {
|
||||||
{
|
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.serialiser = serialiser;
|
this.serialiser = serialiser;
|
||||||
this.toolItem = toolItem;
|
this.toolItem = toolItem;
|
||||||
@@ -81,9 +76,7 @@ public abstract class TurtleUpgradeDataProvider extends UpgradeDataProvider<ITur
|
|||||||
* @param adjective The new adjective to use.
|
* @param adjective The new adjective to use.
|
||||||
* @return The tool builder, for further use.
|
* @return The tool builder, for further use.
|
||||||
*/
|
*/
|
||||||
@Nonnull
|
public ToolBuilder adjective(String adjective) {
|
||||||
public ToolBuilder adjective( @Nonnull String adjective )
|
|
||||||
{
|
|
||||||
this.adjective = adjective;
|
this.adjective = adjective;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@@ -95,9 +88,7 @@ public abstract class TurtleUpgradeDataProvider extends UpgradeDataProvider<ITur
|
|||||||
* @param craftingItem The item used to craft this upgrade.
|
* @param craftingItem The item used to craft this upgrade.
|
||||||
* @return The tool builder, for further use.
|
* @return The tool builder, for further use.
|
||||||
*/
|
*/
|
||||||
@Nonnull
|
public ToolBuilder craftingItem(Item craftingItem) {
|
||||||
public ToolBuilder craftingItem( @Nonnull Item craftingItem )
|
|
||||||
{
|
|
||||||
this.craftingItem = craftingItem;
|
this.craftingItem = craftingItem;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@@ -109,8 +100,7 @@ public abstract class TurtleUpgradeDataProvider extends UpgradeDataProvider<ITur
|
|||||||
* @param damageMultiplier The damage multiplier.
|
* @param damageMultiplier The damage multiplier.
|
||||||
* @return The tool builder, for futher use.
|
* @return The tool builder, for futher use.
|
||||||
*/
|
*/
|
||||||
public ToolBuilder damageMultiplier( float damageMultiplier )
|
public ToolBuilder damageMultiplier(float damageMultiplier) {
|
||||||
{
|
|
||||||
this.damageMultiplier = damageMultiplier;
|
this.damageMultiplier = damageMultiplier;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@@ -124,8 +114,7 @@ public abstract class TurtleUpgradeDataProvider extends UpgradeDataProvider<ITur
|
|||||||
* @return The tool builder, for further use.
|
* @return The tool builder, for further use.
|
||||||
* @see ComputerCraftTags.Blocks
|
* @see ComputerCraftTags.Blocks
|
||||||
*/
|
*/
|
||||||
public ToolBuilder breakable( @Nonnull TagKey<Block> breakable )
|
public ToolBuilder breakable(TagKey<Block> breakable) {
|
||||||
{
|
|
||||||
this.breakable = breakable;
|
this.breakable = breakable;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@@ -135,18 +124,16 @@ public abstract class TurtleUpgradeDataProvider extends UpgradeDataProvider<ITur
|
|||||||
*
|
*
|
||||||
* @param add The callback given to {@link #addUpgrades(Consumer)}.
|
* @param add The callback given to {@link #addUpgrades(Consumer)}.
|
||||||
*/
|
*/
|
||||||
public void add( @Nonnull Consumer<Upgrade<TurtleUpgradeSerialiser<?>>> add )
|
public void add(Consumer<Upgrade<TurtleUpgradeSerialiser<?>>> add) {
|
||||||
{
|
add.accept(new Upgrade<>(id, serialiser, s -> {
|
||||||
add.accept( new Upgrade<>( id, serialiser, s -> {
|
s.addProperty("item", PlatformHelper.get().getRegistryKey(Registries.ITEM, toolItem).toString());
|
||||||
s.addProperty( "item", ForgeRegistries.ITEMS.getKey( toolItem ).toString() );
|
if (adjective != null) s.addProperty("adjective", adjective);
|
||||||
if( adjective != null ) s.addProperty( "adjective", adjective );
|
if (craftingItem != null) {
|
||||||
if( craftingItem != null )
|
s.addProperty("craftItem", PlatformHelper.get().getRegistryKey(Registries.ITEM, craftingItem).toString());
|
||||||
{
|
|
||||||
s.addProperty( "craftItem", ForgeRegistries.ITEMS.getKey( craftingItem ).toString() );
|
|
||||||
}
|
}
|
||||||
if( damageMultiplier != null ) s.addProperty( "damageMultiplier", damageMultiplier );
|
if (damageMultiplier != null) s.addProperty("damageMultiplier", damageMultiplier);
|
||||||
if( breakable != null ) s.addProperty( "breakable", breakable.location().toString() );
|
if (breakable != null) s.addProperty("breakable", breakable.location().toString());
|
||||||
} ) );
|
}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -5,37 +5,30 @@
|
|||||||
*/
|
*/
|
||||||
package dan200.computercraft.api.turtle;
|
package dan200.computercraft.api.turtle;
|
||||||
|
|
||||||
import dan200.computercraft.ComputerCraft;
|
import dan200.computercraft.api.ComputerCraftAPI;
|
||||||
import dan200.computercraft.api.client.ComputerCraftAPIClient;
|
import dan200.computercraft.api.upgrades.UpgradeBase;
|
||||||
import dan200.computercraft.api.client.turtle.TurtleUpgradeModeller;
|
|
||||||
import dan200.computercraft.api.upgrades.IUpgradeBase;
|
|
||||||
import dan200.computercraft.api.upgrades.UpgradeSerialiser;
|
import dan200.computercraft.api.upgrades.UpgradeSerialiser;
|
||||||
import dan200.computercraft.internal.upgrades.SerialiserWithCraftingItem;
|
import dan200.computercraft.impl.upgrades.SerialiserWithCraftingItem;
|
||||||
import dan200.computercraft.internal.upgrades.SimpleSerialiser;
|
import dan200.computercraft.impl.upgrades.SimpleSerialiser;
|
||||||
import net.minecraft.core.Registry;
|
import net.minecraft.core.Registry;
|
||||||
import net.minecraft.resources.ResourceKey;
|
import net.minecraft.resources.ResourceKey;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
import net.minecraft.world.item.ItemStack;
|
import net.minecraft.world.item.ItemStack;
|
||||||
import net.minecraft.world.item.crafting.RecipeSerializer;
|
import net.minecraft.world.item.crafting.RecipeSerializer;
|
||||||
import net.minecraft.world.item.crafting.SimpleRecipeSerializer;
|
import net.minecraft.world.item.crafting.SimpleCraftingRecipeSerializer;
|
||||||
import net.minecraftforge.registries.DeferredRegister;
|
|
||||||
import net.minecraftforge.registries.IForgeRegistry;
|
|
||||||
import net.minecraftforge.registries.RegistryManager;
|
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
|
||||||
import java.util.function.BiFunction;
|
import java.util.function.BiFunction;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reads a {@link ITurtleUpgrade} from disk and reads/writes it to a network packet.
|
* Reads a {@link ITurtleUpgrade} from disk and reads/writes it to a network packet.
|
||||||
* <p>
|
* <p>
|
||||||
* These should be registered in a {@link IForgeRegistry} while the game is loading, much like {@link RecipeSerializer}s.
|
* These should be registered in a {@link Registry} while the game is loading, much like {@link RecipeSerializer}s.
|
||||||
* It is suggested you use a {@link DeferredRegister}.
|
|
||||||
* <p>
|
* <p>
|
||||||
* If your turtle upgrade doesn't have any associated configurable parameters (like most upgrades), you can use
|
* 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.
|
* {@link #simple(Function)} or {@link #simpleWithCustomItem(BiFunction)} to create a basic upgrade serialiser.
|
||||||
*
|
*
|
||||||
* <h2>Example</h2>
|
* <h2>Example (Forge)</h2>
|
||||||
* <pre>{@code
|
* <pre>{@code
|
||||||
* static final DeferredRegister<TurtleUpgradeSerialiser<?>> SERIALISERS = DeferredRegister.create( TurtleUpgradeSerialiser.TYPE, "my_mod" );
|
* static final DeferredRegister<TurtleUpgradeSerialiser<?>> SERIALISERS = DeferredRegister.create( TurtleUpgradeSerialiser.TYPE, "my_mod" );
|
||||||
*
|
*
|
||||||
@@ -57,7 +50,7 @@ import java.util.function.Function;
|
|||||||
* }</pre>
|
* }</pre>
|
||||||
* <p>
|
* <p>
|
||||||
* Finally, we need to register a model for our upgrade. This is done with
|
* 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
|
* <pre>{@code
|
||||||
* // Register our model inside FMLClientSetupEvent
|
* // 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.
|
* @param <T> The type of turtle upgrade this is responsible for serialising.
|
||||||
* @see ITurtleUpgrade
|
* @see ITurtleUpgrade
|
||||||
* @see TurtleUpgradeDataProvider
|
* @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.
|
* 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.
|
* Create an upgrade serialiser for a simple upgrade. This is similar to a {@link SimpleCraftingRecipeSerializer},
|
||||||
*
|
* but for upgrades.
|
||||||
* @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.
|
|
||||||
* <p>
|
* <p>
|
||||||
* If you might want to vary the item, it's suggested you use {@link #simpleWithCustomItem(BiFunction)} instead.
|
* 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.
|
* @param <T> The type of the generated upgrade.
|
||||||
* @return The serialiser for this upgrade
|
* @return The serialiser for this upgrade
|
||||||
*/
|
*/
|
||||||
@Nonnull
|
static <T extends ITurtleUpgrade> TurtleUpgradeSerialiser<T> simple(Function<ResourceLocation, T> factory) {
|
||||||
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) {
|
||||||
final class Impl extends SimpleSerialiser<T> implements TurtleUpgradeSerialiser<T>
|
super(constructor);
|
||||||
{
|
|
||||||
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.
|
* 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
|
* @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.
|
* @param <T> The type of the generated upgrade.
|
||||||
* @return The serialiser for this upgrade.
|
* @return The serialiser for this upgrade.
|
||||||
* @see #simple(Function) For upgrades whose crafting stack should not vary.
|
* @see #simple(Function) For upgrades whose crafting stack should not vary.
|
||||||
*/
|
*/
|
||||||
@Nonnull
|
static <T extends ITurtleUpgrade> TurtleUpgradeSerialiser<T> simpleWithCustomItem(BiFunction<ResourceLocation, ItemStack, T> factory) {
|
||||||
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) {
|
||||||
final class Impl extends SerialiserWithCraftingItem<T> implements TurtleUpgradeSerialiser<T>
|
super(factory);
|
||||||
{
|
|
||||||
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()
|
* @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()}
|
* 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).
|
* and {@code turtle.attack()} methods (Such as pickaxe or sword on Mining and Melee turtles).
|
||||||
@@ -31,13 +30,11 @@ public enum TurtleUpgradeType
|
|||||||
*/
|
*/
|
||||||
BOTH;
|
BOTH;
|
||||||
|
|
||||||
public boolean isTool()
|
public boolean isTool() {
|
||||||
{
|
|
||||||
return this == TOOL || this == BOTH;
|
return this == TOOL || this == BOTH;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isPeripheral()
|
public boolean isPeripheral() {
|
||||||
{
|
|
||||||
return this == PERIPHERAL || this == BOTH;
|
return this == PERIPHERAL || this == BOTH;
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -14,8 +14,7 @@ import net.minecraft.core.Direction;
|
|||||||
* @see ITurtleUpgrade#getType()
|
* @see ITurtleUpgrade#getType()
|
||||||
* @see ITurtleUpgrade#useTool(ITurtleAccess, TurtleSide, TurtleVerb, Direction)
|
* @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()}.
|
* The turtle called {@code turtle.dig()}, {@code turtle.digUp()} or {@code turtle.digDown()}.
|
||||||
*/
|
*/
|
@@ -7,18 +7,17 @@ package dan200.computercraft.api.upgrades;
|
|||||||
|
|
||||||
import dan200.computercraft.api.pocket.IPocketUpgrade;
|
import dan200.computercraft.api.pocket.IPocketUpgrade;
|
||||||
import dan200.computercraft.api.turtle.ITurtleUpgrade;
|
import dan200.computercraft.api.turtle.ITurtleUpgrade;
|
||||||
|
import dan200.computercraft.impl.PlatformHelper;
|
||||||
import net.minecraft.Util;
|
import net.minecraft.Util;
|
||||||
import net.minecraft.nbt.CompoundTag;
|
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
import net.minecraft.world.item.ItemStack;
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import java.util.Objects;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Common functionality between {@link ITurtleUpgrade} and {@link IPocketUpgrade}.
|
* Common functionality between {@link ITurtleUpgrade} and {@link IPocketUpgrade}.
|
||||||
*/
|
*/
|
||||||
public interface IUpgradeBase
|
public interface UpgradeBase {
|
||||||
{
|
|
||||||
/**
|
/**
|
||||||
* Gets a unique identifier representing this type of turtle upgrade. eg: "computercraft:wireless_modem"
|
* Gets a unique identifier representing this type of turtle upgrade. eg: "computercraft:wireless_modem"
|
||||||
* or "my_mod:my_upgrade".
|
* or "my_mod:my_upgrade".
|
||||||
@@ -28,7 +27,6 @@ public interface IUpgradeBase
|
|||||||
*
|
*
|
||||||
* @return The unique ID for this upgrade.
|
* @return The unique ID for this upgrade.
|
||||||
*/
|
*/
|
||||||
@Nonnull
|
|
||||||
ResourceLocation getUpgradeID();
|
ResourceLocation getUpgradeID();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -38,7 +36,6 @@ public interface IUpgradeBase
|
|||||||
*
|
*
|
||||||
* @return The localisation key for this upgrade's adjective.
|
* @return The localisation key for this upgrade's adjective.
|
||||||
*/
|
*/
|
||||||
@Nonnull
|
|
||||||
String getUnlocalisedAdjective();
|
String getUnlocalisedAdjective();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -52,7 +49,6 @@ public interface IUpgradeBase
|
|||||||
*
|
*
|
||||||
* @return The item stack to craft with, or {@link ItemStack#EMPTY} if it cannot be crafted.
|
* @return The item stack to craft with, or {@link ItemStack#EMPTY} if it cannot be crafted.
|
||||||
*/
|
*/
|
||||||
@Nonnull
|
|
||||||
ItemStack getCraftingItem();
|
ItemStack getCraftingItem();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -64,25 +60,24 @@ public interface IUpgradeBase
|
|||||||
* <p>
|
* <p>
|
||||||
* The default check requires that any non-capability NBT is exactly the same as the
|
* The default check requires that any non-capability NBT is exactly the same as the
|
||||||
* crafting item, but this may be relaxed for your upgrade.
|
* crafting item, but this may be relaxed for your upgrade.
|
||||||
|
* <p>
|
||||||
|
* This is based on {@code net.minecraftforge.common.crafting.StrictNBTIngredient}'s check.
|
||||||
*
|
*
|
||||||
* @param stack The stack to check. This is guaranteed to be non-empty and have the same item as
|
* @param stack The stack to check. This is guaranteed to be non-empty and have the same item as
|
||||||
* {@link #getCraftingItem()}.
|
* {@link #getCraftingItem()}.
|
||||||
* @return If this stack may be used to equip this upgrade.
|
* @return If this stack may be used to equip this upgrade.
|
||||||
* @see net.minecraftforge.common.crafting.StrictNBTIngredient#test(ItemStack) For the implementation of the default
|
|
||||||
* check.
|
|
||||||
*/
|
*/
|
||||||
default boolean isItemSuitable( @Nonnull ItemStack stack )
|
default boolean isItemSuitable(ItemStack stack) {
|
||||||
{
|
var crafting = getCraftingItem();
|
||||||
ItemStack crafting = getCraftingItem();
|
|
||||||
|
|
||||||
// A more expanded form of ItemStack.areShareTagsEqual, but allowing an empty tag to be equal to a
|
// A more expanded form of ItemStack.areShareTagsEqual, but allowing an empty tag to be equal to a
|
||||||
// null one.
|
// null one.
|
||||||
CompoundTag shareTag = stack.getItem().getShareTag( stack );
|
var shareTag = PlatformHelper.get().getShareTag(stack);
|
||||||
CompoundTag craftingShareTag = crafting.getItem().getShareTag( crafting );
|
var craftingShareTag = PlatformHelper.get().getShareTag(crafting);
|
||||||
if( shareTag == craftingShareTag ) return true;
|
if (shareTag == craftingShareTag) return true;
|
||||||
if( shareTag == null ) return craftingShareTag.isEmpty();
|
if (shareTag == null) return Objects.requireNonNull(craftingShareTag).isEmpty();
|
||||||
if( craftingShareTag == null ) return shareTag.isEmpty();
|
if (craftingShareTag == null) return shareTag.isEmpty();
|
||||||
return shareTag.equals( craftingShareTag );
|
return shareTag.equals(craftingShareTag);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -93,9 +88,7 @@ public interface IUpgradeBase
|
|||||||
* @return The generated adjective.
|
* @return The generated adjective.
|
||||||
* @see #getUnlocalisedAdjective()
|
* @see #getUnlocalisedAdjective()
|
||||||
*/
|
*/
|
||||||
@Nonnull
|
static String getDefaultAdjective(ResourceLocation id) {
|
||||||
static String getDefaultAdjective( @Nonnull ResourceLocation id )
|
return Util.makeDescriptionId("upgrade", id) + ".adjective";
|
||||||
{
|
|
||||||
return Util.makeDescriptionId( "upgrade", id ) + ".adjective";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -0,0 +1,171 @@
|
|||||||
|
/*
|
||||||
|
* 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.upgrades;
|
||||||
|
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
import com.google.gson.JsonParseException;
|
||||||
|
import dan200.computercraft.api.turtle.TurtleUpgradeSerialiser;
|
||||||
|
import dan200.computercraft.impl.PlatformHelper;
|
||||||
|
import dan200.computercraft.impl.upgrades.SerialiserWithCraftingItem;
|
||||||
|
import dan200.computercraft.impl.upgrades.SimpleSerialiser;
|
||||||
|
import net.minecraft.Util;
|
||||||
|
import net.minecraft.core.Registry;
|
||||||
|
import net.minecraft.core.registries.Registries;
|
||||||
|
import net.minecraft.data.CachedOutput;
|
||||||
|
import net.minecraft.data.DataProvider;
|
||||||
|
import net.minecraft.data.PackOutput;
|
||||||
|
import net.minecraft.resources.ResourceKey;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import net.minecraft.world.item.Item;
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A data generator/provider for turtle and pocket computer upgrades. This should not be extended directly, instead see
|
||||||
|
* the other subclasses.
|
||||||
|
*
|
||||||
|
* @param <T> The base class of upgrades.
|
||||||
|
* @param <R> The upgrade serialiser to register for.
|
||||||
|
*/
|
||||||
|
public abstract class UpgradeDataProvider<T extends UpgradeBase, R extends UpgradeSerialiser<? extends T>> implements DataProvider {
|
||||||
|
private static final Logger LOGGER = LogManager.getLogger();
|
||||||
|
|
||||||
|
private final PackOutput output;
|
||||||
|
private final String name;
|
||||||
|
private final String folder;
|
||||||
|
private final ResourceKey<Registry<R>> registry;
|
||||||
|
|
||||||
|
private @Nullable List<T> upgrades;
|
||||||
|
|
||||||
|
protected UpgradeDataProvider(PackOutput output, String name, String folder, ResourceKey<Registry<R>> registry) {
|
||||||
|
this.output = output;
|
||||||
|
this.name = name;
|
||||||
|
this.folder = folder;
|
||||||
|
this.registry = registry;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register an upgrade using a "simple" serialiser (e.g. {@link TurtleUpgradeSerialiser#simple(Function)}).
|
||||||
|
*
|
||||||
|
* @param id The ID of the upgrade to create.
|
||||||
|
* @param serialiser The simple serialiser.
|
||||||
|
* @return The constructed upgrade, ready to be passed off to {@link #addUpgrades(Consumer)}'s consumer.
|
||||||
|
*/
|
||||||
|
public final Upgrade<R> simple(ResourceLocation id, R serialiser) {
|
||||||
|
if (!(serialiser instanceof SimpleSerialiser)) {
|
||||||
|
throw new IllegalStateException(serialiser + " must be a simple() seriaiser.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Upgrade<>(id, serialiser, s -> {
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register an upgrade using a "simple" serialiser (e.g. {@link TurtleUpgradeSerialiser#simple(Function)}).
|
||||||
|
*
|
||||||
|
* @param id The ID of the upgrade to create.
|
||||||
|
* @param serialiser The simple serialiser.
|
||||||
|
* @param item The crafting upgrade for this item.
|
||||||
|
* @return The constructed upgrade, ready to be passed off to {@link #addUpgrades(Consumer)}'s consumer.
|
||||||
|
*/
|
||||||
|
public final Upgrade<R> simpleWithCustomItem(ResourceLocation id, R serialiser, Item item) {
|
||||||
|
if (!(serialiser instanceof SerialiserWithCraftingItem)) {
|
||||||
|
throw new IllegalStateException(serialiser + " must be a simpleWithCustomItem() serialiser.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Upgrade<>(id, serialiser, s ->
|
||||||
|
s.addProperty("item", PlatformHelper.get().getRegistryKey(Registries.ITEM, item).toString())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add all turtle or pocket computer upgrades.
|
||||||
|
* <p>
|
||||||
|
* <strong>Example usage:</strong>
|
||||||
|
* <pre>{@code
|
||||||
|
* protected void addUpgrades(Consumer<Upgrade<TurtleUpgradeSerialiser<?>>> addUpgrade) {
|
||||||
|
* simple(new ResourceLocation("mymod", "speaker"), SPEAKER_SERIALISER.get()).add(addUpgrade);
|
||||||
|
* }
|
||||||
|
* }</pre>
|
||||||
|
*
|
||||||
|
* @param addUpgrade A callback used to register an upgrade.
|
||||||
|
*/
|
||||||
|
protected abstract void addUpgrades(Consumer<Upgrade<R>> addUpgrade);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final CompletableFuture<?> run(CachedOutput cache) {
|
||||||
|
var base = output.getOutputFolder().resolve("data");
|
||||||
|
|
||||||
|
Set<ResourceLocation> seen = new HashSet<>();
|
||||||
|
List<T> upgrades = new ArrayList<>();
|
||||||
|
List<CompletableFuture<?>> futures = new ArrayList<>();
|
||||||
|
addUpgrades(upgrade -> {
|
||||||
|
if (!seen.add(upgrade.id())) throw new IllegalStateException("Duplicate upgrade " + upgrade.id());
|
||||||
|
|
||||||
|
var json = new JsonObject();
|
||||||
|
json.addProperty("type", PlatformHelper.get().getRegistryKey(registry, upgrade.serialiser()).toString());
|
||||||
|
upgrade.serialise().accept(json);
|
||||||
|
|
||||||
|
futures.add(DataProvider.saveStable(cache, json, base.resolve(upgrade.id().getNamespace() + "/" + folder + "/" + upgrade.id().getPath() + ".json")));
|
||||||
|
|
||||||
|
try {
|
||||||
|
var result = upgrade.serialiser().fromJson(upgrade.id(), json);
|
||||||
|
upgrades.add(result);
|
||||||
|
} catch (IllegalArgumentException | JsonParseException e) {
|
||||||
|
LOGGER.error("Failed to parse {} {}", name, upgrade.id(), e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.upgrades = upgrades;
|
||||||
|
return Util.sequenceFailFast(futures);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final R existingSerialiser(ResourceLocation id) {
|
||||||
|
var result = PlatformHelper.get().getRegistryObject(registry, id);
|
||||||
|
if (result == null) throw new IllegalArgumentException("No such serialiser " + registry);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<T> getGeneratedUpgrades() {
|
||||||
|
if (upgrades == null) throw new IllegalStateException("Upgrades have not beeen generated yet");
|
||||||
|
return upgrades;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A constructed upgrade instance, produced {@link #addUpgrades(Consumer)}.
|
||||||
|
*
|
||||||
|
* @param id The ID for this upgrade.
|
||||||
|
* @param serialiser The serialiser which reads and writes this upgrade.
|
||||||
|
* @param serialise Augment the generated JSON with additional fields.
|
||||||
|
* @param <R> The type of upgrade serialiser.
|
||||||
|
*/
|
||||||
|
public record Upgrade<R extends UpgradeSerialiser<?>>(
|
||||||
|
ResourceLocation id, R serialiser, Consumer<JsonObject> serialise
|
||||||
|
) {
|
||||||
|
/**
|
||||||
|
* Convenience method for registering an upgrade.
|
||||||
|
*
|
||||||
|
* @param add The callback given to {@link #addUpgrades(Consumer)}
|
||||||
|
*/
|
||||||
|
public void add(Consumer<Upgrade<R>> add) {
|
||||||
|
add.accept(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -11,7 +11,6 @@ import dan200.computercraft.api.turtle.TurtleUpgradeSerialiser;
|
|||||||
import net.minecraft.network.FriendlyByteBuf;
|
import net.minecraft.network.FriendlyByteBuf;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base interface for upgrade serialisers. This should generally not be implemented directly, instead implementing one
|
* Base interface for upgrade serialisers. This should generally not be implemented directly, instead implementing one
|
||||||
@@ -23,28 +22,25 @@ import javax.annotation.Nonnull;
|
|||||||
* @see TurtleUpgradeSerialiser
|
* @see TurtleUpgradeSerialiser
|
||||||
* @see PocketUpgradeSerialiser
|
* @see PocketUpgradeSerialiser
|
||||||
*/
|
*/
|
||||||
public interface UpgradeSerialiser<T extends IUpgradeBase>
|
public interface UpgradeSerialiser<T extends UpgradeBase> {
|
||||||
{
|
|
||||||
/**
|
/**
|
||||||
* Read this upgrade from a JSON file in a datapack.
|
* Read this upgrade from a JSON file in a datapack.
|
||||||
*
|
*
|
||||||
* @param id The ID of this upgrade.
|
* @param id The ID of this upgrade.
|
||||||
* @param object The JSON object to load this upgrade from.
|
* @param object The JSON object to load this upgrade from.
|
||||||
* @return The constructed upgrade, with a {@link IUpgradeBase#getUpgradeID()} equal to {@code id}.
|
* @return The constructed upgrade, with a {@link UpgradeBase#getUpgradeID()} equal to {@code id}.
|
||||||
* @see net.minecraft.util.GsonHelper For additional JSON helper methods.
|
* @see net.minecraft.util.GsonHelper For additional JSON helper methods.
|
||||||
*/
|
*/
|
||||||
@Nonnull
|
T fromJson(ResourceLocation id, JsonObject object);
|
||||||
T fromJson( @Nonnull ResourceLocation id, @Nonnull JsonObject object );
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read this upgrade from a network packet, sent from the server.
|
* Read this upgrade from a network packet, sent from the server.
|
||||||
*
|
*
|
||||||
* @param id The ID of this upgrade.
|
* @param id The ID of this upgrade.
|
||||||
* @param buffer The buffer object to read this upgrade from.
|
* @param buffer The buffer object to read this upgrade from.
|
||||||
* @return The constructed upgrade, with a {@link IUpgradeBase#getUpgradeID()} equal to {@code id}.
|
* @return The constructed upgrade, with a {@link UpgradeBase#getUpgradeID()} equal to {@code id}.
|
||||||
*/
|
*/
|
||||||
@Nonnull
|
T fromNetwork(ResourceLocation id, FriendlyByteBuf buffer);
|
||||||
T fromNetwork( @Nonnull ResourceLocation id, @Nonnull FriendlyByteBuf buffer );
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Write this upgrade to a network packet, to be sent to the client.
|
* Write this upgrade to a network packet, to be sent to the client.
|
||||||
@@ -52,6 +48,6 @@ public interface UpgradeSerialiser<T extends IUpgradeBase>
|
|||||||
* @param buffer The buffer object to write this upgrade to
|
* @param buffer The buffer object to write this upgrade to
|
||||||
* @param upgrade The upgrade to write.
|
* @param upgrade The upgrade to write.
|
||||||
*/
|
*/
|
||||||
void toNetwork( @Nonnull FriendlyByteBuf buffer, @Nonnull T upgrade );
|
void toNetwork(FriendlyByteBuf buffer, T upgrade);
|
||||||
|
|
||||||
}
|
}
|
@@ -0,0 +1,84 @@
|
|||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
import dan200.computercraft.api.ComputerCraftAPI;
|
||||||
|
import dan200.computercraft.api.detail.BlockReference;
|
||||||
|
import dan200.computercraft.api.detail.DetailRegistry;
|
||||||
|
import dan200.computercraft.api.filesystem.Mount;
|
||||||
|
import dan200.computercraft.api.filesystem.WritableMount;
|
||||||
|
import dan200.computercraft.api.lua.GenericSource;
|
||||||
|
import dan200.computercraft.api.lua.ILuaAPIFactory;
|
||||||
|
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.redstone.BundledRedstoneProvider;
|
||||||
|
import dan200.computercraft.api.turtle.TurtleRefuelHandler;
|
||||||
|
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 org.jetbrains.annotations.ApiStatus;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Backing interface for {@link ComputerCraftAPI}
|
||||||
|
* <p>
|
||||||
|
* Do <strong>NOT</strong> directly reference this class. It exists for internal use by the API.
|
||||||
|
*/
|
||||||
|
@ApiStatus.Internal
|
||||||
|
public interface ComputerCraftAPIService {
|
||||||
|
static ComputerCraftAPIService get() {
|
||||||
|
var instance = Instance.INSTANCE;
|
||||||
|
return instance == null ? Services.raise(ComputerCraftAPIService.class, Instance.ERROR) : instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
String getInstalledVersion();
|
||||||
|
|
||||||
|
int createUniqueNumberedSaveDir(MinecraftServer server, String parentSubPath);
|
||||||
|
|
||||||
|
WritableMount createSaveDirMount(MinecraftServer server, String subPath, long capacity);
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
Mount createResourceMount(MinecraftServer server, String domain, String subPath);
|
||||||
|
|
||||||
|
void registerGenericSource(GenericSource source);
|
||||||
|
|
||||||
|
void registerBundledRedstoneProvider(BundledRedstoneProvider provider);
|
||||||
|
|
||||||
|
int getBundledRedstoneOutput(Level world, BlockPos pos, Direction side);
|
||||||
|
|
||||||
|
void registerMediaProvider(MediaProvider provider);
|
||||||
|
|
||||||
|
PacketNetwork getWirelessNetwork(MinecraftServer server);
|
||||||
|
|
||||||
|
void registerAPIFactory(ILuaAPIFactory factory);
|
||||||
|
|
||||||
|
WiredNode createWiredNodeForElement(WiredElement element);
|
||||||
|
|
||||||
|
void registerRefuelHandler(TurtleRefuelHandler handler);
|
||||||
|
|
||||||
|
DetailRegistry<ItemStack> getItemStackDetailRegistry();
|
||||||
|
|
||||||
|
DetailRegistry<BlockReference> getBlockInWorldDetailRegistry();
|
||||||
|
|
||||||
|
final class Instance {
|
||||||
|
static final @Nullable ComputerCraftAPIService INSTANCE;
|
||||||
|
static final @Nullable Throwable ERROR;
|
||||||
|
|
||||||
|
static {
|
||||||
|
var helper = Services.tryLoad(ComputerCraftAPIService.class);
|
||||||
|
INSTANCE = helper.instance();
|
||||||
|
ERROR = helper.error();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Instance() {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,82 @@
|
|||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
import net.minecraft.core.Registry;
|
||||||
|
import net.minecraft.nbt.CompoundTag;
|
||||||
|
import net.minecraft.resources.ResourceKey;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
import org.jetbrains.annotations.ApiStatus;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Abstraction layer for Forge and Fabric. See implementations for more details.
|
||||||
|
* <p>
|
||||||
|
* Do <strong>NOT</strong> directly reference this class. It exists for internal use by the API.
|
||||||
|
*/
|
||||||
|
@ApiStatus.Internal
|
||||||
|
public interface PlatformHelper {
|
||||||
|
/**
|
||||||
|
* Get the current {@link PlatformHelper} instance.
|
||||||
|
*
|
||||||
|
* @return The current instance.
|
||||||
|
*/
|
||||||
|
static PlatformHelper get() {
|
||||||
|
var instance = Instance.INSTANCE;
|
||||||
|
return instance == null ? Services.raise(PlatformHelper.class, Instance.ERROR) : instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the unique ID for a registered object.
|
||||||
|
*
|
||||||
|
* @param registry The registry to look up this object in.
|
||||||
|
* @param object The object to look up.
|
||||||
|
* @param <T> The type of object the registry stores.
|
||||||
|
* @return The registered object's ID.
|
||||||
|
* @throws IllegalArgumentException If the registry or object are not registered.
|
||||||
|
*/
|
||||||
|
<T> ResourceLocation getRegistryKey(ResourceKey<Registry<T>> registry, T object);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Look up an ID in a registry, returning the registered object.
|
||||||
|
*
|
||||||
|
* @param registry The registry to look up this object in.
|
||||||
|
* @param id The ID to look up.
|
||||||
|
* @param <T> The type of object the registry stores.
|
||||||
|
* @return The resolved registry object.
|
||||||
|
* @throws IllegalArgumentException If the registry or object are not registered.
|
||||||
|
*/
|
||||||
|
<T> T getRegistryObject(ResourceKey<Registry<T>> registry, ResourceLocation id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the subset of an {@link ItemStack}'s {@linkplain ItemStack#getTag() tag} which is synced to the client.
|
||||||
|
*
|
||||||
|
* @param item The stack.
|
||||||
|
* @return The item's tag.
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
default CompoundTag getShareTag(ItemStack item) {
|
||||||
|
return item.getTag();
|
||||||
|
}
|
||||||
|
|
||||||
|
final class Instance {
|
||||||
|
static final @Nullable PlatformHelper INSTANCE;
|
||||||
|
static final @Nullable Throwable ERROR;
|
||||||
|
|
||||||
|
static {
|
||||||
|
// We don't want class initialisation to fail here (as that results in confusing errors). Instead, capture
|
||||||
|
// the error and rethrow it when accessing. This should be JITted away in the common case.
|
||||||
|
var helper = Services.tryLoad(PlatformHelper.class);
|
||||||
|
INSTANCE = helper.instance();
|
||||||
|
ERROR = helper.error();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Instance() {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -8,6 +8,7 @@ package dan200.computercraft.impl;
|
|||||||
import org.jetbrains.annotations.ApiStatus;
|
import org.jetbrains.annotations.ApiStatus;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
import java.io.Serial;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A ComputerCraft-related service failed to load.
|
* A ComputerCraft-related service failed to load.
|
||||||
@@ -15,12 +16,11 @@ import javax.annotation.Nullable;
|
|||||||
* Do <strong>NOT</strong> directly reference this class. It exists for internal use by the API.
|
* Do <strong>NOT</strong> directly reference this class. It exists for internal use by the API.
|
||||||
*/
|
*/
|
||||||
@ApiStatus.Internal
|
@ApiStatus.Internal
|
||||||
class ServiceException extends RuntimeException
|
class ServiceException extends RuntimeException {
|
||||||
{
|
@Serial
|
||||||
private static final long serialVersionUID = -8392300691666423882L;
|
private static final long serialVersionUID = -8392300691666423882L;
|
||||||
|
|
||||||
ServiceException( String message, @Nullable Throwable cause )
|
ServiceException(String message, @Nullable Throwable cause) {
|
||||||
{
|
super(message, cause);
|
||||||
super( message, cause );
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -8,8 +8,6 @@ package dan200.computercraft.impl;
|
|||||||
import org.jetbrains.annotations.ApiStatus;
|
import org.jetbrains.annotations.ApiStatus;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.ServiceLoader;
|
import java.util.ServiceLoader;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
@@ -19,10 +17,8 @@ import java.util.stream.Collectors;
|
|||||||
* Do <strong>NOT</strong> directly reference this class. It exists for internal use by the API.
|
* Do <strong>NOT</strong> directly reference this class. It exists for internal use by the API.
|
||||||
*/
|
*/
|
||||||
@ApiStatus.Internal
|
@ApiStatus.Internal
|
||||||
public final class Services
|
public final class Services {
|
||||||
{
|
private Services() {
|
||||||
private Services()
|
|
||||||
{
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -33,20 +29,16 @@ public final class Services
|
|||||||
* @return The constructed service instance.
|
* @return The constructed service instance.
|
||||||
* @throws IllegalStateException When the service cannot be loaded.
|
* @throws IllegalStateException When the service cannot be loaded.
|
||||||
*/
|
*/
|
||||||
public static <T> T load( Class<T> klass )
|
public static <T> T load(Class<T> klass) {
|
||||||
{
|
var services = ServiceLoader.load(klass).stream().toList();
|
||||||
List<T> services = new ArrayList<>( 1 );
|
return switch (services.size()) {
|
||||||
for( T provider : ServiceLoader.load( klass ) ) services.add( provider );
|
case 1 -> services.get(0).get();
|
||||||
switch( services.size() )
|
case 0 -> throw new IllegalStateException("Cannot find service for " + klass.getName());
|
||||||
{
|
default -> {
|
||||||
case 1:
|
var serviceTypes = services.stream().map(x -> x.type().getName()).collect(Collectors.joining(", "));
|
||||||
return services.get( 0 );
|
throw new IllegalStateException("Multiple services for " + klass.getName() + ": " + serviceTypes);
|
||||||
case 0:
|
}
|
||||||
throw new IllegalStateException( "Cannot find service for " + klass.getName() );
|
};
|
||||||
default:
|
|
||||||
String serviceTypes = services.stream().map( x -> x.getClass().getName() ).collect( Collectors.joining( ", " ) );
|
|
||||||
throw new IllegalStateException( "Multiple services for " + klass.getName() + ": " + serviceTypes );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -57,15 +49,11 @@ public final class Services
|
|||||||
* @return The result type, either containing the service or an exception.
|
* @return The result type, either containing the service or an exception.
|
||||||
* @see ComputerCraftAPIService Intended usage of this class.
|
* @see ComputerCraftAPIService Intended usage of this class.
|
||||||
*/
|
*/
|
||||||
public static <T> LoadedService<T> tryLoad( Class<T> klass )
|
public static <T> LoadedService<T> tryLoad(Class<T> klass) {
|
||||||
{
|
try {
|
||||||
try
|
return new LoadedService<>(load(klass), null);
|
||||||
{
|
} catch (Exception | LinkageError e) {
|
||||||
return new LoadedService<>( load( klass ), null );
|
return new LoadedService<>(null, e);
|
||||||
}
|
|
||||||
catch( Exception | LinkageError e )
|
|
||||||
{
|
|
||||||
return new LoadedService<>( null, e );
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,32 +67,28 @@ public final class Services
|
|||||||
* @see #tryLoad(Class)
|
* @see #tryLoad(Class)
|
||||||
* @see LoadedService#error()
|
* @see LoadedService#error()
|
||||||
*/
|
*/
|
||||||
public static <T> T raise( Class<T> klass, @Nullable Throwable e )
|
@SuppressWarnings("DoNotCallSuggester")
|
||||||
{
|
public static <T> T raise(Class<T> klass, @Nullable Throwable e) {
|
||||||
// Throw a new exception so there's a useful stack trace there somewhere!
|
// Throw a new exception so there's a useful stack trace there somewhere!
|
||||||
throw new ServiceException( "Failed to instantiate " + klass.getName(), e );
|
throw new ServiceException("Failed to instantiate " + klass.getName(), e);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class LoadedService<T>
|
public static class LoadedService<T> {
|
||||||
{
|
|
||||||
private final @Nullable T instance;
|
private final @Nullable T instance;
|
||||||
private final @Nullable Throwable error;
|
private final @Nullable Throwable error;
|
||||||
|
|
||||||
LoadedService( @Nullable T instance, @Nullable Throwable error )
|
LoadedService(@Nullable T instance, @Nullable Throwable error) {
|
||||||
{
|
|
||||||
this.instance = instance;
|
this.instance = instance;
|
||||||
this.error = error;
|
this.error = error;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public T instance()
|
public T instance() {
|
||||||
{
|
|
||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public Throwable error()
|
public Throwable error() {
|
||||||
{
|
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -0,0 +1,21 @@
|
|||||||
|
/*
|
||||||
|
* 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
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal interfaces for ComputerCraft's API.
|
||||||
|
*/
|
||||||
|
@ApiStatus.Internal
|
||||||
|
@DefaultQualifier(value = NonNull.class, locations = {
|
||||||
|
TypeUseLocation.RETURN,
|
||||||
|
TypeUseLocation.PARAMETER,
|
||||||
|
TypeUseLocation.FIELD,
|
||||||
|
})
|
||||||
|
package dan200.computercraft.impl;
|
||||||
|
|
||||||
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
|
import org.checkerframework.framework.qual.DefaultQualifier;
|
||||||
|
import org.checkerframework.framework.qual.TypeUseLocation;
|
||||||
|
import org.jetbrains.annotations.ApiStatus;
|
@@ -3,17 +3,17 @@
|
|||||||
* Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
|
* Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
|
||||||
* Send enquiries to dratcliffe@gmail.com
|
* Send enquiries to dratcliffe@gmail.com
|
||||||
*/
|
*/
|
||||||
package dan200.computercraft.internal.upgrades;
|
package dan200.computercraft.impl.upgrades;
|
||||||
|
|
||||||
import com.google.gson.JsonObject;
|
import com.google.gson.JsonObject;
|
||||||
import dan200.computercraft.api.upgrades.IUpgradeBase;
|
import dan200.computercraft.api.upgrades.UpgradeBase;
|
||||||
import dan200.computercraft.api.upgrades.UpgradeSerialiser;
|
import dan200.computercraft.api.upgrades.UpgradeSerialiser;
|
||||||
import net.minecraft.network.FriendlyByteBuf;
|
import net.minecraft.network.FriendlyByteBuf;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
import net.minecraft.util.GsonHelper;
|
import net.minecraft.util.GsonHelper;
|
||||||
import net.minecraft.world.item.ItemStack;
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
import org.jetbrains.annotations.ApiStatus;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
|
||||||
import java.util.function.BiFunction;
|
import java.util.function.BiFunction;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -23,34 +23,28 @@ import java.util.function.BiFunction;
|
|||||||
*
|
*
|
||||||
* @param <T> The upgrade that this class can serialise and deserialise.
|
* @param <T> The upgrade that this class can serialise and deserialise.
|
||||||
*/
|
*/
|
||||||
public abstract class SerialiserWithCraftingItem<T extends IUpgradeBase> implements UpgradeSerialiser<T>
|
@ApiStatus.Internal
|
||||||
{
|
public abstract class SerialiserWithCraftingItem<T extends UpgradeBase> implements UpgradeSerialiser<T> {
|
||||||
private final BiFunction<ResourceLocation, ItemStack, T> factory;
|
private final BiFunction<ResourceLocation, ItemStack, T> factory;
|
||||||
|
|
||||||
protected SerialiserWithCraftingItem( BiFunction<ResourceLocation, ItemStack, T> factory )
|
protected SerialiserWithCraftingItem(BiFunction<ResourceLocation, ItemStack, T> factory) {
|
||||||
{
|
|
||||||
this.factory = factory;
|
this.factory = factory;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
|
||||||
@Override
|
@Override
|
||||||
public final T fromJson( @Nonnull ResourceLocation id, @Nonnull JsonObject object )
|
public final T fromJson(ResourceLocation id, JsonObject object) {
|
||||||
{
|
var item = GsonHelper.getAsItem(object, "item");
|
||||||
var item = GsonHelper.getAsItem( object, "item" );
|
return factory.apply(id, new ItemStack(item));
|
||||||
return factory.apply( id, new ItemStack( item ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nonnull
|
|
||||||
@Override
|
|
||||||
public final T fromNetwork( @Nonnull ResourceLocation id, @Nonnull FriendlyByteBuf buffer )
|
|
||||||
{
|
|
||||||
ItemStack item = buffer.readItem();
|
|
||||||
return factory.apply( id, item );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final void toNetwork( @Nonnull FriendlyByteBuf buffer, @Nonnull T upgrade )
|
public final T fromNetwork(ResourceLocation id, FriendlyByteBuf buffer) {
|
||||||
{
|
var item = buffer.readItem();
|
||||||
buffer.writeItem( upgrade.getCraftingItem() );
|
return factory.apply(id, item);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final void toNetwork(FriendlyByteBuf buffer, T upgrade) {
|
||||||
|
buffer.writeItem(upgrade.getCraftingItem());
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -3,15 +3,15 @@
|
|||||||
* Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
|
* Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
|
||||||
* Send enquiries to dratcliffe@gmail.com
|
* Send enquiries to dratcliffe@gmail.com
|
||||||
*/
|
*/
|
||||||
package dan200.computercraft.internal.upgrades;
|
package dan200.computercraft.impl.upgrades;
|
||||||
|
|
||||||
import com.google.gson.JsonObject;
|
import com.google.gson.JsonObject;
|
||||||
import dan200.computercraft.api.upgrades.IUpgradeBase;
|
import dan200.computercraft.api.upgrades.UpgradeBase;
|
||||||
import dan200.computercraft.api.upgrades.UpgradeSerialiser;
|
import dan200.computercraft.api.upgrades.UpgradeSerialiser;
|
||||||
import net.minecraft.network.FriendlyByteBuf;
|
import net.minecraft.network.FriendlyByteBuf;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import org.jetbrains.annotations.ApiStatus;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -21,31 +21,25 @@ import java.util.function.Function;
|
|||||||
*
|
*
|
||||||
* @param <T> The upgrade that this class can serialise and deserialise.
|
* @param <T> The upgrade that this class can serialise and deserialise.
|
||||||
*/
|
*/
|
||||||
public abstract class SimpleSerialiser<T extends IUpgradeBase> implements UpgradeSerialiser<T>
|
@ApiStatus.Internal
|
||||||
{
|
public abstract class SimpleSerialiser<T extends UpgradeBase> implements UpgradeSerialiser<T> {
|
||||||
private final Function<ResourceLocation, T> constructor;
|
private final Function<ResourceLocation, T> constructor;
|
||||||
|
|
||||||
public SimpleSerialiser( Function<ResourceLocation, T> constructor )
|
public SimpleSerialiser(Function<ResourceLocation, T> constructor) {
|
||||||
{
|
|
||||||
this.constructor = constructor;
|
this.constructor = constructor;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
|
||||||
@Override
|
@Override
|
||||||
public final T fromJson( @Nonnull ResourceLocation id, @Nonnull JsonObject object )
|
public final T fromJson(ResourceLocation id, JsonObject object) {
|
||||||
{
|
return constructor.apply(id);
|
||||||
return constructor.apply( id );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nonnull
|
|
||||||
@Override
|
|
||||||
public final T fromNetwork( @Nonnull ResourceLocation id, @Nonnull FriendlyByteBuf buffer )
|
|
||||||
{
|
|
||||||
return constructor.apply( id );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final void toNetwork( @Nonnull FriendlyByteBuf buffer, @Nonnull T upgrade )
|
public final T fromNetwork(ResourceLocation id, FriendlyByteBuf buffer) {
|
||||||
{
|
return constructor.apply(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final void toNetwork(FriendlyByteBuf buffer, T upgrade) {
|
||||||
}
|
}
|
||||||
}
|
}
|
36
projects/common/build.gradle.kts
Normal file
36
projects/common/build.gradle.kts
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
import cc.tweaked.gradle.annotationProcessorEverywhere
|
||||||
|
import cc.tweaked.gradle.clientClasses
|
||||||
|
import cc.tweaked.gradle.commonClasses
|
||||||
|
|
||||||
|
plugins {
|
||||||
|
id("cc-tweaked.vanilla")
|
||||||
|
id("cc-tweaked.gametest")
|
||||||
|
}
|
||||||
|
|
||||||
|
minecraft {
|
||||||
|
accessWideners(
|
||||||
|
"src/main/resources/computercraft.accesswidener",
|
||||||
|
"src/main/resources/computercraft-common.accesswidener",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
// Pull in our other projects. See comments in MinecraftConfigurations on this nastiness.
|
||||||
|
implementation(project(":core"))
|
||||||
|
implementation(commonClasses(project(":common-api")))
|
||||||
|
clientImplementation(clientClasses(project(":common-api")))
|
||||||
|
|
||||||
|
compileOnly(libs.bundles.externalMods.common)
|
||||||
|
|
||||||
|
compileOnly(libs.mixin)
|
||||||
|
annotationProcessorEverywhere(libs.autoService)
|
||||||
|
testFixturesAnnotationProcessor(libs.autoService)
|
||||||
|
|
||||||
|
testImplementation(testFixtures(project(":core")))
|
||||||
|
testImplementation(libs.bundles.test)
|
||||||
|
testRuntimeOnly(libs.bundles.testRuntime)
|
||||||
|
|
||||||
|
testModImplementation(testFixtures(project(":core")))
|
||||||
|
testModImplementation(testFixtures(project(":common")))
|
||||||
|
testModImplementation(libs.bundles.kotlin)
|
||||||
|
}
|
@@ -0,0 +1,190 @@
|
|||||||
|
/*
|
||||||
|
* 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.client;
|
||||||
|
|
||||||
|
import com.mojang.blaze3d.audio.Channel;
|
||||||
|
import com.mojang.blaze3d.vertex.PoseStack;
|
||||||
|
import dan200.computercraft.api.turtle.TurtleSide;
|
||||||
|
import dan200.computercraft.client.pocket.ClientPocketComputers;
|
||||||
|
import dan200.computercraft.client.render.CableHighlightRenderer;
|
||||||
|
import dan200.computercraft.client.render.PocketItemRenderer;
|
||||||
|
import dan200.computercraft.client.render.PrintoutItemRenderer;
|
||||||
|
import dan200.computercraft.client.render.monitor.MonitorHighlightRenderer;
|
||||||
|
import dan200.computercraft.client.render.monitor.MonitorRenderState;
|
||||||
|
import dan200.computercraft.client.sound.SpeakerManager;
|
||||||
|
import dan200.computercraft.shared.CommonHooks;
|
||||||
|
import dan200.computercraft.shared.ModRegistry;
|
||||||
|
import dan200.computercraft.shared.command.CommandComputerCraft;
|
||||||
|
import dan200.computercraft.shared.computer.core.ServerContext;
|
||||||
|
import dan200.computercraft.shared.media.items.PrintoutItem;
|
||||||
|
import dan200.computercraft.shared.peripheral.modem.wired.CableBlock;
|
||||||
|
import dan200.computercraft.shared.peripheral.modem.wired.CableModemVariant;
|
||||||
|
import dan200.computercraft.shared.peripheral.modem.wired.CableShapes;
|
||||||
|
import dan200.computercraft.shared.peripheral.monitor.MonitorBlockEntity;
|
||||||
|
import dan200.computercraft.shared.pocket.items.PocketComputerItem;
|
||||||
|
import dan200.computercraft.shared.turtle.blocks.TurtleBlockEntity;
|
||||||
|
import dan200.computercraft.shared.util.PauseAwareTimer;
|
||||||
|
import dan200.computercraft.shared.util.WorldUtil;
|
||||||
|
import net.minecraft.Util;
|
||||||
|
import net.minecraft.client.Camera;
|
||||||
|
import net.minecraft.client.Minecraft;
|
||||||
|
import net.minecraft.client.renderer.MultiBufferSource;
|
||||||
|
import net.minecraft.client.sounds.AudioStream;
|
||||||
|
import net.minecraft.client.sounds.SoundEngine;
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.world.InteractionHand;
|
||||||
|
import net.minecraft.world.entity.decoration.ItemFrame;
|
||||||
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
|
import net.minecraft.world.phys.BlockHitResult;
|
||||||
|
import net.minecraft.world.phys.HitResult;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Event listeners for client-only code.
|
||||||
|
* <p>
|
||||||
|
* This is the client-only version of {@link CommonHooks}, and so should be where all client-specific event handlers are
|
||||||
|
* defined.
|
||||||
|
*/
|
||||||
|
public final class ClientHooks {
|
||||||
|
private ClientHooks() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void onTick() {
|
||||||
|
FrameInfo.onTick();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void onRenderTick() {
|
||||||
|
PauseAwareTimer.tick(Minecraft.getInstance().isPaused());
|
||||||
|
FrameInfo.onRenderTick();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void onWorldUnload() {
|
||||||
|
MonitorRenderState.destroyAll();
|
||||||
|
SpeakerManager.reset();
|
||||||
|
ClientPocketComputers.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean onChatMessage(String message) {
|
||||||
|
return handleOpenComputerCommand(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean drawHighlight(PoseStack transform, MultiBufferSource bufferSource, Camera camera, BlockHitResult hit) {
|
||||||
|
return CableHighlightRenderer.drawHighlight(transform, bufferSource, camera, hit)
|
||||||
|
|| MonitorHighlightRenderer.drawHighlight(transform, bufferSource, camera, hit);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean onRenderHeldItem(
|
||||||
|
PoseStack transform, MultiBufferSource render, int lightTexture, InteractionHand hand,
|
||||||
|
float pitch, float equipProgress, float swingProgress, ItemStack stack
|
||||||
|
) {
|
||||||
|
if (stack.getItem() instanceof PocketComputerItem) {
|
||||||
|
PocketItemRenderer.INSTANCE.renderItemFirstPerson(transform, render, lightTexture, hand, pitch, equipProgress, swingProgress, stack);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (stack.getItem() instanceof PrintoutItem) {
|
||||||
|
PrintoutItemRenderer.INSTANCE.renderItemFirstPerson(transform, render, lightTexture, hand, pitch, equipProgress, swingProgress, stack);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean onRenderItemFrame(PoseStack transform, MultiBufferSource render, ItemFrame frame, ItemStack stack, int light) {
|
||||||
|
if (stack.getItem() instanceof PrintoutItem) {
|
||||||
|
PrintoutItemRenderer.onRenderInFrame(transform, render, frame, stack, light);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void onPlayStreaming(SoundEngine engine, Channel channel, AudioStream stream) {
|
||||||
|
SpeakerManager.onPlayStreaming(engine, channel, stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle the {@link CommandComputerCraft#OPEN_COMPUTER} "clientside command". This isn't a true command, as we
|
||||||
|
* don't want it to actually be visible to the user.
|
||||||
|
*
|
||||||
|
* @param message The current chat message.
|
||||||
|
* @return Whether to cancel sending this message.
|
||||||
|
*/
|
||||||
|
private static boolean handleOpenComputerCommand(String message) {
|
||||||
|
if (!message.startsWith(CommandComputerCraft.OPEN_COMPUTER)) return false;
|
||||||
|
|
||||||
|
var server = Minecraft.getInstance().getSingleplayerServer();
|
||||||
|
if (server == null) return false;
|
||||||
|
|
||||||
|
var idStr = message.substring(CommandComputerCraft.OPEN_COMPUTER.length()).trim();
|
||||||
|
int id;
|
||||||
|
try {
|
||||||
|
id = Integer.parseInt(idStr);
|
||||||
|
} catch (NumberFormatException ignore) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var file = new File(ServerContext.get(server).storageDir().toFile(), "computer/" + id);
|
||||||
|
if (!file.isDirectory()) return false;
|
||||||
|
|
||||||
|
Util.getPlatform().openFile(file);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add additional information about the currently targeted block to the debug screen.
|
||||||
|
*
|
||||||
|
* @param addText A callback which adds a single line of text.
|
||||||
|
*/
|
||||||
|
public static void addDebugInfo(Consumer<String> addText) {
|
||||||
|
var minecraft = Minecraft.getInstance();
|
||||||
|
if (!minecraft.options.renderDebug || minecraft.level == null) return;
|
||||||
|
if (minecraft.hitResult == null || minecraft.hitResult.getType() != HitResult.Type.BLOCK) return;
|
||||||
|
|
||||||
|
var tile = minecraft.level.getBlockEntity(((BlockHitResult) minecraft.hitResult).getBlockPos());
|
||||||
|
|
||||||
|
if (tile instanceof MonitorBlockEntity monitor) {
|
||||||
|
addText.accept("");
|
||||||
|
addText.accept(
|
||||||
|
String.format("Targeted monitor: (%d, %d), %d x %d", monitor.getXIndex(), monitor.getYIndex(), monitor.getWidth(), monitor.getHeight())
|
||||||
|
);
|
||||||
|
} else if (tile instanceof TurtleBlockEntity turtle) {
|
||||||
|
addText.accept("");
|
||||||
|
addText.accept("Targeted turtle:");
|
||||||
|
addText.accept(String.format("Id: %d", turtle.getComputerID()));
|
||||||
|
addTurtleUpgrade(addText, turtle, TurtleSide.LEFT);
|
||||||
|
addTurtleUpgrade(addText, turtle, TurtleSide.RIGHT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void addTurtleUpgrade(Consumer<String> out, TurtleBlockEntity turtle, TurtleSide side) {
|
||||||
|
var upgrade = turtle.getUpgrade(side);
|
||||||
|
if (upgrade != null) out.accept(String.format("Upgrade[%s]: %s", side, upgrade.getUpgradeID()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static @Nullable BlockState getBlockBreakingState(BlockState state, BlockPos pos) {
|
||||||
|
// Only apply to cables which have both a cable and modem
|
||||||
|
if (state.getBlock() != ModRegistry.Blocks.CABLE.get()
|
||||||
|
|| !state.getValue(CableBlock.CABLE)
|
||||||
|
|| state.getValue(CableBlock.MODEM) == CableModemVariant.None
|
||||||
|
) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var hit = Minecraft.getInstance().hitResult;
|
||||||
|
if (hit == null || hit.getType() != HitResult.Type.BLOCK) return null;
|
||||||
|
var hitPos = ((BlockHitResult) hit).getBlockPos();
|
||||||
|
|
||||||
|
if (!hitPos.equals(pos)) return null;
|
||||||
|
|
||||||
|
return WorldUtil.isVecInside(CableShapes.getModemShape(state), hit.getLocation().subtract(pos.getX(), pos.getY(), pos.getZ()))
|
||||||
|
? state.getBlock().defaultBlockState().setValue(CableBlock.MODEM, state.getValue(CableBlock.MODEM))
|
||||||
|
: state.setValue(CableBlock.MODEM, CableModemVariant.None);
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,198 @@
|
|||||||
|
/*
|
||||||
|
* 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.client;
|
||||||
|
|
||||||
|
import dan200.computercraft.api.ComputerCraftAPI;
|
||||||
|
import dan200.computercraft.api.client.ComputerCraftAPIClient;
|
||||||
|
import dan200.computercraft.api.client.turtle.TurtleUpgradeModeller;
|
||||||
|
import dan200.computercraft.client.gui.*;
|
||||||
|
import dan200.computercraft.client.pocket.ClientPocketComputers;
|
||||||
|
import dan200.computercraft.client.render.RenderTypes;
|
||||||
|
import dan200.computercraft.client.render.TurtleBlockEntityRenderer;
|
||||||
|
import dan200.computercraft.client.render.monitor.MonitorBlockEntityRenderer;
|
||||||
|
import dan200.computercraft.client.turtle.TurtleModemModeller;
|
||||||
|
import dan200.computercraft.core.util.Colour;
|
||||||
|
import dan200.computercraft.shared.ModRegistry;
|
||||||
|
import dan200.computercraft.shared.common.IColouredItem;
|
||||||
|
import dan200.computercraft.shared.computer.inventory.AbstractComputerMenu;
|
||||||
|
import dan200.computercraft.shared.computer.inventory.ViewComputerMenu;
|
||||||
|
import dan200.computercraft.shared.media.items.DiskItem;
|
||||||
|
import dan200.computercraft.shared.media.items.TreasureDiskItem;
|
||||||
|
import net.minecraft.client.color.item.ItemColor;
|
||||||
|
import net.minecraft.client.gui.screens.MenuScreens;
|
||||||
|
import net.minecraft.client.multiplayer.ClientLevel;
|
||||||
|
import net.minecraft.client.renderer.ShaderInstance;
|
||||||
|
import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider;
|
||||||
|
import net.minecraft.client.renderer.item.ClampedItemPropertyFunction;
|
||||||
|
import net.minecraft.client.renderer.item.ItemProperties;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import net.minecraft.server.packs.resources.ResourceProvider;
|
||||||
|
import net.minecraft.world.entity.LivingEntity;
|
||||||
|
import net.minecraft.world.item.Item;
|
||||||
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
import net.minecraft.world.level.ItemLike;
|
||||||
|
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||||
|
import net.minecraft.world.level.block.entity.BlockEntityType;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.function.BiConsumer;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers client-side objects, such as {@link BlockEntityRendererProvider}s and
|
||||||
|
* {@link MenuScreens.ScreenConstructor}.
|
||||||
|
* <p>
|
||||||
|
* The functions in this class should be called from a loader-specific class.
|
||||||
|
*
|
||||||
|
* @see ModRegistry The common registry for actual game objects.
|
||||||
|
*/
|
||||||
|
public final class ClientRegistry {
|
||||||
|
private ClientRegistry() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register any client-side objects which don't have to be done on the main thread.
|
||||||
|
*/
|
||||||
|
public static void register() {
|
||||||
|
ComputerCraftAPIClient.registerTurtleUpgradeModeller(ModRegistry.TurtleSerialisers.SPEAKER.get(), TurtleUpgradeModeller.sided(
|
||||||
|
new ResourceLocation(ComputerCraftAPI.MOD_ID, "block/turtle_speaker_left"),
|
||||||
|
new ResourceLocation(ComputerCraftAPI.MOD_ID, "block/turtle_speaker_right")
|
||||||
|
));
|
||||||
|
ComputerCraftAPIClient.registerTurtleUpgradeModeller(ModRegistry.TurtleSerialisers.WORKBENCH.get(), TurtleUpgradeModeller.sided(
|
||||||
|
new ResourceLocation(ComputerCraftAPI.MOD_ID, "block/turtle_crafting_table_left"),
|
||||||
|
new ResourceLocation(ComputerCraftAPI.MOD_ID, "block/turtle_crafting_table_right")
|
||||||
|
));
|
||||||
|
ComputerCraftAPIClient.registerTurtleUpgradeModeller(ModRegistry.TurtleSerialisers.WIRELESS_MODEM_NORMAL.get(), new TurtleModemModeller(false));
|
||||||
|
ComputerCraftAPIClient.registerTurtleUpgradeModeller(ModRegistry.TurtleSerialisers.WIRELESS_MODEM_ADVANCED.get(), new TurtleModemModeller(true));
|
||||||
|
ComputerCraftAPIClient.registerTurtleUpgradeModeller(ModRegistry.TurtleSerialisers.TOOL.get(), TurtleUpgradeModeller.flatItem());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register any client-side objects which must be done on the main thread.
|
||||||
|
*/
|
||||||
|
public static void registerMainThread() {
|
||||||
|
MenuScreens.<AbstractComputerMenu, ComputerScreen<AbstractComputerMenu>>register(ModRegistry.Menus.COMPUTER.get(), ComputerScreen::new);
|
||||||
|
MenuScreens.<AbstractComputerMenu, ComputerScreen<AbstractComputerMenu>>register(ModRegistry.Menus.POCKET_COMPUTER.get(), ComputerScreen::new);
|
||||||
|
MenuScreens.<AbstractComputerMenu, NoTermComputerScreen<AbstractComputerMenu>>register(ModRegistry.Menus.POCKET_COMPUTER_NO_TERM.get(), NoTermComputerScreen::new);
|
||||||
|
MenuScreens.register(ModRegistry.Menus.TURTLE.get(), TurtleScreen::new);
|
||||||
|
|
||||||
|
MenuScreens.register(ModRegistry.Menus.PRINTER.get(), PrinterScreen::new);
|
||||||
|
MenuScreens.register(ModRegistry.Menus.DISK_DRIVE.get(), DiskDriveScreen::new);
|
||||||
|
MenuScreens.register(ModRegistry.Menus.PRINTOUT.get(), PrintoutScreen::new);
|
||||||
|
|
||||||
|
MenuScreens.<ViewComputerMenu, ComputerScreen<ViewComputerMenu>>register(ModRegistry.Menus.VIEW_COMPUTER.get(), ComputerScreen::new);
|
||||||
|
|
||||||
|
registerItemProperty("state",
|
||||||
|
new UnclampedPropertyFunction((stack, world, player, random) -> ClientPocketComputers.get(stack).getState().ordinal()),
|
||||||
|
ModRegistry.Items.POCKET_COMPUTER_NORMAL, ModRegistry.Items.POCKET_COMPUTER_ADVANCED
|
||||||
|
);
|
||||||
|
registerItemProperty("coloured",
|
||||||
|
(stack, world, player, random) -> IColouredItem.getColourBasic(stack) != -1 ? 1 : 0,
|
||||||
|
ModRegistry.Items.POCKET_COMPUTER_NORMAL, ModRegistry.Items.POCKET_COMPUTER_ADVANCED
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SafeVarargs
|
||||||
|
private static void registerItemProperty(String name, ClampedItemPropertyFunction getter, Supplier<? extends Item>... items) {
|
||||||
|
var id = new ResourceLocation(ComputerCraftAPI.MOD_ID, name);
|
||||||
|
for (var item : items) ItemProperties.register(item.get(), id, getter);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final String[] EXTRA_MODELS = new String[]{
|
||||||
|
// Turtle upgrades
|
||||||
|
"block/turtle_modem_normal_off_left",
|
||||||
|
"block/turtle_modem_normal_on_left",
|
||||||
|
"block/turtle_modem_normal_off_right",
|
||||||
|
"block/turtle_modem_normal_on_right",
|
||||||
|
|
||||||
|
"block/turtle_modem_advanced_off_left",
|
||||||
|
"block/turtle_modem_advanced_on_left",
|
||||||
|
"block/turtle_modem_advanced_off_right",
|
||||||
|
"block/turtle_modem_advanced_on_right",
|
||||||
|
|
||||||
|
"block/turtle_crafting_table_left",
|
||||||
|
"block/turtle_crafting_table_right",
|
||||||
|
|
||||||
|
"block/turtle_speaker_left",
|
||||||
|
"block/turtle_speaker_right",
|
||||||
|
|
||||||
|
// Turtle block renderer
|
||||||
|
"block/turtle_colour",
|
||||||
|
"block/turtle_elf_overlay",
|
||||||
|
};
|
||||||
|
|
||||||
|
public static void registerExtraModels(Consumer<ResourceLocation> register) {
|
||||||
|
for (var model : EXTRA_MODELS) register.accept(new ResourceLocation(ComputerCraftAPI.MOD_ID, model));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void registerItemColours(BiConsumer<ItemColor, ItemLike> register) {
|
||||||
|
register.accept(
|
||||||
|
(stack, layer) -> layer == 1 ? ((DiskItem) stack.getItem()).getColour(stack) : 0xFFFFFF,
|
||||||
|
ModRegistry.Items.DISK.get()
|
||||||
|
);
|
||||||
|
|
||||||
|
register.accept(
|
||||||
|
(stack, layer) -> layer == 1 ? TreasureDiskItem.getColour(stack) : 0xFFFFFF,
|
||||||
|
ModRegistry.Items.TREASURE_DISK.get()
|
||||||
|
);
|
||||||
|
|
||||||
|
register.accept(ClientRegistry::getPocketColour, ModRegistry.Items.POCKET_COMPUTER_NORMAL.get());
|
||||||
|
register.accept(ClientRegistry::getPocketColour, ModRegistry.Items.POCKET_COMPUTER_ADVANCED.get());
|
||||||
|
|
||||||
|
register.accept(ClientRegistry::getTurtleColour, ModRegistry.Blocks.TURTLE_NORMAL.get());
|
||||||
|
register.accept(ClientRegistry::getTurtleColour, ModRegistry.Blocks.TURTLE_ADVANCED.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int getPocketColour(ItemStack stack, int layer) {
|
||||||
|
switch (layer) {
|
||||||
|
case 0:
|
||||||
|
default:
|
||||||
|
return 0xFFFFFF;
|
||||||
|
case 1: // Frame colour
|
||||||
|
return IColouredItem.getColourBasic(stack);
|
||||||
|
case 2: { // Light colour
|
||||||
|
var light = ClientPocketComputers.get(stack).getLightState();
|
||||||
|
return light == -1 ? Colour.BLACK.getHex() : light;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int getTurtleColour(ItemStack stack, int layer) {
|
||||||
|
return layer == 0 ? ((IColouredItem) stack.getItem()).getColour(stack) : 0xFFFFFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void registerBlockEntityRenderers(BlockEntityRenderRegistry register) {
|
||||||
|
register.register(ModRegistry.BlockEntities.MONITOR_NORMAL.get(), MonitorBlockEntityRenderer::new);
|
||||||
|
register.register(ModRegistry.BlockEntities.MONITOR_ADVANCED.get(), MonitorBlockEntityRenderer::new);
|
||||||
|
register.register(ModRegistry.BlockEntities.TURTLE_NORMAL.get(), TurtleBlockEntityRenderer::new);
|
||||||
|
register.register(ModRegistry.BlockEntities.TURTLE_ADVANCED.get(), TurtleBlockEntityRenderer::new);
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface BlockEntityRenderRegistry {
|
||||||
|
<T extends BlockEntity> void register(BlockEntityType<? extends T> type, BlockEntityRendererProvider<T> provider);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void registerShaders(ResourceProvider resources, BiConsumer<ShaderInstance, Consumer<ShaderInstance>> load) throws IOException {
|
||||||
|
RenderTypes.registerShaders(resources, load);
|
||||||
|
}
|
||||||
|
|
||||||
|
private record UnclampedPropertyFunction(
|
||||||
|
ClampedItemPropertyFunction function
|
||||||
|
) implements ClampedItemPropertyFunction {
|
||||||
|
@Override
|
||||||
|
public float unclampedCall(ItemStack stack, @Nullable ClientLevel level, @Nullable LivingEntity entity, int layer) {
|
||||||
|
return function.unclampedCall(stack, level, entity, layer);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
|
@Override
|
||||||
|
public float call(ItemStack stack, @Nullable ClientLevel level, @Nullable LivingEntity entity, int layer) {
|
||||||
|
return function.unclampedCall(stack, level, entity, layer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,81 @@
|
|||||||
|
/*
|
||||||
|
* 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.client;
|
||||||
|
|
||||||
|
import dan200.computercraft.shared.command.text.ChatHelpers;
|
||||||
|
import dan200.computercraft.shared.command.text.TableBuilder;
|
||||||
|
import dan200.computercraft.shared.command.text.TableFormatter;
|
||||||
|
import net.minecraft.ChatFormatting;
|
||||||
|
import net.minecraft.client.GuiMessageTag;
|
||||||
|
import net.minecraft.client.Minecraft;
|
||||||
|
import net.minecraft.client.gui.Font;
|
||||||
|
import net.minecraft.network.chat.Component;
|
||||||
|
import net.minecraft.util.Mth;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
public class ClientTableFormatter implements TableFormatter {
|
||||||
|
public static final ClientTableFormatter INSTANCE = new ClientTableFormatter();
|
||||||
|
|
||||||
|
private static Font renderer() {
|
||||||
|
return Minecraft.getInstance().font;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Nullable
|
||||||
|
public Component getPadding(Component component, int width) {
|
||||||
|
var extraWidth = width - getWidth(component);
|
||||||
|
if (extraWidth <= 0) return null;
|
||||||
|
|
||||||
|
var renderer = renderer();
|
||||||
|
|
||||||
|
float spaceWidth = renderer.width(" ");
|
||||||
|
var spaces = Mth.floor(extraWidth / spaceWidth);
|
||||||
|
var extra = extraWidth - (int) (spaces * spaceWidth);
|
||||||
|
|
||||||
|
return ChatHelpers.coloured(StringUtils.repeat(' ', spaces) + StringUtils.repeat((char) 712, extra), ChatFormatting.GRAY);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getColumnPadding() {
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getWidth(Component component) {
|
||||||
|
return renderer().width(component);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeLine(String label, Component component) {
|
||||||
|
var mc = Minecraft.getInstance();
|
||||||
|
var chat = mc.gui.getChat();
|
||||||
|
|
||||||
|
// TODO: Trim the text if it goes over the allowed length
|
||||||
|
// int maxWidth = MathHelper.floor( chat.getChatWidth() / chat.getScale() );
|
||||||
|
// List<ITextProperties> list = RenderComponentsUtil.wrapComponents( component, maxWidth, mc.fontRenderer );
|
||||||
|
// if( !list.isEmpty() ) chat.printChatMessageWithOptionalDeletion( list.get( 0 ), id );
|
||||||
|
chat.addMessage(component, null, createTag(label));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void display(TableBuilder table) {
|
||||||
|
var chat = Minecraft.getInstance().gui.getChat();
|
||||||
|
|
||||||
|
var tag = createTag(table.getId());
|
||||||
|
if (chat.allMessages.removeIf(guiMessage -> guiMessage.tag() != null && Objects.equals(guiMessage.tag().logTag(), tag.logTag()))) {
|
||||||
|
chat.refreshTrimmedMessage();
|
||||||
|
}
|
||||||
|
|
||||||
|
TableFormatter.super.display(table);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static GuiMessageTag createTag(String id) {
|
||||||
|
return new GuiMessageTag(0xa0a0a0, null, null, "ComputerCraft/" + id);
|
||||||
|
}
|
||||||
|
}
|
@@ -12,14 +12,10 @@ import dan200.computercraft.api.turtle.TurtleUpgradeSerialiser;
|
|||||||
import dan200.computercraft.client.turtle.TurtleUpgradeModellers;
|
import dan200.computercraft.client.turtle.TurtleUpgradeModellers;
|
||||||
import dan200.computercraft.impl.client.ComputerCraftAPIClientService;
|
import dan200.computercraft.impl.client.ComputerCraftAPIClientService;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
@AutoService(ComputerCraftAPIClientService.class)
|
||||||
|
public final class ComputerCraftAPIClientImpl implements ComputerCraftAPIClientService {
|
||||||
@AutoService( ComputerCraftAPIClientService.class )
|
|
||||||
public final class ComputerCraftAPIClientImpl implements ComputerCraftAPIClientService
|
|
||||||
{
|
|
||||||
@Override
|
@Override
|
||||||
public <T extends ITurtleUpgrade> void registerTurtleUpgradeModeller( @Nonnull TurtleUpgradeSerialiser<T> serialiser, @Nonnull TurtleUpgradeModeller<T> modeller )
|
public <T extends ITurtleUpgrade> void registerTurtleUpgradeModeller(TurtleUpgradeSerialiser<T> serialiser, TurtleUpgradeModeller<T> modeller) {
|
||||||
{
|
TurtleUpgradeModellers.register(serialiser, modeller);
|
||||||
TurtleUpgradeModellers.register( serialiser, modeller );
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -0,0 +1,30 @@
|
|||||||
|
/*
|
||||||
|
* 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.client;
|
||||||
|
|
||||||
|
public final class FrameInfo {
|
||||||
|
private static int tick;
|
||||||
|
private static long renderFrame;
|
||||||
|
|
||||||
|
private FrameInfo() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean getGlobalCursorBlink() {
|
||||||
|
return (tick / 8) % 2 == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static long getRenderFrame() {
|
||||||
|
return renderFrame;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void onTick() {
|
||||||
|
tick++;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void onRenderTick() {
|
||||||
|
renderFrame++;
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,219 @@
|
|||||||
|
/*
|
||||||
|
* 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.client.gui;
|
||||||
|
|
||||||
|
import com.mojang.blaze3d.vertex.PoseStack;
|
||||||
|
import dan200.computercraft.client.gui.widgets.ComputerSidebar;
|
||||||
|
import dan200.computercraft.client.gui.widgets.DynamicImageButton;
|
||||||
|
import dan200.computercraft.client.gui.widgets.TerminalWidget;
|
||||||
|
import dan200.computercraft.client.platform.ClientPlatformHelper;
|
||||||
|
import dan200.computercraft.core.terminal.Terminal;
|
||||||
|
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
||||||
|
import dan200.computercraft.shared.computer.core.InputHandler;
|
||||||
|
import dan200.computercraft.shared.computer.inventory.AbstractComputerMenu;
|
||||||
|
import dan200.computercraft.shared.computer.upload.FileUpload;
|
||||||
|
import dan200.computercraft.shared.computer.upload.UploadResult;
|
||||||
|
import dan200.computercraft.shared.config.Config;
|
||||||
|
import dan200.computercraft.shared.network.server.UploadFileMessage;
|
||||||
|
import net.minecraft.ChatFormatting;
|
||||||
|
import net.minecraft.Util;
|
||||||
|
import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen;
|
||||||
|
import net.minecraft.network.chat.Component;
|
||||||
|
import net.minecraft.world.entity.player.Inventory;
|
||||||
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
import org.lwjgl.glfw.GLFW;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import static dan200.computercraft.core.util.Nullability.assertNonNull;
|
||||||
|
|
||||||
|
public abstract class AbstractComputerScreen<T extends AbstractComputerMenu> extends AbstractContainerScreen<T> {
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(AbstractComputerScreen.class);
|
||||||
|
|
||||||
|
private static final Component OK = Component.translatable("gui.ok");
|
||||||
|
private static final Component NO_RESPONSE_TITLE = Component.translatable("gui.computercraft.upload.no_response");
|
||||||
|
private static final Component NO_RESPONSE_MSG = Component.translatable("gui.computercraft.upload.no_response.msg",
|
||||||
|
Component.literal("import").withStyle(ChatFormatting.DARK_GRAY));
|
||||||
|
|
||||||
|
protected @Nullable TerminalWidget terminal;
|
||||||
|
protected Terminal terminalData;
|
||||||
|
protected final ComputerFamily family;
|
||||||
|
protected final InputHandler input;
|
||||||
|
|
||||||
|
protected final int sidebarYOffset;
|
||||||
|
|
||||||
|
private long uploadNagDeadline = Long.MAX_VALUE;
|
||||||
|
private final ItemStack displayStack;
|
||||||
|
|
||||||
|
public AbstractComputerScreen(T container, Inventory player, Component title, int sidebarYOffset) {
|
||||||
|
super(container, player, title);
|
||||||
|
terminalData = container.getTerminal();
|
||||||
|
family = container.getFamily();
|
||||||
|
displayStack = container.getDisplayStack();
|
||||||
|
input = new ClientInputHandler(menu);
|
||||||
|
this.sidebarYOffset = sidebarYOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract TerminalWidget createTerminal();
|
||||||
|
|
||||||
|
protected final TerminalWidget getTerminal() {
|
||||||
|
if (terminal == null) throw new IllegalStateException("Screen has not been initialised yet");
|
||||||
|
return terminal;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void init() {
|
||||||
|
super.init();
|
||||||
|
|
||||||
|
terminal = addRenderableWidget(createTerminal());
|
||||||
|
ComputerSidebar.addButtons(menu::isOn, input, this::addRenderableWidget, leftPos, topPos + sidebarYOffset);
|
||||||
|
setFocused(terminal);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void containerTick() {
|
||||||
|
super.containerTick();
|
||||||
|
getTerminal().update();
|
||||||
|
|
||||||
|
if (uploadNagDeadline != Long.MAX_VALUE && Util.getNanos() >= uploadNagDeadline) {
|
||||||
|
new ItemToast(minecraft, displayStack, NO_RESPONSE_TITLE, NO_RESPONSE_MSG, ItemToast.TRANSFER_NO_RESPONSE_TOKEN)
|
||||||
|
.showOrReplace(minecraft.getToasts());
|
||||||
|
uploadNagDeadline = Long.MAX_VALUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean keyPressed(int key, int scancode, int modifiers) {
|
||||||
|
// Forward the tab key to the terminal, rather than moving between controls.
|
||||||
|
if (key == GLFW.GLFW_KEY_TAB && getFocused() != null && getFocused() == terminal) {
|
||||||
|
return getFocused().keyPressed(key, scancode, modifiers);
|
||||||
|
}
|
||||||
|
|
||||||
|
return super.keyPressed(key, scancode, modifiers);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean mouseReleased(double x, double y, int button) {
|
||||||
|
// Reimplement ContainerEventHandler.mouseReleased, as it's not called in vanilla (it is in Forge, but that
|
||||||
|
// shouldn't matter).
|
||||||
|
setDragging(false);
|
||||||
|
var child = getChildAt(x, y);
|
||||||
|
if (child.isPresent() && child.get().mouseReleased(x, y, button)) return true;
|
||||||
|
|
||||||
|
return super.mouseReleased(x, y, button);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void render(PoseStack stack, int mouseX, int mouseY, float partialTicks) {
|
||||||
|
renderBackground(stack);
|
||||||
|
super.render(stack, mouseX, mouseY, partialTicks);
|
||||||
|
renderTooltip(stack, mouseX, mouseY);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean mouseClicked(double x, double y, int button) {
|
||||||
|
var changed = super.mouseClicked(x, y, button);
|
||||||
|
// Clicking the terminate/shutdown button steals focus, which means then pressing "enter" will click the button
|
||||||
|
// again. Restore the focus to the terminal in these cases.
|
||||||
|
if (getFocused() instanceof DynamicImageButton) setFocused(terminal);
|
||||||
|
return changed;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean mouseDragged(double x, double y, int button, double deltaX, double deltaY) {
|
||||||
|
return (getFocused() != null && getFocused().mouseDragged(x, y, button, deltaX, deltaY))
|
||||||
|
|| super.mouseDragged(x, y, button, deltaX, deltaY);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void renderLabels(PoseStack transform, int mouseX, int mouseY) {
|
||||||
|
// Skip rendering labels.
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFilesDrop(List<Path> files) {
|
||||||
|
if (files.isEmpty()) return;
|
||||||
|
|
||||||
|
if (!menu.isOn()) {
|
||||||
|
alert(UploadResult.FAILED_TITLE, UploadResult.COMPUTER_OFF_MSG);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
long size = 0;
|
||||||
|
|
||||||
|
List<FileUpload> toUpload = new ArrayList<>();
|
||||||
|
for (var file : files) {
|
||||||
|
// TODO: Recurse directories? If so, we probably want to shunt this off-thread.
|
||||||
|
if (!Files.isRegularFile(file)) continue;
|
||||||
|
|
||||||
|
try (var sbc = Files.newByteChannel(file)) {
|
||||||
|
var fileSize = sbc.size();
|
||||||
|
if (fileSize > UploadFileMessage.MAX_SIZE || (size += fileSize) >= UploadFileMessage.MAX_SIZE) {
|
||||||
|
alert(UploadResult.FAILED_TITLE, UploadResult.TOO_MUCH_MSG);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var name = file.getFileName().toString();
|
||||||
|
if (name.length() > UploadFileMessage.MAX_FILE_NAME) {
|
||||||
|
alert(UploadResult.FAILED_TITLE, Component.translatable("gui.computercraft.upload.failed.name_too_long"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var buffer = ByteBuffer.allocateDirect((int) fileSize);
|
||||||
|
sbc.read(buffer);
|
||||||
|
buffer.flip();
|
||||||
|
|
||||||
|
var digest = FileUpload.getDigest(buffer);
|
||||||
|
if (digest == null) {
|
||||||
|
alert(UploadResult.FAILED_TITLE, Component.translatable("gui.computercraft.upload.failed.corrupted"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
toUpload.add(new FileUpload(name, buffer, digest));
|
||||||
|
} catch (IOException e) {
|
||||||
|
LOG.error("Failed uploading files", e);
|
||||||
|
alert(UploadResult.FAILED_TITLE, Component.translatable("gui.computercraft.upload.failed.generic", "Cannot compute checksum"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (toUpload.size() > UploadFileMessage.MAX_FILES) {
|
||||||
|
alert(UploadResult.FAILED_TITLE, Component.translatable("gui.computercraft.upload.failed.too_many_files"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (toUpload.size() > 0) UploadFileMessage.send(menu, toUpload, ClientPlatformHelper.get()::sendToServer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void uploadResult(UploadResult result, @Nullable Component message) {
|
||||||
|
switch (result) {
|
||||||
|
case QUEUED -> {
|
||||||
|
if (Config.uploadNagDelay > 0) {
|
||||||
|
uploadNagDeadline = Util.getNanos() + TimeUnit.SECONDS.toNanos(Config.uploadNagDelay);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case CONSUMED -> uploadNagDeadline = Long.MAX_VALUE;
|
||||||
|
case ERROR -> alert(UploadResult.FAILED_TITLE, assertNonNull(message));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void alert(Component title, Component message) {
|
||||||
|
OptionScreen.show(minecraft, title, message,
|
||||||
|
Collections.singletonList(OptionScreen.newButton(OK, b -> minecraft.setScreen(this))),
|
||||||
|
() -> minecraft.setScreen(this)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user