mirror of
https://github.com/SquidDev-CC/CC-Tweaked
synced 2025-10-17 23:17:38 +00:00
Compare commits
126 Commits
v1.21.1-1.
...
v1.21.1-1.
Author | SHA1 | Date | |
---|---|---|---|
![]() |
fbf994e803 | ||
![]() |
8344c0a5c2 | ||
![]() |
a292d33830 | ||
![]() |
341d1c7bc2 | ||
![]() |
531eacfac7 | ||
![]() |
1f3da5205c | ||
![]() |
798ceefafe | ||
![]() |
7c0f79fc3c | ||
![]() |
b35cefc5dd | ||
![]() |
89dd521930 | ||
![]() |
9272e2efcd | ||
![]() |
69353a4fcf | ||
![]() |
ff363dca5a | ||
![]() |
1c51282426 | ||
![]() |
4a3a1c9275 | ||
![]() |
2557dd0af9 | ||
![]() |
b5c0c6e104 | ||
![]() |
876fd8ddb8 | ||
![]() |
ee3b1343b5 | ||
![]() |
b440b964b7 | ||
![]() |
5dfc401b45 | ||
![]() |
418c9be7ac | ||
![]() |
b491f6b11f | ||
![]() |
4344c3072f | ||
![]() |
947001104d | ||
![]() |
8711512769 | ||
![]() |
fdae94b3c1 | ||
![]() |
9c0ce27ce6 | ||
![]() |
c458360b18 | ||
![]() |
09ad6c1905 | ||
![]() |
0e1e8a72d3 | ||
![]() |
ffa6eadc26 | ||
![]() |
7c1e8e1951 | ||
![]() |
b805a34c2d | ||
![]() |
b03546a158 | ||
![]() |
582713467f | ||
![]() |
b6f41a0df5 | ||
![]() |
594738a022 | ||
![]() |
27f2ab364c | ||
![]() |
5a43273757 | ||
![]() |
97e28516fb | ||
![]() |
676fb5fb53 | ||
![]() |
08dc08b5a3 | ||
![]() |
8f4d4038f6 | ||
![]() |
63ba3fe274 | ||
![]() |
749b3df227 | ||
![]() |
b97634b717 | ||
![]() |
1b8344d0a3 | ||
![]() |
b42bc0a01a | ||
![]() |
70a7478529 | ||
![]() |
0cff73e2fc | ||
![]() |
a892739f8e | ||
![]() |
f8785a092f | ||
![]() |
598fc4aefd | ||
![]() |
dd7e8fcefc | ||
![]() |
29c8f96912 | ||
![]() |
b9267ecbfc | ||
![]() |
9d2c2db22b | ||
![]() |
6660966320 | ||
![]() |
3acb231f01 | ||
![]() |
16324e1eac | ||
![]() |
fa33949113 | ||
![]() |
0c04d9de47 | ||
![]() |
32f5c38485 | ||
![]() |
01fe949b3e | ||
![]() |
c03fce275e | ||
![]() |
0998acaa82 | ||
![]() |
12a44fed6f | ||
![]() |
3f8c3b026a | ||
![]() |
0a8d505323 | ||
![]() |
237a0ac3bb | ||
![]() |
b185d088b3 | ||
![]() |
051c70a731 | ||
![]() |
2e2f308ff3 | ||
![]() |
0f123b5efd | ||
![]() |
1278246cf7 | ||
![]() |
88cb03be6b | ||
![]() |
1e25fa9bc3 | ||
![]() |
74f707aaea | ||
![]() |
9bb62b047a | ||
![]() |
4360485880 | ||
![]() |
b69a44a927 | ||
![]() |
7d8f609c49 | ||
![]() |
e7f56c4d25 | ||
![]() |
fa2140d00b | ||
![]() |
03388149b1 | ||
![]() |
f212861370 | ||
![]() |
4f3663ccc9 | ||
![]() |
53425c1e76 | ||
![]() |
55edced9de | ||
![]() |
dc969c5a78 | ||
![]() |
94ad6dab0e | ||
![]() |
938eb38ad5 | ||
![]() |
6739c4c6c0 | ||
![]() |
d6749f8461 | ||
![]() |
d697c47b80 | ||
![]() |
5ba7f99326 | ||
![]() |
4710ee5bcc | ||
![]() |
62c9e5b08f | ||
![]() |
2ca5850060 | ||
![]() |
ed631b05e7 | ||
![]() |
a2b9490d5c | ||
![]() |
8204944b5f | ||
![]() |
ef0af67e96 | ||
![]() |
9a914e75c4 | ||
![]() |
4a532952d4 | ||
![]() |
546577041b | ||
![]() |
f881c0ced0 | ||
![]() |
0b389e04b0 | ||
![]() |
d3a3ab3c21 | ||
![]() |
22e6c06e59 | ||
![]() |
7337b91692 | ||
![]() |
3c46b8acd7 | ||
![]() |
d9fc1c3a80 | ||
![]() |
479aabdd09 | ||
![]() |
ad74893058 | ||
![]() |
2ba6d5815b | ||
![]() |
7e2f490626 | ||
![]() |
4dc649d5e5 | ||
![]() |
5bab415790 | ||
![]() |
f04c699df6 | ||
![]() |
9bbf3f3e1d | ||
![]() |
a3f8e653d4 | ||
![]() |
7c02979c22 | ||
![]() |
fdb65c9368 | ||
![]() |
ea670cc358 |
@@ -18,11 +18,6 @@ ij_any_if_brace_force = if_multiline
|
|||||||
ij_any_for_brace_force = if_multiline
|
ij_any_for_brace_force = if_multiline
|
||||||
ij_any_spaces_within_array_initializer_braces = true
|
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
|
||||||
|
|
||||||
@@ -31,3 +26,27 @@ indent_size = 2
|
|||||||
|
|
||||||
[*.yml]
|
[*.yml]
|
||||||
indent_size = 2
|
indent_size = 2
|
||||||
|
|
||||||
|
[*.{kt,kts}]
|
||||||
|
ij_kotlin_code_style_defaults = KOTLIN_OFFICIAL
|
||||||
|
ij_kotlin_continuation_indent_size = 4
|
||||||
|
ij_kotlin_spaces_around_equality_operators = true
|
||||||
|
|
||||||
|
ij_kotlin_allow_trailing_comma = true
|
||||||
|
ij_kotlin_allow_trailing_comma_on_call_site = true
|
||||||
|
|
||||||
|
# Prefer to handle these manually
|
||||||
|
ij_kotlin_method_parameters_wrap = off
|
||||||
|
ij_kotlin_call_parameters_wrap = off
|
||||||
|
ij_kotlin_extends_list_wrap = off
|
||||||
|
|
||||||
|
ktlint_code_style = intellij_idea
|
||||||
|
ktlint_standard_class-naming = disabled
|
||||||
|
ktlint_standard_class-signature = disabled
|
||||||
|
ktlint_standard_function-naming = disabled
|
||||||
|
ktlint_standard_no-wildcard-imports = disabled
|
||||||
|
|
||||||
|
# FIXME: These two are disable right now as they're over-eager in putting things
|
||||||
|
# on the same line. We should set max_line_length and handle this properly.
|
||||||
|
ktlint_standard_function-signature = disabled
|
||||||
|
ktlint_standard_function-expression-body = disabled
|
||||||
|
3
.github/ISSUE_TEMPLATE/bug_report.yaml
vendored
3
.github/ISSUE_TEMPLATE/bug_report.yaml
vendored
@@ -1,6 +1,7 @@
|
|||||||
name: Bug report
|
name: Bug report
|
||||||
description: Report some misbehaviour in the mod
|
description: Report some misbehaviour in the mod
|
||||||
labels: [ bug ]
|
labels: [ bug ]
|
||||||
|
type: bug
|
||||||
body:
|
body:
|
||||||
- type: dropdown
|
- type: dropdown
|
||||||
id: mc-version
|
id: mc-version
|
||||||
@@ -29,3 +30,5 @@ body:
|
|||||||
Description of the bug. Please include the following:
|
Description of the bug. Please include the following:
|
||||||
- Logs: These will be located in the `logs/` directory of your Minecraft instance. This is always useful, even if it doesn't include errors, so please upload this!
|
- Logs: These will be located in the `logs/` directory of your Minecraft instance. This is always useful, even if it doesn't include errors, so please upload this!
|
||||||
- Detailed reproduction steps: sometimes I can spot a bug pretty easily, but often it's much more obscure. The more information I have to help reproduce it, the quicker it'll get fixed.
|
- Detailed reproduction steps: sometimes I can spot a bug pretty easily, but often it's much more obscure. The more information I have to help reproduce it, the quicker it'll get fixed.
|
||||||
|
|
||||||
|

|
||||||
|
1
.github/ISSUE_TEMPLATE/feature_request.md
vendored
1
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@@ -2,6 +2,7 @@
|
|||||||
name: Feature request
|
name: Feature request
|
||||||
about: Suggest an idea or improvement
|
about: Suggest an idea or improvement
|
||||||
labels: enhancement
|
labels: enhancement
|
||||||
|
type: feature
|
||||||
---
|
---
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
|
24
.github/workflows/main-ci.yml
vendored
24
.github/workflows/main-ci.yml
vendored
@@ -30,6 +30,18 @@ jobs:
|
|||||||
- name: ⚒️ Build
|
- name: ⚒️ Build
|
||||||
run: ./gradlew assemble || ./gradlew assemble
|
run: ./gradlew assemble || ./gradlew assemble
|
||||||
|
|
||||||
|
- name: 📦 Prepare Jars
|
||||||
|
run: |
|
||||||
|
# Find the main jar and append the git hash onto it.
|
||||||
|
mkdir -p jars
|
||||||
|
find projects/forge/build/libs projects/fabric/build/libs -type f -regex '.*[0-9.]+\(-SNAPSHOT\)?\.jar$' -exec bash -c 'cp {} "jars/$(basename {} .jar)-$(git rev-parse HEAD).jar"' \;
|
||||||
|
|
||||||
|
- name: 📤 Upload Jar
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: CC-Tweaked
|
||||||
|
path: ./jars
|
||||||
|
|
||||||
- name: Cache pre-commit
|
- name: Cache pre-commit
|
||||||
uses: actions/cache@v4
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
@@ -54,18 +66,6 @@ jobs:
|
|||||||
run: ./tools/parse-reports.py
|
run: ./tools/parse-reports.py
|
||||||
if: ${{ failure() }}
|
if: ${{ failure() }}
|
||||||
|
|
||||||
- name: 📦 Prepare Jars
|
|
||||||
run: |
|
|
||||||
# Find the main jar and append the git hash onto it.
|
|
||||||
mkdir -p jars
|
|
||||||
find projects/forge/build/libs projects/fabric/build/libs -type f -regex '.*[0-9.]+\(-SNAPSHOT\)?\.jar$' -exec bash -c 'cp {} "jars/$(basename {} .jar)-$(git rev-parse HEAD).jar"' \;
|
|
||||||
|
|
||||||
- name: 📤 Upload Jar
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: CC-Tweaked
|
|
||||||
path: ./jars
|
|
||||||
|
|
||||||
build-core:
|
build-core:
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@@ -27,6 +27,7 @@
|
|||||||
*.iml
|
*.iml
|
||||||
.idea
|
.idea
|
||||||
.gradle
|
.gradle
|
||||||
|
.kotlin
|
||||||
*.DS_Store
|
*.DS_Store
|
||||||
|
|
||||||
/.classpath
|
/.classpath
|
||||||
|
@@ -6,7 +6,7 @@
|
|||||||
# See https://pre-commit.com/hooks.html for more hooks
|
# See https://pre-commit.com/hooks.html for more hooks
|
||||||
repos:
|
repos:
|
||||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||||
rev: v4.4.0
|
rev: v5.0.0
|
||||||
hooks:
|
hooks:
|
||||||
- id: trailing-whitespace
|
- id: trailing-whitespace
|
||||||
- id: end-of-file-fixer
|
- id: end-of-file-fixer
|
||||||
@@ -27,7 +27,7 @@ repos:
|
|||||||
exclude: "^(.*\\.(bat)|LICENSE)$"
|
exclude: "^(.*\\.(bat)|LICENSE)$"
|
||||||
|
|
||||||
- repo: https://github.com/fsfe/reuse-tool
|
- repo: https://github.com/fsfe/reuse-tool
|
||||||
rev: v4.0.3
|
rev: v5.0.2
|
||||||
hooks:
|
hooks:
|
||||||
- id: reuse
|
- id: reuse
|
||||||
|
|
||||||
@@ -58,6 +58,7 @@ repos:
|
|||||||
exclude: |
|
exclude: |
|
||||||
(?x)^(
|
(?x)^(
|
||||||
projects/[a-z]+/src/generated|
|
projects/[a-z]+/src/generated|
|
||||||
|
projects/[a-z]+/src/examples/generatedResources|
|
||||||
projects/core/src/test/resources/test-rom/data/json-parsing/|
|
projects/core/src/test/resources/test-rom/data/json-parsing/|
|
||||||
.*\.dfpwm
|
.*\.dfpwm
|
||||||
)
|
)
|
||||||
|
@@ -22,8 +22,7 @@ If you have a bug, suggestion, or other feedback, the best thing to do is [file
|
|||||||
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 [CrowdIn], an online interface for managing language strings. Translations may either
|
Translations are managed through [CrowdIn], an online interface for managing language strings.
|
||||||
be contributed there, or directly via a pull request.
|
|
||||||
|
|
||||||
## Setting up a development environment
|
## Setting up a development environment
|
||||||
In order to develop CC: Tweaked, you'll need to download the source code and then run it.
|
In order to develop CC: Tweaked, you'll need to download the source code and then run it.
|
||||||
@@ -49,9 +48,12 @@ If you want to run CC:T in a normal Minecraft instance, run `./gradlew assemble`
|
|||||||
`projects/forge/build/libs` (for Forge) or `projects/fabric/build/libs` (for Fabric).
|
`projects/forge/build/libs` (for Forge) or `projects/fabric/build/libs` (for Fabric).
|
||||||
|
|
||||||
## Developing CC: Tweaked
|
## Developing CC: Tweaked
|
||||||
Before making any major changes to CC: Tweaked, I'd recommend you have a read of the [the architecture
|
Before making any major changes to CC: Tweaked, I'd recommend starting opening an issue or starting a discussion on
|
||||||
document][architecture] first. While it's not a comprehensive document, it gives a good hint of where you should start
|
GitHub first. It's often helpful to discuss features before spending time developing them!
|
||||||
looking to make your changes. As always, if you're not sure, [do ask the community][community]!
|
|
||||||
|
Once you're ready to start programming, have a read of the [the architecture document][architecture] first. While it's
|
||||||
|
not a comprehensive document, it gives a good hint of where you should start looking to make your changes. As always, if
|
||||||
|
you're not sure, [do ask the community][community]!
|
||||||
|
|
||||||
### Testing
|
### Testing
|
||||||
When making larger changes, it may be useful to write a test to make sure your code works as expected.
|
When making larger changes, it may be useful to write a test to make sure your code works as expected.
|
||||||
@@ -86,8 +88,8 @@ You'll first need to [set up a development environment as above](#setting-up-a-d
|
|||||||
|
|
||||||
Once this is set up, you can now run `./gradlew docWebsite`. This generates documentation from our Lua and Java code,
|
Once this is set up, you can now run `./gradlew docWebsite`. This generates documentation from our Lua and Java code,
|
||||||
writing the resulting HTML into `./projects/web/build/site`, which can then be opened in a browser. When iterating on
|
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
|
documentation, you can instead run `./gradlew :web:assemble -x :web:compileTeaVM -t`, which will rebuild documentation
|
||||||
file.
|
every time you change a file.
|
||||||
|
|
||||||
Documentation is built using [illuaminate] which, while not currently documented (somewhat ironic), is largely the same
|
Documentation is built using [illuaminate] which, while not currently documented (somewhat ironic), is largely the same
|
||||||
as [ldoc][ldoc]. Documentation comments are written in Markdown, though note that we do not support many GitHub-specific
|
as [ldoc][ldoc]. Documentation comments are written in Markdown, though note that we do not support many GitHub-specific
|
||||||
|
13
README.md
13
README.md
@@ -60,19 +60,6 @@ dependencies {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
When using ForgeGradle, you may also need to add the following:
|
|
||||||
|
|
||||||
```groovy
|
|
||||||
minecraft {
|
|
||||||
runs {
|
|
||||||
configureEach {
|
|
||||||
property 'mixin.env.remapRefMap', 'true'
|
|
||||||
property 'mixin.env.refMapRemappingFile', "${buildDir}/createSrgToMcp/output.srg"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
You should also be careful to only use classes within the `dan200.computercraft.api` package. Non-API classes are
|
You should also be careful to only use classes within the `dan200.computercraft.api` package. Non-API classes are
|
||||||
subject to change at any point. If you depend on functionality outside the API (or need to mixin to CC:T), please file
|
subject to change at any point. If you depend on functionality outside the API (or need to mixin to CC:T), please file
|
||||||
an issue to let me know!
|
an issue to let me know!
|
||||||
|
33
REUSE.toml
33
REUSE.toml
@@ -8,18 +8,23 @@ SPDX-PackageSupplier = "Jonathan Coates <git@squiddev.cc>"
|
|||||||
SPDX-PackageDownloadLocation = "https://github.com/cc-tweaked/cc-tweaked"
|
SPDX-PackageDownloadLocation = "https://github.com/cc-tweaked/cc-tweaked"
|
||||||
|
|
||||||
[[annotations]]
|
[[annotations]]
|
||||||
# Generated/data files are CC0.
|
|
||||||
SPDX-FileCopyrightText = "The CC: Tweaked Developers"
|
SPDX-FileCopyrightText = "The CC: Tweaked Developers"
|
||||||
SPDX-License-Identifier = "CC0-1.0"
|
SPDX-License-Identifier = "CC0-1.0"
|
||||||
path = [
|
path = [
|
||||||
|
# Generated/data files are CC0.
|
||||||
"gradle/gradle-daemon-jvm.properties",
|
"gradle/gradle-daemon-jvm.properties",
|
||||||
"projects/common/src/main/resources/assets/computercraft/sounds.json",
|
"projects/common/src/main/resources/assets/computercraft/sounds.json",
|
||||||
"projects/common/src/main/resources/assets/computercraft/sounds/empty.ogg",
|
"projects/common/src/main/resources/assets/computercraft/sounds/empty.ogg",
|
||||||
"projects/common/src/testMod/resources/data/cctest/computercraft/turtle_upgrade/**",
|
"projects/common/src/testMod/resources/data/cctest/computercraft/turtle_upgrade/**",
|
||||||
"projects/common/src/testMod/resources/data/cctest/structures/**",
|
"projects/common/src/testMod/resources/data/cctest/structures/**",
|
||||||
"projects/**/src/generated/**",
|
"projects/*/src/generated/**",
|
||||||
"projects/web/src/htmlTransform/export/index.json",
|
"projects/web/src/htmlTransform/export/index.json",
|
||||||
"projects/web/src/htmlTransform/export/items/minecraft/**",
|
"projects/web/src/htmlTransform/export/items/minecraft/**",
|
||||||
|
# GitHub build scripts are CC0. While we could add a header to each file,
|
||||||
|
# it's unclear if it will break actions or issue templates in some way.
|
||||||
|
".github/**",
|
||||||
|
# Example mod is CC0.
|
||||||
|
"projects/*/src/examples/**"
|
||||||
]
|
]
|
||||||
|
|
||||||
[[annotations]]
|
[[annotations]]
|
||||||
@@ -30,24 +35,15 @@ path = [
|
|||||||
"doc/images/**",
|
"doc/images/**",
|
||||||
"package.json",
|
"package.json",
|
||||||
"package-lock.json",
|
"package-lock.json",
|
||||||
"projects/common/src/client/resources/computercraft-client.mixins.json",
|
"projects/*/src/*/resources/*.mixins.json",
|
||||||
|
"projects/fabric/src/*/resources/fabric.mod.json",
|
||||||
"projects/common/src/main/resources/assets/minecraft/shaders/core/computercraft/monitor_tbo.json",
|
"projects/common/src/main/resources/assets/minecraft/shaders/core/computercraft/monitor_tbo.json",
|
||||||
"projects/common/src/main/resources/computercraft.mixins.json",
|
|
||||||
"projects/common/src/testMod/resources/computercraft-gametest.mixins.json",
|
|
||||||
"projects/common/src/testMod/resources/data/computercraft/loot_tables/treasure_disk.json",
|
"projects/common/src/testMod/resources/data/computercraft/loot_tables/treasure_disk.json",
|
||||||
"projects/common/src/testMod/resources/pack.mcmeta",
|
"projects/common/src/testMod/resources/pack.mcmeta",
|
||||||
"projects/core/src/main/resources/data/computercraft/lua/rom/modules/command/.ignoreme",
|
"projects/core/src/main/resources/data/computercraft/lua/rom/modules/command/.ignoreme",
|
||||||
"projects/core/src/main/resources/data/computercraft/lua/rom/modules/main/.ignoreme",
|
"projects/core/src/main/resources/data/computercraft/lua/rom/modules/main/.ignoreme",
|
||||||
"projects/core/src/main/resources/data/computercraft/lua/rom/modules/turtle/.ignoreme",
|
"projects/core/src/main/resources/data/computercraft/lua/rom/modules/turtle/.ignoreme",
|
||||||
"projects/core/src/main/resources/data/computercraft/lua/rom/motd.txt",
|
"projects/core/src/main/resources/data/computercraft/lua/rom/motd.txt",
|
||||||
"projects/fabric-api/src/main/modJson/fabric.mod.json",
|
|
||||||
"projects/fabric/src/client/resources/computercraft-client.fabric.mixins.json",
|
|
||||||
"projects/fabric/src/main/resources/computercraft.fabric.mixins.json",
|
|
||||||
"projects/fabric/src/main/resources/fabric.mod.json",
|
|
||||||
"projects/fabric/src/testMod/resources/computercraft-gametest.fabric.mixins.json",
|
|
||||||
"projects/fabric/src/testMod/resources/fabric.mod.json",
|
|
||||||
"projects/forge/src/client/resources/computercraft-client.forge.mixins.json",
|
|
||||||
"projects/forge/src/main/resources/computercraft.forge.mixins.json",
|
|
||||||
"projects/web/src/frontend/mount/.settings",
|
"projects/web/src/frontend/mount/.settings",
|
||||||
"projects/web/src/frontend/mount/example.nfp",
|
"projects/web/src/frontend/mount/example.nfp",
|
||||||
"projects/web/src/frontend/mount/example.nft",
|
"projects/web/src/frontend/mount/example.nft",
|
||||||
@@ -74,7 +70,7 @@ path = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[[annotations]]
|
[[annotations]]
|
||||||
# Community-contributed license files
|
# Community-contributed language files
|
||||||
SPDX-FileCopyrightText = "2017 The CC: Tweaked Developers"
|
SPDX-FileCopyrightText = "2017 The CC: Tweaked Developers"
|
||||||
SPDX-License-Identifier = "LicenseRef-CCPL"
|
SPDX-License-Identifier = "LicenseRef-CCPL"
|
||||||
path = [
|
path = [
|
||||||
@@ -88,18 +84,11 @@ path = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[[annotations]]
|
[[annotations]]
|
||||||
# Community-contributed license files
|
# Community-contributed language files
|
||||||
SPDX-FileCopyrightText = "2017 The CC: Tweaked Developers"
|
SPDX-FileCopyrightText = "2017 The CC: Tweaked Developers"
|
||||||
SPDX-License-Identifier = "MPL-2.0"
|
SPDX-License-Identifier = "MPL-2.0"
|
||||||
path = "projects/common/src/main/resources/assets/computercraft/lang/**"
|
path = "projects/common/src/main/resources/assets/computercraft/lang/**"
|
||||||
|
|
||||||
[[annotations]]
|
|
||||||
# GitHub build scripts are CC0. While we could add a header to each file,
|
|
||||||
# it's unclear if it will break actions or issue templates in some way.
|
|
||||||
SPDX-FileCopyrightText = "Jonathan Coates <git@squiddev.cc>"
|
|
||||||
SPDX-License-Identifier = "CC0-1.0"
|
|
||||||
path = ".github/**"
|
|
||||||
|
|
||||||
[[annotations]]
|
[[annotations]]
|
||||||
path = ["gradle/wrapper/**"]
|
path = ["gradle/wrapper/**"]
|
||||||
SPDX-FileCopyrightText = "Gradle Inc"
|
SPDX-FileCopyrightText = "Gradle Inc"
|
||||||
|
@@ -24,21 +24,19 @@ val mcVersion: String by extra
|
|||||||
|
|
||||||
githubRelease {
|
githubRelease {
|
||||||
token(findProperty("githubApiKey") as String? ?: "")
|
token(findProperty("githubApiKey") as String? ?: "")
|
||||||
owner.set("cc-tweaked")
|
owner = "cc-tweaked"
|
||||||
repo.set("CC-Tweaked")
|
repo = "CC-Tweaked"
|
||||||
targetCommitish.set(cct.gitBranch)
|
targetCommitish = cct.gitBranch
|
||||||
|
|
||||||
tagName.set("v$mcVersion-$modVersion")
|
tagName = "v$mcVersion-$modVersion"
|
||||||
releaseName.set("[$mcVersion] $modVersion")
|
releaseName = "[$mcVersion] $modVersion"
|
||||||
body.set(
|
body = provider {
|
||||||
provider {
|
"## " + project(":core").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 = isUnstable
|
||||||
)
|
|
||||||
prerelease.set(isUnstable)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.publish { dependsOn(tasks.githubRelease) }
|
tasks.publish { dependsOn(tasks.githubRelease) }
|
||||||
@@ -118,7 +116,7 @@ idea.project.settings.compiler.javac {
|
|||||||
}
|
}
|
||||||
|
|
||||||
versionCatalogUpdate {
|
versionCatalogUpdate {
|
||||||
sortByKey.set(false)
|
sortByKey = false
|
||||||
pin { versions.addAll("fastutil", "guava", "netty", "slf4j") }
|
pin { versions.addAll("fastutil", "guava", "netty", "slf4j") }
|
||||||
keep { keepUnusedLibraries.set(true) }
|
keep { keepUnusedLibraries = true }
|
||||||
}
|
}
|
||||||
|
@@ -14,14 +14,10 @@ repositories {
|
|||||||
mavenCentral()
|
mavenCentral()
|
||||||
gradlePluginPortal()
|
gradlePluginPortal()
|
||||||
|
|
||||||
maven("https://maven.neoforged.net/releases") {
|
maven("https://maven.neoforged.net") {
|
||||||
name = "NeoForge"
|
name = "NeoForge"
|
||||||
content {
|
content {
|
||||||
includeGroup("net.minecraftforge")
|
|
||||||
includeGroup("net.neoforged")
|
includeGroup("net.neoforged")
|
||||||
includeGroup("net.neoforged.gradle")
|
|
||||||
includeModule("codechicken", "DiffPatch")
|
|
||||||
includeModule("net.covers1624", "Quack")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -48,7 +44,7 @@ dependencies {
|
|||||||
implementation(libs.fabric.loom)
|
implementation(libs.fabric.loom)
|
||||||
implementation(libs.ideaExt)
|
implementation(libs.ideaExt)
|
||||||
implementation(libs.minotaur)
|
implementation(libs.minotaur)
|
||||||
implementation(libs.neoGradle.userdev)
|
implementation(libs.modDevGradle)
|
||||||
implementation(libs.vanillaExtract)
|
implementation(libs.vanillaExtract)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -72,7 +68,7 @@ gradlePlugin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
versionCatalogUpdate {
|
versionCatalogUpdate {
|
||||||
sortByKey.set(false)
|
sortByKey = false
|
||||||
keep { keepUnusedLibraries.set(true) }
|
keep { keepUnusedLibraries = true }
|
||||||
catalogFile.set(file("../gradle/libs.versions.toml"))
|
catalogFile = file("../gradle/libs.versions.toml")
|
||||||
}
|
}
|
||||||
|
@@ -30,7 +30,7 @@ repositories {
|
|||||||
|
|
||||||
loom {
|
loom {
|
||||||
splitEnvironmentSourceSets()
|
splitEnvironmentSourceSets()
|
||||||
splitModDependencies.set(true)
|
splitModDependencies = true
|
||||||
}
|
}
|
||||||
|
|
||||||
MinecraftConfigurations.setup(project)
|
MinecraftConfigurations.setup(project)
|
||||||
|
@@ -6,25 +6,22 @@
|
|||||||
|
|
||||||
import cc.tweaked.gradle.CCTweakedExtension
|
import cc.tweaked.gradle.CCTweakedExtension
|
||||||
import cc.tweaked.gradle.CCTweakedPlugin
|
import cc.tweaked.gradle.CCTweakedPlugin
|
||||||
import cc.tweaked.gradle.IdeaRunConfigurations
|
|
||||||
import cc.tweaked.gradle.MinecraftConfigurations
|
import cc.tweaked.gradle.MinecraftConfigurations
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
id("cc-tweaked.java-convention")
|
id("cc-tweaked.java-convention")
|
||||||
id("net.neoforged.gradle.userdev")
|
id("net.neoforged.moddev")
|
||||||
}
|
}
|
||||||
|
|
||||||
plugins.apply(CCTweakedPlugin::class.java)
|
plugins.apply(CCTweakedPlugin::class.java)
|
||||||
|
|
||||||
val mcVersion: String by extra
|
val mcVersion: String by extra
|
||||||
|
|
||||||
minecraft {
|
neoForge {
|
||||||
modIdentifier("computercraft")
|
val libs = project.extensions.getByType<VersionCatalogsExtension>().named("libs")
|
||||||
}
|
version = libs.findVersion("neoForge").get().toString()
|
||||||
|
|
||||||
subsystems {
|
|
||||||
parchment {
|
parchment {
|
||||||
val libs = project.extensions.getByType<VersionCatalogsExtension>().named("libs")
|
|
||||||
minecraftVersion = libs.findVersion("parchmentMc").get().toString()
|
minecraftVersion = libs.findVersion("parchmentMc").get().toString()
|
||||||
mappingsVersion = libs.findVersion("parchment").get().toString()
|
mappingsVersion = libs.findVersion("parchment").get().toString()
|
||||||
}
|
}
|
||||||
|
@@ -29,7 +29,7 @@ base.archivesName.convention("cc-tweaked-$mcVersion-${project.name}")
|
|||||||
|
|
||||||
java {
|
java {
|
||||||
toolchain {
|
toolchain {
|
||||||
languageVersion.set(CCTweakedPlugin.JAVA_VERSION)
|
languageVersion = CCTweakedPlugin.JAVA_VERSION
|
||||||
}
|
}
|
||||||
|
|
||||||
withSourcesJar()
|
withSourcesJar()
|
||||||
@@ -48,7 +48,7 @@ repositories {
|
|||||||
includeGroup("cc.tweaked")
|
includeGroup("cc.tweaked")
|
||||||
// Things we mirror
|
// Things we mirror
|
||||||
includeGroup("com.simibubi.create")
|
includeGroup("com.simibubi.create")
|
||||||
includeGroup("commoble.morered")
|
includeGroup("net.commoble.morered")
|
||||||
includeGroup("dev.architectury")
|
includeGroup("dev.architectury")
|
||||||
includeGroup("dev.emi")
|
includeGroup("dev.emi")
|
||||||
includeGroup("maven.modrinth")
|
includeGroup("maven.modrinth")
|
||||||
@@ -91,14 +91,15 @@ sourceSets.all {
|
|||||||
|
|
||||||
options.errorprone {
|
options.errorprone {
|
||||||
check("InvalidBlockTag", CheckSeverity.OFF) // Broken by @cc.xyz
|
check("InvalidBlockTag", CheckSeverity.OFF) // Broken by @cc.xyz
|
||||||
check("InvalidParam", CheckSeverity.OFF) // Broken by records.
|
|
||||||
check("InlineMeSuggester", CheckSeverity.OFF) // Minecraft uses @Deprecated liberally
|
check("InlineMeSuggester", CheckSeverity.OFF) // Minecraft uses @Deprecated liberally
|
||||||
// Too many false positives right now. Maybe we need an indirection for it later on.
|
// Too many false positives right now. Maybe we need an indirection for it later on.
|
||||||
|
check("AssignmentExpression", CheckSeverity.OFF) // I'm a bad person.
|
||||||
check("ReferenceEquality", CheckSeverity.OFF)
|
check("ReferenceEquality", CheckSeverity.OFF)
|
||||||
check("EnumOrdinal", CheckSeverity.OFF) // For now. We could replace most of these with EnumMap.
|
check("EnumOrdinal", CheckSeverity.OFF) // For now. We could replace most of these with EnumMap.
|
||||||
check("OperatorPrecedence", CheckSeverity.OFF) // For now.
|
check("OperatorPrecedence", CheckSeverity.OFF) // For now.
|
||||||
check("NonOverridingEquals", CheckSeverity.OFF) // Peripheral.equals makes this hard to avoid
|
check("NonOverridingEquals", CheckSeverity.OFF) // Peripheral.equals makes this hard to avoid
|
||||||
check("FutureReturnValueIgnored", CheckSeverity.OFF) // Too many false positives with Netty
|
check("FutureReturnValueIgnored", CheckSeverity.OFF) // Too many false positives with Netty
|
||||||
|
check("InvalidInlineTag", CheckSeverity.OFF) // Triggered by @snippet. Can be removed on Java 21.
|
||||||
|
|
||||||
check("NullAway", CheckSeverity.ERROR)
|
check("NullAway", CheckSeverity.ERROR)
|
||||||
option(
|
option(
|
||||||
@@ -121,7 +122,6 @@ tasks.compileTestJava {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
tasks.withType(JavaCompile::class.java).configureEach {
|
tasks.withType(JavaCompile::class.java).configureEach {
|
||||||
options.encoding = "UTF-8"
|
options.encoding = "UTF-8"
|
||||||
}
|
}
|
||||||
@@ -169,8 +169,8 @@ tasks.test {
|
|||||||
}
|
}
|
||||||
|
|
||||||
tasks.withType(JacocoReport::class.java).configureEach {
|
tasks.withType(JacocoReport::class.java).configureEach {
|
||||||
reports.xml.required.set(true)
|
reports.xml.required = true
|
||||||
reports.html.required.set(true)
|
reports.html.required = true
|
||||||
}
|
}
|
||||||
|
|
||||||
project.plugins.withType(CCTweakedPlugin::class.java) {
|
project.plugins.withType(CCTweakedPlugin::class.java) {
|
||||||
@@ -194,30 +194,23 @@ spotless {
|
|||||||
fun FormatExtension.defaults() {
|
fun FormatExtension.defaults() {
|
||||||
endWithNewline()
|
endWithNewline()
|
||||||
trimTrailingWhitespace()
|
trimTrailingWhitespace()
|
||||||
indentWithSpaces(4)
|
leadingTabsToSpaces(4)
|
||||||
}
|
}
|
||||||
|
|
||||||
java {
|
java {
|
||||||
defaults()
|
defaults()
|
||||||
|
importOrder("", "javax|java", "\\#")
|
||||||
removeUnusedImports()
|
removeUnusedImports()
|
||||||
}
|
}
|
||||||
|
|
||||||
val ktlintConfig = mapOf(
|
|
||||||
"ktlint_standard_no-wildcard-imports" to "disabled",
|
|
||||||
"ktlint_standard_class-naming" to "disabled",
|
|
||||||
"ktlint_standard_function-naming" to "disabled",
|
|
||||||
"ij_kotlin_allow_trailing_comma" to "true",
|
|
||||||
"ij_kotlin_allow_trailing_comma_on_call_site" to "true",
|
|
||||||
)
|
|
||||||
|
|
||||||
kotlinGradle {
|
kotlinGradle {
|
||||||
defaults()
|
defaults()
|
||||||
ktlint().editorConfigOverride(ktlintConfig)
|
ktlint()
|
||||||
}
|
}
|
||||||
|
|
||||||
kotlin {
|
kotlin {
|
||||||
defaults()
|
defaults()
|
||||||
ktlint().editorConfigOverride(ktlintConfig)
|
ktlint()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -226,6 +219,5 @@ idea.module {
|
|||||||
|
|
||||||
// Force Gradle to write to inherit the output directory from the parent, instead of writing to out/xxx/classes.
|
// 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.
|
// 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
|
inheritOutputDirs = true
|
||||||
}
|
}
|
||||||
|
@@ -1,25 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: 2022 The CC: Tweaked Developers
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: MPL-2.0
|
|
||||||
|
|
||||||
import cc.tweaked.gradle.CCTweakedPlugin
|
|
||||||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
|
||||||
|
|
||||||
plugins {
|
|
||||||
kotlin("jvm")
|
|
||||||
}
|
|
||||||
|
|
||||||
kotlin {
|
|
||||||
jvmToolchain {
|
|
||||||
languageVersion.set(CCTweakedPlugin.JAVA_VERSION)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
tasks.withType(KotlinCompile::class.java).configureEach {
|
|
||||||
// So technically we shouldn't need to do this as the toolchain sets it above. However, the option only appears
|
|
||||||
// to be set when the task executes, so doesn't get picked up by IDEs.
|
|
||||||
kotlinOptions.jvmTarget = when {
|
|
||||||
CCTweakedPlugin.JAVA_VERSION.asInt() > 8 -> CCTweakedPlugin.JAVA_VERSION.toString()
|
|
||||||
else -> "1.${CCTweakedPlugin.JAVA_VERSION.asInt()}"
|
|
||||||
}
|
|
||||||
}
|
|
@@ -24,16 +24,16 @@ val modVersion: String by extra
|
|||||||
val mcVersion: String by extra
|
val mcVersion: String by extra
|
||||||
|
|
||||||
modrinth {
|
modrinth {
|
||||||
token.set(findProperty("modrinthApiKey") as String? ?: "")
|
token = findProperty("modrinthApiKey") as String? ?: ""
|
||||||
projectId.set("gu7yAYhd")
|
projectId = "gu7yAYhd"
|
||||||
versionNumber.set(modVersion)
|
versionNumber = modVersion
|
||||||
versionName.set(modVersion)
|
versionName = modVersion
|
||||||
versionType.set(if (isUnstable) "alpha" else "release")
|
versionType = if (isUnstable) "alpha" else "release"
|
||||||
uploadFile.setProvider(modPublishing.output)
|
uploadFile.setProvider(modPublishing.output)
|
||||||
gameVersions.add(mcVersion)
|
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).")
|
changelog = "Release notes can be found on the [GitHub repository](https://github.com/cc-tweaked/CC-Tweaked/releases/tag/v$mcVersion-$modVersion)."
|
||||||
|
|
||||||
syncBodyFrom.set(provider { rootProject.file("doc/mod-page.md").readText() })
|
syncBodyFrom = provider { rootProject.file("doc/mod-page.md").readText() }
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.publish { dependsOn(tasks.modrinth) }
|
tasks.publish { dependsOn(tasks.modrinth) }
|
||||||
|
@@ -2,44 +2,34 @@
|
|||||||
//
|
//
|
||||||
// SPDX-License-Identifier: MPL-2.0
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
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.
|
||||||
*
|
*
|
||||||
* See notes in [cc.tweaked.gradle.MinecraftConfigurations] for the general design behind these cursed ideas.
|
* See notes in [cc.tweaked.gradle.MinecraftConfigurations] for the general design behind these cursed ideas.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import cc.tweaked.gradle.MinecraftConfigurations
|
||||||
|
import cc.tweaked.gradle.clientClasses
|
||||||
|
import cc.tweaked.gradle.commonClasses
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
id("cc-tweaked.kotlin-convention")
|
kotlin("jvm")
|
||||||
id("cc-tweaked.java-convention")
|
id("cc-tweaked.java-convention")
|
||||||
}
|
}
|
||||||
|
|
||||||
val main = sourceSets["main"]
|
val main = sourceSets["main"]
|
||||||
val client = sourceSets["client"]
|
val client = sourceSets["client"]
|
||||||
|
|
||||||
// Both testMod and testFixtures inherit from the main and client classpath, just so we have access to Minecraft classes.
|
MinecraftConfigurations.createDerivedConfiguration(project, MinecraftConfigurations.DATAGEN)
|
||||||
val testMod by sourceSets.creating {
|
MinecraftConfigurations.createDerivedConfiguration(project, MinecraftConfigurations.EXAMPLES)
|
||||||
compileClasspath += main.compileClasspath + client.compileClasspath
|
MinecraftConfigurations.createDerivedConfiguration(project, MinecraftConfigurations.TEST_MOD)
|
||||||
runtimeClasspath += main.runtimeClasspath + client.runtimeClasspath
|
|
||||||
}
|
|
||||||
|
|
||||||
configurations {
|
// Set up generated resources
|
||||||
named(testMod.compileClasspathConfigurationName) {
|
sourceSets.main { resources.srcDir("src/generated/resources") }
|
||||||
shouldResolveConsistentlyWith(compileClasspath.get())
|
sourceSets.named("examples") { resources.srcDir("src/examples/generatedResources") }
|
||||||
}
|
|
||||||
|
|
||||||
named(testMod.runtimeClasspathConfigurationName) {
|
// Make sure our examples compile.
|
||||||
shouldResolveConsistentlyWith(runtimeClasspath.get())
|
tasks.check { dependsOn(tasks.named("compileExamplesJava")) }
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Like the main test configurations, we're safe to depend on source set outputs.
|
|
||||||
dependencies {
|
|
||||||
add(testMod.implementationConfigurationName, main.output)
|
|
||||||
add(testMod.implementationConfigurationName, client.output)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Similar to java-test-fixtures, but tries to avoid putting the obfuscated jar on the classpath.
|
// Similar to java-test-fixtures, but tries to avoid putting the obfuscated jar on the classpath.
|
||||||
|
|
@@ -12,25 +12,26 @@ publishing {
|
|||||||
register<MavenPublication>("maven") {
|
register<MavenPublication>("maven") {
|
||||||
artifactId = base.archivesName.get()
|
artifactId = base.archivesName.get()
|
||||||
from(components["java"])
|
from(components["java"])
|
||||||
|
suppressAllPomMetadataWarnings()
|
||||||
|
|
||||||
pom {
|
pom {
|
||||||
name.set("CC: Tweaked")
|
name = "CC: Tweaked"
|
||||||
description.set("CC: Tweaked is a fork of ComputerCraft, adding programmable computers, turtles and more to Minecraft.")
|
description = "CC: Tweaked is a fork of ComputerCraft, adding programmable computers, turtles and more to Minecraft."
|
||||||
url.set("https://github.com/cc-tweaked/CC-Tweaked")
|
url = "https://github.com/cc-tweaked/CC-Tweaked"
|
||||||
|
|
||||||
scm {
|
scm {
|
||||||
url.set("https://github.com/cc-tweaked/CC-Tweaked.git")
|
url = "https://github.com/cc-tweaked/CC-Tweaked.git"
|
||||||
}
|
}
|
||||||
|
|
||||||
issueManagement {
|
issueManagement {
|
||||||
system.set("github")
|
system = "github"
|
||||||
url.set("https://github.com/cc-tweaked/CC-Tweaked/issues")
|
url = "https://github.com/cc-tweaked/CC-Tweaked/issues"
|
||||||
}
|
}
|
||||||
|
|
||||||
licenses {
|
licenses {
|
||||||
license {
|
license {
|
||||||
name.set("ComputerCraft Public License, Version 1.0")
|
name = "ComputerCraft Public License, Version 1.0"
|
||||||
url.set("https://github.com/cc-tweaked/CC-Tweaked/blob/HEAD/LICENSE")
|
url = "https://github.com/cc-tweaked/CC-Tweaked/blob/HEAD/LICENSE"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -10,14 +10,9 @@ 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.Task
|
||||||
import org.gradle.api.artifacts.Dependency
|
|
||||||
import org.gradle.api.attributes.TestSuiteType
|
|
||||||
import org.gradle.api.file.FileSystemOperations
|
|
||||||
import org.gradle.api.plugins.JavaPluginExtension
|
import org.gradle.api.plugins.JavaPluginExtension
|
||||||
import org.gradle.api.provider.ListProperty
|
|
||||||
import org.gradle.api.provider.Provider
|
import org.gradle.api.provider.Provider
|
||||||
import org.gradle.api.provider.SetProperty
|
import org.gradle.api.provider.SetProperty
|
||||||
import org.gradle.api.reporting.ReportingExtension
|
|
||||||
import org.gradle.api.tasks.SourceSet
|
import org.gradle.api.tasks.SourceSet
|
||||||
import org.gradle.api.tasks.bundling.Jar
|
import org.gradle.api.tasks.bundling.Jar
|
||||||
import org.gradle.api.tasks.compile.JavaCompile
|
import org.gradle.api.tasks.compile.JavaCompile
|
||||||
@@ -25,7 +20,6 @@ import org.gradle.api.tasks.javadoc.Javadoc
|
|||||||
import org.gradle.language.base.plugins.LifecycleBasePlugin
|
import org.gradle.language.base.plugins.LifecycleBasePlugin
|
||||||
import org.gradle.language.jvm.tasks.ProcessResources
|
import org.gradle.language.jvm.tasks.ProcessResources
|
||||||
import org.gradle.process.JavaForkOptions
|
import org.gradle.process.JavaForkOptions
|
||||||
import org.gradle.testing.jacoco.plugins.JacocoCoverageReport
|
|
||||||
import org.gradle.testing.jacoco.plugins.JacocoPluginExtension
|
import org.gradle.testing.jacoco.plugins.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
|
||||||
@@ -36,54 +30,40 @@ import java.io.IOException
|
|||||||
import java.net.URI
|
import java.net.URI
|
||||||
import java.util.regex.Pattern
|
import java.util.regex.Pattern
|
||||||
|
|
||||||
abstract class CCTweakedExtension(
|
abstract class CCTweakedExtension(private val project: Project) {
|
||||||
private val project: Project,
|
|
||||||
private val fs: FileSystemOperations,
|
|
||||||
) {
|
|
||||||
/** 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> =
|
||||||
ProcessHelpers.captureOut("git", "-C", project.rootProject.projectDir.absolutePath, "rev-parse", "HEAD").trim()
|
gitProvider("<no git commit>", listOf("rev-parse", "HEAD")) { it.trim() }
|
||||||
}
|
|
||||||
|
|
||||||
/** Get the current git branch. */
|
/** Get the current git branch. */
|
||||||
val gitBranch: Provider<String> = gitProvider(project, "<no git branch>") {
|
val gitBranch: Provider<String> =
|
||||||
ProcessHelpers.captureOut("git", "-C", project.rootProject.projectDir.absolutePath, "rev-parse", "--abbrev-ref", "HEAD")
|
gitProvider("<no git branch>", listOf("rev-parse", "--abbrev-ref", "HEAD")) { it.trim() }
|
||||||
.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>> =
|
||||||
ProcessHelpers.captureLines(
|
gitProvider(listOf(), listOf("shortlog", "-ns", "--group=author", "--group=trailer:co-authored-by", "HEAD")) { input ->
|
||||||
"git", "-C", project.rootProject.projectDir.absolutePath, "shortlog", "-ns",
|
input.lineSequence()
|
||||||
"--group=author", "--group=trailer:co-authored-by", "HEAD",
|
.filter { it.isNotEmpty() }
|
||||||
)
|
.map {
|
||||||
.asSequence()
|
val matcher = COMMIT_COUNTS.matcher(it)
|
||||||
.map {
|
matcher.find()
|
||||||
val matcher = COMMIT_COUNTS.matcher(it)
|
matcher.group(1)
|
||||||
matcher.find()
|
}
|
||||||
matcher.group(1)
|
.filter { !IGNORED_USERS.contains(it) }
|
||||||
}
|
.toList()
|
||||||
.filter { !IGNORED_USERS.contains(it) }
|
.sortedWith(String.CASE_INSENSITIVE_ORDER)
|
||||||
.toList()
|
}
|
||||||
.sortedWith(String.CASE_INSENSITIVE_ORDER)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* References to other sources
|
* References to other sources
|
||||||
*/
|
*/
|
||||||
val sourceDirectories: SetProperty<SourceSetReference> = project.objects.setProperty(SourceSetReference::class.java)
|
val sourceDirectories: SetProperty<SourceSetReference> = project.objects.setProperty(SourceSetReference::class.java)
|
||||||
|
|
||||||
/**
|
|
||||||
* Dependencies excluded from published artifacts.
|
|
||||||
*/
|
|
||||||
private val excludedDeps: ListProperty<Dependency> = project.objects.listProperty(Dependency::class.java)
|
|
||||||
|
|
||||||
/** All source sets referenced by this project. */
|
/** All source sets referenced by this project. */
|
||||||
val sourceSets = sourceDirectories.map { x -> x.map { it.sourceSet } }
|
val sourceSets = sourceDirectories.map { x -> x.map { it.sourceSet } }
|
||||||
|
|
||||||
init {
|
init {
|
||||||
sourceDirectories.finalizeValueOnRead()
|
sourceDirectories.finalizeValueOnRead()
|
||||||
excludedDeps.finalizeValueOnRead()
|
|
||||||
project.afterEvaluate { sourceDirectories.disallowChanges() }
|
project.afterEvaluate { sourceDirectories.disallowChanges() }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,14 +89,13 @@ abstract class CCTweakedExtension(
|
|||||||
val otherJava = otherProject.extensions.getByType(JavaPluginExtension::class.java)
|
val otherJava = otherProject.extensions.getByType(JavaPluginExtension::class.java)
|
||||||
val main = otherJava.sourceSets.getByName("main")
|
val main = otherJava.sourceSets.getByName("main")
|
||||||
val client = otherJava.sourceSets.getByName("client")
|
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.
|
// Pull in sources from the other project.
|
||||||
extendSourceSet(otherProject, main)
|
extendSourceSet(otherProject, main)
|
||||||
extendSourceSet(otherProject, client)
|
extendSourceSet(otherProject, client)
|
||||||
if (testMod != null) extendSourceSet(otherProject, testMod)
|
for (sourceSet in listOf(MinecraftConfigurations.DATAGEN, MinecraftConfigurations.EXAMPLES, MinecraftConfigurations.TEST_MOD, "testFixtures")) {
|
||||||
if (testFixtures != null) extendSourceSet(otherProject, testFixtures)
|
otherJava.sourceSets.findByName(sourceSet)?.let { extendSourceSet(otherProject, it) }
|
||||||
|
}
|
||||||
|
|
||||||
// The extra source-processing tasks should include these files too.
|
// 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.javadocTaskName, Javadoc::class.java) { source(main.allJava, client.allJava) }
|
||||||
@@ -179,23 +158,19 @@ abstract class CCTweakedExtension(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun <T> jacoco(task: NamedDomainObjectProvider<T>) where T : Task, T : JavaForkOptions {
|
fun <T> jacoco(task: NamedDomainObjectProvider<T>) where T : Task, T : JavaForkOptions {
|
||||||
val classDump = project.layout.buildDirectory.dir("jacocoClassDump/${task.name}")
|
|
||||||
val reportTaskName = "jacoco${task.name.capitalise()}Report"
|
val reportTaskName = "jacoco${task.name.capitalise()}Report"
|
||||||
|
|
||||||
val jacoco = project.extensions.getByType(JacocoPluginExtension::class.java)
|
val jacoco = project.extensions.getByType(JacocoPluginExtension::class.java)
|
||||||
task.configure {
|
task.configure {
|
||||||
finalizedBy(reportTaskName)
|
finalizedBy(reportTaskName)
|
||||||
|
|
||||||
doFirst("Clean class dump directory") { fs.delete { delete(classDump) } }
|
|
||||||
|
|
||||||
jacoco.applyTo(this)
|
jacoco.applyTo(this)
|
||||||
|
|
||||||
extensions.configure(JacocoTaskExtension::class.java) {
|
extensions.configure(JacocoTaskExtension::class.java) {
|
||||||
includes = listOf("dan200.computercraft.*")
|
includes = listOf("dan200.computercraft.*")
|
||||||
classDumpDir = classDump.get().asFile
|
excludes = listOf(
|
||||||
|
"dan200.computercraft.mixin.*", // Exclude mixins, as they're not executed at runtime.
|
||||||
// Older versions of modlauncher don't include a protection domain (and thus no code
|
"dan200.computercraft.shared.Capabilities$*", // Exclude capability tokens, as Forge rewrites them.
|
||||||
// source). Jacoco skips such classes by default, so we need to explicitly include them.
|
)
|
||||||
isIncludeNoLocationClasses = true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -204,15 +179,11 @@ abstract class CCTweakedExtension(
|
|||||||
description = "Generates code coverage report for the ${task.name} task."
|
description = "Generates code coverage report for the ${task.name} task."
|
||||||
|
|
||||||
executionData(task.get())
|
executionData(task.get())
|
||||||
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 don't use all class directories.
|
||||||
for (ref in sourceSets.get()) sourceDirectories.from(ref.allSource.sourceDirectories)
|
for (ref in this@CCTweakedExtension.sourceDirectories.get()) {
|
||||||
}
|
sourceDirectories.from(ref.sourceSet.allSource.sourceDirectories)
|
||||||
|
if (ref.classes) classDirectories.from(ref.sourceSet.output)
|
||||||
project.extensions.configure(ReportingExtension::class.java) {
|
|
||||||
reports.register("${task.name}CodeCoverageReport", JacocoCoverageReport::class.java) {
|
|
||||||
testType.set(TestSuiteType.INTEGRATION_TEST)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -252,18 +223,23 @@ abstract class CCTweakedExtension(
|
|||||||
).resolve().single()
|
).resolve().single()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private fun <T> gitProvider(default: T, command: List<String>, process: (String) -> T): Provider<T> {
|
||||||
* Exclude a dependency from being published in Maven.
|
val baseResult = project.providers.exec {
|
||||||
*/
|
commandLine = listOf("git", "-C", project.rootDir.absolutePath) + command
|
||||||
fun exclude(dep: Dependency) {
|
}
|
||||||
excludedDeps.add(dep)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
return project.provider {
|
||||||
* Configure a [MavenDependencySpec].
|
val res = try {
|
||||||
*/
|
baseResult.standardOutput.asText.get()
|
||||||
fun configureExcludes(spec: MavenDependencySpec) {
|
} catch (e: IOException) {
|
||||||
for (dep in excludedDeps.get()) spec.exclude(dep)
|
project.logger.error("Cannot read Git repository: ${e.message}", e)
|
||||||
|
return@provider default
|
||||||
|
} catch (e: GradleException) {
|
||||||
|
project.logger.error("Cannot read Git repository: ${e.message}", e)
|
||||||
|
return@provider default
|
||||||
|
}
|
||||||
|
process(res)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@@ -272,20 +248,6 @@ abstract class CCTweakedExtension(
|
|||||||
"GitHub", "Daniel Ratcliffe", "NotSquidDev", "Weblate",
|
"GitHub", "Daniel Ratcliffe", "NotSquidDev", "Weblate",
|
||||||
)
|
)
|
||||||
|
|
||||||
private fun <T> gitProvider(project: Project, default: T, supplier: () -> T): Provider<T> {
|
|
||||||
return project.provider {
|
|
||||||
try {
|
|
||||||
supplier()
|
|
||||||
} catch (e: IOException) {
|
|
||||||
project.logger.error("Cannot read Git repository: ${e.message}")
|
|
||||||
default
|
|
||||||
} catch (e: GradleException) {
|
|
||||||
project.logger.error("Cannot read Git repository: ${e.message}")
|
|
||||||
default
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private val isIdeSync: Boolean
|
private val isIdeSync: Boolean
|
||||||
get() = java.lang.Boolean.parseBoolean(System.getProperty("idea.sync.active", "false"))
|
get() = java.lang.Boolean.parseBoolean(System.getProperty("idea.sync.active", "false"))
|
||||||
}
|
}
|
||||||
|
@@ -22,19 +22,19 @@ import org.gradle.language.base.plugins.LifecycleBasePlugin
|
|||||||
|
|
||||||
abstract class DependencyCheck : DefaultTask() {
|
abstract class DependencyCheck : DefaultTask() {
|
||||||
@get:Input
|
@get:Input
|
||||||
abstract val configuration: ListProperty<Configuration>
|
protected abstract val dependencies: ListProperty<DependencyResult>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A mapping of module coordinates (`group:module`) to versions, overriding the requested version.
|
* A mapping of module coordinates (`group:module`) to versions, overriding the requested version.
|
||||||
*/
|
*/
|
||||||
@get:Input
|
@get:Input
|
||||||
abstract val overrides: MapProperty<String, String>
|
protected abstract val overrides: MapProperty<String, String>
|
||||||
|
|
||||||
init {
|
init {
|
||||||
description = "Check :core's dependencies are consistent with Minecraft's."
|
description = "Check :core's dependencies are consistent with Minecraft's."
|
||||||
group = LifecycleBasePlugin.VERIFICATION_GROUP
|
group = LifecycleBasePlugin.VERIFICATION_GROUP
|
||||||
|
|
||||||
configuration.finalizeValueOnRead()
|
dependencies.finalizeValueOnRead()
|
||||||
overrides.finalizeValueOnRead()
|
overrides.finalizeValueOnRead()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -45,13 +45,19 @@ abstract class DependencyCheck : DefaultTask() {
|
|||||||
overrides.putAll(project.provider { mutableMapOf(module.get().module.toString() to version) })
|
overrides.putAll(project.provider { mutableMapOf(module.get().module.toString() to version) })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a configuration to check.
|
||||||
|
*/
|
||||||
|
fun configuration(configuration: Provider<Configuration>) {
|
||||||
|
// We can't store the Configuration in the cache, so store the resolved dependencies instead.
|
||||||
|
dependencies.addAll(configuration.map { it.incoming.resolutionResult.allDependencies })
|
||||||
|
}
|
||||||
|
|
||||||
@TaskAction
|
@TaskAction
|
||||||
fun run() {
|
fun run() {
|
||||||
var ok = true
|
var ok = true
|
||||||
for (configuration in configuration.get()) {
|
for (configuration in dependencies.get()) {
|
||||||
configuration.incoming.resolutionResult.allDependencies {
|
if (!check(configuration)) ok = false
|
||||||
if (!check(this@allDependencies)) ok = false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
|
@@ -5,10 +5,8 @@
|
|||||||
package cc.tweaked.gradle
|
package cc.tweaked.gradle
|
||||||
|
|
||||||
import org.gradle.api.file.DirectoryProperty
|
import org.gradle.api.file.DirectoryProperty
|
||||||
import org.gradle.api.provider.Property
|
|
||||||
import org.gradle.api.tasks.AbstractExecTask
|
import org.gradle.api.tasks.AbstractExecTask
|
||||||
import org.gradle.api.tasks.OutputDirectory
|
import org.gradle.api.tasks.OutputDirectory
|
||||||
import java.io.File
|
|
||||||
|
|
||||||
abstract class ExecToDir : AbstractExecTask<ExecToDir>(ExecToDir::class.java) {
|
abstract class ExecToDir : AbstractExecTask<ExecToDir>(ExecToDir::class.java) {
|
||||||
@get:OutputDirectory
|
@get:OutputDirectory
|
||||||
|
@@ -25,7 +25,6 @@ import javax.xml.xpath.XPathFactory
|
|||||||
* Would be good to PR some (or all) of these changes upstream at some point.
|
* 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.fabricmc.loom.configuration.ide.idea.IdeaSyncTask
|
||||||
* @see net.minecraftforge.gradle.common.util.runs.IntellijRunGenerator
|
|
||||||
*/
|
*/
|
||||||
internal class IdeaRunConfigurations(project: Project) {
|
internal class IdeaRunConfigurations(project: Project) {
|
||||||
private val rootProject = project.rootProject
|
private val rootProject = project.rootProject
|
||||||
@@ -35,22 +34,6 @@ internal class IdeaRunConfigurations(project: Project) {
|
|||||||
private val writer = TransformerFactory.newInstance().newTransformer()
|
private val writer = TransformerFactory.newInstance().newTransformer()
|
||||||
|
|
||||||
private val ideaDir = rootProject.file(".idea/")
|
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) {
|
fun patch() = synchronized(LOCK) {
|
||||||
val runConfigDir = ideaDir.resolve("runConfigurations")
|
val runConfigDir = ideaDir.resolve("runConfigurations")
|
||||||
@@ -58,10 +41,9 @@ internal class IdeaRunConfigurations(project: Project) {
|
|||||||
|
|
||||||
Files.list(runConfigDir.toPath()).use {
|
Files.list(runConfigDir.toPath()).use {
|
||||||
for (configuration in it) {
|
for (configuration in it) {
|
||||||
val filename = configuration.fileName.toString();
|
val filename = configuration.fileName.toString()
|
||||||
when {
|
when {
|
||||||
filename.endsWith("_fabric.xml") -> patchFabric(configuration)
|
filename.endsWith("_fabric.xml") -> patchFabric(configuration)
|
||||||
filename.startsWith("forge_") && filename.endsWith(".xml") -> patchForge(configuration)
|
|
||||||
else -> {}
|
else -> {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -72,65 +54,6 @@ internal class IdeaRunConfigurations(project: Project) {
|
|||||||
setXml("//configuration", "folderName") { "Fabric" }
|
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) {
|
private fun LocatedDocument.setXml(xpath: String, attribute: String, value: (String?) -> String) {
|
||||||
val node = this@IdeaRunConfigurations.xpath.evaluate(xpath, document, XPathConstants.NODE) as Node?
|
val node = this@IdeaRunConfigurations.xpath.evaluate(xpath, document, XPathConstants.NODE) as Node?
|
||||||
if (node == null) {
|
if (node == null) {
|
||||||
@@ -159,16 +82,5 @@ internal class IdeaRunConfigurations(project: Project) {
|
|||||||
companion object {
|
companion object {
|
||||||
private val LOGGER = Logging.getLogger(IdeaRunConfigurations::class.java)
|
private val LOGGER = Logging.getLogger(IdeaRunConfigurations::class.java)
|
||||||
private val LOCK = Any()
|
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",
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,73 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: 2022 The CC: Tweaked Developers
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: MPL-2.0
|
|
||||||
|
|
||||||
package cc.tweaked.gradle
|
|
||||||
|
|
||||||
import org.gradle.api.artifacts.Dependency
|
|
||||||
import org.gradle.api.artifacts.MinimalExternalModuleDependency
|
|
||||||
import org.gradle.api.artifacts.ProjectDependency
|
|
||||||
import org.gradle.api.plugins.BasePluginExtension
|
|
||||||
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 {
|
|
||||||
// We have to cheat a little for project dependencies, as the project name doesn't match the artifact group.
|
|
||||||
val name = when (dep) {
|
|
||||||
is ProjectDependency -> dep.dependencyProject.extensions.getByType(BasePluginExtension::class.java).archivesName.get()
|
|
||||||
else -> dep.name
|
|
||||||
}
|
|
||||||
(dep.group.isNullOrEmpty() || dep.group == it.groupId) &&
|
|
||||||
(name.isNullOrEmpty() || 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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -24,7 +24,6 @@ class MinecraftConfigurations private constructor(private val project: Project)
|
|||||||
private val java = project.extensions.getByType(JavaPluginExtension::class.java)
|
private val java = project.extensions.getByType(JavaPluginExtension::class.java)
|
||||||
private val sourceSets = java.sourceSets
|
private val sourceSets = java.sourceSets
|
||||||
private val configurations = project.configurations
|
private val configurations = project.configurations
|
||||||
private val objects = project.objects
|
|
||||||
|
|
||||||
private val main = sourceSets[SourceSet.MAIN_SOURCE_SET_NAME]
|
private val main = sourceSets[SourceSet.MAIN_SOURCE_SET_NAME]
|
||||||
private val test = sourceSets[SourceSet.TEST_SOURCE_SET_NAME]
|
private val test = sourceSets[SourceSet.TEST_SOURCE_SET_NAME]
|
||||||
@@ -37,13 +36,7 @@ class MinecraftConfigurations private constructor(private val project: Project)
|
|||||||
val client = sourceSets.maybeCreate("client")
|
val client = sourceSets.maybeCreate("client")
|
||||||
|
|
||||||
// Ensure the client classpaths behave the same as the main ones.
|
// Ensure the client classpaths behave the same as the main ones.
|
||||||
configurations.named(client.compileClasspathConfigurationName) {
|
consistentWithMain(client)
|
||||||
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).
|
// Set up an API configuration for clients (to ensure it's consistent with the main source set).
|
||||||
val clientApi = configurations.maybeCreate(client.apiConfigurationName).apply {
|
val clientApi = configurations.maybeCreate(client.apiConfigurationName).apply {
|
||||||
@@ -85,6 +78,16 @@ class MinecraftConfigurations private constructor(private val project: Project)
|
|||||||
setupBasic()
|
setupBasic()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun consistentWithMain(sourceSet: SourceSet) {
|
||||||
|
configurations.named(sourceSet.compileClasspathConfigurationName) {
|
||||||
|
shouldResolveConsistentlyWith(configurations[main.compileClasspathConfigurationName])
|
||||||
|
}
|
||||||
|
|
||||||
|
configurations.named(sourceSet.runtimeClasspathConfigurationName) {
|
||||||
|
shouldResolveConsistentlyWith(configurations[main.runtimeClasspathConfigurationName])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun setupBasic() {
|
private fun setupBasic() {
|
||||||
val client = sourceSets["client"]
|
val client = sourceSets["client"]
|
||||||
|
|
||||||
@@ -96,13 +99,30 @@ class MinecraftConfigurations private constructor(private val project: Project)
|
|||||||
val checkDependencyConsistency =
|
val checkDependencyConsistency =
|
||||||
project.tasks.register("checkDependencyConsistency", DependencyCheck::class.java) {
|
project.tasks.register("checkDependencyConsistency", DependencyCheck::class.java) {
|
||||||
// We need to check both the main and client classpath *configurations*, as the actual configuration
|
// We need to check both the main and client classpath *configurations*, as the actual configuration
|
||||||
configuration.add(configurations.named(main.runtimeClasspathConfigurationName))
|
configuration(configurations.named(main.runtimeClasspathConfigurationName))
|
||||||
configuration.add(configurations.named(client.runtimeClasspathConfigurationName))
|
configuration(configurations.named(client.runtimeClasspathConfigurationName))
|
||||||
}
|
}
|
||||||
project.tasks.named("check") { dependsOn(checkDependencyConsistency) }
|
project.tasks.named("check") { dependsOn(checkDependencyConsistency) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new configuration that pulls in the main and client classes from the mod.
|
||||||
|
*/
|
||||||
|
private fun createDerivedConfiguration(name: String) {
|
||||||
|
val client = sourceSets["client"]
|
||||||
|
val sourceSet = sourceSets.create(name)
|
||||||
|
sourceSet.compileClasspath += main.compileClasspath + client.compileClasspath
|
||||||
|
sourceSet.runtimeClasspath += main.runtimeClasspath + client.runtimeClasspath
|
||||||
|
consistentWithMain(sourceSet)
|
||||||
|
project.dependencies.add(sourceSet.implementationConfigurationName, main.output)
|
||||||
|
project.dependencies.add(sourceSet.implementationConfigurationName, client.output)
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
const val DATAGEN = "datagen"
|
||||||
|
const val EXAMPLES = "examples"
|
||||||
|
const val TEST_MOD = "testMod"
|
||||||
|
|
||||||
fun setupBasic(project: Project) {
|
fun setupBasic(project: Project) {
|
||||||
MinecraftConfigurations(project).setupBasic()
|
MinecraftConfigurations(project).setupBasic()
|
||||||
}
|
}
|
||||||
@@ -110,6 +130,10 @@ class MinecraftConfigurations private constructor(private val project: Project)
|
|||||||
fun setup(project: Project) {
|
fun setup(project: Project) {
|
||||||
MinecraftConfigurations(project).setup()
|
MinecraftConfigurations(project).setup()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun createDerivedConfiguration(project: Project, name: String) {
|
||||||
|
MinecraftConfigurations(project).createDerivedConfiguration(name)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
package cc.tweaked.gradle
|
package cc.tweaked.gradle
|
||||||
|
|
||||||
|
import net.neoforged.moddevgradle.internal.RunGameTask
|
||||||
import org.gradle.api.GradleException
|
import org.gradle.api.GradleException
|
||||||
import org.gradle.api.file.FileSystemOperations
|
import org.gradle.api.file.FileSystemOperations
|
||||||
import org.gradle.api.invocation.Gradle
|
import org.gradle.api.invocation.Gradle
|
||||||
@@ -65,6 +66,22 @@ abstract class ClientJavaExec : JavaExec() {
|
|||||||
setTestProperties()
|
setTestProperties()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun copyFromForge(path: String) = copyFromForge(project.tasks.getByName(path, RunGameTask::class))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set this task to run a given [RunGameTask].
|
||||||
|
*/
|
||||||
|
fun copyFromForge(task: RunGameTask) {
|
||||||
|
copyFrom(task)
|
||||||
|
|
||||||
|
// Eagerly evaluate the behaviour of RunGameTask.exec
|
||||||
|
environment.putAll(task.environmentProperty.get())
|
||||||
|
classpath(task.classpathProvider)
|
||||||
|
workingDir = task.gameDirectory.get().asFile
|
||||||
|
|
||||||
|
setTestProperties() // setRunConfig may clobber some properties, ensure everything is set.
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Copy configuration from a task with the given name.
|
* Copy configuration from a task with the given name.
|
||||||
*/
|
*/
|
||||||
|
@@ -11,7 +11,9 @@ import org.gradle.api.file.Directory
|
|||||||
import org.gradle.api.file.DirectoryProperty
|
import org.gradle.api.file.DirectoryProperty
|
||||||
import org.gradle.api.provider.Provider
|
import org.gradle.api.provider.Provider
|
||||||
import org.gradle.api.tasks.*
|
import org.gradle.api.tasks.*
|
||||||
|
import org.gradle.process.ExecOperations
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
class NodePlugin : Plugin<Project> {
|
class NodePlugin : Plugin<Project> {
|
||||||
override fun apply(project: Project) {
|
override fun apply(project: Project) {
|
||||||
@@ -43,9 +45,12 @@ abstract class NpmInstall : DefaultTask() {
|
|||||||
@get:OutputDirectory
|
@get:OutputDirectory
|
||||||
val nodeModules: Provider<Directory> = projectRoot.dir("node_modules")
|
val nodeModules: Provider<Directory> = projectRoot.dir("node_modules")
|
||||||
|
|
||||||
|
@get:Inject
|
||||||
|
protected abstract val execOperations: ExecOperations
|
||||||
|
|
||||||
@TaskAction
|
@TaskAction
|
||||||
fun install() {
|
fun install() {
|
||||||
project.exec {
|
execOperations.exec {
|
||||||
commandLine(ProcessHelpers.getExecutable("npm"), "ci")
|
commandLine(ProcessHelpers.getExecutable("npm"), "ci")
|
||||||
workingDir = projectRoot.get().asFile
|
workingDir = projectRoot.get().asFile
|
||||||
}
|
}
|
||||||
|
@@ -4,45 +4,10 @@
|
|||||||
|
|
||||||
package cc.tweaked.gradle
|
package cc.tweaked.gradle
|
||||||
|
|
||||||
import org.codehaus.groovy.runtime.ProcessGroovyMethods
|
|
||||||
import org.gradle.api.GradleException
|
import org.gradle.api.GradleException
|
||||||
import java.io.BufferedReader
|
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.InputStreamReader
|
|
||||||
import java.nio.charset.StandardCharsets
|
|
||||||
|
|
||||||
internal object ProcessHelpers {
|
internal object ProcessHelpers {
|
||||||
fun startProcess(vararg command: String): Process {
|
|
||||||
// Something randomly passes in "GIT_DIR=" as an environment variable which clobbers everything else. Don't
|
|
||||||
// inherit the environment array!
|
|
||||||
return ProcessBuilder()
|
|
||||||
.command(*command)
|
|
||||||
.redirectError(ProcessBuilder.Redirect.INHERIT)
|
|
||||||
.also { it.environment().clear() }
|
|
||||||
.start()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun captureOut(vararg command: String): String {
|
|
||||||
val process = startProcess(*command)
|
|
||||||
process.outputStream.close()
|
|
||||||
|
|
||||||
val result = ProcessGroovyMethods.getText(process)
|
|
||||||
process.waitForOrThrow("Failed to run command")
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
fun captureLines(vararg command: String): List<String> {
|
|
||||||
val process = startProcess(*command)
|
|
||||||
process.outputStream.close()
|
|
||||||
|
|
||||||
val out = BufferedReader(InputStreamReader(process.inputStream, StandardCharsets.UTF_8)).use { reader ->
|
|
||||||
reader.lines().filter { it.isNotEmpty() }.toList()
|
|
||||||
}
|
|
||||||
ProcessGroovyMethods.closeStreams(process)
|
|
||||||
process.waitForOrThrow("Failed to run command")
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
||||||
fun onPath(name: String): Boolean {
|
fun onPath(name: String): Boolean {
|
||||||
val path = System.getenv("PATH") ?: return false
|
val path = System.getenv("PATH") ?: return false
|
||||||
return path.splitToSequence(File.pathSeparator).any { File(it, name).exists() }
|
return path.splitToSequence(File.pathSeparator).any { File(it, name).exists() }
|
||||||
|
@@ -1,16 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: 2022 The CC: Tweaked Developers
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: MPL-2.0
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
@@ -101,7 +101,10 @@ SPDX-License-Identifier: MPL-2.0
|
|||||||
<module name="InvalidJavadocPosition" />
|
<module name="InvalidJavadocPosition" />
|
||||||
<module name="JavadocBlockTagLocation" />
|
<module name="JavadocBlockTagLocation" />
|
||||||
<module name="JavadocMethod"/>
|
<module name="JavadocMethod"/>
|
||||||
<module name="JavadocType"/>
|
<module name="JavadocType">
|
||||||
|
<!-- Seems to complain about @hidden!? -->
|
||||||
|
<property name="allowUnknownTags" value="true" />
|
||||||
|
</module>
|
||||||
<module name="JavadocStyle">
|
<module name="JavadocStyle">
|
||||||
<property name="checkHtml" value="false" />
|
<property name="checkHtml" value="false" />
|
||||||
</module>
|
</module>
|
||||||
@@ -133,7 +136,7 @@ SPDX-License-Identifier: MPL-2.0
|
|||||||
</module>
|
</module>
|
||||||
<module name="MethodTypeParameterName" />
|
<module name="MethodTypeParameterName" />
|
||||||
<module name="PackageName">
|
<module name="PackageName">
|
||||||
<property name="format" value="^(dan200\.computercraft|cc\.tweaked)(\.[a-z][a-z0-9]*)*" />
|
<property name="format" value="^(dan200\.computercraft|cc\.tweaked|com\.example\.examplemod)(\.[a-z][a-z0-9]*)*" />
|
||||||
</module>
|
</module>
|
||||||
<module name="ParameterName" />
|
<module name="ParameterName" />
|
||||||
<module name="StaticVariableName">
|
<module name="StaticVariableName">
|
||||||
@@ -152,7 +155,10 @@ SPDX-License-Identifier: MPL-2.0
|
|||||||
<module name="NoWhitespaceAfter">
|
<module name="NoWhitespaceAfter">
|
||||||
<property name="tokens" value="AT,INC,DEC,UNARY_MINUS,UNARY_PLUS,BNOT,LNOT,DOT,ARRAY_DECLARATOR,INDEX_OP,METHOD_REF" />
|
<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">
|
||||||
|
<!-- Allow whitespace before "..." for @Nullable annotations -->
|
||||||
|
<property name="tokens" value="COMMA,SEMI,POST_INC,POST_DEC,LABELED_STAT" />
|
||||||
|
</module>
|
||||||
<!-- TODO: Decide on an OperatorWrap style. -->
|
<!-- TODO: Decide on an OperatorWrap style. -->
|
||||||
<module name="ParenPad" />
|
<module name="ParenPad" />
|
||||||
<module name="SeparatorWrap">
|
<module name="SeparatorWrap">
|
||||||
|
@@ -8,7 +8,7 @@ SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
|
|||||||
SPDX-License-Identifier: MPL-2.0
|
SPDX-License-Identifier: MPL-2.0
|
||||||
-->
|
-->
|
||||||
|
|
||||||
The [`monitor_resize`] event is fired when an adjacent or networked monitor's size is changed.
|
The [`monitor_resize`] event is fired when an adjacent or networked [monitor's][`monitor`] size is changed.
|
||||||
|
|
||||||
## Return Values
|
## Return Values
|
||||||
1. [`string`]: The event name.
|
1. [`string`]: The event name.
|
||||||
|
@@ -8,7 +8,7 @@ SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
|
|||||||
SPDX-License-Identifier: MPL-2.0
|
SPDX-License-Identifier: MPL-2.0
|
||||||
-->
|
-->
|
||||||
|
|
||||||
The [`monitor_touch`] event is fired when an adjacent or networked Advanced Monitor is right-clicked.
|
The [`monitor_touch`] event is fired when an adjacent or networked [Advanced Monitor][`monitor`] is right-clicked.
|
||||||
|
|
||||||
## Return Values
|
## Return Values
|
||||||
1. [`string`]: The event name.
|
1. [`string`]: The event name.
|
||||||
|
@@ -12,7 +12,7 @@ neogradle.subsystems.conventions.runs.enabled=false
|
|||||||
|
|
||||||
# Mod properties
|
# Mod properties
|
||||||
isUnstable=true
|
isUnstable=true
|
||||||
modVersion=1.114.2
|
modVersion=1.116.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.21.1
|
mcVersion=1.21.1
|
||||||
|
@@ -10,7 +10,7 @@
|
|||||||
fabric-api = "0.102.1+1.21.1"
|
fabric-api = "0.102.1+1.21.1"
|
||||||
fabric-loader = "0.15.11"
|
fabric-loader = "0.15.11"
|
||||||
neoForge = "21.1.9"
|
neoForge = "21.1.9"
|
||||||
neoForgeSpi = "8.0.1"
|
neoMergeTool = "2.0.0"
|
||||||
mixin = "0.8.5"
|
mixin = "0.8.5"
|
||||||
parchment = "2024.07.28"
|
parchment = "2024.07.28"
|
||||||
parchmentMc = "1.21"
|
parchmentMc = "1.21"
|
||||||
@@ -26,14 +26,14 @@ slf4j = "2.0.9"
|
|||||||
asm = "9.6"
|
asm = "9.6"
|
||||||
autoService = "1.1.1"
|
autoService = "1.1.1"
|
||||||
checkerFramework = "3.42.0"
|
checkerFramework = "3.42.0"
|
||||||
cobalt = { strictly = "0.9.5" }
|
cobalt = { strictly = "0.9.6" }
|
||||||
commonsCli = "1.6.0"
|
commonsCli = "1.6.0"
|
||||||
jetbrainsAnnotations = "24.1.0"
|
jetbrainsAnnotations = "24.1.0"
|
||||||
jsr305 = "3.0.2"
|
jspecify = "1.0.0"
|
||||||
jzlib = "1.1.3"
|
jzlib = "1.1.3"
|
||||||
kotlin = "1.9.21"
|
kotlin = "2.1.10"
|
||||||
kotlin-coroutines = "1.7.3"
|
kotlin-coroutines = "1.10.1"
|
||||||
nightConfig = "3.6.7"
|
nightConfig = "3.8.1"
|
||||||
|
|
||||||
# Minecraft mods
|
# Minecraft mods
|
||||||
emi = "1.1.7+1.21"
|
emi = "1.1.7+1.21"
|
||||||
@@ -42,39 +42,40 @@ iris-fabric = "1.8.0-beta.3+1.21-fabric"
|
|||||||
iris-forge = "1.8.0-beta.3+1.21-neoforge"
|
iris-forge = "1.8.0-beta.3+1.21-neoforge"
|
||||||
jei = "19.8.2.99"
|
jei = "19.8.2.99"
|
||||||
modmenu = "11.0.0-rc.4"
|
modmenu = "11.0.0-rc.4"
|
||||||
moreRed = "4.0.0.4"
|
moreRed = "6.0.0.3"
|
||||||
rei = "16.0.729"
|
rei = "16.0.729"
|
||||||
sodium-fabric = "mc1.21-0.6.0-beta.1-fabric"
|
sodium-fabric = "mc1.21-0.6.0-beta.1-fabric"
|
||||||
sodium-forge = "mc1.21-0.6.0-beta.1-neoforge"
|
sodium-forge = "mc1.21-0.6.0-beta.1-neoforge"
|
||||||
mixinExtra = "0.3.5"
|
mixinExtra = "0.3.5"
|
||||||
create-forge = "0.5.1.f-33"
|
create-forge = "6.0.0-6"
|
||||||
create-fabric = "0.5.1-f-build.1467+mc1.20.1"
|
create-fabric = "0.5.1-f-build.1467+mc1.20.1"
|
||||||
|
|
||||||
# Testing
|
# Testing
|
||||||
hamcrest = "2.2"
|
hamcrest = "2.2"
|
||||||
jqwik = "1.8.2"
|
jqwik = "1.8.2"
|
||||||
junit = "5.10.1"
|
junit = "5.11.4"
|
||||||
|
junitPlatform = "1.11.4"
|
||||||
jmh = "1.37"
|
jmh = "1.37"
|
||||||
|
|
||||||
# Build tools
|
# Build tools
|
||||||
cctJavadoc = "1.8.3"
|
cctJavadoc = "1.8.4"
|
||||||
checkstyle = "10.20.1"
|
checkstyle = "10.23.1"
|
||||||
errorProne-core = "2.27.0"
|
errorProne-core = "2.38.0"
|
||||||
errorProne-plugin = "3.1.0"
|
errorProne-plugin = "4.1.0"
|
||||||
fabric-loom = "1.7.1"
|
fabric-loom = "1.10.4"
|
||||||
githubRelease = "2.5.2"
|
githubRelease = "2.5.2"
|
||||||
gradleVersions = "0.50.0"
|
gradleVersions = "0.50.0"
|
||||||
ideaExt = "1.1.7"
|
ideaExt = "1.1.7"
|
||||||
illuaminate = "0.1.0-74-gf1551d5"
|
illuaminate = "0.1.0-83-g1131f68"
|
||||||
lwjgl = "3.3.3"
|
lwjgl = "3.3.3"
|
||||||
minotaur = "2.8.7"
|
minotaur = "2.8.7"
|
||||||
neoGradle = "7.0.170"
|
modDevGradle = "2.0.95"
|
||||||
nullAway = "0.10.25"
|
nullAway = "0.12.7"
|
||||||
shadow = "8.3.1"
|
shadow = "8.3.1"
|
||||||
spotless = "6.23.3"
|
spotless = "7.0.2"
|
||||||
taskTree = "2.1.1"
|
taskTree = "2.1.1"
|
||||||
teavm = "0.11.0-SQUID.1"
|
teavm = "0.11.0-SQUID.1"
|
||||||
vanillaExtract = "0.1.3"
|
vanillaExtract = "0.2.1"
|
||||||
versionCatalogUpdate = "0.8.1"
|
versionCatalogUpdate = "0.8.1"
|
||||||
|
|
||||||
[libraries]
|
[libraries]
|
||||||
@@ -86,10 +87,10 @@ checkerFramework = { module = "org.checkerframework:checker-qual", version.ref =
|
|||||||
cobalt = { module = "cc.tweaked:cobalt", version.ref = "cobalt" }
|
cobalt = { module = "cc.tweaked:cobalt", version.ref = "cobalt" }
|
||||||
commonsCli = { module = "commons-cli:commons-cli", version.ref = "commonsCli" }
|
commonsCli = { module = "commons-cli:commons-cli", version.ref = "commonsCli" }
|
||||||
fastutil = { module = "it.unimi.dsi:fastutil", version.ref = "fastutil" }
|
fastutil = { module = "it.unimi.dsi:fastutil", version.ref = "fastutil" }
|
||||||
neoForgeSpi = { module = "net.neoforged:neoforgespi", version.ref = "neoForgeSpi" }
|
neoMergeTool = { module = "net.neoforged:mergetool", version.ref = "neoMergeTool" }
|
||||||
guava = { module = "com.google.guava:guava", version.ref = "guava" }
|
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" }
|
jspecify = { module = "org.jspecify:jspecify", version.ref = "jspecify" }
|
||||||
jzlib = { module = "com.jcraft:jzlib", version.ref = "jzlib" }
|
jzlib = { module = "com.jcraft:jzlib", version.ref = "jzlib" }
|
||||||
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-platform = { module = "org.jetbrains.kotlin:kotlin-bom", version.ref = "kotlin" }
|
kotlin-platform = { module = "org.jetbrains.kotlin:kotlin-bom", version.ref = "kotlin" }
|
||||||
@@ -104,7 +105,7 @@ slf4j = { module = "org.slf4j:slf4j-api", version.ref = "slf4j" }
|
|||||||
|
|
||||||
# Minecraft mods
|
# Minecraft mods
|
||||||
create-fabric = { module = "com.simibubi.create:create-fabric-1.20.1", version.ref = "create-fabric" }
|
create-fabric = { module = "com.simibubi.create:create-fabric-1.20.1", version.ref = "create-fabric" }
|
||||||
create-forge = { module = "com.simibubi.create:create-1.20.1", version.ref = "create-forge" }
|
create-forge = { module = "com.simibubi.create:create-1.21.1", version.ref = "create-forge" }
|
||||||
emi = { module = "dev.emi:emi-xplat-mojmap", version.ref = "emi" }
|
emi = { module = "dev.emi:emi-xplat-mojmap", version.ref = "emi" }
|
||||||
fabric-api = { module = "net.fabricmc.fabric-api:fabric-api", version.ref = "fabric-api" }
|
fabric-api = { module = "net.fabricmc.fabric-api:fabric-api", version.ref = "fabric-api" }
|
||||||
fabric-junit = { module = "net.fabricmc:fabric-loader-junit", version.ref = "fabric-loader" }
|
fabric-junit = { module = "net.fabricmc:fabric-loader-junit", version.ref = "fabric-loader" }
|
||||||
@@ -118,7 +119,7 @@ jei-forge = { module = "mezz.jei:jei-1.21-neoforge", version.ref = "jei" }
|
|||||||
mixin = { module = "org.spongepowered:mixin", version.ref = "mixin" }
|
mixin = { module = "org.spongepowered:mixin", version.ref = "mixin" }
|
||||||
mixinExtra = { module = "io.github.llamalad7:mixinextras-common", version.ref = "mixinExtra" }
|
mixinExtra = { module = "io.github.llamalad7:mixinextras-common", version.ref = "mixinExtra" }
|
||||||
modmenu = { module = "com.terraformersmc:modmenu", version.ref = "modmenu" }
|
modmenu = { module = "com.terraformersmc:modmenu", version.ref = "modmenu" }
|
||||||
moreRed = { module = "commoble.morered:morered-1.20.1", version.ref = "moreRed" }
|
moreRed = { module = "net.commoble.morered:morered-1.21.1", version.ref = "moreRed" }
|
||||||
rei-api = { module = "me.shedaniel:RoughlyEnoughItems-api", version.ref = "rei" }
|
rei-api = { module = "me.shedaniel:RoughlyEnoughItems-api", version.ref = "rei" }
|
||||||
rei-builtin = { module = "me.shedaniel:RoughlyEnoughItems-default-plugin", version.ref = "rei" }
|
rei-builtin = { module = "me.shedaniel:RoughlyEnoughItems-default-plugin", version.ref = "rei" }
|
||||||
rei-fabric = { module = "me.shedaniel:RoughlyEnoughItems-fabric", version.ref = "rei" }
|
rei-fabric = { module = "me.shedaniel:RoughlyEnoughItems-fabric", version.ref = "rei" }
|
||||||
@@ -132,6 +133,7 @@ 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" }
|
||||||
|
junit-platform-launcher = { module = "org.junit.platform:junit-platform-launcher", version.ref = "junitPlatform" }
|
||||||
slf4j-simple = { module = "org.slf4j:slf4j-simple", version.ref = "slf4j" }
|
slf4j-simple = { module = "org.slf4j:slf4j-simple", version.ref = "slf4j" }
|
||||||
jmh = { module = "org.openjdk.jmh:jmh-core", version.ref = "jmh" }
|
jmh = { module = "org.openjdk.jmh:jmh-core", version.ref = "jmh" }
|
||||||
jmh-processor = { module = "org.openjdk.jmh:jmh-generator-annprocess", version.ref = "jmh" }
|
jmh-processor = { module = "org.openjdk.jmh:jmh-generator-annprocess", version.ref = "jmh" }
|
||||||
@@ -154,8 +156,8 @@ fabric-loom = { module = "net.fabricmc:fabric-loom", version.ref = "fabric-loom"
|
|||||||
ideaExt = { module = "gradle.plugin.org.jetbrains.gradle.plugin.idea-ext:gradle-idea-ext", version.ref = "ideaExt" }
|
ideaExt = { module = "gradle.plugin.org.jetbrains.gradle.plugin.idea-ext:gradle-idea-ext", version.ref = "ideaExt" }
|
||||||
kotlin-plugin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" }
|
kotlin-plugin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" }
|
||||||
minotaur = { module = "com.modrinth.minotaur:Minotaur", version.ref = "minotaur" }
|
minotaur = { module = "com.modrinth.minotaur:Minotaur", version.ref = "minotaur" }
|
||||||
neoGradle-userdev = { module = "net.neoforged.gradle:userdev", version.ref = "neoGradle" }
|
|
||||||
nullAway = { module = "com.uber.nullaway:nullaway", version.ref = "nullAway" }
|
nullAway = { module = "com.uber.nullaway:nullaway", version.ref = "nullAway" }
|
||||||
|
modDevGradle = { module = "net.neoforged:moddev-gradle", version.ref = "modDevGradle" }
|
||||||
spotless = { module = "com.diffplug.spotless:spotless-plugin-gradle", version.ref = "spotless" }
|
spotless = { module = "com.diffplug.spotless:spotless-plugin-gradle", version.ref = "spotless" }
|
||||||
teavm-classlib = { module = "org.teavm:teavm-classlib", version.ref = "teavm" }
|
teavm-classlib = { module = "org.teavm:teavm-classlib", version.ref = "teavm" }
|
||||||
teavm-core = { module = "org.teavm:teavm-core", version.ref = "teavm" }
|
teavm-core = { module = "org.teavm:teavm-core", version.ref = "teavm" }
|
||||||
@@ -178,7 +180,7 @@ taskTree = { id = "com.dorongold.task-tree", version.ref = "taskTree" }
|
|||||||
versionCatalogUpdate = { id = "nl.littlerobots.version-catalog-update", version.ref = "versionCatalogUpdate" }
|
versionCatalogUpdate = { id = "nl.littlerobots.version-catalog-update", version.ref = "versionCatalogUpdate" }
|
||||||
|
|
||||||
[bundles]
|
[bundles]
|
||||||
annotations = ["jsr305", "checkerFramework", "jetbrainsAnnotations"]
|
annotations = ["checkerFramework", "jetbrainsAnnotations", "jspecify"]
|
||||||
kotlin = ["kotlin-stdlib", "kotlin-coroutines"]
|
kotlin = ["kotlin-stdlib", "kotlin-coroutines"]
|
||||||
|
|
||||||
# Minecraft
|
# Minecraft
|
||||||
@@ -190,7 +192,7 @@ externalMods-fabric-runtime = ["jei-fabric", "modmenu"]
|
|||||||
|
|
||||||
# Testing
|
# Testing
|
||||||
test = ["junit-jupiter-api", "junit-jupiter-params", "hamcrest", "jqwik-api"]
|
test = ["junit-jupiter-api", "junit-jupiter-params", "hamcrest", "jqwik-api"]
|
||||||
testRuntime = ["junit-jupiter-engine", "jqwik-engine"]
|
testRuntime = ["junit-jupiter-engine", "junit-platform-launcher", "jqwik-engine"]
|
||||||
|
|
||||||
# Build tools
|
# Build tools
|
||||||
teavm-api = ["teavm-jso", "teavm-jso-apis", "teavm-platform", "teavm-classlib", "teavm-metaprogramming-api"]
|
teavm-api = ["teavm-jso", "teavm-jso-apis", "teavm-platform", "teavm-classlib", "teavm-metaprogramming-api"]
|
||||||
|
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,6 +1,6 @@
|
|||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-bin.zip
|
||||||
networkTimeout=10000
|
networkTimeout=10000
|
||||||
validateDistributionUrl=true
|
validateDistributionUrl=true
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
3
gradlew
vendored
3
gradlew
vendored
@@ -86,8 +86,7 @@ done
|
|||||||
# shellcheck disable=SC2034
|
# shellcheck disable=SC2034
|
||||||
APP_BASE_NAME=${0##*/}
|
APP_BASE_NAME=${0##*/}
|
||||||
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
|
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
|
||||||
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s
|
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
|
||||||
' "$PWD" ) || exit
|
|
||||||
|
|
||||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||||
MAX_FD=maximum
|
MAX_FD=maximum
|
||||||
|
969
package-lock.json
generated
969
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -12,11 +12,11 @@
|
|||||||
"tslib": "^2.0.3"
|
"tslib": "^2.0.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@rollup/plugin-node-resolve": "^15.2.1",
|
"@rollup/plugin-node-resolve": "^16.0.0",
|
||||||
"@rollup/plugin-typescript": "^12.0.0",
|
"@rollup/plugin-typescript": "^12.0.0 && <12.1.3",
|
||||||
"@rollup/plugin-url": "^8.0.1",
|
"@rollup/plugin-url": "^8.0.1",
|
||||||
"@swc/core": "^1.3.92",
|
"@swc/core": "^1.3.92",
|
||||||
"@types/node": "^22.0.0",
|
"@types/node": "^24.0.0",
|
||||||
"lightningcss": "^1.22.0",
|
"lightningcss": "^1.22.0",
|
||||||
"preact-render-to-string": "^6.2.1",
|
"preact-render-to-string": "^6.2.1",
|
||||||
"rehype": "^13.0.0",
|
"rehype": "^13.0.0",
|
||||||
|
@@ -18,13 +18,28 @@ dependencies {
|
|||||||
api(project(":core-api"))
|
api(project(":core-api"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val javadocOverview by tasks.registering(Copy::class) {
|
||||||
|
from("src/overview.html")
|
||||||
|
into(layout.buildDirectory.dir(name))
|
||||||
|
|
||||||
|
expand(
|
||||||
|
mapOf(
|
||||||
|
"mcVersion" to mcVersion,
|
||||||
|
"modVersion" to version,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
tasks.javadoc {
|
tasks.javadoc {
|
||||||
title = "CC: Tweaked $version Minecraft $mcVersion"
|
title = "CC: Tweaked $version for Minecraft $mcVersion"
|
||||||
include("dan200/computercraft/api/**/*.java")
|
include("dan200/computercraft/api/**/*.java")
|
||||||
|
|
||||||
options {
|
options {
|
||||||
(this as StandardJavadocDocletOptions)
|
(this as StandardJavadocDocletOptions)
|
||||||
|
|
||||||
|
inputs.files(javadocOverview)
|
||||||
|
overview(javadocOverview.get().destinationDir.resolve("overview.html").absolutePath)
|
||||||
|
|
||||||
groups = mapOf(
|
groups = mapOf(
|
||||||
"Common" to listOf(
|
"Common" to listOf(
|
||||||
"dan200.computercraft.api",
|
"dan200.computercraft.api",
|
||||||
@@ -47,6 +62,12 @@ tasks.javadoc {
|
|||||||
<link href=" https://cdn.jsdelivr.net/npm/prismjs@1.29.0/themes/prism.min.css " rel="stylesheet">
|
<link href=" https://cdn.jsdelivr.net/npm/prismjs@1.29.0/themes/prism.min.css " rel="stylesheet">
|
||||||
""".trimIndent(),
|
""".trimIndent(),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
val snippetSources = listOf(":common", ":fabric", ":forge").flatMap {
|
||||||
|
project(it).sourceSets["examples"].allSource.sourceDirectories
|
||||||
|
}
|
||||||
|
inputs.files(snippetSources)
|
||||||
|
addPathOption("-snippet-path").value = snippetSources
|
||||||
}
|
}
|
||||||
|
|
||||||
// Include the core-api in our javadoc export. This is wrong, but it means we can export a single javadoc dump.
|
// Include the core-api in our javadoc export. This is wrong, but it means we can export a single javadoc dump.
|
||||||
|
@@ -10,7 +10,7 @@ import net.minecraft.client.resources.model.BakedModel;
|
|||||||
import net.minecraft.client.resources.model.ModelManager;
|
import net.minecraft.client.resources.model.ModelManager;
|
||||||
import net.minecraft.client.resources.model.ModelResourceLocation;
|
import net.minecraft.client.resources.model.ModelResourceLocation;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
@@ -12,8 +12,8 @@ import dan200.computercraft.api.turtle.TurtleSide;
|
|||||||
import net.minecraft.client.resources.model.UnbakedModel;
|
import net.minecraft.client.resources.model.UnbakedModel;
|
||||||
import net.minecraft.core.component.DataComponentPatch;
|
import net.minecraft.core.component.DataComponentPatch;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -21,7 +21,14 @@ import java.util.stream.Stream;
|
|||||||
* <p>
|
* <p>
|
||||||
* Use {@code dan200.computercraft.api.client.FabricComputerCraftAPIClient#registerTurtleUpgradeModeller} to register a
|
* Use {@code dan200.computercraft.api.client.FabricComputerCraftAPIClient#registerTurtleUpgradeModeller} to register a
|
||||||
* modeller on Fabric and {@code dan200.computercraft.api.client.turtle.RegisterTurtleModellersEvent} to register one
|
* modeller on Fabric and {@code dan200.computercraft.api.client.turtle.RegisterTurtleModellersEvent} to register one
|
||||||
* on Forge
|
* on Forge.
|
||||||
|
*
|
||||||
|
* <h2>Example</h2>
|
||||||
|
* <h3>Fabric</h3>
|
||||||
|
* {@snippet class=com.example.examplemod.FabricExampleModClient region=turtle_modellers}
|
||||||
|
*
|
||||||
|
* <h3>Forge</h3>
|
||||||
|
* {@snippet class=com.example.examplemod.FabricExampleModClient region=turtle_modellers}
|
||||||
*
|
*
|
||||||
* @param <T> The type of turtle upgrade this modeller applies to.
|
* @param <T> The type of turtle upgrade this modeller applies to.
|
||||||
* @see RegisterTurtleUpgradeModeller For multi-loader registration support.
|
* @see RegisterTurtleUpgradeModeller For multi-loader registration support.
|
||||||
|
@@ -13,8 +13,7 @@ import dan200.computercraft.impl.client.ClientPlatformHelper;
|
|||||||
import net.minecraft.client.Minecraft;
|
import net.minecraft.client.Minecraft;
|
||||||
import net.minecraft.core.component.DataComponentPatch;
|
import net.minecraft.core.component.DataComponentPatch;
|
||||||
import org.joml.Matrix4f;
|
import org.joml.Matrix4f;
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
import javax.annotation.Nullable;
|
|
||||||
|
|
||||||
final class TurtleUpgradeModellers {
|
final class TurtleUpgradeModellers {
|
||||||
private static final Transformation leftTransform = getMatrixFor(-0.4065f);
|
private static final Transformation leftTransform = getMatrixFor(-0.4065f);
|
||||||
|
@@ -12,8 +12,7 @@ import net.minecraft.client.resources.model.ModelManager;
|
|||||||
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 org.jetbrains.annotations.ApiStatus;
|
import org.jetbrains.annotations.ApiStatus;
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
import javax.annotation.Nullable;
|
|
||||||
|
|
||||||
@ApiStatus.Internal
|
@ApiStatus.Internal
|
||||||
public interface ClientPlatformHelper {
|
public interface ClientPlatformHelper {
|
||||||
|
@@ -26,8 +26,7 @@ import net.minecraft.core.Direction;
|
|||||||
import net.minecraft.server.MinecraftServer;
|
import net.minecraft.server.MinecraftServer;
|
||||||
import net.minecraft.world.item.ItemStack;
|
import net.minecraft.world.item.ItemStack;
|
||||||
import net.minecraft.world.level.Level;
|
import net.minecraft.world.level.Level;
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
import javax.annotation.Nullable;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The static entry point to the ComputerCraft API.
|
* The static entry point to the ComputerCraft API.
|
||||||
@@ -148,7 +147,10 @@ public final class ComputerCraftAPI {
|
|||||||
*
|
*
|
||||||
* @param provider The media provider to register.
|
* @param provider The media provider to register.
|
||||||
* @see MediaProvider
|
* @see MediaProvider
|
||||||
|
* @deprecated Prefer {@code dan200.computercraft.api.media.MediaLookup} (Fabric) or
|
||||||
|
* {@code dan200.computercraft.api.media.MediaCapability} (NeoForge).
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public static void registerMediaProvider(MediaProvider provider) {
|
public static void registerMediaProvider(MediaProvider provider) {
|
||||||
getInstance().registerMediaProvider(provider);
|
getInstance().registerMediaProvider(provider);
|
||||||
}
|
}
|
||||||
@@ -171,16 +173,9 @@ public final class ComputerCraftAPI {
|
|||||||
* using {@link ILuaAPI#getModuleName()} to expose this library as a module instead of as a global.
|
* using {@link ILuaAPI#getModuleName()} to expose this library as a module instead of as a global.
|
||||||
* <p>
|
* <p>
|
||||||
* This may be used with {@link IComputerSystem#getComponent(ComputerComponent)} to only attach APIs to specific
|
* This may be used with {@link IComputerSystem#getComponent(ComputerComponent)} to only attach APIs to specific
|
||||||
* computers. For example, one can add an additional API just to turtles with the following code:
|
* computers. For example, one can add a new API just to turtles with the following code:
|
||||||
*
|
*
|
||||||
* {@snippet lang="java":
|
* {@snippet class=com.example.examplemod.ExampleAPI region=register}
|
||||||
* ComputerCraftAPI.registerAPIFactory(computer -> {
|
|
||||||
* // Read the turtle component.
|
|
||||||
* var turtle = computer.getComponent(ComputerComponents.TURTLE);
|
|
||||||
* // If present then add our API.
|
|
||||||
* return turtle == null ? null : new MyCustomTurtleApi(turtle);
|
|
||||||
* });
|
|
||||||
* }
|
|
||||||
*
|
*
|
||||||
* @param factory The factory for your API subclass.
|
* @param factory The factory for your API subclass.
|
||||||
* @see ILuaAPIFactory
|
* @see ILuaAPIFactory
|
||||||
|
@@ -28,6 +28,20 @@ public class ComputerCraftTags {
|
|||||||
public static final TagKey<Item> WIRED_MODEM = make("wired_modem");
|
public static final TagKey<Item> WIRED_MODEM = make("wired_modem");
|
||||||
public static final TagKey<Item> MONITOR = make("monitor");
|
public static final TagKey<Item> MONITOR = make("monitor");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Floppy disks. Both the read/write version, and treasure disks.
|
||||||
|
*
|
||||||
|
* @since 1.116.0
|
||||||
|
*/
|
||||||
|
public static final TagKey<Item> DISKS = make("disks");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* All pocket computers.
|
||||||
|
*
|
||||||
|
* @since 1.116.0
|
||||||
|
*/
|
||||||
|
public static final TagKey<Item> POCKET_COMPUTERS = make("pocket_computers");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Items which can be {@linkplain Item#use(Level, Player, InteractionHand) used} when calling
|
* Items which can be {@linkplain Item#use(Level, Player, InteractionHand) used} when calling
|
||||||
* {@code turtle.place()}.
|
* {@code turtle.place()}.
|
||||||
|
@@ -6,8 +6,8 @@ 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 org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
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;
|
||||||
|
@@ -8,8 +8,7 @@ import net.minecraft.core.BlockPos;
|
|||||||
import net.minecraft.world.level.Level;
|
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 org.jspecify.annotations.Nullable;
|
||||||
import javax.annotation.Nullable;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A reference to a block in the world, used by block detail providers.
|
* A reference to a block in the world, used by block detail providers.
|
||||||
|
@@ -7,8 +7,8 @@ package dan200.computercraft.api.detail;
|
|||||||
import net.minecraft.core.component.DataComponentHolder;
|
import net.minecraft.core.component.DataComponentHolder;
|
||||||
import net.minecraft.core.component.DataComponentType;
|
import net.minecraft.core.component.DataComponentType;
|
||||||
import net.minecraft.world.item.ItemStack;
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
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;
|
||||||
@@ -51,10 +51,10 @@ public abstract class ComponentDetailProvider<T> implements DetailProvider<DataC
|
|||||||
* This method is always called on the server thread, so it is safe to interact with the world here, but you should
|
* This method is always called on the server thread, so it is safe to interact with the world here, but you should
|
||||||
* take care to avoid long blocking operations as this will stall the server and other computers.
|
* take care to avoid long blocking operations as this will stall the server and other computers.
|
||||||
*
|
*
|
||||||
* @param data The full details to be returned for this item stack. New properties should be added to this map.
|
* @param data The full details to be returned for this item stack. New properties should be added to this map.
|
||||||
* @param item The component to provide details for.
|
* @param component The component to provide details for.
|
||||||
*/
|
*/
|
||||||
public abstract void provideComponentDetails(Map<? super String, Object> data, T item);
|
public abstract void provideComponentDetails(Map<? super String, Object> data, T component);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final void provideDetails(Map<? super String, Object> data, DataComponentHolder holder) {
|
public final void provideDetails(Map<? super String, Object> data, DataComponentHolder holder) {
|
||||||
|
@@ -14,6 +14,7 @@ import java.util.Map;
|
|||||||
*
|
*
|
||||||
* @param <T> The type of object that this provider can provide details for.
|
* @param <T> The type of object that this provider can provide details for.
|
||||||
* @see DetailRegistry
|
* @see DetailRegistry
|
||||||
|
* @see dan200.computercraft.api.detail An overview of the detail system.
|
||||||
*/
|
*/
|
||||||
@FunctionalInterface
|
@FunctionalInterface
|
||||||
public interface DetailProvider<T> {
|
public interface DetailProvider<T> {
|
||||||
|
@@ -17,6 +17,7 @@ import java.util.Map;
|
|||||||
* also in this package.
|
* also in this package.
|
||||||
*
|
*
|
||||||
* @param <T> The type of object that this registry provides details for.
|
* @param <T> The type of object that this registry provides details for.
|
||||||
|
* @see dan200.computercraft.api.detail An overview of the detail system.
|
||||||
*/
|
*/
|
||||||
@ApiStatus.NonExtendable
|
@ApiStatus.NonExtendable
|
||||||
public interface DetailRegistry<T> {
|
public interface DetailRegistry<T> {
|
||||||
|
@@ -17,6 +17,9 @@ public class VanillaDetailRegistries {
|
|||||||
* <p>
|
* <p>
|
||||||
* This instance's {@link DetailRegistry#getBasicDetails(Object)} is thread safe (assuming the stack is immutable)
|
* This instance's {@link DetailRegistry#getBasicDetails(Object)} is thread safe (assuming the stack is immutable)
|
||||||
* and may be called from the computer thread.
|
* and may be called from the computer thread.
|
||||||
|
* <p>
|
||||||
|
* This does not have special handling for {@linkplain ItemStack#isEmpty() empty item stacks}, and so the returned
|
||||||
|
* details will be an empty stack of air. Callers should generally check for empty stacks before calling this.
|
||||||
*/
|
*/
|
||||||
public static final DetailRegistry<ItemStack> ITEM_STACK = ComputerCraftAPIService.get().getItemStackDetailRegistry();
|
public static final DetailRegistry<ItemStack> ITEM_STACK = ComputerCraftAPIService.get().getItemStackDetailRegistry();
|
||||||
|
|
||||||
|
@@ -0,0 +1,48 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2025 The CC: Tweaked Developers
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The detail system provides a standard way for mods to return descriptions of common game objects, such as blocks or
|
||||||
|
* items, as well as registering additional detail to be included in those descriptions.
|
||||||
|
* <p>
|
||||||
|
* For instance, the built-in {@code turtle.getItemDetail()} method uses
|
||||||
|
* {@linkplain dan200.computercraft.api.detail.VanillaDetailRegistries#ITEM_STACK in order to provide information about}
|
||||||
|
* the selected item:
|
||||||
|
*
|
||||||
|
* <pre class="language language-lua">{@code
|
||||||
|
* local item = turtle.getItemDetail(nil, true)
|
||||||
|
* --[[
|
||||||
|
* item = {
|
||||||
|
* name = "minecraft:wheat",
|
||||||
|
* displayName = "Wheat",
|
||||||
|
* count = 1,
|
||||||
|
* maxCount = 64,
|
||||||
|
* tags = {},
|
||||||
|
* }
|
||||||
|
* ]]
|
||||||
|
* }</pre>
|
||||||
|
*
|
||||||
|
* <h2>Built-in detail providers</h2>
|
||||||
|
* While you can define your own detail providers (perhaps for types from your own mod), CC comes with several built-in
|
||||||
|
* detail registries for vanilla and mod-loader objects:
|
||||||
|
*
|
||||||
|
* <ul>
|
||||||
|
* <li>{@link dan200.computercraft.api.detail.VanillaDetailRegistries}, for vanilla objects</li>
|
||||||
|
* <li>{@code dan200.computercraft.api.detail.ForgeDetailRegistries} for Forge-specific objects</li>
|
||||||
|
* <li>{@code dan200.computercraft.api.detail.FabricDetailRegistries} for Fabric-specific objects</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* <h2>Example: Returning details from methods</h2>
|
||||||
|
* Here we define a {@code getHeldItem()} method for pocket computers which finds the currently held item of the player
|
||||||
|
* and returns it to the user using {@link dan200.computercraft.api.detail.VanillaDetailRegistries#ITEM_STACK} and
|
||||||
|
* {@link dan200.computercraft.api.detail.DetailRegistry#getDetails(java.lang.Object)}.
|
||||||
|
*
|
||||||
|
* {@snippet class=com.example.examplemod.ExamplePocketPeripheral region=details}
|
||||||
|
*
|
||||||
|
* <h2>Example: Registering custom detail registries</h2>
|
||||||
|
* Here we define a new detail provider for items that includes the nutrition and saturation values in the returned object.
|
||||||
|
*
|
||||||
|
* {@snippet class=com.example.examplemod.ExampleMod region=details}
|
||||||
|
*/
|
||||||
|
package dan200.computercraft.api.detail;
|
@@ -9,8 +9,7 @@ import dan200.computercraft.api.peripheral.IComputerAccess;
|
|||||||
import net.minecraft.core.BlockPos;
|
import net.minecraft.core.BlockPos;
|
||||||
import net.minecraft.server.level.ServerLevel;
|
import net.minecraft.server.level.ServerLevel;
|
||||||
import org.jetbrains.annotations.ApiStatus;
|
import org.jetbrains.annotations.ApiStatus;
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
import javax.annotation.Nullable;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An interface passed to {@link ILuaAPIFactory} in order to provide additional information
|
* An interface passed to {@link ILuaAPIFactory} in order to provide additional information
|
||||||
|
@@ -5,8 +5,7 @@
|
|||||||
package dan200.computercraft.api.lua;
|
package dan200.computercraft.api.lua;
|
||||||
|
|
||||||
import dan200.computercraft.api.ComputerCraftAPI;
|
import dan200.computercraft.api.ComputerCraftAPI;
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
import javax.annotation.Nullable;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct an {@link ILuaAPI} for a computer.
|
* Construct an {@link ILuaAPI} for a computer.
|
||||||
|
@@ -14,14 +14,14 @@ import net.minecraft.server.level.ServerLevel;
|
|||||||
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.item.JukeboxSong;
|
import net.minecraft.world.item.JukeboxSong;
|
||||||
|
import org.jspecify.annotations.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, or register via
|
||||||
* a {@link MediaProvider}.
|
* {@code dan200.computercraft.api.media.MediaLookup} (Fabric) or {@code dan200.computercraft.api.media.MediaCapability}
|
||||||
|
* (NeoForge).
|
||||||
*/
|
*/
|
||||||
public interface IMedia {
|
public interface IMedia {
|
||||||
/**
|
/**
|
||||||
|
@@ -5,8 +5,7 @@
|
|||||||
package dan200.computercraft.api.media;
|
package dan200.computercraft.api.media;
|
||||||
|
|
||||||
import net.minecraft.world.item.ItemStack;
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
import org.jspecify.annotations.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}.
|
||||||
|
@@ -0,0 +1,46 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2025 The CC: Tweaked Developers
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
|
package dan200.computercraft.api.media;
|
||||||
|
|
||||||
|
import dan200.computercraft.impl.ComputerCraftAPIService;
|
||||||
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The contents of a page (or book) created by a ComputerCraft printer.
|
||||||
|
*
|
||||||
|
* @since 1.115
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
public interface PrintoutContents {
|
||||||
|
/**
|
||||||
|
* Get the (possibly empty) title for this printout.
|
||||||
|
*
|
||||||
|
* @return The title of this printout.
|
||||||
|
*/
|
||||||
|
String getTitle();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the text contents of this printout, as a sequence of lines.
|
||||||
|
* <p>
|
||||||
|
* The lines in the printout may include blank lines at the end of the document, as well as trailing spaces on each
|
||||||
|
* line.
|
||||||
|
*
|
||||||
|
* @return The text contents of this printout.
|
||||||
|
*/
|
||||||
|
Stream<String> getTextLines();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the printout contents for a particular stack.
|
||||||
|
*
|
||||||
|
* @param stack The stack to get the contents for.
|
||||||
|
* @return The printout contents, or {@code null} if this is not a printout item.
|
||||||
|
*/
|
||||||
|
static @Nullable PrintoutContents get(ItemStack stack) {
|
||||||
|
return ComputerCraftAPIService.get().getPrintoutContents(stack);
|
||||||
|
}
|
||||||
|
}
|
@@ -19,8 +19,8 @@ import dan200.computercraft.api.ComputerCraftAPI;
|
|||||||
*/
|
*/
|
||||||
public interface WiredElement extends WiredSender {
|
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 peripherals on the network change. This may occur when network nodes are added or removed, or when
|
||||||
* peripherals change.
|
* peripherals are attached or detached from a modem.
|
||||||
*
|
*
|
||||||
* @param change The change which occurred.
|
* @param change The change which occurred.
|
||||||
* @see WiredNetworkChange
|
* @see WiredNetworkChange
|
||||||
|
@@ -12,8 +12,7 @@ import net.minecraft.world.entity.Entity;
|
|||||||
import net.minecraft.world.item.ItemStack;
|
import net.minecraft.world.item.ItemStack;
|
||||||
import net.minecraft.world.phys.Vec3;
|
import net.minecraft.world.phys.Vec3;
|
||||||
import org.jetbrains.annotations.ApiStatus;
|
import org.jetbrains.annotations.ApiStatus;
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
import javax.annotation.Nullable;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wrapper class for pocket computers.
|
* Wrapper class for pocket computers.
|
||||||
|
@@ -13,39 +13,16 @@ 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.level.Level;
|
import net.minecraft.world.level.Level;
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
import javax.annotation.Nullable;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A peripheral which can be equipped to the back side of a pocket computer.
|
* A peripheral which can be equipped to the back side of a pocket computer.
|
||||||
* <p>
|
* <p>
|
||||||
* Pocket upgrades are defined in two stages. First, one creates a {@link IPocketUpgrade} subclass and corresponding
|
* Pocket upgrades are defined in two stages. First, one creates a {@link IPocketUpgrade} subclass and corresponding
|
||||||
* {@link UpgradeType} instance, which are then registered in a registry.
|
* {@link UpgradeType} instance, which are then registered in a Minecraft registry.
|
||||||
* <p>
|
* <p>
|
||||||
* You then write a JSON file in your mod's {@literal data/} folder. This is then parsed when the world is loaded, and
|
* You then write a JSON file in your mod's {@literal data/} folder. This is then parsed when the world is loaded, and
|
||||||
* the upgrade automatically registered. It is recommended this is done via
|
* the upgrade registered internally.
|
||||||
* <a href="../upgrades/UpgradeType.html#datagen">data generators</a>.
|
|
||||||
*
|
|
||||||
* <h2>Example</h2>
|
|
||||||
* {@snippet lang="java" :
|
|
||||||
* // We use Forge's DeferredRegister to register our upgrade type. Fabric mods may register their type directly.
|
|
||||||
* static final DeferredRegister<UpgradeType<? extends IPocketUpgrade>> POCKET_UPGRADES = DeferredRegister.create(IPocketUpgrade.typeRegistry(), "my_mod");
|
|
||||||
*
|
|
||||||
* // Register a new upgrade upgrade type called "my_upgrade".
|
|
||||||
* public static final RegistryObject<UpgradeType<MyUpgrade>> MY_UPGRADE =
|
|
||||||
* POCKET_UPGRADES.register("my_upgrade", () -> UpgradeType.simple(new MyUpgrade()));
|
|
||||||
*
|
|
||||||
* // Then in your constructor
|
|
||||||
* POCKET_UPGRADES.register(bus);
|
|
||||||
* }
|
|
||||||
* <p>
|
|
||||||
* We can then define a new upgrade using JSON by placing the following in
|
|
||||||
* {@code data/<my_mod>/computercraft/pocket_upgrade/<my_upgrade_id>.json}.
|
|
||||||
* {@snippet lang="json" :
|
|
||||||
* {
|
|
||||||
* "type": "my_mod:my_upgrade"
|
|
||||||
* }
|
|
||||||
* }
|
|
||||||
*/
|
*/
|
||||||
public interface IPocketUpgrade extends UpgradeBase {
|
public interface IPocketUpgrade extends UpgradeBase {
|
||||||
ResourceKey<Registry<IPocketUpgrade>> REGISTRY = ResourceKey.createRegistryKey(ResourceLocation.fromNamespaceAndPath(ComputerCraftAPI.MOD_ID, "pocket_upgrade"));
|
ResourceKey<Registry<IPocketUpgrade>> REGISTRY = ResourceKey.createRegistryKey(ResourceLocation.fromNamespaceAndPath(ComputerCraftAPI.MOD_ID, "pocket_upgrade"));
|
||||||
|
@@ -17,8 +17,7 @@ import net.minecraft.world.Container;
|
|||||||
import net.minecraft.world.item.ItemStack;
|
import net.minecraft.world.item.ItemStack;
|
||||||
import net.minecraft.world.level.Level;
|
import net.minecraft.world.level.Level;
|
||||||
import org.jetbrains.annotations.ApiStatus;
|
import org.jetbrains.annotations.ApiStatus;
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
import javax.annotation.Nullable;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The interface passed to turtle by turtles, providing methods that they can call.
|
* The interface passed to turtle by turtles, providing methods that they can call.
|
||||||
|
@@ -11,47 +11,79 @@ import dan200.computercraft.api.upgrades.UpgradeType;
|
|||||||
import dan200.computercraft.impl.ComputerCraftAPIService;
|
import dan200.computercraft.impl.ComputerCraftAPIService;
|
||||||
import net.minecraft.core.Direction;
|
import net.minecraft.core.Direction;
|
||||||
import net.minecraft.core.Registry;
|
import net.minecraft.core.Registry;
|
||||||
|
import net.minecraft.core.RegistrySetBuilder.PatchedRegistries;
|
||||||
import net.minecraft.core.component.DataComponentPatch;
|
import net.minecraft.core.component.DataComponentPatch;
|
||||||
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.Items;
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import java.util.function.Function;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The primary interface for defining an update for Turtles. A turtle update can either be a new tool, or a new
|
* The primary interface for defining an update for Turtles. A turtle update can either be a new tool, or a new
|
||||||
* peripheral.
|
* peripheral.
|
||||||
* <p>
|
* <p>
|
||||||
* Turtle upgrades are defined in two stages. First, one creates a {@link ITurtleUpgrade} subclass and corresponding
|
* Turtle upgrades are defined in two stages. First, one creates a {@link ITurtleUpgrade} subclass and corresponding
|
||||||
* {@link UpgradeType} instance, which are then registered in a registry.
|
* {@link UpgradeType} instance, which are then registered in a Minecraft registry.
|
||||||
* <p>
|
* <p>
|
||||||
* You then write a JSON file in your mod's {@literal data/} folder. This is then parsed when the world is loaded, and
|
* You then write a JSON file in your mod's {@literal data/} folder. This is then parsed when the world is loaded, and
|
||||||
* the upgrade automatically registered. It is recommended this is done via
|
* the upgrade automatically registered.
|
||||||
* <a href="../upgrades/UpgradeType.html#datagen">data generators</a>.
|
|
||||||
*
|
*
|
||||||
* <h2>Example</h2>
|
* <h2>Example</h2>
|
||||||
* {@snippet lang="java" :
|
* <h3>Registering the upgrade type</h3>
|
||||||
* // We use Forge's DeferredRegister to register our upgrade type. Fabric mods may register their type directly.
|
* First, let's create a new class that implements {@link ITurtleUpgrade}. It is recommended to subclass
|
||||||
* static final DeferredRegister<UpgradeType<? extends ITurtleUpgrade>> TURTLE_UPGRADES = DeferredRegister.create(ITurtleUpgrade.typeRegistry(), "my_mod");
|
* {@link AbstractTurtleUpgrade}, as that provides a default implementation of most methods.
|
||||||
*
|
*
|
||||||
* // Register a new upgrade type called "my_upgrade".
|
* {@snippet class=com.example.examplemod.ExampleTurtleUpgrade region=body}
|
||||||
* public static final RegistryObject<UpgradeType<MyUpgrade>> MY_UPGRADE =
|
|
||||||
* TURTLE_UPGRADES.register("my_upgrade", () -> UpgradeType.simple(MyUpgrade::new));
|
|
||||||
*
|
*
|
||||||
* // Then in your constructor
|
* Now we must construct a new upgrade type. In most cases, you can use one of the helper methods (e.g.
|
||||||
* TURTLE_UPGRADES.register(bus);
|
* {@link UpgradeType#simpleWithCustomItem(Function)}), rather than defining your own implementation.
|
||||||
* }
|
*
|
||||||
|
* {@snippet class=com.example.examplemod.ExampleMod region=turtle_upgrades}
|
||||||
|
*
|
||||||
|
* We now must register this upgrade type. This is done the same way as you'd register blocks, items, or other
|
||||||
|
* Minecraft objects. The approach to do this will depend on mod-loader.
|
||||||
|
*
|
||||||
|
* <h4>Fabric</h4>
|
||||||
|
* {@snippet class=com.example.examplemod.FabricExampleMod region=turtle_upgrades}
|
||||||
|
*
|
||||||
|
* <h4>Forge</h4>
|
||||||
|
* {@snippet class=com.example.examplemod.ForgeExampleMod region=turtle_upgrades}
|
||||||
|
*
|
||||||
|
* <h3>Rendering the upgrade</h3>
|
||||||
|
* Next, we need to register a model for our upgrade. This is done by registering a
|
||||||
|
* {@link dan200.computercraft.api.client.turtle.TurtleUpgradeModeller} for your upgrade type.
|
||||||
|
*
|
||||||
|
* <h4>Fabric</h4>
|
||||||
|
* {@snippet class=com.example.examplemod.FabricExampleModClient region=turtle_modellers}
|
||||||
|
*
|
||||||
|
* <h4>Forge</h4>
|
||||||
|
* {@snippet class=com.example.examplemod.FabricExampleModClient region=turtle_modellers}
|
||||||
|
*
|
||||||
|
* <h3 id="datagen">Registering the upgrade itself</h3>
|
||||||
|
* Upgrades themselves are loaded from datapacks when a level is loaded. In order to register our new upgrade, we must
|
||||||
|
* create a new JSON file at {@code data/<my_mod>/computercraft/turtle_upgrade/<my_upgrade_id>.json}.
|
||||||
|
*
|
||||||
|
* {@snippet file=data/examplemod/computercraft/turtle_upgrade/example_turtle_upgrade.json}
|
||||||
|
*
|
||||||
|
* The {@code "type"} field points to the ID of the upgrade type we've just registered, while the other fields are read
|
||||||
|
* by the type itself. As our upgrade was defined with {@link UpgradeType#simpleWithCustomItem(Function)}, the
|
||||||
|
* {@code "item"} field will construct our upgrade with {@link Items#COMPASS}.
|
||||||
* <p>
|
* <p>
|
||||||
* We can then define a new upgrade using JSON by placing the following in
|
* Rather than manually creating the file, it is recommended to use data-generators to generate this file. First, we
|
||||||
* {@code data/<my_mod>/computercraft/turtle_upgrade/<my_upgrade_id>.json}.
|
* register our new upgrades into a {@linkplain PatchedRegistries patched registry}.
|
||||||
* <p>
|
*
|
||||||
* {@snippet lang="json" :
|
* {@snippet class=com.example.examplemod.data.TurtleUpgradeProvider region=body}
|
||||||
* {
|
*
|
||||||
* "type": "my_mod:my_upgrade"
|
* Next, we must write these upgrades to disk. Vanilla does not have complete support for this yet, so this must be done
|
||||||
* }
|
* with mod-loader-specific APIs.
|
||||||
* }
|
*
|
||||||
* <p>
|
* <h4>Fabric</h4>
|
||||||
* Finally, we need to register a model for our upgrade, see
|
* {@snippet class=com.example.examplemod.FabricExampleModDataGenerator region=turtle_upgrades}
|
||||||
* {@link dan200.computercraft.api.client.turtle.TurtleUpgradeModeller} for more information.
|
*
|
||||||
|
* <h4>Forge</h4>
|
||||||
|
* {@snippet class=com.example.examplemod.ForgeExampleModDataGenerator region=turtle_upgrades}
|
||||||
*/
|
*/
|
||||||
public interface ITurtleUpgrade extends UpgradeBase {
|
public interface ITurtleUpgrade extends UpgradeBase {
|
||||||
/**
|
/**
|
||||||
|
@@ -5,8 +5,7 @@
|
|||||||
package dan200.computercraft.api.turtle;
|
package dan200.computercraft.api.turtle;
|
||||||
|
|
||||||
import net.minecraft.core.Direction;
|
import net.minecraft.core.Direction;
|
||||||
|
import org.jspecify.annotations.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.
|
||||||
@@ -60,9 +59,9 @@ public final class TurtleCommandResult {
|
|||||||
|
|
||||||
private final boolean success;
|
private final boolean success;
|
||||||
private final @Nullable String errorMessage;
|
private final @Nullable String errorMessage;
|
||||||
private final @Nullable Object[] results;
|
private final @Nullable Object @Nullable [] results;
|
||||||
|
|
||||||
private TurtleCommandResult(boolean success, @Nullable String errorMessage, @Nullable Object[] results) {
|
private TurtleCommandResult(boolean success, @Nullable String errorMessage, @Nullable Object @Nullable [] results) {
|
||||||
this.success = success;
|
this.success = success;
|
||||||
this.errorMessage = errorMessage;
|
this.errorMessage = errorMessage;
|
||||||
this.results = results;
|
this.results = results;
|
||||||
@@ -92,8 +91,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
|
public @Nullable Object @Nullable [] getResults() {
|
||||||
public Object[] getResults() {
|
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -18,26 +18,18 @@ import net.minecraft.world.entity.ai.attributes.Attributes;
|
|||||||
import net.minecraft.world.item.Item;
|
import net.minecraft.world.item.Item;
|
||||||
import net.minecraft.world.item.ItemStack;
|
import net.minecraft.world.item.ItemStack;
|
||||||
import net.minecraft.world.level.block.Block;
|
import net.minecraft.world.level.block.Block;
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A builder for custom turtle tool upgrades.
|
* A builder for custom turtle tool upgrades.
|
||||||
* <p>
|
* <p>
|
||||||
* This can be used from your <a href="../upgrades/UpgradeType.html#datagen">data generator</a> code in order to
|
* This can be used from your <a href="./ITurtleUpgrade.html#datagen">data generator</a> code in order to
|
||||||
* register turtle tools for your mod's tools.
|
* register turtle tools for your mod's tools.
|
||||||
*
|
*
|
||||||
* <h2>Example:</h2>
|
* <h2>Example</h2>
|
||||||
* {@snippet lang = "java":
|
* {@snippet class=com.example.examplemod.data.TurtleToolProvider region=body}
|
||||||
* import net.minecraft.data.worldgen.BootstrapContext;
|
|
||||||
* import net.minecraft.resources.ResourceLocation;
|
|
||||||
* import net.minecraft.world.item.Items;
|
|
||||||
*
|
|
||||||
* public void registerTool(BootstrapContext<ITurtleUpgrade> upgrades) {
|
|
||||||
* TurtleToolBuilder.tool(ResourceLocation.fromNamespaceAndPath("my_mod", "wooden_pickaxe"), Items.WOODEN_PICKAXE).register(upgrades);
|
|
||||||
* }
|
|
||||||
*}
|
|
||||||
*/
|
*/
|
||||||
public final class TurtleToolBuilder {
|
public final class TurtleToolBuilder {
|
||||||
private final ResourceKey<ITurtleUpgrade> id;
|
private final ResourceKey<ITurtleUpgrade> id;
|
||||||
|
@@ -7,9 +7,7 @@ package dan200.computercraft.api.upgrades;
|
|||||||
import com.mojang.serialization.MapCodec;
|
import com.mojang.serialization.MapCodec;
|
||||||
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.upgrades.UpgradeTypeImpl;
|
|
||||||
import net.minecraft.core.registries.BuiltInRegistries;
|
import net.minecraft.core.registries.BuiltInRegistries;
|
||||||
import net.minecraft.data.registries.RegistryPatchGenerator;
|
|
||||||
import net.minecraft.world.item.ItemStack;
|
import net.minecraft.world.item.ItemStack;
|
||||||
import net.minecraft.world.item.crafting.Recipe;
|
import net.minecraft.world.item.crafting.Recipe;
|
||||||
import net.minecraft.world.level.storage.loot.functions.LootItemFunction;
|
import net.minecraft.world.level.storage.loot.functions.LootItemFunction;
|
||||||
@@ -23,13 +21,10 @@ import java.util.function.Function;
|
|||||||
* follow a similar design to other dynamic content, such as {@linkplain Recipe recipes} or {@link LootItemFunction
|
* follow a similar design to other dynamic content, such as {@linkplain Recipe recipes} or {@link LootItemFunction
|
||||||
* loot functions}.
|
* loot functions}.
|
||||||
* <p>
|
* <p>
|
||||||
* First, one adds a new class implementing {@link ITurtleUpgrade} or {@link IPocketUpgrade}). This is responsible for
|
* While the {@link ITurtleUpgrade}/{@link IPocketUpgrade} class should contain the core logic of the upgrade, they are
|
||||||
* handling all the logic of your upgrade.
|
* not registered directly. Instead, each upgrade class has a corresponding {@link UpgradeType}, which is responsible
|
||||||
* <p>
|
* for loading the upgrade from a datapack. The upgrade type should then be registered in its appropriate registry
|
||||||
* However, the upgrades are not registered directly. Instead, each upgrade class should have a corresponding
|
* ({@link ITurtleUpgrade#typeRegistry()}, {@link IPocketUpgrade#typeRegistry()}).
|
||||||
* {@link UpgradeType}, which is responsible for loading the upgrade from a datapack. The upgrade type should then be
|
|
||||||
* registered in its appropriate registry ({@link ITurtleUpgrade#typeRegistry()},
|
|
||||||
* {@link IPocketUpgrade#typeRegistry()}).
|
|
||||||
* <p>
|
* <p>
|
||||||
* In order to register the actual upgrade, a JSON file referencing your upgrade type should be added to a datapack. It
|
* In order to register the actual upgrade, a JSON file referencing your upgrade type should be added to a datapack. It
|
||||||
* is recommended to do this via the data generators.
|
* is recommended to do this via the data generators.
|
||||||
@@ -38,29 +33,7 @@ import java.util.function.Function;
|
|||||||
* As turtle and pocket upgrades are just loaded using vanilla's dynamic loaders, one may use the same data generation
|
* As turtle and pocket upgrades are just loaded using vanilla's dynamic loaders, one may use the same data generation
|
||||||
* tools as you would for any other dynamic registry.
|
* tools as you would for any other dynamic registry.
|
||||||
* <p>
|
* <p>
|
||||||
* This can typically be done by extending vanilla's built-in registries using {@link RegistryPatchGenerator}, and then
|
* See <a href="../turtle/ITurtleUpgrade.html#datagen">the turtle upgrade docs</a> for a concrete example.
|
||||||
* writing out the new registries using {@code net.fabricmc.fabric.api.datagen.v1.provider.FabricDynamicRegistryProvider}
|
|
||||||
* on Fabric or {@code net.neoforged.neoforge.common.data.DatapackBuiltinEntriesProvider} on Forge.
|
|
||||||
* <p>
|
|
||||||
* {@snippet lang="java" :
|
|
||||||
* import dan200.computercraft.api.turtle.ITurtleUpgrade;
|
|
||||||
* import net.minecraft.Util;
|
|
||||||
* import net.minecraft.core.HolderLookup;
|
|
||||||
* import net.minecraft.core.RegistrySetBuilder;
|
|
||||||
* import net.minecraft.data.DataGenerator;
|
|
||||||
* import net.neoforged.neoforge.common.data.DatapackBuiltinEntriesProvider;
|
|
||||||
*
|
|
||||||
* import java.util.concurrent.CompletableFuture;
|
|
||||||
*
|
|
||||||
* public void generate(DataGenerator.PackGenerator output, CompletableFuture<HolderLookup.Provider> registries) {
|
|
||||||
* var newRegistries = RegistryPatchGenerator.createLookup(registries, Util.make(new RegistrySetBuilder(), builder -> {
|
|
||||||
* builder.add(ITurtleUpgrade.REGISTRY, upgrades -> {
|
|
||||||
* upgrades.register(ITurtleUpgrade.createKey(ResourceLocation.fromNamespaceAndPath("my_mod", "my_upgrade")), new MyUpgrade());
|
|
||||||
* });
|
|
||||||
* }));
|
|
||||||
* output.addProvider(o -> new DatapackBuiltinEntriesProvider(o, newRegistries, Set.of("my_mod")));
|
|
||||||
* }
|
|
||||||
* }
|
|
||||||
*
|
*
|
||||||
* @param <T> The upgrade subclass that this upgrade type represents.
|
* @param <T> The upgrade subclass that this upgrade type represents.
|
||||||
* @see ITurtleUpgrade
|
* @see ITurtleUpgrade
|
||||||
|
@@ -2,12 +2,9 @@
|
|||||||
//
|
//
|
||||||
// SPDX-License-Identifier: MPL-2.0
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
package dan200.computercraft.impl.upgrades;
|
package dan200.computercraft.api.upgrades;
|
||||||
|
|
||||||
import com.mojang.serialization.MapCodec;
|
import com.mojang.serialization.MapCodec;
|
||||||
import dan200.computercraft.api.upgrades.UpgradeBase;
|
|
||||||
import dan200.computercraft.api.upgrades.UpgradeType;
|
|
||||||
import org.jetbrains.annotations.ApiStatus;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Simple implementation of {@link UpgradeType}.
|
* Simple implementation of {@link UpgradeType}.
|
||||||
@@ -15,6 +12,5 @@ import org.jetbrains.annotations.ApiStatus;
|
|||||||
* @param codec The codec to read/write upgrades with.
|
* @param codec The codec to read/write upgrades with.
|
||||||
* @param <T> The upgrade subclass that this upgrade type represents.
|
* @param <T> The upgrade subclass that this upgrade type represents.
|
||||||
*/
|
*/
|
||||||
@ApiStatus.Internal
|
record UpgradeTypeImpl<T extends UpgradeBase>(MapCodec<T> codec) implements UpgradeType<T> {
|
||||||
public record UpgradeTypeImpl<T extends UpgradeBase>(MapCodec<T> codec) implements UpgradeType<T> {
|
|
||||||
}
|
}
|
@@ -13,6 +13,7 @@ import dan200.computercraft.api.filesystem.WritableMount;
|
|||||||
import dan200.computercraft.api.lua.GenericSource;
|
import dan200.computercraft.api.lua.GenericSource;
|
||||||
import dan200.computercraft.api.lua.ILuaAPIFactory;
|
import dan200.computercraft.api.lua.ILuaAPIFactory;
|
||||||
import dan200.computercraft.api.media.MediaProvider;
|
import dan200.computercraft.api.media.MediaProvider;
|
||||||
|
import dan200.computercraft.api.media.PrintoutContents;
|
||||||
import dan200.computercraft.api.network.PacketNetwork;
|
import dan200.computercraft.api.network.PacketNetwork;
|
||||||
import dan200.computercraft.api.network.wired.WiredElement;
|
import dan200.computercraft.api.network.wired.WiredElement;
|
||||||
import dan200.computercraft.api.network.wired.WiredNode;
|
import dan200.computercraft.api.network.wired.WiredNode;
|
||||||
@@ -30,8 +31,7 @@ import net.minecraft.server.MinecraftServer;
|
|||||||
import net.minecraft.world.item.ItemStack;
|
import net.minecraft.world.item.ItemStack;
|
||||||
import net.minecraft.world.level.Level;
|
import net.minecraft.world.level.Level;
|
||||||
import org.jetbrains.annotations.ApiStatus;
|
import org.jetbrains.annotations.ApiStatus;
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
import javax.annotation.Nullable;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Backing interface for {@link ComputerCraftAPI}
|
* Backing interface for {@link ComputerCraftAPI}
|
||||||
@@ -84,6 +84,9 @@ public interface ComputerCraftAPIService {
|
|||||||
|
|
||||||
DetailRegistry<BlockReference> getBlockInWorldDetailRegistry();
|
DetailRegistry<BlockReference> getBlockInWorldDetailRegistry();
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
PrintoutContents getPrintoutContents(ItemStack stack);
|
||||||
|
|
||||||
final class Instance {
|
final class Instance {
|
||||||
static final @Nullable ComputerCraftAPIService INSTANCE;
|
static final @Nullable ComputerCraftAPIService INSTANCE;
|
||||||
static final @Nullable Throwable ERROR;
|
static final @Nullable Throwable ERROR;
|
||||||
|
@@ -5,8 +5,8 @@
|
|||||||
package dan200.computercraft.impl;
|
package dan200.computercraft.impl;
|
||||||
|
|
||||||
import org.jetbrains.annotations.ApiStatus;
|
import org.jetbrains.annotations.ApiStatus;
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
import java.io.Serial;
|
import java.io.Serial;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -5,8 +5,8 @@
|
|||||||
package dan200.computercraft.impl;
|
package dan200.computercraft.impl;
|
||||||
|
|
||||||
import org.jetbrains.annotations.ApiStatus;
|
import org.jetbrains.annotations.ApiStatus;
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
import java.util.ServiceLoader;
|
import java.util.ServiceLoader;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
128
projects/common-api/src/overview.html
Normal file
128
projects/common-api/src/overview.html
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
<!--
|
||||||
|
SPDX-FileCopyrightText: 2025 The CC: Tweaked Developers
|
||||||
|
|
||||||
|
SPDX-License-Identifier: MPL-2.0
|
||||||
|
-->
|
||||||
|
|
||||||
|
<!DOCTYPE HTML>
|
||||||
|
<html lang="en">
|
||||||
|
<body>
|
||||||
|
<p>
|
||||||
|
This is the documentation for CC: Tweaked $modVersion for Minecraft $mcVersion. Documentation for other versions of
|
||||||
|
Minecraft are available on the CC: Tweaked website:
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li><a href="/mc-1.20.x/javadoc/">Minecraft 1.20.1</a>
|
||||||
|
<li><a href="/mc-1.21.x/javadoc/">Minecraft 1.21.1</a>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h1>Quick links</h1>
|
||||||
|
<p>
|
||||||
|
You probably want to start in the following places:
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li>{@linkplain dan200.computercraft.api.peripheral Registering new peripherals}</li>
|
||||||
|
<li>
|
||||||
|
{@link dan200.computercraft.api.lua.LuaFunction} and {@link dan200.computercraft.api.lua.IArguments} for
|
||||||
|
adding methods to your peripheral or Lua objects.
|
||||||
|
</li>
|
||||||
|
<li>{@linkplain dan200.computercraft.api.turtle.ITurtleUpgrade Turtle upgrades}</li>
|
||||||
|
<li>{@linkplain dan200.computercraft.api.pocket.IPocketUpgrade Pocket upgrades}</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h1>Using</h1>
|
||||||
|
<p>
|
||||||
|
CC: Tweaked is hosted on my maven repo, and so is relatively simple to depend on. You may wish to add a soft (or
|
||||||
|
hard) dependency in your <code>mods.toml</code> file, with the appropriate version bounds, to ensure that API
|
||||||
|
functionality you depend on is present.
|
||||||
|
|
||||||
|
<pre class="language language-groovy"><code>repositories {
|
||||||
|
maven {
|
||||||
|
url "https://maven.squiddev.cc"
|
||||||
|
content { includeGroup("cc.tweaked") }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
// Vanilla (i.e. for multi-loader systems)
|
||||||
|
compileOnly("cc.tweaked:cc-tweaked-$mcVersion-common-api:$modVersion")
|
||||||
|
|
||||||
|
// Forge Gradle
|
||||||
|
compileOnly("cc.tweaked:cc-tweaked-$mcVersion-core-api:$modVersion")
|
||||||
|
compileOnly(fg.deobf("cc.tweaked:cc-tweaked-$mcVersion-forge-api:$modVersion"))
|
||||||
|
runtimeOnly(fg.deobf("cc.tweaked:cc-tweaked-$mcVersion-forge:$modVersion"))
|
||||||
|
|
||||||
|
// Fabric Loom
|
||||||
|
modCompileOnly("cc.tweaked:cc-tweaked-$mcVersion-fabric-api:$modVersion")
|
||||||
|
modRuntimeOnly("cc.tweaked:cc-tweaked-$mcVersion-fabric:$modVersion")
|
||||||
|
}
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
You should also be careful to only use classes within the <code>dan200.computercraft.api</code> package. Non-API
|
||||||
|
classes are subject to change at any point. If you depend on functionality outside the API (or need to mixin to
|
||||||
|
CC:T), please <a href="https://github.com/cc-tweaked/CC-Tweaked/discussions/new/choose">start a discussion</a> to
|
||||||
|
let me know!
|
||||||
|
|
||||||
|
|
||||||
|
<h1>Updating from Minecraft 1.20.1 to 1.21.1</h1>
|
||||||
|
|
||||||
|
<h2>Peripherals</h2>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<p>
|
||||||
|
On NeoForge, the peripheral capability has migrated to NeoForge's new capability system.
|
||||||
|
<code>dan200.computercraft.api.peripheral.PeripheralCapability</code> can be used to register a peripheral.
|
||||||
|
<code>IPeripheralProvider</code> has also been removed, as capabilities can now be used for arbitrary
|
||||||
|
blocks.
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
{@linkplain dan200.computercraft.api.peripheral Read more on registering peripherals}.
|
||||||
|
|
||||||
|
<h2>Turtle and pocket upgrades</h2>
|
||||||
|
Turtle and pocket upgrades have been migrated to use Minecraft's dynamic registries. While upgrades themselves have not
|
||||||
|
changed much, the interface for registering them is dramatically different.
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<p>
|
||||||
|
<code>TurtleUpgradeSerialiser</code> and <code>PocketUpgradeSerialiser</code> have been unified into a
|
||||||
|
single {@link dan200.computercraft.api.upgrades.UpgradeType} class
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
Replace <code>TurtleUpgradeSerialiser.registryId()</code> with
|
||||||
|
{@link dan200.computercraft.api.turtle.ITurtleUpgrade#typeRegistry()} and <code>PocketUpgradeSerialiser.registryId()</code>
|
||||||
|
with {@link dan200.computercraft.api.pocket.IPocketUpgrade#typeRegistry()}.
|
||||||
|
<li>
|
||||||
|
Replace all other usages of <code>TurtleUpgradeSerialiser</code> and <code>PocketUpgradeSerialiser</code>
|
||||||
|
with {@link dan200.computercraft.api.upgrades.UpgradeType}.
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<li>
|
||||||
|
Upgrades are now (de)serialised using codecs, rather than manually reading from JSON and encoding/decoding
|
||||||
|
network packets. Instead of subclassing {@link dan200.computercraft.api.upgrades.UpgradeType}, it is recommended
|
||||||
|
you use {@link dan200.computercraft.api.upgrades.UpgradeType#create} to create a new type from a
|
||||||
|
<code>MapCodec</code>.
|
||||||
|
|
||||||
|
<li>
|
||||||
|
Upgrades are no longer aware of their ID, and so cannot compute their adjective. The adjective must now either
|
||||||
|
be hard-coded, or read as part of the codec.
|
||||||
|
|
||||||
|
<li>
|
||||||
|
The upgrade data providers have been removed, in favour of mod-loaders built-in support for dynamic registries.
|
||||||
|
I'm afraid it's probably easier if you delete your existing upgrade datagen code and start from scratch. See
|
||||||
|
<a href="./dan200/computercraft/api/turtle/ITurtleUpgrade.html#datagen">the <code>ITurtleUpgrade</code>
|
||||||
|
documentation for an example</a>.
|
||||||
|
|
||||||
|
<li>
|
||||||
|
Upgrades now store their additional data ({@link dan200.computercraft.api.turtle.ITurtleAccess#getUpgradeData},
|
||||||
|
{@link dan200.computercraft.api.pocket.IPocketAccess#getUpgradeData()}) as an immutable component map, rather
|
||||||
|
than a compound tag.
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
{@linkplain dan200.computercraft.api.turtle.ITurtleUpgrade Read more on registering turtle upgrades}.
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
@@ -6,17 +6,11 @@ import cc.tweaked.gradle.*
|
|||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
id("cc-tweaked.vanilla")
|
id("cc-tweaked.vanilla")
|
||||||
id("cc-tweaked.gametest")
|
|
||||||
id("cc-tweaked.illuaminate")
|
id("cc-tweaked.illuaminate")
|
||||||
|
id("cc-tweaked.mod")
|
||||||
id("cc-tweaked.publishing")
|
id("cc-tweaked.publishing")
|
||||||
}
|
}
|
||||||
|
|
||||||
sourceSets {
|
|
||||||
main {
|
|
||||||
resources.srcDir("src/generated/resources")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
minecraft {
|
minecraft {
|
||||||
accessWideners(
|
accessWideners(
|
||||||
"src/main/resources/computercraft.accesswidener",
|
"src/main/resources/computercraft.accesswidener",
|
||||||
@@ -45,7 +39,6 @@ dependencies {
|
|||||||
compileOnly(libs.mixin)
|
compileOnly(libs.mixin)
|
||||||
compileOnly(libs.mixinExtra)
|
compileOnly(libs.mixinExtra)
|
||||||
compileOnly(libs.bundles.externalMods.common)
|
compileOnly(libs.bundles.externalMods.common)
|
||||||
compileOnly(variantOf(libs.create.forge) { classifier("slim") }) { isTransitive = false }
|
|
||||||
clientCompileOnly(variantOf(libs.emi) { classifier("api") })
|
clientCompileOnly(variantOf(libs.emi) { classifier("api") })
|
||||||
|
|
||||||
annotationProcessorEverywhere(libs.autoService)
|
annotationProcessorEverywhere(libs.autoService)
|
||||||
@@ -69,7 +62,7 @@ dependencies {
|
|||||||
}
|
}
|
||||||
|
|
||||||
illuaminate {
|
illuaminate {
|
||||||
version.set(libs.versions.illuaminate)
|
version = libs.versions.illuaminate
|
||||||
}
|
}
|
||||||
|
|
||||||
val luaJavadoc by tasks.registering(Javadoc::class) {
|
val luaJavadoc by tasks.registering(Javadoc::class) {
|
||||||
@@ -90,11 +83,7 @@ val luaJavadoc by tasks.registering(Javadoc::class) {
|
|||||||
options.addStringOption("project-root", rootProject.file(".").absolutePath)
|
options.addStringOption("project-root", rootProject.file(".").absolutePath)
|
||||||
options.noTimestamp(false)
|
options.noTimestamp(false)
|
||||||
|
|
||||||
javadocTool.set(
|
javadocTool = javaToolchains.javadocToolFor { languageVersion = CCTweakedPlugin.JAVA_VERSION }
|
||||||
javaToolchains.javadocToolFor {
|
|
||||||
languageVersion.set(CCTweakedPlugin.JAVA_VERSION)
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val lintLua by tasks.registering(IlluaminateExec::class) {
|
val lintLua by tasks.registering(IlluaminateExec::class) {
|
||||||
@@ -115,20 +104,31 @@ val lintLua by tasks.registering(IlluaminateExec::class) {
|
|||||||
doLast { if (System.getenv("GITHUB_ACTIONS") != null) println("::remove-matcher owner=illuaminate::") }
|
doLast { if (System.getenv("GITHUB_ACTIONS") != null) println("::remove-matcher owner=illuaminate::") }
|
||||||
}
|
}
|
||||||
|
|
||||||
val runData by tasks.registering(MergeTrees::class) {
|
fun MergeTrees.configureForDatagen(source: SourceSet, outputFolder: String) {
|
||||||
output = layout.projectDirectory.dir("src/generated/resources")
|
output = layout.projectDirectory.dir(outputFolder)
|
||||||
|
|
||||||
for (loader in listOf("forge", "fabric")) {
|
for (loader in listOf("forge", "fabric")) {
|
||||||
mustRunAfter(":$loader:runData")
|
mustRunAfter(":$loader:$name")
|
||||||
source {
|
source {
|
||||||
input {
|
input {
|
||||||
from(project(":$loader").layout.buildDirectory.dir("generatedResources"))
|
from(project(":$loader").layout.buildDirectory.dir(source.getTaskName("generateResources", null)))
|
||||||
exclude(".cache")
|
exclude(".cache")
|
||||||
}
|
}
|
||||||
|
|
||||||
output = project(":$loader").layout.projectDirectory.dir("src/generated/resources")
|
output = project(":$loader").layout.projectDirectory.dir(outputFolder)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.withType(GenerateModuleMetadata::class).configureEach { isEnabled = false }
|
val runData by tasks.registering(MergeTrees::class) {
|
||||||
|
configureForDatagen(sourceSets.main.get(), "src/generated/resources")
|
||||||
|
}
|
||||||
|
|
||||||
|
val runExampleData by tasks.registering(MergeTrees::class) {
|
||||||
|
configureForDatagen(sourceSets.examples.get(), "src/examples/generatedResources")
|
||||||
|
}
|
||||||
|
|
||||||
|
// We can't create accurate module metadata for our additional capabilities, so disable it.
|
||||||
|
project.tasks.withType(GenerateModuleMetadata::class.java).configureEach {
|
||||||
|
isEnabled = false
|
||||||
|
}
|
||||||
|
@@ -38,8 +38,8 @@ import net.minecraft.world.item.ItemStack;
|
|||||||
import net.minecraft.world.level.block.state.BlockState;
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
import net.minecraft.world.phys.BlockHitResult;
|
import net.minecraft.world.phys.BlockHitResult;
|
||||||
import net.minecraft.world.phys.HitResult;
|
import net.minecraft.world.phys.HitResult;
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -25,7 +25,6 @@ import dan200.computercraft.shared.command.CommandComputerCraft;
|
|||||||
import dan200.computercraft.shared.computer.core.ComputerState;
|
import dan200.computercraft.shared.computer.core.ComputerState;
|
||||||
import dan200.computercraft.shared.computer.core.ServerContext;
|
import dan200.computercraft.shared.computer.core.ServerContext;
|
||||||
import dan200.computercraft.shared.computer.inventory.AbstractComputerMenu;
|
import dan200.computercraft.shared.computer.inventory.AbstractComputerMenu;
|
||||||
import dan200.computercraft.shared.media.items.DiskItem;
|
|
||||||
import dan200.computercraft.shared.turtle.TurtleOverlay;
|
import dan200.computercraft.shared.turtle.TurtleOverlay;
|
||||||
import net.minecraft.Util;
|
import net.minecraft.Util;
|
||||||
import net.minecraft.client.Minecraft;
|
import net.minecraft.client.Minecraft;
|
||||||
@@ -41,7 +40,6 @@ import net.minecraft.client.renderer.item.ClampedItemPropertyFunction;
|
|||||||
import net.minecraft.client.renderer.item.ItemProperties;
|
import net.minecraft.client.renderer.item.ItemProperties;
|
||||||
import net.minecraft.network.chat.Component;
|
import net.minecraft.network.chat.Component;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
import net.minecraft.server.packs.resources.PreparableReloadListener;
|
|
||||||
import net.minecraft.server.packs.resources.ResourceProvider;
|
import net.minecraft.server.packs.resources.ResourceProvider;
|
||||||
import net.minecraft.util.FastColor;
|
import net.minecraft.util.FastColor;
|
||||||
import net.minecraft.world.entity.LivingEntity;
|
import net.minecraft.world.entity.LivingEntity;
|
||||||
@@ -51,8 +49,10 @@ import net.minecraft.world.item.Item;
|
|||||||
import net.minecraft.world.item.ItemStack;
|
import net.minecraft.world.item.ItemStack;
|
||||||
import net.minecraft.world.item.component.DyedItemColor;
|
import net.minecraft.world.item.component.DyedItemColor;
|
||||||
import net.minecraft.world.level.ItemLike;
|
import net.minecraft.world.level.ItemLike;
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
@@ -69,6 +69,8 @@ import java.util.function.Supplier;
|
|||||||
* @see ModRegistry The common registry for actual game objects.
|
* @see ModRegistry The common registry for actual game objects.
|
||||||
*/
|
*/
|
||||||
public final class ClientRegistry {
|
public final class ClientRegistry {
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(ClientRegistry.class);
|
||||||
|
|
||||||
private ClientRegistry() {
|
private ClientRegistry() {
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -143,10 +145,6 @@ public final class ClientRegistry {
|
|||||||
void register(Item item, ResourceLocation name, ClampedItemPropertyFunction property);
|
void register(Item item, ResourceLocation name, ClampedItemPropertyFunction property);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void registerReloadListeners(Consumer<PreparableReloadListener> register, Minecraft minecraft) {
|
|
||||||
register.accept(GuiSprites.initialise(minecraft.getTextureManager()));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final ResourceLocation[] EXTRA_MODELS = {
|
private static final ResourceLocation[] EXTRA_MODELS = {
|
||||||
TurtleOverlay.ELF_MODEL,
|
TurtleOverlay.ELF_MODEL,
|
||||||
TurtleBlockEntityRenderer.COLOUR_TURTLE_MODEL,
|
TurtleBlockEntityRenderer.COLOUR_TURTLE_MODEL,
|
||||||
@@ -160,7 +158,7 @@ public final class ClientRegistry {
|
|||||||
|
|
||||||
public static void registerItemColours(BiConsumer<ItemColor, ItemLike> register) {
|
public static void registerItemColours(BiConsumer<ItemColor, ItemLike> register) {
|
||||||
register.accept(
|
register.accept(
|
||||||
(stack, layer) -> layer == 1 ? DiskItem.getColour(stack) : -1,
|
(stack, layer) -> layer == 1 ? DyedItemColor.getOrDefault(stack, Colour.WHITE.getARGB()) : -1,
|
||||||
ModRegistry.Items.DISK.get()
|
ModRegistry.Items.DISK.get()
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -192,7 +190,18 @@ public final class ClientRegistry {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static void registerShaders(ResourceProvider resources, BiConsumer<ShaderInstance, Consumer<ShaderInstance>> load) throws IOException {
|
public static void registerShaders(ResourceProvider resources, BiConsumer<ShaderInstance, Consumer<ShaderInstance>> load) throws IOException {
|
||||||
RenderTypes.registerShaders(resources, load);
|
RenderTypes.registerShaders(resources, (name, create, onLoaded) -> {
|
||||||
|
ShaderInstance shader;
|
||||||
|
try {
|
||||||
|
shader = create.get();
|
||||||
|
} catch (Exception e) {
|
||||||
|
LOG.error("Failed to load {}", name, e);
|
||||||
|
onLoaded.accept(null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
load.accept(shader, onLoaded);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private record UnclampedPropertyFunction(
|
private record UnclampedPropertyFunction(
|
||||||
|
@@ -15,8 +15,8 @@ import net.minecraft.client.gui.components.ChatComponent;
|
|||||||
import net.minecraft.network.chat.Component;
|
import net.minecraft.network.chat.Component;
|
||||||
import net.minecraft.util.Mth;
|
import net.minecraft.util.Mth;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -26,11 +26,11 @@ import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen;
|
|||||||
import net.minecraft.network.chat.Component;
|
import net.minecraft.network.chat.Component;
|
||||||
import net.minecraft.world.entity.player.Inventory;
|
import net.minecraft.world.entity.player.Inventory;
|
||||||
import net.minecraft.world.item.ItemStack;
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
import org.lwjgl.glfw.GLFW;
|
import org.lwjgl.glfw.GLFW;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
|
@@ -10,10 +10,10 @@ import dan200.computercraft.shared.computer.menu.ComputerMenu;
|
|||||||
import dan200.computercraft.shared.network.server.ComputerActionServerMessage;
|
import dan200.computercraft.shared.network.server.ComputerActionServerMessage;
|
||||||
import dan200.computercraft.shared.network.server.KeyEventServerMessage;
|
import dan200.computercraft.shared.network.server.KeyEventServerMessage;
|
||||||
import dan200.computercraft.shared.network.server.MouseEventServerMessage;
|
import dan200.computercraft.shared.network.server.MouseEventServerMessage;
|
||||||
import dan200.computercraft.shared.network.server.QueueEventServerMessage;
|
import dan200.computercraft.shared.network.server.PasteEventComputerMessage;
|
||||||
import net.minecraft.world.inventory.AbstractContainerMenu;
|
import net.minecraft.world.inventory.AbstractContainerMenu;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An {@link InputHandler} for use on the client.
|
* An {@link InputHandler} for use on the client.
|
||||||
@@ -27,6 +27,11 @@ public final class ClientInputHandler implements InputHandler {
|
|||||||
this.menu = menu;
|
this.menu = menu;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void terminate() {
|
||||||
|
ClientNetworking.sendToServer(new ComputerActionServerMessage(menu, ComputerActionServerMessage.Action.TERMINATE));
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void turnOn() {
|
public void turnOn() {
|
||||||
ClientNetworking.sendToServer(new ComputerActionServerMessage(menu, ComputerActionServerMessage.Action.TURN_ON));
|
ClientNetworking.sendToServer(new ComputerActionServerMessage(menu, ComputerActionServerMessage.Action.TURN_ON));
|
||||||
@@ -42,11 +47,6 @@ public final class ClientInputHandler implements InputHandler {
|
|||||||
ClientNetworking.sendToServer(new ComputerActionServerMessage(menu, ComputerActionServerMessage.Action.REBOOT));
|
ClientNetworking.sendToServer(new ComputerActionServerMessage(menu, ComputerActionServerMessage.Action.REBOOT));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void queueEvent(String event, @Nullable Object[] arguments) {
|
|
||||||
ClientNetworking.sendToServer(new QueueEventServerMessage(menu, event, arguments));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void keyDown(int key, boolean repeat) {
|
public void keyDown(int key, boolean repeat) {
|
||||||
ClientNetworking.sendToServer(new KeyEventServerMessage(menu, repeat ? KeyEventServerMessage.Action.REPEAT : KeyEventServerMessage.Action.DOWN, key));
|
ClientNetworking.sendToServer(new KeyEventServerMessage(menu, repeat ? KeyEventServerMessage.Action.REPEAT : KeyEventServerMessage.Action.DOWN, key));
|
||||||
@@ -57,6 +57,16 @@ public final class ClientInputHandler implements InputHandler {
|
|||||||
ClientNetworking.sendToServer(new KeyEventServerMessage(menu, KeyEventServerMessage.Action.UP, key));
|
ClientNetworking.sendToServer(new KeyEventServerMessage(menu, KeyEventServerMessage.Action.UP, key));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void charTyped(byte chr) {
|
||||||
|
ClientNetworking.sendToServer(new KeyEventServerMessage(menu, KeyEventServerMessage.Action.CHAR, chr));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void paste(ByteBuffer contents) {
|
||||||
|
ClientNetworking.sendToServer(new PasteEventComputerMessage(menu, contents));
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void mouseClick(int button, int x, int y) {
|
public void mouseClick(int button, int x, int y) {
|
||||||
ClientNetworking.sendToServer(new MouseEventServerMessage(menu, MouseEventServerMessage.Action.CLICK, button, x, y));
|
ClientNetworking.sendToServer(new MouseEventServerMessage(menu, MouseEventServerMessage.Action.CLICK, button, x, y));
|
||||||
|
@@ -6,9 +6,7 @@ package dan200.computercraft.client.gui;
|
|||||||
|
|
||||||
import dan200.computercraft.client.gui.widgets.ComputerSidebar;
|
import dan200.computercraft.client.gui.widgets.ComputerSidebar;
|
||||||
import dan200.computercraft.client.gui.widgets.TerminalWidget;
|
import dan200.computercraft.client.gui.widgets.TerminalWidget;
|
||||||
import dan200.computercraft.client.render.ComputerBorderRenderer;
|
import dan200.computercraft.core.util.Nullability;
|
||||||
import dan200.computercraft.client.render.RenderTypes;
|
|
||||||
import dan200.computercraft.client.render.SpriteRenderer;
|
|
||||||
import dan200.computercraft.shared.computer.inventory.AbstractComputerMenu;
|
import dan200.computercraft.shared.computer.inventory.AbstractComputerMenu;
|
||||||
import net.minecraft.client.gui.GuiGraphics;
|
import net.minecraft.client.gui.GuiGraphics;
|
||||||
import net.minecraft.network.chat.Component;
|
import net.minecraft.network.chat.Component;
|
||||||
@@ -40,14 +38,15 @@ public final class ComputerScreen<T extends AbstractComputerMenu> extends Abstra
|
|||||||
public void renderBg(GuiGraphics graphics, float partialTicks, int mouseX, int mouseY) {
|
public void renderBg(GuiGraphics graphics, float partialTicks, int mouseX, int mouseY) {
|
||||||
// Draw a border around the terminal
|
// Draw a border around the terminal
|
||||||
var terminal = getTerminal();
|
var terminal = getTerminal();
|
||||||
var spriteRenderer = SpriteRenderer.createForGui(graphics, RenderTypes.GUI_SPRITES);
|
|
||||||
var computerTextures = GuiSprites.getComputerTextures(family);
|
var computerTextures = GuiSprites.getComputerTextures(family);
|
||||||
|
|
||||||
ComputerBorderRenderer.render(
|
graphics.blitSprite(
|
||||||
spriteRenderer, computerTextures,
|
computerTextures.border(),
|
||||||
terminal.getX(), terminal.getY(), terminal.getWidth(), terminal.getHeight(), false
|
terminal.getX() - BORDER, terminal.getY() - BORDER, terminal.getWidth() + BORDER * 2, terminal.getHeight() + BORDER * 2
|
||||||
|
);
|
||||||
|
graphics.blitSprite(
|
||||||
|
Nullability.assertNonNull(computerTextures.sidebar()),
|
||||||
|
leftPos, topPos + sidebarYOffset, AbstractComputerMenu.SIDEBAR_WIDTH, ComputerSidebar.HEIGHT
|
||||||
);
|
);
|
||||||
ComputerSidebar.renderBackground(spriteRenderer, computerTextures, leftPos, topPos + sidebarYOffset);
|
|
||||||
graphics.flush(); // Flush to ensure background textures are drawn before foreground.
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -6,24 +6,17 @@ package dan200.computercraft.client.gui;
|
|||||||
|
|
||||||
import dan200.computercraft.api.ComputerCraftAPI;
|
import dan200.computercraft.api.ComputerCraftAPI;
|
||||||
import dan200.computercraft.client.render.ComputerBorderRenderer;
|
import dan200.computercraft.client.render.ComputerBorderRenderer;
|
||||||
import dan200.computercraft.data.client.ClientDataProviders;
|
|
||||||
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
||||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
|
|
||||||
import net.minecraft.client.renderer.texture.TextureManager;
|
|
||||||
import net.minecraft.client.resources.TextureAtlasHolder;
|
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sprite sheet for all GUI texutres in the mod.
|
* Sprite sheet for all GUI texutres in the mod.
|
||||||
*/
|
*/
|
||||||
public final class GuiSprites extends TextureAtlasHolder {
|
public final class GuiSprites {
|
||||||
public static final ResourceLocation SPRITE_SHEET = ResourceLocation.fromNamespaceAndPath(ComputerCraftAPI.MOD_ID, "gui");
|
|
||||||
public static final ResourceLocation TEXTURE = SPRITE_SHEET.withPath(x -> "textures/atlas/" + x + ".png");
|
|
||||||
|
|
||||||
public static final ButtonTextures TURNED_OFF = button("turned_off");
|
public static final ButtonTextures TURNED_OFF = button("turned_off");
|
||||||
public static final ButtonTextures TURNED_ON = button("turned_on");
|
public static final ButtonTextures TURNED_ON = button("turned_on");
|
||||||
public static final ButtonTextures TERMINATE = button("terminate");
|
public static final ButtonTextures TERMINATE = button("terminate");
|
||||||
@@ -33,10 +26,13 @@ public final class GuiSprites extends TextureAtlasHolder {
|
|||||||
public static final ComputerTextures COMPUTER_COMMAND = computer("command", false, true);
|
public static final ComputerTextures COMPUTER_COMMAND = computer("command", false, true);
|
||||||
public static final ComputerTextures COMPUTER_COLOUR = computer("colour", true, false);
|
public static final ComputerTextures COMPUTER_COLOUR = computer("colour", true, false);
|
||||||
|
|
||||||
|
private GuiSprites() {
|
||||||
|
}
|
||||||
|
|
||||||
private static ButtonTextures button(String name) {
|
private static ButtonTextures button(String name) {
|
||||||
return new ButtonTextures(
|
return new ButtonTextures(
|
||||||
ResourceLocation.fromNamespaceAndPath(ComputerCraftAPI.MOD_ID, "gui/buttons/" + name),
|
ResourceLocation.fromNamespaceAndPath(ComputerCraftAPI.MOD_ID, "buttons/" + name),
|
||||||
ResourceLocation.fromNamespaceAndPath(ComputerCraftAPI.MOD_ID, "gui/buttons/" + name + "_hover")
|
ResourceLocation.fromNamespaceAndPath(ComputerCraftAPI.MOD_ID, "buttons/" + name + "_hover")
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -48,34 +44,6 @@ public final class GuiSprites extends TextureAtlasHolder {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static @Nullable GuiSprites instance;
|
|
||||||
|
|
||||||
private GuiSprites(TextureManager textureManager) {
|
|
||||||
super(textureManager, TEXTURE, SPRITE_SHEET);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initialise the singleton {@link GuiSprites} instance.
|
|
||||||
*
|
|
||||||
* @param textureManager The current texture manager.
|
|
||||||
* @return The singleton {@link GuiSprites} instance, to register as resource reload listener.
|
|
||||||
*/
|
|
||||||
public static GuiSprites initialise(TextureManager textureManager) {
|
|
||||||
if (instance != null) throw new IllegalStateException("GuiSprites has already been initialised");
|
|
||||||
return instance = new GuiSprites(textureManager);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Lookup a texture on the atlas.
|
|
||||||
*
|
|
||||||
* @param texture The texture to find.
|
|
||||||
* @return The sprite on the atlas.
|
|
||||||
*/
|
|
||||||
public static TextureAtlasSprite get(ResourceLocation texture) {
|
|
||||||
if (instance == null) throw new IllegalStateException("GuiSprites has not been initialised");
|
|
||||||
return instance.getSprite(texture);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the appropriate textures to use for a particular computer family.
|
* Get the appropriate textures to use for a particular computer family.
|
||||||
*
|
*
|
||||||
@@ -97,12 +65,8 @@ public final class GuiSprites extends TextureAtlasHolder {
|
|||||||
* @param active The texture for the button when it is active (hovered or focused).
|
* @param active The texture for the button when it is active (hovered or focused).
|
||||||
*/
|
*/
|
||||||
public record ButtonTextures(ResourceLocation normal, ResourceLocation active) {
|
public record ButtonTextures(ResourceLocation normal, ResourceLocation active) {
|
||||||
public TextureAtlasSprite get(boolean active) {
|
public ResourceLocation get(boolean isActive) {
|
||||||
return GuiSprites.get(active ? this.active : normal);
|
return isActive ? active : normal;
|
||||||
}
|
|
||||||
|
|
||||||
public Stream<ResourceLocation> textures() {
|
|
||||||
return Stream.of(normal, active);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -113,7 +77,6 @@ public final class GuiSprites extends TextureAtlasHolder {
|
|||||||
* @param pocketBottom The texture for the bottom of a pocket computer.
|
* @param pocketBottom The texture for the bottom of a pocket computer.
|
||||||
* @param sidebar The texture for the computer sidebar.
|
* @param sidebar The texture for the computer sidebar.
|
||||||
* @see ComputerBorderRenderer
|
* @see ComputerBorderRenderer
|
||||||
* @see ClientDataProviders
|
|
||||||
*/
|
*/
|
||||||
public record ComputerTextures(
|
public record ComputerTextures(
|
||||||
ResourceLocation border,
|
ResourceLocation border,
|
||||||
|
@@ -0,0 +1,40 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2025 The CC: Tweaked Developers
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
|
package dan200.computercraft.client.gui;
|
||||||
|
|
||||||
|
import org.lwjgl.glfw.GLFW;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Supports for converting/translating key codes.
|
||||||
|
*/
|
||||||
|
public class KeyConverter {
|
||||||
|
/**
|
||||||
|
* GLFW's key events refer to the physical key code, rather than the "actual" key code (with keyboard layout
|
||||||
|
* applied).
|
||||||
|
* <p>
|
||||||
|
* This makes sense for WASD-style input, but is a right pain for keyboard shortcuts — this function attempts to
|
||||||
|
* translate those keys back to their "actual" key code. See also
|
||||||
|
* <a href="https://github.com/glfw/glfw/issues/1502"> this discussion on GLFW's GitHub.</a>
|
||||||
|
*
|
||||||
|
* @param key The current key code.
|
||||||
|
* @param scanCode The current scan code.
|
||||||
|
* @return The translated key code.
|
||||||
|
*/
|
||||||
|
public static int physicalToActual(int key, int scanCode) {
|
||||||
|
var name = GLFW.glfwGetKeyName(key, scanCode);
|
||||||
|
if (name == null || name.length() != 1) return key;
|
||||||
|
|
||||||
|
// If we've got a single character as the key name, treat that as the ASCII value of the key,
|
||||||
|
// and map that back to a key code.
|
||||||
|
var character = name.charAt(0);
|
||||||
|
|
||||||
|
// 0-9 and A-Z map directly to their GLFW key (they're the same ASCII code).
|
||||||
|
if ((character >= '0' && character <= '9') || (character >= 'A' && character <= 'Z')) return character;
|
||||||
|
// a-z map to GLFW_KEY_{A,Z}
|
||||||
|
if (character >= 'a' && character <= 'z') return GLFW.GLFW_KEY_A + (character - 'a');
|
||||||
|
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
}
|
@@ -15,9 +15,9 @@ import net.minecraft.client.gui.screens.Screen;
|
|||||||
import net.minecraft.client.gui.screens.inventory.MenuAccess;
|
import net.minecraft.client.gui.screens.inventory.MenuAccess;
|
||||||
import net.minecraft.network.chat.Component;
|
import net.minecraft.network.chat.Component;
|
||||||
import net.minecraft.world.entity.player.Inventory;
|
import net.minecraft.world.entity.player.Inventory;
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
import org.lwjgl.glfw.GLFW;
|
import org.lwjgl.glfw.GLFW;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
import static dan200.computercraft.core.util.Nullability.assertNonNull;
|
import static dan200.computercraft.core.util.Nullability.assertNonNull;
|
||||||
|
@@ -12,8 +12,8 @@ import net.minecraft.client.gui.components.MultiLineLabel;
|
|||||||
import net.minecraft.client.gui.screens.Screen;
|
import net.minecraft.client.gui.screens.Screen;
|
||||||
import net.minecraft.network.chat.Component;
|
import net.minecraft.network.chat.Component;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import static dan200.computercraft.core.util.Nullability.assertNonNull;
|
import static dan200.computercraft.core.util.Nullability.assertNonNull;
|
||||||
|
@@ -127,6 +127,7 @@ public final class PrintoutScreen extends AbstractContainerScreen<PrintoutMenu>
|
|||||||
// Skip rendering labels.
|
// Skip rendering labels.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("ArrayRecordComponent")
|
||||||
record PrintoutInfo(int pages, boolean book, TextBuffer[] text, TextBuffer[] colour) {
|
record PrintoutInfo(int pages, boolean book, TextBuffer[] text, TextBuffer[] colour) {
|
||||||
public static final PrintoutInfo DEFAULT = of(PrintoutData.EMPTY, false);
|
public static final PrintoutInfo DEFAULT = of(PrintoutData.EMPTY, false);
|
||||||
|
|
||||||
|
@@ -7,8 +7,7 @@ package dan200.computercraft.client.gui;
|
|||||||
import dan200.computercraft.api.ComputerCraftAPI;
|
import dan200.computercraft.api.ComputerCraftAPI;
|
||||||
import dan200.computercraft.client.gui.widgets.ComputerSidebar;
|
import dan200.computercraft.client.gui.widgets.ComputerSidebar;
|
||||||
import dan200.computercraft.client.gui.widgets.TerminalWidget;
|
import dan200.computercraft.client.gui.widgets.TerminalWidget;
|
||||||
import dan200.computercraft.client.render.RenderTypes;
|
import dan200.computercraft.core.util.Nullability;
|
||||||
import dan200.computercraft.client.render.SpriteRenderer;
|
|
||||||
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
||||||
import dan200.computercraft.shared.computer.inventory.AbstractComputerMenu;
|
import dan200.computercraft.shared.computer.inventory.AbstractComputerMenu;
|
||||||
import dan200.computercraft.shared.turtle.inventory.TurtleMenu;
|
import dan200.computercraft.shared.turtle.inventory.TurtleMenu;
|
||||||
@@ -26,6 +25,9 @@ public class TurtleScreen extends AbstractComputerScreen<TurtleMenu> {
|
|||||||
private static final ResourceLocation BACKGROUND_NORMAL = ResourceLocation.fromNamespaceAndPath(ComputerCraftAPI.MOD_ID, "textures/gui/turtle_normal.png");
|
private static final ResourceLocation BACKGROUND_NORMAL = ResourceLocation.fromNamespaceAndPath(ComputerCraftAPI.MOD_ID, "textures/gui/turtle_normal.png");
|
||||||
private static final ResourceLocation BACKGROUND_ADVANCED = ResourceLocation.fromNamespaceAndPath(ComputerCraftAPI.MOD_ID, "textures/gui/turtle_advanced.png");
|
private static final ResourceLocation BACKGROUND_ADVANCED = ResourceLocation.fromNamespaceAndPath(ComputerCraftAPI.MOD_ID, "textures/gui/turtle_advanced.png");
|
||||||
|
|
||||||
|
private static final ResourceLocation SELECTED_NORMAL = ResourceLocation.fromNamespaceAndPath(ComputerCraftAPI.MOD_ID, "turtle_normal_selected_slot");
|
||||||
|
private static final ResourceLocation SELECTED_ADVANCED = ResourceLocation.fromNamespaceAndPath(ComputerCraftAPI.MOD_ID, "turtle_advanced_selected_slot");
|
||||||
|
|
||||||
private static final int TEX_WIDTH = 278;
|
private static final int TEX_WIDTH = 278;
|
||||||
private static final int TEX_HEIGHT = 217;
|
private static final int TEX_HEIGHT = 217;
|
||||||
|
|
||||||
@@ -54,15 +56,16 @@ public class TurtleScreen extends AbstractComputerScreen<TurtleMenu> {
|
|||||||
if (slot >= 0) {
|
if (slot >= 0) {
|
||||||
var slotX = slot % 4;
|
var slotX = slot % 4;
|
||||||
var slotY = slot / 4;
|
var slotY = slot / 4;
|
||||||
graphics.blit(texture,
|
graphics.blitSprite(
|
||||||
leftPos + TURTLE_START_X - 2 + slotX * 18, topPos + PLAYER_START_Y - 2 + slotY * 18, 0,
|
advanced ? SELECTED_ADVANCED : SELECTED_NORMAL,
|
||||||
0, 217, 24, 24, FULL_TEX_SIZE, FULL_TEX_SIZE
|
leftPos + TURTLE_START_X - 2 + slotX * 18, topPos + PLAYER_START_Y - 2 + slotY * 18, 0, 22, 22
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Render sidebar
|
// Render sidebar
|
||||||
var spriteRenderer = SpriteRenderer.createForGui(graphics, RenderTypes.GUI_SPRITES);
|
graphics.blitSprite(
|
||||||
ComputerSidebar.renderBackground(spriteRenderer, GuiSprites.getComputerTextures(family), leftPos, topPos + sidebarYOffset);
|
Nullability.assertNonNull(GuiSprites.getComputerTextures(family).sidebar()),
|
||||||
graphics.flush(); // Flush to ensure background textures are drawn before foreground.
|
leftPos, topPos + sidebarYOffset, AbstractComputerMenu.SIDEBAR_WIDTH, ComputerSidebar.HEIGHT
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -6,9 +6,7 @@ package dan200.computercraft.client.gui.widgets;
|
|||||||
|
|
||||||
import dan200.computercraft.client.gui.GuiSprites;
|
import dan200.computercraft.client.gui.GuiSprites;
|
||||||
import dan200.computercraft.client.gui.widgets.DynamicImageButton.HintedMessage;
|
import dan200.computercraft.client.gui.widgets.DynamicImageButton.HintedMessage;
|
||||||
import dan200.computercraft.client.render.SpriteRenderer;
|
|
||||||
import dan200.computercraft.shared.computer.core.InputHandler;
|
import dan200.computercraft.shared.computer.core.InputHandler;
|
||||||
import dan200.computercraft.shared.computer.inventory.AbstractComputerMenu;
|
|
||||||
import net.minecraft.client.gui.components.AbstractWidget;
|
import net.minecraft.client.gui.components.AbstractWidget;
|
||||||
import net.minecraft.network.chat.Component;
|
import net.minecraft.network.chat.Component;
|
||||||
|
|
||||||
@@ -24,12 +22,9 @@ public final class ComputerSidebar {
|
|||||||
private static final int ICON_MARGIN = 2;
|
private static final int ICON_MARGIN = 2;
|
||||||
|
|
||||||
private static final int CORNERS_BORDER = 3;
|
private static final int CORNERS_BORDER = 3;
|
||||||
private static final int FULL_BORDER = CORNERS_BORDER + ICON_MARGIN;
|
|
||||||
|
|
||||||
private static final int BUTTONS = 2;
|
private static final int BUTTONS = 2;
|
||||||
private static final int HEIGHT = (ICON_HEIGHT + ICON_MARGIN * 2) * BUTTONS + CORNERS_BORDER * 2;
|
public static final int HEIGHT = (ICON_HEIGHT + ICON_MARGIN * 2) * BUTTONS + CORNERS_BORDER * 2;
|
||||||
|
|
||||||
private static final int TEX_HEIGHT = 14;
|
|
||||||
|
|
||||||
private ComputerSidebar() {
|
private ComputerSidebar() {
|
||||||
}
|
}
|
||||||
@@ -55,7 +50,7 @@ public final class ComputerSidebar {
|
|||||||
add.accept(new DynamicImageButton(
|
add.accept(new DynamicImageButton(
|
||||||
x, y, ICON_WIDTH, ICON_HEIGHT,
|
x, y, ICON_WIDTH, ICON_HEIGHT,
|
||||||
GuiSprites.TERMINATE::get,
|
GuiSprites.TERMINATE::get,
|
||||||
b -> input.queueEvent("terminate"),
|
b -> input.terminate(),
|
||||||
new HintedMessage(
|
new HintedMessage(
|
||||||
Component.translatable("gui.computercraft.tooltip.terminate"),
|
Component.translatable("gui.computercraft.tooltip.terminate"),
|
||||||
Component.translatable("gui.computercraft.tooltip.terminate.key")
|
Component.translatable("gui.computercraft.tooltip.terminate.key")
|
||||||
@@ -63,14 +58,6 @@ public final class ComputerSidebar {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void renderBackground(SpriteRenderer renderer, GuiSprites.ComputerTextures textures, int x, int y) {
|
|
||||||
var texture = textures.sidebar();
|
|
||||||
if (texture == null) throw new NullPointerException(textures + " has no sidebar texture");
|
|
||||||
var sprite = GuiSprites.get(texture);
|
|
||||||
|
|
||||||
renderer.blitVerticalSliced(sprite, x, y, AbstractComputerMenu.SIDEBAR_WIDTH, HEIGHT, FULL_BORDER, FULL_BORDER, TEX_HEIGHT);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void toggleComputer(BooleanSupplier isOn, InputHandler input) {
|
private static void toggleComputer(BooleanSupplier isOn, InputHandler input) {
|
||||||
if (isOn.getAsBoolean()) {
|
if (isOn.getAsBoolean()) {
|
||||||
input.shutdown();
|
input.shutdown();
|
||||||
|
@@ -10,10 +10,10 @@ import net.minecraft.ChatFormatting;
|
|||||||
import net.minecraft.client.gui.GuiGraphics;
|
import net.minecraft.client.gui.GuiGraphics;
|
||||||
import net.minecraft.client.gui.components.Button;
|
import net.minecraft.client.gui.components.Button;
|
||||||
import net.minecraft.client.gui.components.Tooltip;
|
import net.minecraft.client.gui.components.Tooltip;
|
||||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
|
|
||||||
import net.minecraft.network.chat.Component;
|
import net.minecraft.network.chat.Component;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -21,11 +21,11 @@ import java.util.function.Supplier;
|
|||||||
* dynamically.
|
* dynamically.
|
||||||
*/
|
*/
|
||||||
public class DynamicImageButton extends Button {
|
public class DynamicImageButton extends Button {
|
||||||
private final Boolean2ObjectFunction<TextureAtlasSprite> texture;
|
private final Boolean2ObjectFunction<ResourceLocation> texture;
|
||||||
private final Supplier<HintedMessage> message;
|
private final Supplier<HintedMessage> message;
|
||||||
|
|
||||||
public DynamicImageButton(
|
public DynamicImageButton(
|
||||||
int x, int y, int width, int height, Boolean2ObjectFunction<TextureAtlasSprite> texture, OnPress onPress,
|
int x, int y, int width, int height, Boolean2ObjectFunction<ResourceLocation> texture, OnPress onPress,
|
||||||
HintedMessage message
|
HintedMessage message
|
||||||
) {
|
) {
|
||||||
this(x, y, width, height, texture, onPress, () -> message);
|
this(x, y, width, height, texture, onPress, () -> message);
|
||||||
@@ -33,7 +33,7 @@ public class DynamicImageButton extends Button {
|
|||||||
|
|
||||||
public DynamicImageButton(
|
public DynamicImageButton(
|
||||||
int x, int y, int width, int height,
|
int x, int y, int width, int height,
|
||||||
Boolean2ObjectFunction<TextureAtlasSprite> texture,
|
Boolean2ObjectFunction<ResourceLocation> texture,
|
||||||
OnPress onPress, Supplier<HintedMessage> message
|
OnPress onPress, Supplier<HintedMessage> message
|
||||||
) {
|
) {
|
||||||
super(x, y, width, height, Component.empty(), onPress, DEFAULT_NARRATION);
|
super(x, y, width, height, Component.empty(), onPress, DEFAULT_NARRATION);
|
||||||
@@ -50,7 +50,7 @@ public class DynamicImageButton extends Button {
|
|||||||
var texture = this.texture.get(isHoveredOrFocused());
|
var texture = this.texture.get(isHoveredOrFocused());
|
||||||
|
|
||||||
RenderSystem.disableDepthTest();
|
RenderSystem.disableDepthTest();
|
||||||
graphics.blit(getX(), getY(), 0, width, height, texture);
|
graphics.blitSprite(texture, getX(), getY(), 0, width, height);
|
||||||
RenderSystem.enableDepthTest();
|
RenderSystem.enableDepthTest();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
package dan200.computercraft.client.gui.widgets;
|
package dan200.computercraft.client.gui.widgets;
|
||||||
|
|
||||||
|
import dan200.computercraft.client.gui.KeyConverter;
|
||||||
import dan200.computercraft.client.render.RenderTypes;
|
import dan200.computercraft.client.render.RenderTypes;
|
||||||
import dan200.computercraft.client.render.text.FixedWidthFontRenderer;
|
import dan200.computercraft.client.render.text.FixedWidthFontRenderer;
|
||||||
import dan200.computercraft.core.terminal.Terminal;
|
import dan200.computercraft.core.terminal.Terminal;
|
||||||
@@ -69,11 +70,8 @@ public class TerminalWidget extends AbstractWidget {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean charTyped(char ch, int modifiers) {
|
public boolean charTyped(char ch, int modifiers) {
|
||||||
if (ch >= 32 && ch <= 126 || ch >= 160 && ch <= 255) {
|
var terminalChar = StringUtil.unicodeToTerminal(ch);
|
||||||
// Queue the char event for any printable chars in byte range
|
if (StringUtil.isTypableChar(terminalChar)) computer.charTyped((byte) terminalChar);
|
||||||
computer.queueEvent("char", new Object[]{ Character.toString(ch) });
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -86,7 +84,7 @@ public class TerminalWidget extends AbstractWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ((modifiers & GLFW.GLFW_MOD_CONTROL) != 0) {
|
if ((modifiers & GLFW.GLFW_MOD_CONTROL) != 0) {
|
||||||
switch (key) {
|
switch (KeyConverter.physicalToActual(key, scancode)) {
|
||||||
case GLFW.GLFW_KEY_T -> {
|
case GLFW.GLFW_KEY_T -> {
|
||||||
if (terminateTimer < 0) terminateTimer = 0;
|
if (terminateTimer < 0) terminateTimer = 0;
|
||||||
}
|
}
|
||||||
@@ -110,8 +108,8 @@ public class TerminalWidget extends AbstractWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void paste() {
|
private void paste() {
|
||||||
var clipboard = StringUtil.normaliseClipboardString(Minecraft.getInstance().keyboardHandler.getClipboard());
|
var clipboard = StringUtil.getClipboardString(Minecraft.getInstance().keyboardHandler.getClipboard());
|
||||||
if (!clipboard.isEmpty()) computer.queueEvent("paste", new Object[]{ clipboard });
|
if (clipboard.remaining() > 0) computer.paste(clipboard);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -122,7 +120,7 @@ public class TerminalWidget extends AbstractWidget {
|
|||||||
computer.keyUp(key);
|
computer.keyUp(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (key) {
|
switch (KeyConverter.physicalToActual(key, scancode)) {
|
||||||
case GLFW.GLFW_KEY_T -> terminateTimer = -1;
|
case GLFW.GLFW_KEY_T -> terminateTimer = -1;
|
||||||
case GLFW.GLFW_KEY_R -> rebootTimer = -1;
|
case GLFW.GLFW_KEY_R -> rebootTimer = -1;
|
||||||
case GLFW.GLFW_KEY_S -> shutdownTimer = -1;
|
case GLFW.GLFW_KEY_S -> shutdownTimer = -1;
|
||||||
@@ -220,7 +218,7 @@ public class TerminalWidget extends AbstractWidget {
|
|||||||
|
|
||||||
public void update() {
|
public void update() {
|
||||||
if (terminateTimer >= 0 && terminateTimer < TERMINATE_TIME && (terminateTimer += 0.05f) > TERMINATE_TIME) {
|
if (terminateTimer >= 0 && terminateTimer < TERMINATE_TIME && (terminateTimer += 0.05f) > TERMINATE_TIME) {
|
||||||
computer.queueEvent("terminate");
|
computer.terminate();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (shutdownTimer >= 0 && shutdownTimer < TERMINATE_TIME && (shutdownTimer += 0.05f) > TERMINATE_TIME) {
|
if (shutdownTimer >= 0 && shutdownTimer < TERMINATE_TIME && (shutdownTimer += 0.05f) > TERMINATE_TIME) {
|
||||||
|
@@ -8,7 +8,6 @@ import dan200.computercraft.api.ComputerCraftAPI;
|
|||||||
import dan200.computercraft.api.turtle.TurtleSide;
|
import dan200.computercraft.api.turtle.TurtleSide;
|
||||||
import dan200.computercraft.shared.ModRegistry;
|
import dan200.computercraft.shared.ModRegistry;
|
||||||
import dan200.computercraft.shared.integration.RecipeModHelpers;
|
import dan200.computercraft.shared.integration.RecipeModHelpers;
|
||||||
import dan200.computercraft.shared.media.items.DiskItem;
|
|
||||||
import dan200.computercraft.shared.pocket.items.PocketComputerItem;
|
import dan200.computercraft.shared.pocket.items.PocketComputerItem;
|
||||||
import dan200.computercraft.shared.turtle.items.TurtleItem;
|
import dan200.computercraft.shared.turtle.items.TurtleItem;
|
||||||
import mezz.jei.api.IModPlugin;
|
import mezz.jei.api.IModPlugin;
|
||||||
@@ -23,6 +22,7 @@ import net.minecraft.client.Minecraft;
|
|||||||
import net.minecraft.core.RegistryAccess;
|
import net.minecraft.core.RegistryAccess;
|
||||||
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.component.DyedItemColor;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@@ -100,7 +100,7 @@ public class JEIComputerCraft implements IModPlugin {
|
|||||||
/**
|
/**
|
||||||
* Distinguishes disks by colour.
|
* Distinguishes disks by colour.
|
||||||
*/
|
*/
|
||||||
private static final IIngredientSubtypeInterpreter<ItemStack> diskSubtype = (stack, ctx) -> Integer.toString(DiskItem.getColour(stack));
|
private static final IIngredientSubtypeInterpreter<ItemStack> diskSubtype = (stack, ctx) -> Integer.toString(DyedItemColor.getOrDefault(stack, -1));
|
||||||
|
|
||||||
private static RegistryAccess getRegistryAccess() {
|
private static RegistryAccess getRegistryAccess() {
|
||||||
return Minecraft.getInstance().level.registryAccess();
|
return Minecraft.getInstance().level.registryAccess();
|
||||||
|
@@ -0,0 +1,90 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2024 The CC: Tweaked Developers
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
|
package dan200.computercraft.client.model;
|
||||||
|
|
||||||
|
import com.mojang.blaze3d.vertex.PoseStack;
|
||||||
|
import dan200.computercraft.api.ComputerCraftAPI;
|
||||||
|
import dan200.computercraft.client.pocket.PocketComputerData;
|
||||||
|
import dan200.computercraft.client.render.CustomLecternRenderer;
|
||||||
|
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
||||||
|
import dan200.computercraft.shared.pocket.items.PocketComputerItem;
|
||||||
|
import net.minecraft.client.model.geom.ModelPart;
|
||||||
|
import net.minecraft.client.model.geom.PartPose;
|
||||||
|
import net.minecraft.client.model.geom.builders.CubeListBuilder;
|
||||||
|
import net.minecraft.client.model.geom.builders.MeshDefinition;
|
||||||
|
import net.minecraft.client.renderer.LightTexture;
|
||||||
|
import net.minecraft.client.renderer.MultiBufferSource;
|
||||||
|
import net.minecraft.client.renderer.RenderType;
|
||||||
|
import net.minecraft.client.resources.model.Material;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import net.minecraft.world.inventory.InventoryMenu;
|
||||||
|
import net.minecraft.world.item.component.DyedItemColor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A model for {@linkplain PocketComputerItem pocket computers} placed on a lectern.
|
||||||
|
*
|
||||||
|
* @see CustomLecternRenderer
|
||||||
|
*/
|
||||||
|
public class LecternPocketModel {
|
||||||
|
public static final ResourceLocation TEXTURE_NORMAL = ResourceLocation.fromNamespaceAndPath(ComputerCraftAPI.MOD_ID, "entity/pocket_computer_normal");
|
||||||
|
public static final ResourceLocation TEXTURE_ADVANCED = ResourceLocation.fromNamespaceAndPath(ComputerCraftAPI.MOD_ID, "entity/pocket_computer_advanced");
|
||||||
|
public static final ResourceLocation TEXTURE_COLOUR = ResourceLocation.fromNamespaceAndPath(ComputerCraftAPI.MOD_ID, "entity/pocket_computer_colour");
|
||||||
|
public static final ResourceLocation TEXTURE_FRAME = ResourceLocation.fromNamespaceAndPath(ComputerCraftAPI.MOD_ID, "entity/pocket_computer_frame");
|
||||||
|
public static final ResourceLocation TEXTURE_LIGHT = ResourceLocation.fromNamespaceAndPath(ComputerCraftAPI.MOD_ID, "entity/pocket_computer_light");
|
||||||
|
|
||||||
|
private static final Material MATERIAL_NORMAL = new Material(InventoryMenu.BLOCK_ATLAS, TEXTURE_NORMAL);
|
||||||
|
private static final Material MATERIAL_ADVANCED = new Material(InventoryMenu.BLOCK_ATLAS, TEXTURE_ADVANCED);
|
||||||
|
private static final Material MATERIAL_COLOUR = new Material(InventoryMenu.BLOCK_ATLAS, TEXTURE_COLOUR);
|
||||||
|
private static final Material MATERIAL_FRAME = new Material(InventoryMenu.BLOCK_ATLAS, TEXTURE_FRAME);
|
||||||
|
private static final Material MATERIAL_LIGHT = new Material(InventoryMenu.BLOCK_ATLAS, TEXTURE_LIGHT);
|
||||||
|
|
||||||
|
// The size of the terminal within the model.
|
||||||
|
public static final float TERM_WIDTH = 12.0f / 32.0f;
|
||||||
|
public static final float TERM_HEIGHT = 14.0f / 32.0f;
|
||||||
|
|
||||||
|
// The size of the texture. The texture is 36x36, but is at 2x resolution.
|
||||||
|
private static final int TEXTURE_WIDTH = 48 / 2;
|
||||||
|
private static final int TEXTURE_HEIGHT = 48 / 2;
|
||||||
|
|
||||||
|
private final ModelPart root;
|
||||||
|
|
||||||
|
public LecternPocketModel() {
|
||||||
|
root = buildPages();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ModelPart buildPages() {
|
||||||
|
var mesh = new MeshDefinition();
|
||||||
|
var parts = mesh.getRoot();
|
||||||
|
parts.addOrReplaceChild(
|
||||||
|
"root",
|
||||||
|
CubeListBuilder.create().texOffs(0, 0).addBox(0f, -5.0f, -4.0f, 1f, 10.0f, 8.0f),
|
||||||
|
PartPose.ZERO
|
||||||
|
);
|
||||||
|
return mesh.getRoot().bake(TEXTURE_WIDTH, TEXTURE_HEIGHT);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render the pocket computer model.
|
||||||
|
*
|
||||||
|
* @param poseStack The current pose stack.
|
||||||
|
* @param bufferSource The buffer source to draw to.
|
||||||
|
* @param packedLight The current light level.
|
||||||
|
* @param packedOverlay The overlay texture (used for entity hurt animation).
|
||||||
|
* @param family The computer family.
|
||||||
|
* @param frameColour The pocket computer's {@linkplain DyedItemColor colour}.
|
||||||
|
* @param lightColour The pocket computer's {@linkplain PocketComputerData#getLightState() light colour}.
|
||||||
|
*/
|
||||||
|
public void render(PoseStack poseStack, MultiBufferSource bufferSource, int packedLight, int packedOverlay, ComputerFamily family, int frameColour, int lightColour) {
|
||||||
|
if (frameColour != -1) {
|
||||||
|
root.render(poseStack, MATERIAL_FRAME.buffer(bufferSource, RenderType::entityCutout), packedLight, packedOverlay);
|
||||||
|
root.render(poseStack, MATERIAL_COLOUR.buffer(bufferSource, RenderType::entityCutout), packedLight, packedOverlay, frameColour);
|
||||||
|
} else {
|
||||||
|
var buffer = (family == ComputerFamily.ADVANCED ? MATERIAL_ADVANCED : MATERIAL_NORMAL).buffer(bufferSource, RenderType::entityCutout);
|
||||||
|
root.render(poseStack, buffer, packedLight, packedOverlay);
|
||||||
|
}
|
||||||
|
|
||||||
|
root.render(poseStack, MATERIAL_LIGHT.buffer(bufferSource, RenderType::entityCutout), LightTexture.FULL_BRIGHT, packedOverlay, lightColour);
|
||||||
|
}
|
||||||
|
}
|
@@ -11,8 +11,8 @@ import net.minecraft.client.resources.model.BakedModel;
|
|||||||
import net.minecraft.core.Direction;
|
import net.minecraft.core.Direction;
|
||||||
import org.joml.Matrix4f;
|
import org.joml.Matrix4f;
|
||||||
import org.joml.Vector4f;
|
import org.joml.Vector4f;
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
@@ -23,8 +23,8 @@ import net.minecraft.client.resources.model.ModelManager;
|
|||||||
import net.minecraft.core.component.DataComponents;
|
import net.minecraft.core.component.DataComponents;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
import net.minecraft.world.item.ItemStack;
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
@@ -27,8 +27,8 @@ import net.minecraft.resources.ResourceLocation;
|
|||||||
import net.minecraft.world.entity.player.Player;
|
import net.minecraft.world.entity.player.Player;
|
||||||
import net.minecraft.world.item.JukeboxSong;
|
import net.minecraft.world.item.JukeboxSong;
|
||||||
import net.minecraft.world.level.Level;
|
import net.minecraft.world.level.Level;
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -7,8 +7,7 @@ package dan200.computercraft.client.platform;
|
|||||||
import com.mojang.blaze3d.vertex.PoseStack;
|
import com.mojang.blaze3d.vertex.PoseStack;
|
||||||
import net.minecraft.client.renderer.MultiBufferSource;
|
import net.minecraft.client.renderer.MultiBufferSource;
|
||||||
import net.minecraft.client.resources.model.BakedModel;
|
import net.minecraft.client.resources.model.BakedModel;
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
import javax.annotation.Nullable;
|
|
||||||
|
|
||||||
public interface ClientPlatformHelper extends dan200.computercraft.impl.client.ClientPlatformHelper {
|
public interface ClientPlatformHelper extends dan200.computercraft.impl.client.ClientPlatformHelper {
|
||||||
static ClientPlatformHelper get() {
|
static ClientPlatformHelper get() {
|
||||||
@@ -25,5 +24,5 @@ public interface ClientPlatformHelper extends dan200.computercraft.impl.client.C
|
|||||||
* @param overlayLight The current overlay light.
|
* @param overlayLight The current overlay light.
|
||||||
* @param tints Block colour tints to apply to the model.
|
* @param tints Block colour tints to apply to the model.
|
||||||
*/
|
*/
|
||||||
void renderBakedModel(PoseStack transform, MultiBufferSource buffers, BakedModel model, int lightmapCoord, int overlayLight, @Nullable int[] tints);
|
void renderBakedModel(PoseStack transform, MultiBufferSource buffers, BakedModel model, int lightmapCoord, int overlayLight, int @Nullable [] tints);
|
||||||
}
|
}
|
||||||
|
@@ -10,8 +10,8 @@ import dan200.computercraft.shared.computer.core.ServerComputer;
|
|||||||
import dan200.computercraft.shared.computer.terminal.TerminalState;
|
import dan200.computercraft.shared.computer.terminal.TerminalState;
|
||||||
import dan200.computercraft.shared.network.client.PocketComputerDataMessage;
|
import dan200.computercraft.shared.network.client.PocketComputerDataMessage;
|
||||||
import net.minecraft.world.item.ItemStack;
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
@@ -8,8 +8,7 @@ import dan200.computercraft.shared.computer.core.ComputerState;
|
|||||||
import dan200.computercraft.shared.computer.terminal.NetworkedTerminal;
|
import dan200.computercraft.shared.computer.terminal.NetworkedTerminal;
|
||||||
import dan200.computercraft.shared.computer.terminal.TerminalState;
|
import dan200.computercraft.shared.computer.terminal.TerminalState;
|
||||||
import dan200.computercraft.shared.pocket.core.PocketServerComputer;
|
import dan200.computercraft.shared.pocket.core.PocketServerComputer;
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
import javax.annotation.Nullable;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clientside data about a pocket computer.
|
* Clientside data about a pocket computer.
|
||||||
|
@@ -1,17 +1,14 @@
|
|||||||
// Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
|
// SPDX-FileCopyrightText: 2025 The CC: Tweaked Developers
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: LicenseRef-CCPL
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
package dan200.computercraft.client.render;
|
package dan200.computercraft.client.render;
|
||||||
|
|
||||||
import dan200.computercraft.client.gui.GuiSprites;
|
import dan200.computercraft.client.gui.ComputerScreen;
|
||||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
|
import net.minecraft.client.resources.metadata.gui.GuiSpriteScaling;
|
||||||
|
|
||||||
import static dan200.computercraft.client.render.SpriteRenderer.u;
|
|
||||||
import static dan200.computercraft.client.render.SpriteRenderer.v;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Renders the borders of computers, either for a GUI ({@link dan200.computercraft.client.gui.ComputerScreen}) or
|
* Constants for the borders of computers, either for a {@linkplain ComputerScreen GUI} or
|
||||||
* {@linkplain PocketItemRenderer in-hand pocket computers}.
|
* {@linkplain PocketItemRenderer in-hand pocket computers}.
|
||||||
*/
|
*/
|
||||||
public final class ComputerBorderRenderer {
|
public final class ComputerBorderRenderer {
|
||||||
@@ -21,55 +18,13 @@ public final class ComputerBorderRenderer {
|
|||||||
public static final int MARGIN = 2;
|
public static final int MARGIN = 2;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The width of the terminal border.
|
* The size of the terminal border.
|
||||||
|
* <p>
|
||||||
|
* This is only used for layout of elements within UI. When rendering, the size of the computer's border is
|
||||||
|
* determined by its {@link GuiSpriteScaling}.
|
||||||
*/
|
*/
|
||||||
public static final int BORDER = 12;
|
public static final int BORDER = 12;
|
||||||
|
|
||||||
public static final int LIGHT_HEIGHT = 8;
|
|
||||||
|
|
||||||
private static final int TEX_SIZE = 36;
|
|
||||||
|
|
||||||
private ComputerBorderRenderer() {
|
private ComputerBorderRenderer() {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void render(SpriteRenderer renderer, GuiSprites.ComputerTextures textures, int x, int y, int width, int height, boolean withLight) {
|
|
||||||
var endX = x + width;
|
|
||||||
var endY = y + height;
|
|
||||||
|
|
||||||
var border = GuiSprites.get(textures.border());
|
|
||||||
|
|
||||||
// Top bar
|
|
||||||
blitBorder(renderer, border, x - BORDER, y - BORDER, 0, 0, BORDER, BORDER);
|
|
||||||
blitBorder(renderer, border, x, y - BORDER, BORDER, 0, width, BORDER);
|
|
||||||
blitBorder(renderer, border, endX, y - BORDER, BORDER * 2, 0, BORDER, BORDER);
|
|
||||||
|
|
||||||
// Vertical bars
|
|
||||||
blitBorder(renderer, border, x - BORDER, y, 0, BORDER, BORDER, height);
|
|
||||||
blitBorder(renderer, border, endX, y, BORDER * 2, BORDER, BORDER, height);
|
|
||||||
|
|
||||||
// Bottom bar. We allow for drawing a stretched version, which allows for additional elements (such as the
|
|
||||||
// pocket computer's lights).
|
|
||||||
if (withLight) {
|
|
||||||
var pocketBottomTexture = textures.pocketBottom();
|
|
||||||
if (pocketBottomTexture == null) throw new NullPointerException(textures + " has no pocket texture");
|
|
||||||
var pocketBottom = GuiSprites.get(pocketBottomTexture);
|
|
||||||
|
|
||||||
renderer.blitHorizontalSliced(
|
|
||||||
pocketBottom, x - BORDER, endY, width + BORDER * 2, BORDER + LIGHT_HEIGHT,
|
|
||||||
BORDER, BORDER, BORDER * 3
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
blitBorder(renderer, border, x - BORDER, endY, 0, BORDER * 2, BORDER, BORDER);
|
|
||||||
blitBorder(renderer, border, x, endY, BORDER, BORDER * 2, width, BORDER);
|
|
||||||
blitBorder(renderer, border, endX, endY, BORDER * 2, BORDER * 2, BORDER, BORDER);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void blitBorder(SpriteRenderer renderer, TextureAtlasSprite sprite, int x, int y, int u, int v, int width, int height) {
|
|
||||||
renderer.blit(
|
|
||||||
x, y, width, height,
|
|
||||||
u(sprite, u, TEX_SIZE), v(sprite, v, TEX_SIZE),
|
|
||||||
u(sprite, u + BORDER, TEX_SIZE), v(sprite, v + BORDER, TEX_SIZE)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@@ -6,16 +6,31 @@ package dan200.computercraft.client.render;
|
|||||||
|
|
||||||
import com.mojang.blaze3d.vertex.PoseStack;
|
import com.mojang.blaze3d.vertex.PoseStack;
|
||||||
import com.mojang.math.Axis;
|
import com.mojang.math.Axis;
|
||||||
|
import dan200.computercraft.client.model.LecternPocketModel;
|
||||||
import dan200.computercraft.client.model.LecternPrintoutModel;
|
import dan200.computercraft.client.model.LecternPrintoutModel;
|
||||||
|
import dan200.computercraft.client.pocket.ClientPocketComputers;
|
||||||
|
import dan200.computercraft.client.render.text.FixedWidthFontRenderer;
|
||||||
|
import dan200.computercraft.core.terminal.Terminal;
|
||||||
|
import dan200.computercraft.core.util.Colour;
|
||||||
|
import dan200.computercraft.shared.ModRegistry;
|
||||||
import dan200.computercraft.shared.lectern.CustomLecternBlockEntity;
|
import dan200.computercraft.shared.lectern.CustomLecternBlockEntity;
|
||||||
import dan200.computercraft.shared.media.items.PrintoutData;
|
import dan200.computercraft.shared.media.items.PrintoutData;
|
||||||
import dan200.computercraft.shared.media.items.PrintoutItem;
|
import dan200.computercraft.shared.media.items.PrintoutItem;
|
||||||
|
import dan200.computercraft.shared.pocket.items.PocketComputerItem;
|
||||||
import net.minecraft.client.renderer.MultiBufferSource;
|
import net.minecraft.client.renderer.MultiBufferSource;
|
||||||
import net.minecraft.client.renderer.RenderType;
|
import net.minecraft.client.renderer.RenderType;
|
||||||
|
import net.minecraft.client.renderer.blockentity.BlockEntityRenderDispatcher;
|
||||||
import net.minecraft.client.renderer.blockentity.BlockEntityRenderer;
|
import net.minecraft.client.renderer.blockentity.BlockEntityRenderer;
|
||||||
import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider;
|
import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider;
|
||||||
import net.minecraft.client.renderer.blockentity.LecternRenderer;
|
import net.minecraft.client.renderer.blockentity.LecternRenderer;
|
||||||
|
import net.minecraft.util.FastColor;
|
||||||
|
import net.minecraft.world.item.component.DyedItemColor;
|
||||||
import net.minecraft.world.level.block.LecternBlock;
|
import net.minecraft.world.level.block.LecternBlock;
|
||||||
|
import net.minecraft.world.phys.Vec3;
|
||||||
|
|
||||||
|
import static dan200.computercraft.client.render.ComputerBorderRenderer.MARGIN;
|
||||||
|
import static dan200.computercraft.client.render.text.FixedWidthFontRenderer.FONT_HEIGHT;
|
||||||
|
import static dan200.computercraft.client.render.text.FixedWidthFontRenderer.FONT_WIDTH;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A block entity renderer for our {@linkplain CustomLecternBlockEntity lectern}.
|
* A block entity renderer for our {@linkplain CustomLecternBlockEntity lectern}.
|
||||||
@@ -23,10 +38,17 @@ import net.minecraft.world.level.block.LecternBlock;
|
|||||||
* This largely follows {@link LecternRenderer}, but with support for multiple types of item.
|
* This largely follows {@link LecternRenderer}, but with support for multiple types of item.
|
||||||
*/
|
*/
|
||||||
public class CustomLecternRenderer implements BlockEntityRenderer<CustomLecternBlockEntity> {
|
public class CustomLecternRenderer implements BlockEntityRenderer<CustomLecternBlockEntity> {
|
||||||
|
private static final int POCKET_TERMINAL_RENDER_DISTANCE = 32;
|
||||||
|
|
||||||
|
private final BlockEntityRenderDispatcher berDispatcher;
|
||||||
private final LecternPrintoutModel printoutModel;
|
private final LecternPrintoutModel printoutModel;
|
||||||
|
private final LecternPocketModel pocketModel;
|
||||||
|
|
||||||
public CustomLecternRenderer(BlockEntityRendererProvider.Context context) {
|
public CustomLecternRenderer(BlockEntityRendererProvider.Context context) {
|
||||||
|
berDispatcher = context.getBlockEntityRenderDispatcher();
|
||||||
|
|
||||||
printoutModel = new LecternPrintoutModel();
|
printoutModel = new LecternPrintoutModel();
|
||||||
|
pocketModel = new LecternPocketModel();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -38,15 +60,53 @@ public class CustomLecternRenderer implements BlockEntityRenderer<CustomLecternB
|
|||||||
poseStack.translate(0, -0.125f, 0);
|
poseStack.translate(0, -0.125f, 0);
|
||||||
|
|
||||||
var item = lectern.getItem();
|
var item = lectern.getItem();
|
||||||
if (item.getItem() instanceof PrintoutItem printout) {
|
if (item.getItem() instanceof PrintoutItem) {
|
||||||
var vertexConsumer = LecternPrintoutModel.MATERIAL.buffer(buffer, RenderType::entitySolid);
|
var vertexConsumer = LecternPrintoutModel.MATERIAL.buffer(buffer, RenderType::entitySolid);
|
||||||
if (printout.getType() == PrintoutItem.Type.BOOK) {
|
if (item.is(ModRegistry.Items.PRINTED_BOOK.get())) {
|
||||||
printoutModel.renderBook(poseStack, vertexConsumer, packedLight, packedOverlay);
|
printoutModel.renderBook(poseStack, vertexConsumer, packedLight, packedOverlay);
|
||||||
} else {
|
} else {
|
||||||
printoutModel.renderPages(poseStack, vertexConsumer, packedLight, packedOverlay, PrintoutData.getOrEmpty(item).pages());
|
printoutModel.renderPages(poseStack, vertexConsumer, packedLight, packedOverlay, PrintoutData.getOrEmpty(item).pages());
|
||||||
}
|
}
|
||||||
|
} else if (item.getItem() instanceof PocketComputerItem pocket) {
|
||||||
|
var computer = ClientPocketComputers.get(item);
|
||||||
|
|
||||||
|
pocketModel.render(
|
||||||
|
poseStack, buffer, packedLight, packedOverlay, pocket.getFamily(), DyedItemColor.getOrDefault(item, -1),
|
||||||
|
FastColor.ARGB32.opaque(computer == null || computer.getLightState() == -1 ? Colour.BLACK.getHex() : computer.getLightState())
|
||||||
|
);
|
||||||
|
|
||||||
|
// Jiggle the terminal about a bit, so (0, 0) is in the top left of the model's terminal hole.
|
||||||
|
poseStack.mulPose(Axis.YP.rotationDegrees(90f));
|
||||||
|
poseStack.translate(-0.5 * LecternPocketModel.TERM_WIDTH, 0.5 * LecternPocketModel.TERM_HEIGHT + 1f / 32.0f, 1 / 16.0f);
|
||||||
|
poseStack.mulPose(Axis.XP.rotationDegrees(180));
|
||||||
|
|
||||||
|
// Either render the terminal or a black screen, depending on how close we are.
|
||||||
|
var terminal = computer == null ? null : computer.getTerminal();
|
||||||
|
var quadEmitter = FixedWidthFontRenderer.toVertexConsumer(poseStack, buffer.getBuffer(RenderTypes.TERMINAL));
|
||||||
|
if (terminal != null && Vec3.atCenterOf(lectern.getBlockPos()).closerThan(berDispatcher.camera.getPosition(), POCKET_TERMINAL_RENDER_DISTANCE)) {
|
||||||
|
renderPocketTerminal(poseStack, quadEmitter, terminal);
|
||||||
|
} else {
|
||||||
|
FixedWidthFontRenderer.drawEmptyTerminal(quadEmitter, 0, 0, LecternPocketModel.TERM_WIDTH, LecternPocketModel.TERM_HEIGHT);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
poseStack.popPose();
|
poseStack.popPose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void renderPocketTerminal(PoseStack poseStack, FixedWidthFontRenderer.QuadEmitter quadEmitter, Terminal terminal) {
|
||||||
|
var width = terminal.getWidth() * FONT_WIDTH;
|
||||||
|
var height = terminal.getHeight() * FONT_HEIGHT;
|
||||||
|
|
||||||
|
// Scale the terminal down to fit in the available space.
|
||||||
|
var scaleX = LecternPocketModel.TERM_WIDTH / (width + MARGIN * 2);
|
||||||
|
var scaleY = LecternPocketModel.TERM_HEIGHT / (height + MARGIN * 2);
|
||||||
|
var scale = Math.min(scaleX, scaleY);
|
||||||
|
poseStack.scale(scale, scale, -1.0f);
|
||||||
|
|
||||||
|
// Convert the model dimensions to terminal space, then find out how large the margin should be.
|
||||||
|
var marginX = ((LecternPocketModel.TERM_WIDTH / scale) - width) / 2;
|
||||||
|
var marginY = ((LecternPocketModel.TERM_HEIGHT / scale) - height) / 2;
|
||||||
|
|
||||||
|
FixedWidthFontRenderer.drawTerminal(quadEmitter, marginX, marginY, terminal, marginY, marginY, marginX, marginX);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -14,8 +14,8 @@ import net.minecraft.client.renderer.entity.ItemRenderer;
|
|||||||
import net.minecraft.client.resources.model.BakedModel;
|
import net.minecraft.client.resources.model.BakedModel;
|
||||||
import net.minecraft.world.item.ItemStack;
|
import net.minecraft.world.item.ItemStack;
|
||||||
import org.joml.Vector4f;
|
import org.joml.Vector4f;
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -39,7 +39,7 @@ public final class ModelRenderer {
|
|||||||
* @param overlayLight The current overlay light.
|
* @param overlayLight The current overlay light.
|
||||||
* @param tints Block colour tints to apply to the model.
|
* @param tints Block colour tints to apply to the model.
|
||||||
*/
|
*/
|
||||||
public static void renderQuads(PoseStack transform, VertexConsumer buffer, List<BakedQuad> quads, int lightmapCoord, int overlayLight, @Nullable int[] tints) {
|
public static void renderQuads(PoseStack transform, VertexConsumer buffer, List<BakedQuad> quads, int lightmapCoord, int overlayLight, int @Nullable [] tints) {
|
||||||
var matrix = transform.last();
|
var matrix = transform.last();
|
||||||
var inverted = matrix.pose().determinant() < 0;
|
var inverted = matrix.pose().determinant() < 0;
|
||||||
|
|
||||||
|
@@ -13,13 +13,16 @@ import dan200.computercraft.core.util.Colour;
|
|||||||
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
||||||
import dan200.computercraft.shared.config.Config;
|
import dan200.computercraft.shared.config.Config;
|
||||||
import dan200.computercraft.shared.pocket.items.PocketComputerItem;
|
import dan200.computercraft.shared.pocket.items.PocketComputerItem;
|
||||||
|
import net.minecraft.client.Minecraft;
|
||||||
import net.minecraft.client.renderer.MultiBufferSource;
|
import net.minecraft.client.renderer.MultiBufferSource;
|
||||||
|
import net.minecraft.client.resources.metadata.gui.GuiSpriteScaling;
|
||||||
import net.minecraft.util.FastColor;
|
import net.minecraft.util.FastColor;
|
||||||
import net.minecraft.world.item.ItemStack;
|
import net.minecraft.world.item.ItemStack;
|
||||||
import net.minecraft.world.item.component.DyedItemColor;
|
import net.minecraft.world.item.component.DyedItemColor;
|
||||||
import org.joml.Matrix4f;
|
import org.joml.Matrix4f;
|
||||||
|
|
||||||
import static dan200.computercraft.client.render.ComputerBorderRenderer.*;
|
import static dan200.computercraft.client.render.ComputerBorderRenderer.BORDER;
|
||||||
|
import static dan200.computercraft.client.render.ComputerBorderRenderer.MARGIN;
|
||||||
import static dan200.computercraft.client.render.text.FixedWidthFontRenderer.FONT_HEIGHT;
|
import static dan200.computercraft.client.render.text.FixedWidthFontRenderer.FONT_HEIGHT;
|
||||||
import static dan200.computercraft.client.render.text.FixedWidthFontRenderer.FONT_WIDTH;
|
import static dan200.computercraft.client.render.text.FixedWidthFontRenderer.FONT_WIDTH;
|
||||||
|
|
||||||
@@ -29,6 +32,11 @@ import static dan200.computercraft.client.render.text.FixedWidthFontRenderer.FON
|
|||||||
public final class PocketItemRenderer extends ItemMapLikeRenderer {
|
public final class PocketItemRenderer extends ItemMapLikeRenderer {
|
||||||
public static final PocketItemRenderer INSTANCE = new PocketItemRenderer();
|
public static final PocketItemRenderer INSTANCE = new PocketItemRenderer();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The height of the pocket computer's light.
|
||||||
|
*/
|
||||||
|
private static final int LIGHT_HEIGHT = 8;
|
||||||
|
|
||||||
private PocketItemRenderer() {
|
private PocketItemRenderer() {
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -39,8 +47,8 @@ public final class PocketItemRenderer extends ItemMapLikeRenderer {
|
|||||||
|
|
||||||
int termWidth, termHeight;
|
int termWidth, termHeight;
|
||||||
if (terminal == null) {
|
if (terminal == null) {
|
||||||
termWidth = Config.pocketTermWidth;
|
termWidth = Config.DEFAULT_POCKET_TERM_WIDTH;
|
||||||
termHeight = Config.pocketTermHeight;
|
termHeight = Config.DEFAULT_POCKET_TERM_HEIGHT;
|
||||||
} else {
|
} else {
|
||||||
termWidth = terminal.getWidth();
|
termWidth = terminal.getWidth();
|
||||||
termHeight = terminal.getHeight();
|
termHeight = terminal.getHeight();
|
||||||
@@ -83,14 +91,69 @@ public final class PocketItemRenderer extends ItemMapLikeRenderer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static void renderFrame(Matrix4f transform, MultiBufferSource render, ComputerFamily family, int colour, int light, int width, int height) {
|
private static void renderFrame(Matrix4f transform, MultiBufferSource render, ComputerFamily family, int colour, int light, int width, int height) {
|
||||||
var texture = colour != -1 ? GuiSprites.COMPUTER_COLOUR : GuiSprites.getComputerTextures(family);
|
var textures = colour != -1 ? GuiSprites.COMPUTER_COLOUR : GuiSprites.getComputerTextures(family);
|
||||||
|
var spriteRenderer = new SpriteRenderer(transform, render, 0, light, colour);
|
||||||
|
renderBorder(spriteRenderer, textures, width, height);
|
||||||
|
}
|
||||||
|
|
||||||
var r = (colour >>> 16) & 0xFF;
|
private static void renderBorder(SpriteRenderer renderer, GuiSprites.ComputerTextures textures, int width, int height) {
|
||||||
var g = (colour >>> 8) & 0xFF;
|
var sprites = Minecraft.getInstance().getGuiSprites();
|
||||||
var b = colour & 0xFF;
|
|
||||||
|
|
||||||
var spriteRenderer = new SpriteRenderer(transform, render.getBuffer(RenderTypes.GUI_SPRITES), 0, light, r, g, b);
|
// Find our border, forcing it to be a nine-sliced texture.
|
||||||
ComputerBorderRenderer.render(spriteRenderer, texture, 0, 0, width, height, true);
|
var borderSprite = sprites.getSprite(textures.border());
|
||||||
|
var borderSlice = getSlice(sprites.getSpriteScaling(borderSprite), DEFAULT_BORDER);
|
||||||
|
var borderBounds = borderSlice.border();
|
||||||
|
|
||||||
|
// And take the separate bottom bit of the pocket computer.
|
||||||
|
var bottomTexture = textures.pocketBottom();
|
||||||
|
if (bottomTexture == null) throw new NullPointerException(textures + " has no pocket texture");
|
||||||
|
var bottomSprite = sprites.getSprite(bottomTexture);
|
||||||
|
var bottomSlice = getSlice(sprites.getSpriteScaling(bottomSprite), DEFAULT_BOTTOM);
|
||||||
|
var bottomBounds = bottomSlice.border();
|
||||||
|
|
||||||
|
// Now draw a nine-sliced texture, by stitching together the top parts of the border with the pocket bottom.
|
||||||
|
|
||||||
|
// Top bar
|
||||||
|
renderer.blit(
|
||||||
|
borderSprite, -borderBounds.left(), -borderBounds.top(), borderBounds.left(), borderBounds.top(),
|
||||||
|
0, 0, borderSlice.width(), borderSlice.height()
|
||||||
|
);
|
||||||
|
renderer.blitTiled(
|
||||||
|
borderSprite, 0, -borderBounds.top(), width, borderBounds.top(),
|
||||||
|
borderBounds.left(), 0, borderSlice.width() - borderBounds.left() - borderBounds.right(), borderBounds.top(),
|
||||||
|
borderSlice.width(), borderSlice.height()
|
||||||
|
);
|
||||||
|
renderer.blit(
|
||||||
|
borderSprite, width, -borderBounds.top(), borderBounds.right(), borderBounds.top(),
|
||||||
|
borderSlice.width() - borderBounds.right(), 0, borderSlice.width(), borderSlice.height()
|
||||||
|
);
|
||||||
|
|
||||||
|
// Vertical bars
|
||||||
|
renderer.blitTiled(
|
||||||
|
borderSprite, -borderBounds.left(), 0, borderBounds.left(), height,
|
||||||
|
0, borderBounds.top(), borderBounds.left(), borderSlice.height() - borderBounds.top() - borderBounds.bottom(),
|
||||||
|
borderSlice.width(), borderSlice.height()
|
||||||
|
);
|
||||||
|
renderer.blitTiled(
|
||||||
|
borderSprite, width, 0, borderBounds.right(), height,
|
||||||
|
borderSlice.width() - borderBounds.right(), borderBounds.top(), borderBounds.right(), borderSlice.height() - borderBounds.top() - borderBounds.bottom(),
|
||||||
|
borderSlice.width(), borderSlice.height()
|
||||||
|
);
|
||||||
|
|
||||||
|
// Bottom
|
||||||
|
renderer.blit(
|
||||||
|
bottomSprite, -bottomBounds.left(), height, bottomBounds.left(), bottomSlice.height(),
|
||||||
|
0, 0, bottomSlice.width(), bottomSlice.height()
|
||||||
|
);
|
||||||
|
renderer.blitTiled(
|
||||||
|
bottomSprite, 0, height, width, bottomSlice.height(),
|
||||||
|
bottomBounds.left(), 0, bottomSlice.width() - bottomBounds.left() - bottomBounds.right(), bottomSlice.height(),
|
||||||
|
bottomSlice.width(), bottomSlice.height()
|
||||||
|
);
|
||||||
|
renderer.blit(
|
||||||
|
bottomSprite, width, height, bottomBounds.right(), bottomSlice.height(),
|
||||||
|
bottomSlice.width() - bottomBounds.right(), 0, bottomSlice.width(), bottomSlice.height()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void renderLight(PoseStack transform, MultiBufferSource render, int colour, int width, int height) {
|
private static void renderLight(PoseStack transform, MultiBufferSource render, int colour, int width, int height) {
|
||||||
@@ -101,4 +164,16 @@ public final class PocketItemRenderer extends ItemMapLikeRenderer {
|
|||||||
FastColor.ARGB32.opaque(colour), RenderTypes.FULL_BRIGHT_LIGHTMAP
|
FastColor.ARGB32.opaque(colour), RenderTypes.FULL_BRIGHT_LIGHTMAP
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static final GuiSpriteScaling.NineSlice DEFAULT_BORDER = new GuiSpriteScaling.NineSlice(
|
||||||
|
36, 36, new GuiSpriteScaling.NineSlice.Border(12, 12, 12, 12)
|
||||||
|
);
|
||||||
|
|
||||||
|
private static final GuiSpriteScaling.NineSlice DEFAULT_BOTTOM = new GuiSpriteScaling.NineSlice(
|
||||||
|
36, 20, new GuiSpriteScaling.NineSlice.Border(12, 0, 12, 0)
|
||||||
|
);
|
||||||
|
|
||||||
|
private static GuiSpriteScaling.NineSlice getSlice(GuiSpriteScaling scaling, GuiSpriteScaling.NineSlice fallback) {
|
||||||
|
return scaling instanceof GuiSpriteScaling.NineSlice slice ? slice : fallback;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -8,7 +8,6 @@ import com.mojang.blaze3d.vertex.PoseStack;
|
|||||||
import com.mojang.math.Axis;
|
import com.mojang.math.Axis;
|
||||||
import dan200.computercraft.shared.ModRegistry;
|
import dan200.computercraft.shared.ModRegistry;
|
||||||
import dan200.computercraft.shared.media.items.PrintoutData;
|
import dan200.computercraft.shared.media.items.PrintoutData;
|
||||||
import dan200.computercraft.shared.media.items.PrintoutItem;
|
|
||||||
import net.minecraft.client.renderer.MultiBufferSource;
|
import net.minecraft.client.renderer.MultiBufferSource;
|
||||||
import net.minecraft.world.entity.EntityType;
|
import net.minecraft.world.entity.EntityType;
|
||||||
import net.minecraft.world.entity.decoration.ItemFrame;
|
import net.minecraft.world.entity.decoration.ItemFrame;
|
||||||
@@ -53,7 +52,7 @@ public final class PrintoutItemRenderer extends ItemMapLikeRenderer {
|
|||||||
var pageData = stack.getOrDefault(ModRegistry.DataComponents.PRINTOUT.get(), PrintoutData.EMPTY);
|
var pageData = stack.getOrDefault(ModRegistry.DataComponents.PRINTOUT.get(), PrintoutData.EMPTY);
|
||||||
|
|
||||||
var pages = pageData.pages();
|
var pages = pageData.pages();
|
||||||
var book = ((PrintoutItem) stack.getItem()).getType() == PrintoutItem.Type.BOOK;
|
var book = stack.is(ModRegistry.Items.PRINTED_BOOK.get());
|
||||||
|
|
||||||
double width = LINE_LENGTH * FONT_WIDTH + X_TEXT_MARGIN * 2;
|
double width = LINE_LENGTH * FONT_WIDTH + X_TEXT_MARGIN * 2;
|
||||||
double height = LINES_PER_PAGE * FONT_HEIGHT + Y_TEXT_MARGIN * 2;
|
double height = LINES_PER_PAGE * FONT_HEIGHT + Y_TEXT_MARGIN * 2;
|
||||||
|
@@ -7,7 +7,6 @@ package dan200.computercraft.client.render;
|
|||||||
import com.mojang.blaze3d.vertex.DefaultVertexFormat;
|
import com.mojang.blaze3d.vertex.DefaultVertexFormat;
|
||||||
import com.mojang.blaze3d.vertex.VertexFormat;
|
import com.mojang.blaze3d.vertex.VertexFormat;
|
||||||
import dan200.computercraft.api.ComputerCraftAPI;
|
import dan200.computercraft.api.ComputerCraftAPI;
|
||||||
import dan200.computercraft.client.gui.GuiSprites;
|
|
||||||
import dan200.computercraft.client.render.monitor.MonitorTextureBufferShader;
|
import dan200.computercraft.client.render.monitor.MonitorTextureBufferShader;
|
||||||
import dan200.computercraft.client.render.text.FixedWidthFontRenderer;
|
import dan200.computercraft.client.render.text.FixedWidthFontRenderer;
|
||||||
import net.minecraft.client.renderer.GameRenderer;
|
import net.minecraft.client.renderer.GameRenderer;
|
||||||
@@ -16,11 +15,11 @@ import net.minecraft.client.renderer.RenderType;
|
|||||||
import net.minecraft.client.renderer.ShaderInstance;
|
import net.minecraft.client.renderer.ShaderInstance;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
import net.minecraft.server.packs.resources.ResourceProvider;
|
import net.minecraft.server.packs.resources.ResourceProvider;
|
||||||
|
import org.apache.commons.io.function.IOSupplier;
|
||||||
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.function.BiConsumer;
|
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -54,11 +53,6 @@ public class RenderTypes {
|
|||||||
*/
|
*/
|
||||||
public static final RenderType PRINTOUT_BACKGROUND = RenderType.text(ResourceLocation.fromNamespaceAndPath("computercraft", "textures/gui/printout.png"));
|
public static final RenderType PRINTOUT_BACKGROUND = RenderType.text(ResourceLocation.fromNamespaceAndPath("computercraft", "textures/gui/printout.png"));
|
||||||
|
|
||||||
/**
|
|
||||||
* Render type for {@linkplain GuiSprites GUI sprites}.
|
|
||||||
*/
|
|
||||||
public static final RenderType GUI_SPRITES = RenderType.text(GuiSprites.TEXTURE);
|
|
||||||
|
|
||||||
public static MonitorTextureBufferShader getMonitorTextureBufferShader() {
|
public static MonitorTextureBufferShader getMonitorTextureBufferShader() {
|
||||||
if (monitorTboShader == null) throw new NullPointerException("MonitorTboShader has not been registered");
|
if (monitorTboShader == null) throw new NullPointerException("MonitorTboShader has not been registered");
|
||||||
return monitorTboShader;
|
return monitorTboShader;
|
||||||
@@ -68,9 +62,12 @@ public class RenderTypes {
|
|||||||
return Objects.requireNonNull(GameRenderer.getRendertypeTextShader(), "Text shader has not been registered");
|
return Objects.requireNonNull(GameRenderer.getRendertypeTextShader(), "Text shader has not been registered");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void registerShaders(ResourceProvider resources, BiConsumer<ShaderInstance, Consumer<ShaderInstance>> load) throws IOException {
|
public interface ShaderLoader {
|
||||||
load.accept(
|
void tryLoad(String name, IOSupplier<ShaderInstance> create, Consumer<@Nullable ShaderInstance> accept) throws IOException;
|
||||||
new MonitorTextureBufferShader(
|
}
|
||||||
|
|
||||||
|
public static void registerShaders(ResourceProvider resources, ShaderLoader load) throws IOException {
|
||||||
|
load.tryLoad("monitor shader", () -> new MonitorTextureBufferShader(
|
||||||
resources,
|
resources,
|
||||||
ComputerCraftAPI.MOD_ID + "/monitor_tbo",
|
ComputerCraftAPI.MOD_ID + "/monitor_tbo",
|
||||||
MONITOR_TBO.format()
|
MONITOR_TBO.format()
|
||||||
|
@@ -6,129 +6,69 @@ package dan200.computercraft.client.render;
|
|||||||
|
|
||||||
import com.mojang.blaze3d.vertex.VertexConsumer;
|
import com.mojang.blaze3d.vertex.VertexConsumer;
|
||||||
import net.minecraft.client.gui.GuiGraphics;
|
import net.minecraft.client.gui.GuiGraphics;
|
||||||
|
import net.minecraft.client.renderer.MultiBufferSource;
|
||||||
import net.minecraft.client.renderer.RenderType;
|
import net.minecraft.client.renderer.RenderType;
|
||||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
|
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
import org.joml.Matrix4f;
|
import org.joml.Matrix4f;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A {@link GuiGraphics}-equivalent which is suitable for both rendering in to a GUI and in-world (as part of an entity
|
* A {@link GuiGraphics}-equivalent renders to a {@link VertexConsumer}. This is suitable for rendering outside of a
|
||||||
* renderer).
|
* GUI, such as part of an entity renderer.
|
||||||
* <p>
|
* <p>
|
||||||
* This batches all render calls together, though requires that all {@link TextureAtlasSprite}s are on the same sprite
|
* This batches all render calls together, though requires that all {@link TextureAtlasSprite}s are on the same sprite
|
||||||
* sheet.
|
* sheet.
|
||||||
*/
|
*/
|
||||||
public class SpriteRenderer {
|
public class SpriteRenderer {
|
||||||
|
public static final ResourceLocation TEXTURE = ResourceLocation.withDefaultNamespace("textures/atlas/gui.png");
|
||||||
|
|
||||||
private final Matrix4f transform;
|
private final Matrix4f transform;
|
||||||
private final VertexConsumer builder;
|
private final MultiBufferSource buffers;
|
||||||
private final int light;
|
private final int light;
|
||||||
private final int z;
|
private final int z;
|
||||||
private final int r, g, b;
|
private final int colour;
|
||||||
|
|
||||||
public SpriteRenderer(Matrix4f transform, VertexConsumer builder, int z, int light, int r, int g, int b) {
|
public SpriteRenderer(Matrix4f transform, MultiBufferSource buffers, int z, int light, int colour) {
|
||||||
this.transform = transform;
|
this.transform = transform;
|
||||||
this.builder = builder;
|
this.buffers = buffers;
|
||||||
this.z = z;
|
this.z = z;
|
||||||
this.light = light;
|
this.light = light;
|
||||||
this.r = r;
|
this.colour = colour;
|
||||||
this.g = g;
|
|
||||||
this.b = b;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static SpriteRenderer createForGui(GuiGraphics graphics, RenderType renderType) {
|
public void blit(TextureAtlasSprite sprite, int x0, int y0, int width, int height, int spriteX, int spriteY, int spriteWidth, int spriteHeight) {
|
||||||
return new SpriteRenderer(
|
if (width == 0 || height == 0) return;
|
||||||
graphics.pose().last().pose(), graphics.bufferSource().getBuffer(renderType),
|
|
||||||
0, RenderTypes.FULL_BRIGHT_LIGHTMAP, 255, 255, 255
|
var x1 = x0 + width;
|
||||||
);
|
var y1 = y0 + height;
|
||||||
|
var u0 = sprite.getU((float) spriteX / spriteWidth);
|
||||||
|
var u1 = sprite.getU((float) (spriteX + width) / spriteWidth);
|
||||||
|
var v0 = sprite.getV((float) spriteY / spriteHeight);
|
||||||
|
var v1 = sprite.getV((float) (spriteY + height) / spriteHeight);
|
||||||
|
|
||||||
|
var vertices = buffers.getBuffer(RenderType.text(sprite.atlasLocation()));
|
||||||
|
vertices.addVertex(transform, x0, y1, z).setColor(colour).setUv(u0, v1).setLight(light);
|
||||||
|
vertices.addVertex(transform, x1, y1, z).setColor(colour).setUv(u1, v1).setLight(light);
|
||||||
|
vertices.addVertex(transform, x1, y0, z).setColor(colour).setUv(u1, v0).setLight(light);
|
||||||
|
vertices.addVertex(transform, x0, y0, z).setColor(colour).setUv(u0, v0).setLight(light);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public void blitTiled(
|
||||||
* Render a single sprite.
|
TextureAtlasSprite sprite,
|
||||||
*
|
int x, int y, int width, int height,
|
||||||
* @param sprite The texture to draw.
|
int tileX, int tileY, int tileWidth, int tileHeight, int spriteWidth, int spriteHeight
|
||||||
* @param x The x position of the rectangle we'll draw.
|
) {
|
||||||
* @param y The x position of the rectangle we'll draw.
|
if (width <= 0 || height <= 0) return;
|
||||||
* @param width The width of the rectangle we'll draw.
|
if (tileWidth <= 0 || tileHeight <= 0) {
|
||||||
* @param height The height of the rectangle we'll draw.
|
throw new IllegalArgumentException("Tiled sprite texture size must be positive, got " + tileWidth + "x" + tileHeight);
|
||||||
*/
|
}
|
||||||
public void blit(TextureAtlasSprite sprite, int x, int y, int width, int height) {
|
|
||||||
blit(x, y, width, height, sprite.getU0(), sprite.getV0(), sprite.getU1(), sprite.getV1());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
for (var xOffset = 0; xOffset < width; xOffset += tileWidth) {
|
||||||
* Render a horizontal 3-sliced texture (i.e. split into left, middle and right). Unlike {@link GuiGraphics#blitNineSliced},
|
var sliceWidth = Math.min(tileWidth, width - xOffset);
|
||||||
* the middle texture is stretched rather than repeated.
|
for (var yOffset = 0; yOffset < height; yOffset += tileHeight) {
|
||||||
*
|
var sliceHeight = Math.min(tileHeight, height - yOffset);
|
||||||
* @param sprite The texture to draw.
|
blit(sprite, x + xOffset, y + yOffset, sliceWidth, sliceHeight, tileX, tileY, spriteWidth, spriteHeight);
|
||||||
* @param x The x position of the rectangle we'll draw.
|
}
|
||||||
* @param y The x position of the rectangle we'll draw.
|
}
|
||||||
* @param width The width of the rectangle we'll draw.
|
|
||||||
* @param height The height of the rectangle we'll draw.
|
|
||||||
* @param leftBorder The width of the left border.
|
|
||||||
* @param rightBorder The width of the right border.
|
|
||||||
* @param textureWidth The width of the whole texture.
|
|
||||||
*/
|
|
||||||
public void blitHorizontalSliced(TextureAtlasSprite sprite, int x, int y, int width, int height, int leftBorder, int rightBorder, int textureWidth) {
|
|
||||||
// TODO(1.20.2)/TODO(1.21.0): Drive this from mcmeta files, like vanilla does.
|
|
||||||
if (width < leftBorder + rightBorder) throw new IllegalArgumentException("width is less than two borders");
|
|
||||||
|
|
||||||
var centerStart = SpriteRenderer.u(sprite, leftBorder, textureWidth);
|
|
||||||
var centerEnd = SpriteRenderer.u(sprite, textureWidth - rightBorder, textureWidth);
|
|
||||||
|
|
||||||
blit(x, y, leftBorder, height, sprite.getU0(), sprite.getV0(), centerStart, sprite.getV1());
|
|
||||||
blit(x + leftBorder, y, width - leftBorder - rightBorder, height, centerStart, sprite.getV0(), centerEnd, sprite.getV1());
|
|
||||||
blit(x + width - rightBorder, y, rightBorder, height, centerEnd, sprite.getV0(), sprite.getU1(), sprite.getV1());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Render a vertical 3-sliced texture (i.e. split into top, middle and bottom). Unlike {@link GuiGraphics#blitNineSliced},
|
|
||||||
* the middle texture is stretched rather than repeated.
|
|
||||||
*
|
|
||||||
* @param sprite The texture to draw.
|
|
||||||
* @param x The x position of the rectangle we'll draw.
|
|
||||||
* @param y The x position of the rectangle we'll draw.
|
|
||||||
* @param width The width of the rectangle we'll draw.
|
|
||||||
* @param height The height of the rectangle we'll draw.
|
|
||||||
* @param topBorder The height of the top border.
|
|
||||||
* @param bottomBorder The height of the bottom border.
|
|
||||||
* @param textureHeight The height of the whole texture.
|
|
||||||
*/
|
|
||||||
public void blitVerticalSliced(TextureAtlasSprite sprite, int x, int y, int width, int height, int topBorder, int bottomBorder, int textureHeight) {
|
|
||||||
// TODO(1.20.2)/TODO(1.21.0): Drive this from mcmeta files, like vanilla does.
|
|
||||||
if (width < topBorder + bottomBorder) throw new IllegalArgumentException("height is less than two borders");
|
|
||||||
|
|
||||||
var centerStart = SpriteRenderer.v(sprite, topBorder, textureHeight);
|
|
||||||
var centerEnd = SpriteRenderer.v(sprite, textureHeight - bottomBorder, textureHeight);
|
|
||||||
|
|
||||||
blit(x, y, width, topBorder, sprite.getU0(), sprite.getV0(), sprite.getU1(), centerStart);
|
|
||||||
blit(x, y + topBorder, width, height - topBorder - bottomBorder, sprite.getU0(), centerStart, sprite.getU1(), centerEnd);
|
|
||||||
blit(x, y + height - bottomBorder, width, bottomBorder, sprite.getU0(), centerEnd, sprite.getU1(), sprite.getV1());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The low-level blit function, used to render a portion of the sprite sheet. Unlike other functions, this takes uvs rather than a single sprite.
|
|
||||||
*
|
|
||||||
* @param x The x position of the rectangle we'll draw.
|
|
||||||
* @param y The x position of the rectangle we'll draw.
|
|
||||||
* @param width The width of the rectangle we'll draw.
|
|
||||||
* @param height The height of the rectangle we'll draw.
|
|
||||||
* @param u0 The first U coordinate.
|
|
||||||
* @param v0 The first V coordinate.
|
|
||||||
* @param u1 The second U coordinate.
|
|
||||||
* @param v1 The second V coordinate.
|
|
||||||
*/
|
|
||||||
public void blit(
|
|
||||||
int x, int y, int width, int height, float u0, float v0, float u1, float v1) {
|
|
||||||
builder.addVertex(transform, x, y + height, z).setColor(r, g, b, 255).setUv(u0, v1).setLight(light);
|
|
||||||
builder.addVertex(transform, x + width, y + height, z).setColor(r, g, b, 255).setUv(u1, v1).setLight(light);
|
|
||||||
builder.addVertex(transform, x + width, y, z).setColor(r, g, b, 255).setUv(u1, v0).setLight(light);
|
|
||||||
builder.addVertex(transform, x, y, z).setColor(r, g, b, 255).setUv(u0, v0).setLight(light);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static float u(TextureAtlasSprite sprite, int x, int width) {
|
|
||||||
return sprite.getU((float) x / width);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static float v(TextureAtlasSprite sprite, int y, int height) {
|
|
||||||
return sprite.getV((float) y / height);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user