mirror of
https://github.com/SquidDev-CC/CC-Tweaked
synced 2025-10-16 14:37:39 +00:00
Compare commits
124 Commits
v1.19.4-1.
...
v1.19.4-1.
Author | SHA1 | Date | |
---|---|---|---|
![]() |
2a04fb71fd | ||
![]() |
df61389304 | ||
![]() |
b6632c9ed9 | ||
![]() |
41b6711b38 | ||
![]() |
02f0b7ec14 | ||
![]() |
a9d31cd3c6 | ||
![]() |
43744b0e85 | ||
![]() |
55510c42db | ||
![]() |
57a944fd90 | ||
![]() |
940f59b116 | ||
![]() |
dd08d1ec8e | ||
![]() |
90ed0b24e7 | ||
![]() |
0ad399a528 | ||
![]() |
1b88213eca | ||
![]() |
eef05b9854 | ||
![]() |
24d74f5c80 | ||
![]() |
c2988366d8 | ||
![]() |
ec0765ead1 | ||
![]() |
b94e34f372 | ||
![]() |
af3263dec2 | ||
![]() |
7f25c9a66b | ||
![]() |
9ca3efff3c | ||
![]() |
aaf8c248a8 | ||
![]() |
df26cd267a | ||
![]() |
8914b78816 | ||
![]() |
9ea7f45fa7 | ||
![]() |
d351bc33c6 | ||
![]() |
5d71770931 | ||
![]() |
4bbde8c50c | ||
![]() |
cc8c1f38e7 | ||
![]() |
cab9c9772a | ||
![]() |
e337a63712 | ||
![]() |
efa92b741b | ||
![]() |
a91ac6f214 | ||
![]() |
943a9406b1 | ||
![]() |
0b2bb5e7b5 | ||
![]() |
8708048b6e | ||
![]() |
d138d9c4a5 | ||
![]() |
f54cb8a432 | ||
![]() |
94f5ede75a | ||
![]() |
1977556da4 | ||
![]() |
9eabb29999 | ||
![]() |
ecf880ed82 | ||
![]() |
655d5aeca8 | ||
![]() |
34f41c4039 | ||
![]() |
f5b16261cc | ||
![]() |
7eb3b691da | ||
![]() |
910a63214e | ||
![]() |
591a7eca23 | ||
![]() |
a29a516a3f | ||
![]() |
4a5e03c11a | ||
![]() |
50d460624f | ||
![]() |
bc500df921 | ||
![]() |
4accda6b8e | ||
![]() |
54ab98473f | ||
![]() |
7ffdbb2316 | ||
![]() |
672c2cf029 | ||
![]() |
c3bdb0440e | ||
![]() |
88f0c44152 | ||
![]() |
c8523bf479 | ||
![]() |
953372b1b7 | ||
![]() |
36b9f4ec55 | ||
![]() |
ccfed0059b | ||
![]() |
7b4ba11fb4 | ||
![]() |
8ccd5a560c | ||
![]() |
0f866836a0 | ||
![]() |
df591cd7c6 | ||
![]() |
c7f3d4f45d | ||
![]() |
77ac04cb7a | ||
![]() |
201df7e987 | ||
![]() |
5722e51735 | ||
![]() |
7a291619ab | ||
![]() |
4b9b19b02d | ||
![]() |
ec52f3e0e8 | ||
![]() |
96847bb8c2 | ||
![]() |
68ef9f717b | ||
![]() |
cba207d62d | ||
![]() |
e157978afc | ||
![]() |
ef19988c37 | ||
![]() |
c91bb5ac33 | ||
![]() |
d45f2bfe80 | ||
![]() |
8fdef542c9 | ||
![]() |
6e7cbf25e8 | ||
![]() |
b691430889 | ||
![]() |
f0abb83f6e | ||
![]() |
4d064d1552 | ||
![]() |
12ca8583f4 | ||
![]() |
9cca908bff | ||
![]() |
5082b1677c | ||
![]() |
590ef86c93 | ||
![]() |
b09200a270 | ||
![]() |
d3b39be9a8 | ||
![]() |
e153839a98 | ||
![]() |
842eb67e17 | ||
![]() |
ebed357f05 | ||
![]() |
9d16fda266 | ||
![]() |
2ae14b4c08 | ||
![]() |
55ed0dc3ef | ||
![]() |
3112f455ae | ||
![]() |
e0216f8792 | ||
![]() |
03c794cd53 | ||
![]() |
b91bca8830 | ||
![]() |
90177c9f8b | ||
![]() |
952674a161 | ||
![]() |
25ab7d0dcb | ||
![]() |
98ee37719d | ||
![]() |
e24b5f0888 | ||
![]() |
090d17c528 | ||
![]() |
470f2b098d | ||
![]() |
722b870f98 | ||
![]() |
3bf29695fb | ||
![]() |
99e8cd29a1 | ||
![]() |
0e48ac1dfe | ||
![]() |
232c051526 | ||
![]() |
34928257c6 | ||
![]() |
5eb4a9033b | ||
![]() |
54b7366b2d | ||
![]() |
d6751b8e3d | ||
![]() |
5d7cbc8c64 | ||
![]() |
e8fd460935 | ||
![]() |
a0efe637d6 | ||
![]() |
f099b21f6f | ||
![]() |
3920ff08ab | ||
![]() |
ccd7f6326a |
1
.gitattributes
vendored
1
.gitattributes
vendored
@@ -11,6 +11,7 @@ projects/common/src/testMod/resources/data/cctest/structures/* linguist-generate
|
||||
*.gradle eol=lf diff=java
|
||||
*.java eol=lf diff=java
|
||||
*.kt eol=lf diff=java
|
||||
*.kts eol=lf diff=java
|
||||
*.lua eol=lf
|
||||
*.md eol=lf diff=markdown
|
||||
*.txt eol=lf
|
||||
|
1
.github/ISSUE_TEMPLATE/bug_report.yaml
vendored
1
.github/ISSUE_TEMPLATE/bug_report.yaml
vendored
@@ -11,6 +11,7 @@ body:
|
||||
- 1.16.x
|
||||
- 1.18.x
|
||||
- 1.19.x
|
||||
- 1.20.x
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
|
44
.github/workflows/main-ci.yml
vendored
44
.github/workflows/main-ci.yml
vendored
@@ -8,16 +8,16 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Clone repository
|
||||
- name: 📥 Clone repository
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Set up Java
|
||||
- name: 📥 Set up Java
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
java-version: 17
|
||||
distribution: 'temurin'
|
||||
|
||||
- name: Setup Gradle
|
||||
- name: 📥 Setup Gradle
|
||||
uses: gradle/gradle-build-action@v2
|
||||
with:
|
||||
cache-read-only: ${{ !startsWith(github.ref, 'refs/heads/mc-') }}
|
||||
@@ -27,39 +27,45 @@ jobs:
|
||||
mkdir -p ~/.gradle
|
||||
echo "org.gradle.daemon=false" >> ~/.gradle/gradle.properties
|
||||
|
||||
- name: Build with Gradle
|
||||
run: |
|
||||
./gradlew assemble || ./gradlew assemble
|
||||
./gradlew downloadAssets || ./gradlew downloadAssets
|
||||
./gradlew build
|
||||
- name: ⚒️ Build
|
||||
run: ./gradlew assemble || ./gradlew assemble
|
||||
|
||||
- name: Run client tests
|
||||
- name: 💡 Lint
|
||||
uses: pre-commit/action@v3.0.0
|
||||
|
||||
- name: 🧪 Run tests
|
||||
run: ./gradlew test validateMixinNames checkChangelog
|
||||
|
||||
- name: 📥 Download assets for game tests
|
||||
run: ./gradlew downloadAssets || ./gradlew downloadAssets
|
||||
|
||||
- name: 🧪 Run integration tests
|
||||
run: ./gradlew runGametest
|
||||
|
||||
- name: 🧪 Run client tests
|
||||
run: ./gradlew runGametestClient # Not checkClient, as no point running rendering tests.
|
||||
# These are a little flaky on GH actions: its useful to run them, but don't break the build.
|
||||
continue-on-error: true
|
||||
|
||||
- name: Prepare Jars
|
||||
- name: 🧪 Parse test reports
|
||||
run: ./tools/parse-reports.py
|
||||
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
|
||||
- name: 📤 Upload Jar
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: CC-Tweaked
|
||||
path: ./jars
|
||||
|
||||
- name: Upload coverage
|
||||
- name: 📤 Upload coverage
|
||||
uses: codecov/codecov-action@v3
|
||||
|
||||
- name: Parse test reports
|
||||
run: ./tools/parse-reports.py
|
||||
if: ${{ failure() }}
|
||||
|
||||
- name: Run linters
|
||||
uses: pre-commit/action@v3.0.0
|
||||
|
||||
build-core:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
|
1
.github/workflows/make-doc.yml
vendored
1
.github/workflows/make-doc.yml
vendored
@@ -4,6 +4,7 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- mc-1.19.x
|
||||
- mc-1.20.x
|
||||
|
||||
jobs:
|
||||
make_doc:
|
||||
|
12
.reuse/dep5
12
.reuse/dep5
@@ -6,6 +6,7 @@ Upstream-Contact: Jonathan Coates <git@squiddev.cc>
|
||||
Files:
|
||||
projects/common/src/main/resources/assets/computercraft/sounds.json
|
||||
projects/common/src/main/resources/assets/computercraft/sounds/empty.ogg
|
||||
projects/common/src/testMod/resources/data/cctest/computercraft/turtle_upgrades/*
|
||||
projects/common/src/testMod/resources/data/cctest/structures/*
|
||||
projects/fabric/src/generated/*
|
||||
projects/forge/src/generated/*
|
||||
@@ -16,6 +17,7 @@ Copyright: The CC: Tweaked Developers
|
||||
License: CC0-1.0
|
||||
|
||||
Files:
|
||||
doc/images/*
|
||||
package.json
|
||||
package-lock.json
|
||||
projects/common/src/client/resources/computercraft-client.mixins.json
|
||||
@@ -24,6 +26,10 @@ Files:
|
||||
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/pack.mcmeta
|
||||
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/turtle/.ignoreme
|
||||
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
|
||||
@@ -41,18 +47,14 @@ Copyright: The CC: Tweaked Developers
|
||||
License: MPL-2.0
|
||||
|
||||
Files:
|
||||
doc/images/*
|
||||
doc/logo.png
|
||||
doc/logo-darkmode.png
|
||||
projects/common/src/main/resources/assets/computercraft/models/*
|
||||
projects/common/src/main/resources/assets/computercraft/textures/*
|
||||
projects/common/src/main/resources/pack.mcmeta
|
||||
projects/common/src/main/resources/pack.png
|
||||
projects/core/src/main/resources/data/computercraft/lua/rom/autorun/.ignoreme
|
||||
projects/core/src/main/resources/data/computercraft/lua/rom/help/*
|
||||
projects/core/src/main/resources/data/computercraft/lua/rom/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/turtle/.ignoreme
|
||||
projects/core/src/main/resources/data/computercraft/lua/rom/motd.txt
|
||||
projects/core/src/main/resources/data/computercraft/lua/rom/programs/fun/advanced/levels/*
|
||||
projects/web/src/export/items/computercraft/*
|
||||
Comment: Bulk-license original assets as CCPL.
|
||||
|
@@ -6,7 +6,7 @@ SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
# Contributing to CC: Tweaked
|
||||
As with many open source projects, CC: Tweaked thrives on contributions from other people! This document (hopefully)
|
||||
provides an introduction as to how to get started in helping out.
|
||||
provides an introduction as to how to get started with helping out.
|
||||
|
||||
If you've any other questions, [just ask the community][community] or [open an issue][new-issue].
|
||||
|
||||
@@ -28,7 +28,7 @@ automatically with GitHub, so please don't submit PRs adding/changing translatio
|
||||
## Setting up a development environment
|
||||
In order to develop CC: Tweaked, you'll need to download the source code and then run it.
|
||||
|
||||
- Make sure you've got the following software instealled:
|
||||
- Make sure you've got the following software installed:
|
||||
- Java Development Kit (JDK) installed. This can be downloaded from [Adoptium].
|
||||
- [Git](https://git-scm.com/).
|
||||
- If you want to work on documentation, [NodeJS][node].
|
||||
@@ -51,10 +51,10 @@ If you want to run CC:T in a normal Minecraft instance, run `./gradlew assemble`
|
||||
## Developing CC: Tweaked
|
||||
Before making any major changes to CC: Tweaked, I'd recommend you have a read of the [the architecture
|
||||
document][architecture] first. While it's not a comprehensive document, it gives a good hint of where you should start
|
||||
looking to make your changes. As always, if you're not sure [do ask the community][community]!
|
||||
looking to make your changes. As always, if you're not sure, [do ask the community][community]!
|
||||
|
||||
### Testing
|
||||
When making larger changes, it's may be useful to write a test to make sure your code works as expected.
|
||||
When making larger changes, it may be useful to write a test to make sure your code works as expected.
|
||||
|
||||
CC: Tweaked has several test suites, each designed to test something different:
|
||||
|
||||
@@ -91,11 +91,11 @@ file.
|
||||
|
||||
Documentation is built using [illuaminate] which, while not currently documented (somewhat ironic), is largely the same
|
||||
as [ldoc][ldoc]. Documentation comments are written in Markdown, though note that we do not support many GitHub-specific
|
||||
markdown features - if you can, do check what the documentation looks like locally!
|
||||
markdown features. If you can, do check what the documentation looks like locally!
|
||||
|
||||
When writing long-form documentation (such as the guides in [doc/guides](doc/guides)), I find it useful to tell a
|
||||
narrative. Think of what you want the user to learn or achieve, then start introducing a simple concept and then talk
|
||||
about how you can build on that, until you've covered everything!
|
||||
narrative. Think of what you want the user to learn or achieve, then start introducing a simple concept, and then talk
|
||||
about how you can build on that until you've covered everything!
|
||||
|
||||
[new-issue]: https://github.com/cc-tweaked/CC-Tweaked/issues/new/choose "Create a new issue"
|
||||
[community]: README.md#community "Get in touch with the community."
|
||||
|
36
README.md
36
README.md
@@ -4,14 +4,21 @@ SPDX-FileCopyrightText: 2017 The CC: Tweaked Developers
|
||||
SPDX-License-Identifier: MPL-2.0
|
||||
-->
|
||||
|
||||
# 
|
||||
[](https://github.com/cc-tweaked/CC-Tweaked/actions "Current build status") [][CurseForge]
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="./doc/logo-darkmode.png">
|
||||
<source media="(prefers-color-scheme: light)" srcset="./doc/logo.png">
|
||||
<img alt="CC: Tweaked" src="./doc/logo.png">
|
||||
</picture>
|
||||
|
||||
[](https://github.com/cc-tweaked/CC-Tweaked/actions "Current build status")
|
||||
[][CurseForge]
|
||||
[][Modrinth]
|
||||
|
||||
CC: Tweaked is a mod for Minecraft which adds programmable computers, turtles and more to the game. A fork of the
|
||||
much-beloved [ComputerCraft], it continues its legacy with better performance, stability, and a wealth of new features.
|
||||
much-beloved [ComputerCraft], it continues its legacy with improved performance and stability, along with a wealth of
|
||||
new features.
|
||||
|
||||
CC: Tweaked can be installed from [CurseForge] or [Modrinth]. It requires the [Minecraft Forge][forge] mod loader, but
|
||||
[versions are available for Fabric][ccrestitched].
|
||||
CC: Tweaked can be installed from [CurseForge] or [Modrinth]. It runs on both [Minecraft Forge] and [Fabric].
|
||||
|
||||
## Contributing
|
||||
Any contribution is welcome, be that using the mod, reporting bugs or contributing code. If you want to get started
|
||||
@@ -42,7 +49,7 @@ repositories {
|
||||
|
||||
dependencies {
|
||||
// Vanilla (i.e. for multi-loader systems)
|
||||
compileOnly("cc.tweaked:cc-tweaked-$mcVersion-common-api")
|
||||
compileOnly("cc.tweaked:cc-tweaked-$mcVersion-common-api:$cctVersion")
|
||||
|
||||
// Forge Gradle
|
||||
compileOnly("cc.tweaked:cc-tweaked-$mcVersion-core-api:$cctVersion")
|
||||
@@ -55,6 +62,19 @@ dependencies {
|
||||
}
|
||||
```
|
||||
|
||||
When using ForgeGradle, you may also need to add the following:
|
||||
|
||||
```groovy
|
||||
minecraft {
|
||||
runs {
|
||||
configureEach {
|
||||
property 'mixin.env.remapRefMap', 'true'
|
||||
property 'mixin.env.refMapRemappingFile', "${buildDir}/createSrgToMcp/output.srg"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
You should also be careful to only use classes within the `dan200.computercraft.api` package. Non-API classes are
|
||||
subject to change at any point. If you depend on functionality outside the API, file an issue, and we can look into
|
||||
exposing more features.
|
||||
@@ -65,8 +85,8 @@ the generated documentation [can be browsed online](https://tweaked.cc/javadoc/)
|
||||
[computercraft]: https://github.com/dan200/ComputerCraft "ComputerCraft on GitHub"
|
||||
[curseforge]: https://minecraft.curseforge.com/projects/cc-tweaked "Download CC: Tweaked from CurseForge"
|
||||
[modrinth]: https://modrinth.com/mod/gu7yAYhd "Download CC: Tweaked from Modrinth"
|
||||
[forge]: https://files.minecraftforge.net/ "Download Minecraft Forge."
|
||||
[ccrestitched]: https://www.curseforge.com/minecraft/mc-mods/cc-restitched "Download CC: Restitched from CurseForge"
|
||||
[Minecraft Forge]: https://files.minecraftforge.net/ "Download Minecraft Forge."
|
||||
[Fabric]: https://fabricmc.net/use/installer/ "Download Fabric."
|
||||
[forum]: https://forums.computercraft.cc/
|
||||
[GitHub Discussions]: https://github.com/cc-tweaked/CC-Tweaked/discussions
|
||||
[IRC]: https://webchat.esper.net/?channels=computercraft "#computercraft on EsperNet"
|
||||
|
@@ -10,8 +10,9 @@ import cc.tweaked.gradle.IdeaRunConfigurations
|
||||
import cc.tweaked.gradle.MinecraftConfigurations
|
||||
|
||||
plugins {
|
||||
id("cc-tweaked.java-convention")
|
||||
id("net.minecraftforge.gradle")
|
||||
// We must apply java-convention after Forge, as we need the fg extension to be present.
|
||||
id("cc-tweaked.java-convention")
|
||||
id("org.parchmentmc.librarian.forgegradle")
|
||||
}
|
||||
|
||||
|
@@ -37,22 +37,37 @@ java {
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
maven("https://squiddev.cc/maven") {
|
||||
|
||||
val mainMaven = maven("https://squiddev.cc/maven") {
|
||||
name = "SquidDev"
|
||||
content {
|
||||
includeGroup("org.squiddev")
|
||||
includeGroup("cc.tweaked")
|
||||
// Things we mirror
|
||||
includeGroup("dev.architectury")
|
||||
includeGroup("maven.modrinth")
|
||||
includeGroup("me.shedaniel")
|
||||
includeGroup("me.shedaniel.cloth")
|
||||
includeGroup("mezz.jei")
|
||||
includeModule("com.terraformersmc", "modmenu")
|
||||
// Until https://github.com/SpongePowered/Mixin/pull/593 is merged
|
||||
includeModule("org.spongepowered", "mixin")
|
||||
}
|
||||
}
|
||||
|
||||
exclusiveContent {
|
||||
forRepositories(mainMaven)
|
||||
|
||||
// Include the ForgeGradle repository if present. This requires that ForgeGradle is already present, which we
|
||||
// enforce in our Forge overlay.
|
||||
val fg =
|
||||
project.extensions.findByType(net.minecraftforge.gradle.userdev.DependencyManagementExtension::class.java)
|
||||
if (fg != null) forRepositories(fg.repository)
|
||||
|
||||
filter {
|
||||
includeGroup("cc.tweaked")
|
||||
includeModule("org.squiddev", "Cobalt")
|
||||
// Things we mirror
|
||||
includeGroup("dev.architectury")
|
||||
includeGroup("dev.emi")
|
||||
includeGroup("maven.modrinth")
|
||||
includeGroup("me.shedaniel.cloth")
|
||||
includeGroup("me.shedaniel")
|
||||
includeGroup("mezz.jei")
|
||||
includeModule("com.terraformersmc", "modmenu")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
@@ -104,6 +119,7 @@ tasks.withType(JavaCompile::class.java).configureEach {
|
||||
|
||||
tasks.processResources {
|
||||
exclude("**/*.license")
|
||||
exclude(".cache")
|
||||
}
|
||||
|
||||
tasks.withType(AbstractArchiveTask::class.java).configureEach {
|
||||
|
@@ -33,7 +33,6 @@ val publishCurseForge by tasks.registering(TaskPublishCurseForge::class) {
|
||||
enabled = apiToken != ""
|
||||
|
||||
val mainFile = upload("282001", modPublishing.output.get().archiveFile)
|
||||
dependsOn(modPublishing.output) // See https://github.com/Darkhax/CurseForgeGradle/pull/7.
|
||||
mainFile.changelog =
|
||||
"Release notes can be found on the [GitHub repository](https://github.com/cc-tweaked/CC-Tweaked/releases/tag/v$mcVersion-$modVersion)."
|
||||
mainFile.changelogType = "markdown"
|
||||
@@ -46,14 +45,14 @@ tasks.publish { dependsOn(publishCurseForge) }
|
||||
modrinth {
|
||||
token.set(findProperty("modrinthApiKey") as String? ?: "")
|
||||
projectId.set("gu7yAYhd")
|
||||
versionNumber.set("$mcVersion-$modVersion")
|
||||
versionNumber.set(modVersion)
|
||||
versionName.set(modVersion)
|
||||
versionType.set(if (isUnstable) "alpha" else "release")
|
||||
uploadFile.setProvider(modPublishing.output)
|
||||
gameVersions.add(mcVersion)
|
||||
changelog.set("Release notes can be found on the [GitHub repository](https://github.com/cc-tweaked/CC-Tweaked/releases/tag/v$mcVersion-$modVersion).")
|
||||
|
||||
syncBodyFrom.set(provider { file("doc/mod-page.md").readText() })
|
||||
syncBodyFrom.set(provider { rootProject.file("doc/mod-page.md").readText() })
|
||||
}
|
||||
|
||||
tasks.publish { dependsOn(tasks.modrinth) }
|
||||
|
@@ -2,8 +2,6 @@
|
||||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
import org.gradle.kotlin.dsl.`maven-publish`
|
||||
|
||||
plugins {
|
||||
`java-library`
|
||||
`maven-publish`
|
||||
|
@@ -0,0 +1,26 @@
|
||||
// SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers
|
||||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package cc.tweaked.gradle
|
||||
|
||||
import net.minecraftforge.gradle.common.util.RunConfig
|
||||
import net.minecraftforge.gradle.common.util.runs.setRunConfigInternal
|
||||
import org.gradle.api.plugins.JavaPluginExtension
|
||||
import org.gradle.api.tasks.JavaExec
|
||||
import org.gradle.jvm.toolchain.JavaToolchainService
|
||||
import java.nio.file.Files
|
||||
|
||||
/**
|
||||
* Set [JavaExec] task to run a given [RunConfig].
|
||||
*/
|
||||
fun JavaExec.setRunConfig(config: RunConfig) {
|
||||
dependsOn("prepareRuns")
|
||||
setRunConfigInternal(project, this, config)
|
||||
doFirst("Create working directory") { Files.createDirectories(workingDir.toPath()) }
|
||||
|
||||
javaLauncher.set(
|
||||
project.extensions.getByType(JavaToolchainService::class.java)
|
||||
.launcherFor(project.extensions.getByType(JavaPluginExtension::class.java).toolchain),
|
||||
)
|
||||
}
|
@@ -60,7 +60,7 @@ class IlluaminatePlugin : Plugin<Project> {
|
||||
|
||||
/** Define a dependency for illuaminate from a version number and the current operating system. */
|
||||
private fun illuaminateArtifact(project: Project, version: String): Dependency {
|
||||
val osName = System.getProperty("os.name").toLowerCase()
|
||||
val osName = System.getProperty("os.name").lowercase()
|
||||
val (os, suffix) = when {
|
||||
osName.contains("windows") -> Pair("windows", ".exe")
|
||||
osName.contains("mac os") || osName.contains("darwin") -> Pair("macos", "")
|
||||
@@ -68,7 +68,7 @@ class IlluaminatePlugin : Plugin<Project> {
|
||||
else -> error("Unsupported OS $osName for illuaminate")
|
||||
}
|
||||
|
||||
val osArch = System.getProperty("os.arch").toLowerCase()
|
||||
val osArch = System.getProperty("os.arch").lowercase()
|
||||
val arch = when {
|
||||
// On macOS the x86_64 binary will work for both ARM and Intel Macs through Rosetta.
|
||||
os == "macos" -> "x86_64"
|
||||
|
@@ -4,6 +4,7 @@
|
||||
|
||||
package cc.tweaked.gradle
|
||||
|
||||
import net.minecraftforge.gradle.common.util.RunConfig
|
||||
import org.gradle.api.GradleException
|
||||
import org.gradle.api.file.FileSystemOperations
|
||||
import org.gradle.api.invocation.Gradle
|
||||
@@ -32,11 +33,14 @@ abstract class ClientJavaExec : JavaExec() {
|
||||
usesService(clientRunner)
|
||||
}
|
||||
|
||||
@get:Input
|
||||
val renderdoc get() = project.hasProperty("renderdoc")
|
||||
|
||||
/**
|
||||
* When [false], tests will not be run automatically, allowing the user to debug rendering.
|
||||
*/
|
||||
@get:Input
|
||||
val clientDebug get() = project.hasProperty("clientDebug")
|
||||
val clientDebug get() = renderdoc || project.hasProperty("clientDebug")
|
||||
|
||||
/**
|
||||
* When [false], tests will not run under a framebuffer.
|
||||
@@ -50,6 +54,25 @@ abstract class ClientJavaExec : JavaExec() {
|
||||
@get:OutputFile
|
||||
val testResults = project.layout.buildDirectory.file("test-results/$name.xml")
|
||||
|
||||
private fun setTestProperties() {
|
||||
if (!clientDebug) systemProperty("cctest.client", "")
|
||||
if (renderdoc) environment("LD_PRELOAD", "/usr/lib/librenderdoc.so")
|
||||
systemProperty("cctest.gametest-report", testResults.get().asFile.absoluteFile)
|
||||
workingDir(project.buildDir.resolve("gametest").resolve(name))
|
||||
}
|
||||
|
||||
init {
|
||||
setTestProperties()
|
||||
}
|
||||
|
||||
/**
|
||||
* Set this task to run a given [RunConfig].
|
||||
*/
|
||||
fun setRunConfig(config: RunConfig) {
|
||||
(this as JavaExec).setRunConfig(config)
|
||||
setTestProperties() // setRunConfig may clobber some properties, ensure everything is set.
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy configuration from a task with the given name.
|
||||
*/
|
||||
@@ -61,10 +84,7 @@ abstract class ClientJavaExec : JavaExec() {
|
||||
fun copyFrom(task: JavaExec) {
|
||||
for (dep in task.dependsOn) dependsOn(dep)
|
||||
task.copyToFull(this)
|
||||
|
||||
if (!clientDebug) systemProperty("cctest.client", "")
|
||||
systemProperty("cctest.gametest-report", testResults.get().asFile.absoluteFile)
|
||||
workingDir(project.buildDir.resolve("gametest").resolve(name))
|
||||
setTestProperties() // copyToFull may clobber some properties, ensure everything is set.
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -0,0 +1,51 @@
|
||||
// SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers
|
||||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package net.minecraftforge.gradle.common.util.runs
|
||||
|
||||
import net.minecraftforge.gradle.common.util.RunConfig
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.process.CommandLineArgumentProvider
|
||||
import org.gradle.process.JavaExecSpec
|
||||
import java.io.File
|
||||
import java.util.function.Supplier
|
||||
import java.util.stream.Collectors
|
||||
import java.util.stream.Stream
|
||||
|
||||
/**
|
||||
* Set up a [JavaExecSpec] to execute a [RunConfig].
|
||||
*
|
||||
* [MinecraftRunTask] sets up all its properties when the task is executed, rather than when configured. As such, it's
|
||||
* not possible to use [cc.tweaked.gradle.copyToFull] like we do for Fabric. Instead, we set up the task manually.
|
||||
*
|
||||
* Unfortunately most of the functionality we need is package-private, and so we have to put our code into the package.
|
||||
*/
|
||||
internal fun setRunConfigInternal(project: Project, spec: JavaExecSpec, config: RunConfig) {
|
||||
spec.workingDir = File(config.workingDirectory)
|
||||
|
||||
spec.mainClass.set(config.main)
|
||||
for (source in config.allSources) spec.classpath(source.runtimeClasspath)
|
||||
|
||||
val originalTask = project.tasks.named(config.taskName, MinecraftRunTask::class.java)
|
||||
|
||||
// Add argument and JVM argument via providers, to be as lazy as possible with fetching artifacts.
|
||||
val lazyTokens = RunConfigGenerator.configureTokensLazy(
|
||||
project, config, RunConfigGenerator.mapModClassesToGradle(project, config),
|
||||
originalTask.get().minecraftArtifacts,
|
||||
originalTask.get().runtimeClasspathArtifacts,
|
||||
)
|
||||
spec.argumentProviders.add(
|
||||
CommandLineArgumentProvider {
|
||||
RunConfigGenerator.getArgsStream(config, lazyTokens, false).toList()
|
||||
},
|
||||
)
|
||||
spec.jvmArgumentProviders.add(
|
||||
CommandLineArgumentProvider {
|
||||
(if (config.isClient) config.jvmArgs + originalTask.get().additionalClientArgs.get() else config.jvmArgs).map { config.replace(lazyTokens, it) } +
|
||||
config.properties.map { (k, v) -> "-D${k}=${config.replace(lazyTokens, v)}" }
|
||||
},
|
||||
)
|
||||
|
||||
for ((key, value) in config.environment) spec.environment(key, config.replace(lazyTokens, value))
|
||||
}
|
@@ -13,6 +13,15 @@ The @{websocket_closed} event is fired when an open WebSocket connection is clos
|
||||
## Return Values
|
||||
1. @{string}: The event name.
|
||||
2. @{string}: The URL of the WebSocket that was closed.
|
||||
3. <span class="type">@{string}|@{nil}</span>: The [server-provided reason][close_reason]
|
||||
the websocket was closed. This will be @{nil} if the connection was closed
|
||||
abnormally.
|
||||
4. <span class="type">@{number}|@{nil}</span>: The [connection close code][close_code],
|
||||
indicating why the socket was closed. This will be @{nil} if the connection
|
||||
was closed abnormally.
|
||||
|
||||
[close_reason]: https://www.rfc-editor.org/rfc/rfc6455.html#section-7.1.6 "The WebSocket Connection Close Reason, RFC 6455"
|
||||
[close_code]: https://www.rfc-editor.org/rfc/rfc6455.html#section-7.1.5 "The WebSocket Connection Close Code, RFC 6455"
|
||||
|
||||
## Example
|
||||
Prints a message when a WebSocket is closed (this may take a minute):
|
||||
|
@@ -6,10 +6,10 @@ SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
# 
|
||||
CC: Tweaked is a mod for Minecraft which adds programmable computers, turtles and more to the game. A fork of the
|
||||
much-beloved [ComputerCraft], it continues its legacy with better performance, stability, and a wealth of new features.
|
||||
much-beloved [ComputerCraft], it continues its legacy with improved performance and stability, along with a wealth of
|
||||
new features.
|
||||
|
||||
CC: Tweaked can be installed from [CurseForge] or [Modrinth]. It requires the [Minecraft Forge][forge] mod loader, but
|
||||
[versions are available for Fabric][ccrestitched].
|
||||
CC: Tweaked can be installed from [CurseForge] or [Modrinth]. It runs on both [Minecraft Forge] and [Fabric].
|
||||
|
||||
## Features
|
||||
Controlled using the [Lua programming language][lua], CC: Tweaked's computers provides all the tools you need to start
|
||||
@@ -54,7 +54,8 @@ CC: Tweaked lives on [GitHub]. If you've got any ideas, feedback or bugs please
|
||||
[curseforge]: https://minecraft.curseforge.com/projects/cc-tweaked "Download CC: Tweaked from CurseForge"
|
||||
[modrinth]: https://modrinth.com/mod/gu7yAYhd "Download CC: Tweaked from Modrinth"
|
||||
[forge]: https://files.minecraftforge.net/ "Download Minecraft Forge."
|
||||
[ccrestitched]: https://www.curseforge.com/minecraft/mc-mods/cc-restitched "Download CC: Restitched from CurseForge"
|
||||
[Minecraft Forge]: https://files.minecraftforge.net/ "Download Minecraft Forge."
|
||||
[Fabric]: https://fabricmc.net/use/installer/ "Download Fabric."
|
||||
[lua]: https://www.lua.org/ "Lua's main website"
|
||||
[GitHub Discussions]: https://github.com/cc-tweaked/CC-Tweaked/discussions
|
||||
[IRC]: https://webchat.esper.net/?channels=computercraft "#computercraft on EsperNet"
|
||||
|
BIN
doc/logo-darkmode.png
Normal file
BIN
doc/logo-darkmode.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.3 KiB |
@@ -5,7 +5,7 @@ SPDX-License-Identifier: MPL-2.0
|
||||
-->
|
||||
|
||||
# 
|
||||
CC: Tweaked is a mod for Minecraft which adds programmable computers, turtles and more to the game. A fork of the much-beloved [ComputerCraft], it continues its legacy with better performance, stability, and a wealth of new features.
|
||||
CC: Tweaked is a mod for Minecraft which adds programmable computers, turtles and more to the game. A fork of the much-beloved [ComputerCraft], it continues its legacy with improved performance and stability, along with a wealth of new features.
|
||||
|
||||
## Testimonials
|
||||
|
||||
@@ -22,13 +22,13 @@ CC: Tweaked is a mod for Minecraft which adds programmable computers, turtles an
|
||||
Controlled using the [Lua programming language][lua], CC: Tweaked's computers provides all the tools you need to start
|
||||
writing code and automating your Minecraft world.
|
||||
|
||||

|
||||

|
||||
|
||||
While computers are incredibly powerful, they're rather limited by their inability to move about. *Turtles* are the
|
||||
solution here. They can move about the world, placing and breaking blocks, swinging a sword to protect you from zombies,
|
||||
or whatever else you program them to!
|
||||
|
||||

|
||||

|
||||
|
||||
Not all problems can be solved with a pickaxe though, and so CC: Tweaked also provides a bunch of additional peripherals
|
||||
for your computers. You can play a tune with speakers, display text or images on a monitor, connect all your
|
||||
@@ -37,7 +37,7 @@ computers together with modems, and much more.
|
||||
Computers can now also interact with inventories such as chests, allowing you to build complex inventory and item
|
||||
management systems.
|
||||
|
||||

|
||||

|
||||
|
||||
## Getting Started
|
||||
While ComputerCraft is lovely for both experienced programmers and for people who have never coded before, it can be a
|
||||
@@ -58,8 +58,6 @@ CC: Tweaked lives on [GitHub]. If you've got any ideas, feedback or bugs please
|
||||
[github]: https://github.com/cc-tweaked/CC-Tweaked/ "CC: Tweaked on GitHub"
|
||||
[bug]: https://github.com/cc-tweaked/CC-Tweaked/issues/new/choose
|
||||
[computercraft]: https://github.com/dan200/ComputerCraft "ComputerCraft on GitHub"
|
||||
[forge]: https://files.minecraftforge.net/ "Download Minecraft Forge."
|
||||
[ccrestitched]: https://modrinth.com/mod/cc-restitched "Download CC: Restitched from Modrinth"
|
||||
[lua]: https://www.lua.org/ "Lua's main website"
|
||||
[GitHub Discussions]: https://github.com/cc-tweaked/CC-Tweaked/discussions
|
||||
[IRC]: http://webchat.esper.net/?channels=computercraft "#computercraft on EsperNet"
|
||||
|
@@ -10,7 +10,7 @@ kotlin.jvm.target.validation.mode=error
|
||||
|
||||
# Mod properties
|
||||
isUnstable=false
|
||||
modVersion=1.104.0
|
||||
modVersion=1.107.0
|
||||
|
||||
# Minecraft properties: We want to configure this here so we can read it in settings.gradle
|
||||
mcVersion=1.19.4
|
||||
|
@@ -7,20 +7,20 @@
|
||||
# Minecraft
|
||||
# MC version is specified in gradle.properties, as we need that in settings.gradle.
|
||||
# Remember to update corresponding versions in fabric.mod.json/mods.toml
|
||||
fabric-api = "0.75.3+1.19.4"
|
||||
fabric-loader = "0.14.17"
|
||||
fabric-api = "0.86.1+1.19.4"
|
||||
fabric-loader = "0.14.21"
|
||||
forge = "45.0.42"
|
||||
forgeSpi = "6.0.0"
|
||||
mixin = "0.8.5"
|
||||
parchment = "2023.03.12"
|
||||
parchmentMc = "1.19.3"
|
||||
parchment = "2023.06.26"
|
||||
parchmentMc = "1.19.4"
|
||||
|
||||
# Normal dependencies
|
||||
asm = "9.3"
|
||||
autoService = "1.0.1"
|
||||
checkerFramework = "3.32.0"
|
||||
cobalt = "0.7.0"
|
||||
cobalt-next = "0.7.1" # Not a real version, used to constrain the version we accept.
|
||||
cobalt = "0.7.1"
|
||||
cobalt-next = "0.7.2" # Not a real version, used to constrain the version we accept.
|
||||
fastutil = "8.5.9"
|
||||
guava = "31.1-jre"
|
||||
jetbrainsAnnotations = "24.0.1"
|
||||
@@ -33,8 +33,9 @@ nightConfig = "3.6.5"
|
||||
slf4j = "1.7.36"
|
||||
|
||||
# Minecraft mods
|
||||
emi = "1.0.8+1.19.4"
|
||||
iris = "1.5.2+1.19.4"
|
||||
jei = "11.3.0.262"
|
||||
jei = "13.1.0.11"
|
||||
modmenu = "6.1.0-rc.1"
|
||||
oculus = "1.2.5"
|
||||
rei = "10.0.578"
|
||||
@@ -48,21 +49,21 @@ jqwik = "1.7.2"
|
||||
junit = "5.9.2"
|
||||
|
||||
# Build tools
|
||||
cctJavadoc = "1.6.1"
|
||||
cctJavadoc = "1.7.0"
|
||||
checkstyle = "10.3.4"
|
||||
curseForgeGradle = "1.0.11"
|
||||
curseForgeGradle = "1.0.14"
|
||||
errorProne-core = "2.18.0"
|
||||
errorProne-plugin = "3.0.1"
|
||||
fabric-loom = "1.1.10"
|
||||
forgeGradle = "5.1.+"
|
||||
fabric-loom = "1.3.7"
|
||||
forgeGradle = "6.0.8"
|
||||
githubRelease = "2.2.12"
|
||||
ideaExt = "1.1.6"
|
||||
illuaminate = "0.1.0-24-gdb28902"
|
||||
illuaminate = "0.1.0-28-ga7efd71"
|
||||
librarian = "1.+"
|
||||
minotaur = "2.+"
|
||||
mixinGradle = "0.7.+"
|
||||
nullAway = "0.9.9"
|
||||
quiltflower = "1.8.0"
|
||||
quiltflower = "1.10.0"
|
||||
spotless = "6.17.0"
|
||||
taskTree = "2.1.1"
|
||||
vanillaGradle = "0.2.1-SNAPSHOT"
|
||||
@@ -83,6 +84,8 @@ kotlin-coroutines = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core",
|
||||
kotlin-platform = { module = "org.jetbrains.kotlin:kotlin-bom", version.ref = "kotlin" }
|
||||
kotlin-stdlib = { module = "org.jetbrains.kotlin:kotlin-stdlib", version.ref = "kotlin" }
|
||||
netty-http = { module = "io.netty:netty-codec-http", version.ref = "netty" }
|
||||
netty-socks = { module = "io.netty:netty-codec-socks", version.ref = "netty" }
|
||||
netty-proxy = { module = "io.netty:netty-handler-proxy", version.ref = "netty" }
|
||||
nightConfig-core = { module = "com.electronwill.night-config:core", version.ref = "nightConfig" }
|
||||
nightConfig-toml = { module = "com.electronwill.night-config:toml", version.ref = "nightConfig" }
|
||||
slf4j = { module = "org.slf4j:slf4j-api", version.ref = "slf4j" }
|
||||
@@ -90,10 +93,11 @@ slf4j = { module = "org.slf4j:slf4j-api", version.ref = "slf4j" }
|
||||
# Minecraft mods
|
||||
fabric-api = { module = "net.fabricmc.fabric-api:fabric-api", version.ref = "fabric-api" }
|
||||
fabric-loader = { module = "net.fabricmc:fabric-loader", version.ref = "fabric-loader" }
|
||||
emi = { module = "dev.emi:emi-xplat-mojmap", version.ref = "emi" }
|
||||
iris = { module = "maven.modrinth:iris", version.ref = "iris" }
|
||||
jei-api = { module = "mezz.jei:jei-1.19.2-common-api", version.ref = "jei" }
|
||||
jei-fabric = { module = "mezz.jei:jei-1.19.2-fabric", version.ref = "jei" }
|
||||
jei-forge = { module = "mezz.jei:jei-1.19.2-forge", version.ref = "jei" }
|
||||
jei-api = { module = "mezz.jei:jei-1.19.4-common-api", version.ref = "jei" }
|
||||
jei-fabric = { module = "mezz.jei:jei-1.19.4-fabric", version.ref = "jei" }
|
||||
jei-forge = { module = "mezz.jei:jei-1.19.4-forge", version.ref = "jei" }
|
||||
mixin = { module = "org.spongepowered:mixin", version.ref = "mixin" }
|
||||
modmenu = { module = "com.terraformersmc:modmenu", version.ref = "modmenu" }
|
||||
oculus = { module = "maven.modrinth:oculus", version.ref = "oculus" }
|
||||
@@ -148,10 +152,10 @@ kotlin = ["kotlin-stdlib", "kotlin-coroutines"]
|
||||
# Minecraft
|
||||
externalMods-common = ["jei-api", "nightConfig-core", "nightConfig-toml"]
|
||||
externalMods-forge-compile = ["oculus", "jei-api"]
|
||||
externalMods-forge-runtime = []
|
||||
externalMods-forge-runtime = ["jei-forge"]
|
||||
externalMods-fabric = ["nightConfig-core", "nightConfig-toml"]
|
||||
externalMods-fabric-compile = ["iris", "jei-api", "rei-api", "rei-builtin"]
|
||||
externalMods-fabric-runtime = ["modmenu"]
|
||||
externalMods-fabric-runtime = ["jei-fabric", "modmenu"]
|
||||
|
||||
# Testing
|
||||
test = ["junit-jupiter-api", "junit-jupiter-params", "hamcrest", "jqwik-api"]
|
||||
|
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
3
gradle/wrapper/gradle-wrapper.properties
vendored
3
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,5 +1,6 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-bin.zip
|
||||
networkTimeout=10000
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
19
gradlew
vendored
19
gradlew
vendored
@@ -55,7 +55,7 @@
|
||||
# Darwin, MinGW, and NonStop.
|
||||
#
|
||||
# (3) This script is generated from the Groovy template
|
||||
# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# within the Gradle project.
|
||||
#
|
||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||
@@ -80,13 +80,10 @@ do
|
||||
esac
|
||||
done
|
||||
|
||||
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
|
||||
|
||||
APP_NAME="Gradle"
|
||||
# This is normally unused
|
||||
# shellcheck disable=SC2034
|
||||
APP_BASE_NAME=${0##*/}
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD=maximum
|
||||
@@ -143,12 +140,16 @@ fi
|
||||
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||
case $MAX_FD in #(
|
||||
max*)
|
||||
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC3045
|
||||
MAX_FD=$( ulimit -H -n ) ||
|
||||
warn "Could not query maximum file descriptor limit"
|
||||
esac
|
||||
case $MAX_FD in #(
|
||||
'' | soft) :;; #(
|
||||
*)
|
||||
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC3045
|
||||
ulimit -n "$MAX_FD" ||
|
||||
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||
esac
|
||||
@@ -193,6 +194,10 @@ if "$cygwin" || "$msys" ; then
|
||||
done
|
||||
fi
|
||||
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# Collect all arguments for the java command;
|
||||
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
|
||||
# shell script including quotes and variable substitutions, so put them in
|
||||
|
1
gradlew.bat
vendored
1
gradlew.bat
vendored
@@ -26,6 +26,7 @@ if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%"=="" set DIRNAME=.
|
||||
@rem This is normally unused
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
|
@@ -9,7 +9,11 @@ import dan200.computercraft.api.turtle.ITurtleUpgrade;
|
||||
import dan200.computercraft.api.turtle.TurtleUpgradeSerialiser;
|
||||
import dan200.computercraft.impl.client.ComputerCraftAPIClientService;
|
||||
|
||||
|
||||
/**
|
||||
* The public API for client-only code.
|
||||
*
|
||||
* @see dan200.computercraft.api.ComputerCraftAPI The main API
|
||||
*/
|
||||
public final class ComputerCraftAPIClient {
|
||||
private ComputerCraftAPIClient() {
|
||||
}
|
||||
|
@@ -11,9 +11,13 @@ import dan200.computercraft.api.turtle.ITurtleUpgrade;
|
||||
import dan200.computercraft.api.turtle.TurtleSide;
|
||||
import dan200.computercraft.api.turtle.TurtleUpgradeSerialiser;
|
||||
import net.minecraft.client.resources.model.ModelResourceLocation;
|
||||
import net.minecraft.client.resources.model.UnbakedModel;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Provides models for a {@link ITurtleUpgrade}.
|
||||
@@ -28,15 +32,45 @@ public interface TurtleUpgradeModeller<T extends ITurtleUpgrade> {
|
||||
* When the current turtle is {@literal null}, this function should be constant for a given upgrade and side.
|
||||
*
|
||||
* @param upgrade The upgrade that you're getting the model for.
|
||||
* @param turtle Access to the turtle that the upgrade resides on. This will be null when getting item models!
|
||||
* @param turtle Access to the turtle that the upgrade resides on. This will be null when getting item models, unless
|
||||
* {@link #getModel(ITurtleUpgrade, CompoundTag, TurtleSide)} is overriden.
|
||||
* @param side Which side of the turtle (left or right) the upgrade resides on.
|
||||
* @return The model that you wish to be used to render your upgrade.
|
||||
*/
|
||||
TransformedModel getModel(T upgrade, @Nullable ITurtleAccess turtle, TurtleSide side);
|
||||
|
||||
/**
|
||||
* A basic {@link TurtleUpgradeModeller} which renders using the upgrade's {@linkplain ITurtleUpgrade#getCraftingItem()
|
||||
* crafting item}.
|
||||
* Obtain the model to be used when rendering a turtle peripheral.
|
||||
* <p>
|
||||
* This is used when rendering the turtle's item model, and so no {@link ITurtleAccess} is available.
|
||||
*
|
||||
* @param upgrade The upgrade that you're getting the model for.
|
||||
* @param data Upgrade data instance for current turtle side.
|
||||
* @param side Which side of the turtle (left or right) the upgrade resides on.
|
||||
* @return The model that you wish to be used to render your upgrade.
|
||||
*/
|
||||
default TransformedModel getModel(T upgrade, CompoundTag data, TurtleSide side) {
|
||||
return getModel(upgrade, (ITurtleAccess) null, side);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get a list of models that this turtle modeller depends on.
|
||||
* <p>
|
||||
* Models included in this list will be loaded and baked alongside item and block models, and so may be referenced
|
||||
* by {@link TransformedModel#of(ResourceLocation)}. You do not need to override this method if you will load models
|
||||
* by other means.
|
||||
*
|
||||
* @return A list of models that this modeller depends on.
|
||||
* @see UnbakedModel#getDependencies()
|
||||
*/
|
||||
default Collection<ResourceLocation> getDependencies() {
|
||||
return List.of();
|
||||
}
|
||||
|
||||
/**
|
||||
* A basic {@link TurtleUpgradeModeller} which renders using the upgrade's {@linkplain ITurtleUpgrade#getUpgradeItem(CompoundTag)}
|
||||
* upgrade item}.
|
||||
* <p>
|
||||
* This uses appropriate transformations for "flat" items, namely those extending the {@literal minecraft:item/generated}
|
||||
* model type. It will not appear correct for 3D models with additional depth, such as blocks.
|
||||
@@ -46,7 +80,7 @@ public interface TurtleUpgradeModeller<T extends ITurtleUpgrade> {
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
static <T extends ITurtleUpgrade> TurtleUpgradeModeller<T> flatItem() {
|
||||
return (TurtleUpgradeModeller<T>) TurtleUpgradeModellers.FLAT_ITEM;
|
||||
return (TurtleUpgradeModeller<T>) TurtleUpgradeModellers.UPGRADE_ITEM;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -58,7 +92,8 @@ public interface TurtleUpgradeModeller<T extends ITurtleUpgrade> {
|
||||
* @return The constructed modeller.
|
||||
*/
|
||||
static <T extends ITurtleUpgrade> TurtleUpgradeModeller<T> sided(ModelResourceLocation left, ModelResourceLocation right) {
|
||||
return (upgrade, turtle, side) -> TransformedModel.of(side == TurtleSide.LEFT ? left : right);
|
||||
// TODO(1.21.0): Remove this.
|
||||
return sided((ResourceLocation) left, right);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -70,6 +105,16 @@ public interface TurtleUpgradeModeller<T extends ITurtleUpgrade> {
|
||||
* @return The constructed modeller.
|
||||
*/
|
||||
static <T extends ITurtleUpgrade> TurtleUpgradeModeller<T> sided(ResourceLocation left, ResourceLocation right) {
|
||||
return (upgrade, turtle, side) -> TransformedModel.of(side == TurtleSide.LEFT ? left : right);
|
||||
return new TurtleUpgradeModeller<>() {
|
||||
@Override
|
||||
public TransformedModel getModel(T upgrade, @Nullable ITurtleAccess turtle, TurtleSide side) {
|
||||
return TransformedModel.of(side == TurtleSide.LEFT ? left : right);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<ResourceLocation> getDependencies() {
|
||||
return List.of(left, right);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@@ -6,13 +6,20 @@ package dan200.computercraft.api.client.turtle;
|
||||
|
||||
import com.mojang.math.Transformation;
|
||||
import dan200.computercraft.api.client.TransformedModel;
|
||||
import dan200.computercraft.api.turtle.ITurtleAccess;
|
||||
import dan200.computercraft.api.turtle.ITurtleUpgrade;
|
||||
import dan200.computercraft.api.turtle.TurtleSide;
|
||||
import dan200.computercraft.impl.client.ClientPlatformHelper;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import org.joml.Matrix4f;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
class TurtleUpgradeModellers {
|
||||
private static final Transformation leftTransform = getMatrixFor(-0.40625f);
|
||||
private static final Transformation rightTransform = getMatrixFor(0.40625f);
|
||||
private static final Transformation leftTransform = getMatrixFor(-0.4065f);
|
||||
private static final Transformation rightTransform = getMatrixFor(0.4065f);
|
||||
|
||||
private static Transformation getMatrixFor(float offset) {
|
||||
var matrix = new Matrix4f();
|
||||
@@ -26,6 +33,23 @@ class TurtleUpgradeModellers {
|
||||
return new Transformation(matrix);
|
||||
}
|
||||
|
||||
static final TurtleUpgradeModeller<ITurtleUpgrade> FLAT_ITEM = (upgrade, turtle, side) ->
|
||||
TransformedModel.of(upgrade.getCraftingItem(), side == TurtleSide.LEFT ? leftTransform : rightTransform);
|
||||
static final TurtleUpgradeModeller<ITurtleUpgrade> UPGRADE_ITEM = new UpgradeItemModeller();
|
||||
|
||||
private static class UpgradeItemModeller implements TurtleUpgradeModeller<ITurtleUpgrade> {
|
||||
@Override
|
||||
public TransformedModel getModel(ITurtleUpgrade upgrade, @Nullable ITurtleAccess turtle, TurtleSide side) {
|
||||
return getModel(turtle == null ? upgrade.getCraftingItem() : upgrade.getUpgradeItem(turtle.getUpgradeNBTData(side)), side);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TransformedModel getModel(ITurtleUpgrade upgrade, CompoundTag data, TurtleSide side) {
|
||||
return getModel(upgrade.getUpgradeItem(data), side);
|
||||
}
|
||||
|
||||
private TransformedModel getModel(ItemStack stack, TurtleSide side) {
|
||||
var model = Minecraft.getInstance().getItemRenderer().getItemModelShaper().getItemModel(stack);
|
||||
if (stack.hasFoil()) model = ClientPlatformHelper.get().createdFoiledModel(model);
|
||||
return new TransformedModel(model, side == TurtleSide.LEFT ? leftTransform : rightTransform);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -5,6 +5,7 @@
|
||||
package dan200.computercraft.impl.client;
|
||||
|
||||
import dan200.computercraft.impl.Services;
|
||||
import net.minecraft.client.renderer.RenderType;
|
||||
import net.minecraft.client.resources.model.BakedModel;
|
||||
import net.minecraft.client.resources.model.ModelManager;
|
||||
import net.minecraft.client.resources.model.ModelResourceLocation;
|
||||
@@ -24,6 +25,15 @@ public interface ClientPlatformHelper {
|
||||
*/
|
||||
BakedModel getModel(ModelManager manager, ResourceLocation location);
|
||||
|
||||
/**
|
||||
* Wrap this model in a version which renders a foil/enchantment glint.
|
||||
*
|
||||
* @param model The model to wrap.
|
||||
* @return The wrapped model.
|
||||
* @see RenderType#glint()
|
||||
*/
|
||||
BakedModel createdFoiledModel(BakedModel model);
|
||||
|
||||
static ClientPlatformHelper get() {
|
||||
var instance = Instance.INSTANCE;
|
||||
return instance == null ? Services.raise(ClientPlatformHelper.class, Instance.ERROR) : instance;
|
||||
|
@@ -5,9 +5,11 @@
|
||||
package dan200.computercraft.api.pocket;
|
||||
|
||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||
import dan200.computercraft.api.upgrades.UpgradeBase;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Map;
|
||||
@@ -69,6 +71,8 @@ public interface IPocketAccess {
|
||||
*
|
||||
* @return The upgrade's NBT.
|
||||
* @see #updateUpgradeNBTData()
|
||||
* @see UpgradeBase#getUpgradeItem(CompoundTag)
|
||||
* @see UpgradeBase#getUpgradeData(ItemStack)
|
||||
*/
|
||||
CompoundTag getUpgradeNBTData();
|
||||
|
||||
@@ -80,7 +84,10 @@ public interface IPocketAccess {
|
||||
void updateUpgradeNBTData();
|
||||
|
||||
/**
|
||||
* Remove the current peripheral and create a new one. You may wish to do this if the methods available change.
|
||||
* Remove the current peripheral and create a new one.
|
||||
* <p>
|
||||
* You may wish to do this if the methods available change, for instance when the {@linkplain #getEntity() owning
|
||||
* entity} changes.
|
||||
*/
|
||||
void invalidatePeripheral();
|
||||
|
||||
@@ -88,6 +95,8 @@ public interface IPocketAccess {
|
||||
* Get a list of all upgrades for the pocket computer.
|
||||
*
|
||||
* @return A collection of all upgrade names.
|
||||
* @deprecated This is a relic of a previous API, which no longer makes sense with newer versions of ComputerCraft.
|
||||
*/
|
||||
@Deprecated(forRemoval = true)
|
||||
Map<ResourceLocation, IPeripheral> getUpgrades();
|
||||
}
|
||||
|
@@ -21,6 +21,6 @@ import java.util.function.Consumer;
|
||||
*/
|
||||
public abstract class PocketUpgradeDataProvider extends UpgradeDataProvider<IPocketUpgrade, PocketUpgradeSerialiser<?>> {
|
||||
public PocketUpgradeDataProvider(PackOutput output) {
|
||||
super(output, "Pocket Computer Upgrades", "computercraft/pocket_upgrades", PocketUpgradeSerialiser.REGISTRY_ID);
|
||||
super(output, "Pocket Computer Upgrades", "computercraft/pocket_upgrades", PocketUpgradeSerialiser.registryId());
|
||||
}
|
||||
}
|
||||
|
@@ -7,6 +7,7 @@ package dan200.computercraft.api.pocket;
|
||||
import dan200.computercraft.api.ComputerCraftAPI;
|
||||
import dan200.computercraft.api.upgrades.UpgradeBase;
|
||||
import dan200.computercraft.api.upgrades.UpgradeSerialiser;
|
||||
import dan200.computercraft.impl.ComputerCraftAPIService;
|
||||
import dan200.computercraft.impl.upgrades.SerialiserWithCraftingItem;
|
||||
import dan200.computercraft.impl.upgrades.SimpleSerialiser;
|
||||
import net.minecraft.core.Registry;
|
||||
@@ -31,9 +32,21 @@ import java.util.function.Function;
|
||||
public interface PocketUpgradeSerialiser<T extends IPocketUpgrade> extends UpgradeSerialiser<T> {
|
||||
/**
|
||||
* The ID for the associated registry.
|
||||
*
|
||||
* @deprecated Use {@link #registryId()} instead.
|
||||
*/
|
||||
@Deprecated(forRemoval = true)
|
||||
ResourceKey<Registry<PocketUpgradeSerialiser<?>>> REGISTRY_ID = ResourceKey.createRegistryKey(new ResourceLocation(ComputerCraftAPI.MOD_ID, "pocket_upgrade_serialiser"));
|
||||
|
||||
/**
|
||||
* The ID for the associated registry.
|
||||
*
|
||||
* @return The registry key.
|
||||
*/
|
||||
static ResourceKey<Registry<PocketUpgradeSerialiser<?>>> registryId() {
|
||||
return ComputerCraftAPIService.get().pocketUpgradeRegistryId();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an upgrade serialiser for a simple upgrade. This is similar to a {@link SimpleCraftingRecipeSerializer},
|
||||
* but for upgrades.
|
||||
|
@@ -8,10 +8,13 @@ import com.mojang.authlib.GameProfile;
|
||||
import dan200.computercraft.api.lua.ILuaCallback;
|
||||
import dan200.computercraft.api.lua.MethodResult;
|
||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||
import dan200.computercraft.api.upgrades.UpgradeBase;
|
||||
import dan200.computercraft.api.upgrades.UpgradeData;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.world.Container;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
@@ -75,7 +78,9 @@ public interface ITurtleAccess {
|
||||
* @param f The subframe fraction.
|
||||
* @return A vector containing the floating point co-ordinates at which the turtle resides.
|
||||
* @see #getVisualYaw(float)
|
||||
* @deprecated Will be removed in 1.20.
|
||||
*/
|
||||
@Deprecated(forRemoval = true)
|
||||
Vec3 getVisualPosition(float f);
|
||||
|
||||
/**
|
||||
@@ -84,7 +89,9 @@ public interface ITurtleAccess {
|
||||
* @param f The subframe fraction.
|
||||
* @return The yaw the turtle is facing.
|
||||
* @see #getVisualPosition(float)
|
||||
* @deprecated Will be removed in 1.20.
|
||||
*/
|
||||
@Deprecated(forRemoval = true)
|
||||
float getVisualYaw(float f);
|
||||
|
||||
/**
|
||||
@@ -241,23 +248,51 @@ public interface ITurtleAccess {
|
||||
void playAnimation(TurtleAnimation animation);
|
||||
|
||||
/**
|
||||
* Returns the turtle on the specified side of the turtle, if there is one.
|
||||
* Returns the upgrade on the specified side of the turtle, if there is one.
|
||||
*
|
||||
* @param side The side to get the upgrade from.
|
||||
* @return The upgrade on the specified side of the turtle, if there is one.
|
||||
* @see #setUpgrade(TurtleSide, ITurtleUpgrade)
|
||||
* @see #getUpgradeWithData(TurtleSide)
|
||||
* @see #setUpgradeWithData(TurtleSide, UpgradeData)
|
||||
*/
|
||||
@Nullable
|
||||
ITurtleUpgrade getUpgrade(TurtleSide side);
|
||||
|
||||
/**
|
||||
* Returns the upgrade on the specified side of the turtle, along with its {@linkplain #getUpgradeNBTData(TurtleSide)
|
||||
* update data}.
|
||||
*
|
||||
* @param side The side to get the upgrade from.
|
||||
* @return The upgrade on the specified side of the turtle, along with its upgrade data, if there is one.
|
||||
* @see #getUpgradeWithData(TurtleSide)
|
||||
* @see #setUpgradeWithData(TurtleSide, UpgradeData)
|
||||
*/
|
||||
default @Nullable UpgradeData<ITurtleUpgrade> getUpgradeWithData(TurtleSide side) {
|
||||
var upgrade = getUpgrade(side);
|
||||
return upgrade == null ? null : UpgradeData.of(upgrade, getUpgradeNBTData(side));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the upgrade for a given side, resetting peripherals and clearing upgrade specific data.
|
||||
*
|
||||
* @param side The side to set the upgrade on.
|
||||
* @param upgrade The upgrade to set, may be {@code null} to clear.
|
||||
* @see #getUpgrade(TurtleSide)
|
||||
* @deprecated Use {@link #setUpgradeWithData(TurtleSide, UpgradeData)}
|
||||
*/
|
||||
void setUpgrade(TurtleSide side, @Nullable ITurtleUpgrade upgrade);
|
||||
@Deprecated
|
||||
default void setUpgrade(TurtleSide side, @Nullable ITurtleUpgrade upgrade) {
|
||||
setUpgradeWithData(side, upgrade == null ? null : UpgradeData.ofDefault(upgrade));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the upgrade for a given side and its upgrade data.
|
||||
*
|
||||
* @param side The side to set the upgrade on.
|
||||
* @param upgrade The upgrade to set, may be {@code null} to clear.
|
||||
* @see #getUpgradeWithData(TurtleSide)
|
||||
*/
|
||||
void setUpgradeWithData(TurtleSide side, @Nullable UpgradeData<ITurtleUpgrade> upgrade);
|
||||
|
||||
/**
|
||||
* Returns the peripheral created by the upgrade on the specified side of the turtle, if there is one.
|
||||
@@ -277,6 +312,8 @@ public interface ITurtleAccess {
|
||||
* @param side The side to get the upgrade data for.
|
||||
* @return The upgrade-specific data.
|
||||
* @see #updateUpgradeNBTData(TurtleSide)
|
||||
* @see UpgradeBase#getUpgradeItem(CompoundTag)
|
||||
* @see UpgradeBase#getUpgradeData(ItemStack)
|
||||
*/
|
||||
CompoundTag getUpgradeNBTData(TurtleSide side);
|
||||
|
||||
|
@@ -7,6 +7,7 @@ package dan200.computercraft.api.turtle;
|
||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||
import dan200.computercraft.api.upgrades.UpgradeBase;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
@@ -79,4 +80,17 @@ public interface ITurtleUpgrade extends UpgradeBase {
|
||||
*/
|
||||
default void update(ITurtleAccess turtle, TurtleSide side) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get upgrade data that should be persisted when the turtle was broken.
|
||||
* <p>
|
||||
* This method should be overridden when you don't need to store all upgrade data by default. For instance, if you
|
||||
* store peripheral state in the upgrade data, which should be lost when the turtle is broken.
|
||||
*
|
||||
* @param upgradeData Data that currently stored for this upgrade
|
||||
* @return Filtered version of this data.
|
||||
*/
|
||||
default CompoundTag getPersistedData(CompoundTag upgradeData) {
|
||||
return upgradeData;
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,49 @@
|
||||
// SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers
|
||||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package dan200.computercraft.api.turtle;
|
||||
|
||||
import net.minecraft.util.StringRepresentable;
|
||||
import net.minecraft.world.entity.EquipmentSlot;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
|
||||
/**
|
||||
* Indicates if an equipped turtle item will consume durability.
|
||||
*
|
||||
* @see TurtleUpgradeDataProvider.ToolBuilder#consumeDurability(TurtleToolDurability)
|
||||
*/
|
||||
public enum TurtleToolDurability implements StringRepresentable {
|
||||
/**
|
||||
* The equipped tool always consumes durability when using.
|
||||
*/
|
||||
ALWAYS("always"),
|
||||
|
||||
/**
|
||||
* The equipped tool consumes durability if it is {@linkplain ItemStack#isEnchanted() enchanted} or has
|
||||
* {@linkplain ItemStack#getAttributeModifiers(EquipmentSlot) custom attribute modifiers}.
|
||||
*/
|
||||
WHEN_ENCHANTED("when_enchanted"),
|
||||
|
||||
/**
|
||||
* The equipped tool never consumes durability. Tools which have been damaged cannot be used as upgrades.
|
||||
*/
|
||||
NEVER("never");
|
||||
|
||||
private final String serialisedName;
|
||||
|
||||
/**
|
||||
* The codec which may be used for serialising/deserialising {@link TurtleToolDurability}s.
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public static final StringRepresentable.EnumCodec<TurtleToolDurability> CODEC = StringRepresentable.fromEnum(TurtleToolDurability::values);
|
||||
|
||||
TurtleToolDurability(String serialisedName) {
|
||||
this.serialisedName = serialisedName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSerializedName() {
|
||||
return serialisedName;
|
||||
}
|
||||
}
|
@@ -13,8 +13,10 @@ import net.minecraft.data.DataGenerator;
|
||||
import net.minecraft.data.PackOutput;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.tags.TagKey;
|
||||
import net.minecraft.world.entity.EquipmentSlot;
|
||||
import net.minecraft.world.entity.ai.attributes.Attributes;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
@@ -33,7 +35,7 @@ public abstract class TurtleUpgradeDataProvider extends UpgradeDataProvider<ITur
|
||||
private static final ResourceLocation TOOL_ID = new ResourceLocation(ComputerCraftAPI.MOD_ID, "tool");
|
||||
|
||||
public TurtleUpgradeDataProvider(PackOutput output) {
|
||||
super(output, "Turtle Upgrades", "computercraft/turtle_upgrades", TurtleUpgradeSerialiser.REGISTRY_ID);
|
||||
super(output, "Turtle Upgrades", "computercraft/turtle_upgrades", TurtleUpgradeSerialiser.registryId());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -61,6 +63,8 @@ public abstract class TurtleUpgradeDataProvider extends UpgradeDataProvider<ITur
|
||||
private @Nullable Item craftingItem;
|
||||
private @Nullable Float damageMultiplier = null;
|
||||
private @Nullable TagKey<Block> breakable;
|
||||
private boolean allowEnchantments = false;
|
||||
private TurtleToolDurability consumeDurability = TurtleToolDurability.NEVER;
|
||||
|
||||
ToolBuilder(ResourceLocation id, TurtleUpgradeSerialiser<?> serialiser, Item toolItem) {
|
||||
this.id = id;
|
||||
@@ -104,6 +108,28 @@ public abstract class TurtleUpgradeDataProvider extends UpgradeDataProvider<ITur
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate that this upgrade allows items which have been {@linkplain ItemStack#isEnchanted() enchanted} or have
|
||||
* {@linkplain ItemStack#getAttributeModifiers(EquipmentSlot) custom attribute modifiers}.
|
||||
*
|
||||
* @return The tool builder, for further use.
|
||||
*/
|
||||
public ToolBuilder allowEnchantments() {
|
||||
allowEnchantments = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set when the tool will consume durability.
|
||||
*
|
||||
* @param durability The durability predicate.
|
||||
* @return The tool builder, for further use.
|
||||
*/
|
||||
public ToolBuilder consumeDurability(TurtleToolDurability durability) {
|
||||
consumeDurability = durability;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide a list of breakable blocks. If not given, the tool can break all blocks. If given, only blocks
|
||||
* in this tag, those in {@link ComputerCraftTags.Blocks#TURTLE_ALWAYS_BREAKABLE} and "insta-mine" ones can
|
||||
@@ -132,6 +158,10 @@ public abstract class TurtleUpgradeDataProvider extends UpgradeDataProvider<ITur
|
||||
}
|
||||
if (damageMultiplier != null) s.addProperty("damageMultiplier", damageMultiplier);
|
||||
if (breakable != null) s.addProperty("breakable", breakable.location().toString());
|
||||
if (allowEnchantments) s.addProperty("allowEnchantments", true);
|
||||
if (consumeDurability != TurtleToolDurability.NEVER) {
|
||||
s.addProperty("consumeDurability", consumeDurability.getSerializedName());
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
@@ -7,6 +7,7 @@ package dan200.computercraft.api.turtle;
|
||||
import dan200.computercraft.api.ComputerCraftAPI;
|
||||
import dan200.computercraft.api.upgrades.UpgradeBase;
|
||||
import dan200.computercraft.api.upgrades.UpgradeSerialiser;
|
||||
import dan200.computercraft.impl.ComputerCraftAPIService;
|
||||
import dan200.computercraft.impl.upgrades.SerialiserWithCraftingItem;
|
||||
import dan200.computercraft.impl.upgrades.SimpleSerialiser;
|
||||
import net.minecraft.core.Registry;
|
||||
@@ -66,9 +67,21 @@ import java.util.function.Function;
|
||||
public interface TurtleUpgradeSerialiser<T extends ITurtleUpgrade> extends UpgradeSerialiser<T> {
|
||||
/**
|
||||
* The ID for the associated registry.
|
||||
*
|
||||
* @deprecated Use {@link #registryId()} instead.
|
||||
*/
|
||||
@Deprecated(forRemoval = true)
|
||||
ResourceKey<Registry<TurtleUpgradeSerialiser<?>>> REGISTRY_ID = ResourceKey.createRegistryKey(new ResourceLocation(ComputerCraftAPI.MOD_ID, "turtle_upgrade_serialiser"));
|
||||
|
||||
/**
|
||||
* The ID for the associated registry.
|
||||
*
|
||||
* @return The registry key.
|
||||
*/
|
||||
static ResourceKey<Registry<TurtleUpgradeSerialiser<?>>> registryId() {
|
||||
return ComputerCraftAPIService.get().turtleUpgradeRegistryId();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an upgrade serialiser for a simple upgrade. This is similar to a {@link SimpleCraftingRecipeSerializer},
|
||||
* but for upgrades.
|
||||
|
@@ -4,10 +4,14 @@
|
||||
|
||||
package dan200.computercraft.api.upgrades;
|
||||
|
||||
import dan200.computercraft.api.pocket.IPocketAccess;
|
||||
import dan200.computercraft.api.pocket.IPocketUpgrade;
|
||||
import dan200.computercraft.api.turtle.ITurtleAccess;
|
||||
import dan200.computercraft.api.turtle.ITurtleUpgrade;
|
||||
import dan200.computercraft.api.turtle.TurtleSide;
|
||||
import dan200.computercraft.impl.PlatformHelper;
|
||||
import net.minecraft.Util;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
|
||||
@@ -50,6 +54,42 @@ public interface UpgradeBase {
|
||||
*/
|
||||
ItemStack getCraftingItem();
|
||||
|
||||
/**
|
||||
* Returns the item stack representing a currently equipped turtle upgrade.
|
||||
* <p>
|
||||
* While upgrades can store upgrade data ({@link ITurtleAccess#getUpgradeNBTData(TurtleSide)} and
|
||||
* {@link IPocketAccess#getUpgradeNBTData()}}, by default this data is discarded when an upgrade is unequipped,
|
||||
* and the original item stack is returned.
|
||||
* <p>
|
||||
* By overriding this method, you can create a new {@link ItemStack} which contains enough data to
|
||||
* {@linkplain #getUpgradeData(ItemStack) re-create the upgrade data} if the item is re-equipped.
|
||||
* <p>
|
||||
* When overriding this, you should override {@link #getUpgradeData(ItemStack)} and {@link #isItemSuitable(ItemStack)}
|
||||
* at the same time,
|
||||
*
|
||||
* @param upgradeData The current upgrade data. This should <strong>NOT</strong> be mutated.
|
||||
* @return The item stack returned when unequipping.
|
||||
*/
|
||||
default ItemStack getUpgradeItem(CompoundTag upgradeData) {
|
||||
return getCraftingItem();
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract upgrade data from an {@link ItemStack}.
|
||||
* <p>
|
||||
* This upgrade data will be available with {@link ITurtleAccess#getUpgradeNBTData(TurtleSide)} or
|
||||
* {@link IPocketAccess#getUpgradeNBTData()}.
|
||||
* <p>
|
||||
* This should be an inverse to {@link #getUpgradeItem(CompoundTag)}.
|
||||
*
|
||||
* @param stack The stack that was equipped by the turtle or pocket computer. This will have the same item as
|
||||
* {@link #getCraftingItem()}.
|
||||
* @return The upgrade data that should be set on the turtle or pocket computer.
|
||||
*/
|
||||
default CompoundTag getUpgradeData(ItemStack stack) {
|
||||
return new CompoundTag();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if an item is suitable for being used for this upgrade.
|
||||
* <p>
|
||||
|
@@ -0,0 +1,82 @@
|
||||
// SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers
|
||||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package dan200.computercraft.api.upgrades;
|
||||
|
||||
import dan200.computercraft.api.pocket.IPocketUpgrade;
|
||||
import dan200.computercraft.api.turtle.ITurtleUpgrade;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* An upgrade (i.e. a {@link ITurtleUpgrade}) and its current upgrade data.
|
||||
* <p>
|
||||
* <strong>IMPORTANT:</strong> The {@link #data()} in an upgrade data is often a reference to the original upgrade data.
|
||||
* Be careful to take a {@linkplain #copy() defensive copy} if you plan to use the data in this upgrade.
|
||||
*
|
||||
* @param upgrade The current upgrade.
|
||||
* @param data The upgrade's data.
|
||||
* @param <T> The type of upgrade, either {@link ITurtleUpgrade} or {@link IPocketUpgrade}.
|
||||
*/
|
||||
public record UpgradeData<T extends UpgradeBase>(T upgrade, CompoundTag data) {
|
||||
/**
|
||||
* A utility method to construct a new {@link UpgradeData} instance.
|
||||
*
|
||||
* @param upgrade An upgrade.
|
||||
* @param data The upgrade's data.
|
||||
* @param <T> The type of upgrade.
|
||||
* @return The new {@link UpgradeData} instance.
|
||||
*/
|
||||
public static <T extends UpgradeBase> UpgradeData<T> of(T upgrade, CompoundTag data) {
|
||||
return new UpgradeData<>(upgrade, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an {@link UpgradeData} containing the default {@linkplain #data() data} for an upgrade.
|
||||
*
|
||||
* @param upgrade The upgrade instance.
|
||||
* @param <T> The type of upgrade.
|
||||
* @return The default upgrade data.
|
||||
*/
|
||||
public static <T extends UpgradeBase> UpgradeData<T> ofDefault(T upgrade) {
|
||||
return of(upgrade, upgrade.getUpgradeData(upgrade.getCraftingItem()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Take a copy of a (possibly {@code null}) {@link UpgradeData} instance.
|
||||
*
|
||||
* @param upgrade The copied upgrade data.
|
||||
* @param <T> The type of upgrade.
|
||||
* @return The newly created upgrade data.
|
||||
*/
|
||||
@Contract("!null -> !null; null -> null")
|
||||
public static <T extends UpgradeBase> @Nullable UpgradeData<T> copyOf(@Nullable UpgradeData<T> upgrade) {
|
||||
return upgrade == null ? null : upgrade.copy();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@linkplain UpgradeBase#getUpgradeItem(CompoundTag) upgrade item} for this upgrade.
|
||||
* <p>
|
||||
* This returns a defensive copy of the item, to prevent accidental mutation of the upgrade data or original
|
||||
* {@linkplain UpgradeBase#getCraftingItem() upgrade stack}.
|
||||
*
|
||||
* @return This upgrade's item.
|
||||
*/
|
||||
public ItemStack getUpgradeItem() {
|
||||
return upgrade.getUpgradeItem(data).copy();
|
||||
}
|
||||
|
||||
/**
|
||||
* Take a copy of this {@link UpgradeData}. This returns a new instance with the same upgrade and a fresh copy of
|
||||
* the upgrade data.
|
||||
*
|
||||
* @return A copy of the current instance.
|
||||
*/
|
||||
public UpgradeData<T> copy() {
|
||||
return new UpgradeData<>(upgrade(), data().copy());
|
||||
}
|
||||
}
|
@@ -23,10 +23,7 @@ import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
@@ -104,7 +101,7 @@ public abstract class UpgradeDataProvider<T extends UpgradeBase, R extends Upgra
|
||||
protected abstract void addUpgrades(Consumer<Upgrade<R>> addUpgrade);
|
||||
|
||||
@Override
|
||||
public final CompletableFuture<?> run(CachedOutput cache) {
|
||||
public CompletableFuture<?> run(CachedOutput cache) {
|
||||
var base = output.getOutputFolder().resolve("data");
|
||||
|
||||
Set<ResourceLocation> seen = new HashSet<>();
|
||||
@@ -127,7 +124,7 @@ public abstract class UpgradeDataProvider<T extends UpgradeBase, R extends Upgra
|
||||
}
|
||||
});
|
||||
|
||||
this.upgrades = upgrades;
|
||||
this.upgrades = Collections.unmodifiableList(upgrades);
|
||||
return Util.sequenceFailFast(futures);
|
||||
}
|
||||
|
||||
@@ -166,5 +163,21 @@ public abstract class UpgradeDataProvider<T extends UpgradeBase, R extends Upgra
|
||||
public void add(Consumer<Upgrade<R>> add) {
|
||||
add.accept(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new {@link Upgrade} which requires the given mod to be present.
|
||||
* <p>
|
||||
* This uses mod-loader-specific hooks (Forge's crafting conditions and Fabric's resource conditions). If using
|
||||
* this in a multi-loader setup, you must generate resources separately for the two loaders.
|
||||
*
|
||||
* @param modId The id of the mod.
|
||||
* @return A new upgrade instance.
|
||||
*/
|
||||
public Upgrade<R> requireMod(String modId) {
|
||||
return new Upgrade<>(id, serialiser, json -> {
|
||||
PlatformHelper.get().addRequiredModCondition(json, modId);
|
||||
serialise.accept(json);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -15,10 +15,14 @@ import dan200.computercraft.api.media.MediaProvider;
|
||||
import dan200.computercraft.api.network.PacketNetwork;
|
||||
import dan200.computercraft.api.network.wired.WiredElement;
|
||||
import dan200.computercraft.api.network.wired.WiredNode;
|
||||
import dan200.computercraft.api.pocket.PocketUpgradeSerialiser;
|
||||
import dan200.computercraft.api.redstone.BundledRedstoneProvider;
|
||||
import dan200.computercraft.api.turtle.TurtleRefuelHandler;
|
||||
import dan200.computercraft.api.turtle.TurtleUpgradeSerialiser;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.level.Level;
|
||||
@@ -63,6 +67,10 @@ public interface ComputerCraftAPIService {
|
||||
|
||||
void registerRefuelHandler(TurtleRefuelHandler handler);
|
||||
|
||||
ResourceKey<Registry<TurtleUpgradeSerialiser<?>>> turtleUpgradeRegistryId();
|
||||
|
||||
ResourceKey<Registry<PocketUpgradeSerialiser<?>>> pocketUpgradeRegistryId();
|
||||
|
||||
DetailRegistry<ItemStack> getItemStackDetailRegistry();
|
||||
|
||||
DetailRegistry<BlockReference> getBlockInWorldDetailRegistry();
|
||||
|
@@ -4,6 +4,8 @@
|
||||
|
||||
package dan200.computercraft.impl;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import dan200.computercraft.api.upgrades.UpgradeDataProvider;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
@@ -63,6 +65,15 @@ public interface PlatformHelper {
|
||||
return item.getTag();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a resource condition which requires a mod to be loaded. This should be used by data providers such as
|
||||
* {@link UpgradeDataProvider}.
|
||||
*
|
||||
* @param object The JSON object we're generating.
|
||||
* @param modId The mod ID that we require.
|
||||
*/
|
||||
void addRequiredModCondition(JsonObject object, String modId);
|
||||
|
||||
final class Instance {
|
||||
static final @Nullable PlatformHelper INSTANCE;
|
||||
static final @Nullable Throwable ERROR;
|
||||
|
@@ -7,6 +7,7 @@ import cc.tweaked.gradle.clientClasses
|
||||
import cc.tweaked.gradle.commonClasses
|
||||
|
||||
plugins {
|
||||
id("cc-tweaked.publishing")
|
||||
id("cc-tweaked.vanilla")
|
||||
id("cc-tweaked.gametest")
|
||||
}
|
||||
@@ -25,6 +26,7 @@ dependencies {
|
||||
clientImplementation(clientClasses(project(":common-api")))
|
||||
|
||||
compileOnly(libs.bundles.externalMods.common)
|
||||
clientCompileOnly(variantOf(libs.emi) { classifier("api") })
|
||||
|
||||
compileOnly(libs.mixin)
|
||||
annotationProcessorEverywhere(libs.autoService)
|
||||
|
@@ -174,7 +174,7 @@ public final class ClientHooks {
|
||||
* @param addText A callback which adds a single line of text.
|
||||
*/
|
||||
public static void addGameDebugInfo(Consumer<String> addText) {
|
||||
if (MonitorBlockEntityRenderer.hasRenderedThisFrame()) {
|
||||
if (MonitorBlockEntityRenderer.hasRenderedThisFrame() && Minecraft.getInstance().options.renderDebug) {
|
||||
addText.accept("[CC:T] Monitor renderer: " + MonitorBlockEntityRenderer.currentRenderer());
|
||||
}
|
||||
}
|
||||
|
@@ -13,6 +13,7 @@ import dan200.computercraft.client.render.RenderTypes;
|
||||
import dan200.computercraft.client.render.TurtleBlockEntityRenderer;
|
||||
import dan200.computercraft.client.render.monitor.MonitorBlockEntityRenderer;
|
||||
import dan200.computercraft.client.turtle.TurtleModemModeller;
|
||||
import dan200.computercraft.client.turtle.TurtleUpgradeModellers;
|
||||
import dan200.computercraft.core.util.Colour;
|
||||
import dan200.computercraft.shared.ModRegistry;
|
||||
import dan200.computercraft.shared.common.IColouredItem;
|
||||
@@ -107,24 +108,6 @@ public final class ClientRegistry {
|
||||
}
|
||||
|
||||
private static final String[] EXTRA_MODELS = new String[]{
|
||||
// Turtle upgrades
|
||||
"block/turtle_modem_normal_off_left",
|
||||
"block/turtle_modem_normal_on_left",
|
||||
"block/turtle_modem_normal_off_right",
|
||||
"block/turtle_modem_normal_on_right",
|
||||
|
||||
"block/turtle_modem_advanced_off_left",
|
||||
"block/turtle_modem_advanced_on_left",
|
||||
"block/turtle_modem_advanced_off_right",
|
||||
"block/turtle_modem_advanced_on_right",
|
||||
|
||||
"block/turtle_crafting_table_left",
|
||||
"block/turtle_crafting_table_right",
|
||||
|
||||
"block/turtle_speaker_left",
|
||||
"block/turtle_speaker_right",
|
||||
|
||||
// Turtle block renderer
|
||||
"block/turtle_colour",
|
||||
"block/turtle_elf_overlay",
|
||||
"block/turtle_rainbow_overlay",
|
||||
@@ -133,6 +116,7 @@ public final class ClientRegistry {
|
||||
|
||||
public static void registerExtraModels(Consumer<ResourceLocation> register) {
|
||||
for (var model : EXTRA_MODELS) register.accept(new ResourceLocation(ComputerCraftAPI.MOD_ID, model));
|
||||
TurtleUpgradeModellers.getDependencies().forEach(register);
|
||||
}
|
||||
|
||||
public static void registerItemColours(BiConsumer<ItemColor, ItemLike> register) {
|
||||
|
@@ -11,6 +11,7 @@ import net.minecraft.ChatFormatting;
|
||||
import net.minecraft.client.GuiMessageTag;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.Font;
|
||||
import net.minecraft.client.gui.components.ChatComponent;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.util.Mth;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
@@ -18,6 +19,12 @@ import org.apache.commons.lang3.StringUtils;
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* A {@link TableFormatter} subclass which writes directly to {@linkplain ChatComponent the chat GUI}.
|
||||
* <p>
|
||||
* Each message written gets a special {@link GuiMessageTag}, so we can remove the previous table of the same
|
||||
* {@linkplain TableBuilder#getId() id}.
|
||||
*/
|
||||
public class ClientTableFormatter implements TableFormatter {
|
||||
public static final ClientTableFormatter INSTANCE = new ClientTableFormatter();
|
||||
|
||||
|
@@ -4,6 +4,11 @@
|
||||
|
||||
package dan200.computercraft.client;
|
||||
|
||||
/**
|
||||
* Tracks the current client-side tick and frame.
|
||||
* <p>
|
||||
* These are updated via {@link ClientHooks}.
|
||||
*/
|
||||
public final class FrameInfo {
|
||||
private static int tick;
|
||||
private static long renderFrame;
|
||||
|
@@ -39,6 +39,12 @@ import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static dan200.computercraft.core.util.Nullability.assertNonNull;
|
||||
|
||||
/**
|
||||
* The base class of all screens with a computer terminal (i.e. {@link ComputerScreen}). This works with
|
||||
* {@link AbstractComputerMenu} to handle the common behaviour such as the terminal, input and file uploading.
|
||||
*
|
||||
* @param <T> The concrete type of the associated menu.
|
||||
*/
|
||||
public abstract class AbstractComputerScreen<T extends AbstractComputerMenu> extends AbstractContainerScreen<T> {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(AbstractComputerScreen.class);
|
||||
|
||||
@@ -55,6 +61,7 @@ public abstract class AbstractComputerScreen<T extends AbstractComputerMenu> ext
|
||||
protected final int sidebarYOffset;
|
||||
|
||||
private long uploadNagDeadline = Long.MAX_VALUE;
|
||||
private final int uploadMaxSize;
|
||||
private final ItemStack displayStack;
|
||||
|
||||
public AbstractComputerScreen(T container, Inventory player, Component title, int sidebarYOffset) {
|
||||
@@ -62,6 +69,7 @@ public abstract class AbstractComputerScreen<T extends AbstractComputerMenu> ext
|
||||
terminalData = container.getTerminal();
|
||||
family = container.getFamily();
|
||||
displayStack = container.getDisplayStack();
|
||||
uploadMaxSize = container.getUploadMaxSize();
|
||||
input = new ClientInputHandler(menu);
|
||||
this.sidebarYOffset = sidebarYOffset;
|
||||
}
|
||||
@@ -161,7 +169,7 @@ public abstract class AbstractComputerScreen<T extends AbstractComputerMenu> ext
|
||||
|
||||
try (var sbc = Files.newByteChannel(file)) {
|
||||
var fileSize = sbc.size();
|
||||
if (fileSize > UploadFileMessage.MAX_SIZE || (size += fileSize) >= UploadFileMessage.MAX_SIZE) {
|
||||
if (fileSize > uploadMaxSize || (size += fileSize) >= uploadMaxSize) {
|
||||
alert(UploadResult.FAILED_TITLE, UploadResult.TOO_MUCH_MSG);
|
||||
return;
|
||||
}
|
||||
|
@@ -16,9 +16,9 @@ import net.minecraft.world.inventory.AbstractContainerMenu;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* An {@link InputHandler} which for use on the client.
|
||||
* An {@link InputHandler} for use on the client.
|
||||
* <p>
|
||||
* This queues events on the remote player's open {@link ComputerMenu}
|
||||
* This queues events on the remote player's open {@link ComputerMenu}.
|
||||
*/
|
||||
public final class ClientInputHandler implements InputHandler {
|
||||
private final AbstractContainerMenu menu;
|
||||
|
@@ -15,6 +15,13 @@ import net.minecraft.world.entity.player.Inventory;
|
||||
import static dan200.computercraft.client.render.ComputerBorderRenderer.BORDER;
|
||||
import static dan200.computercraft.client.render.RenderTypes.FULL_BRIGHT_LIGHTMAP;
|
||||
|
||||
/**
|
||||
* A GUI for computers which renders the terminal (and border), but with no UI elements.
|
||||
* <p>
|
||||
* This is used by computers and pocket computers.
|
||||
*
|
||||
* @param <T> The concrete type of the associated menu.
|
||||
*/
|
||||
public final class ComputerScreen<T extends AbstractComputerMenu> extends AbstractComputerScreen<T> {
|
||||
public ComputerScreen(T container, Inventory player, Component title) {
|
||||
super(container, player, title, BORDER);
|
||||
|
@@ -12,7 +12,9 @@ import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.entity.player.Inventory;
|
||||
|
||||
|
||||
/**
|
||||
* The GUI for disk drives.
|
||||
*/
|
||||
public class DiskDriveScreen extends AbstractContainerScreen<DiskDriveMenu> {
|
||||
private static final ResourceLocation BACKGROUND = new ResourceLocation("computercraft", "textures/gui/disk_drive.png");
|
||||
|
||||
|
@@ -19,6 +19,11 @@ import javax.annotation.Nullable;
|
||||
|
||||
import static dan200.computercraft.core.util.Nullability.assertNonNull;
|
||||
|
||||
/**
|
||||
* The GUI for off-hand computers. This accepts keyboard input, but does not render a terminal.
|
||||
*
|
||||
* @param <T> The concrete type of the associated menu.
|
||||
*/
|
||||
public class NoTermComputerScreen<T extends AbstractComputerMenu> extends Screen implements MenuAccess<T> {
|
||||
private final T menu;
|
||||
private final Terminal terminalData;
|
||||
|
@@ -19,6 +19,11 @@ import java.util.List;
|
||||
|
||||
import static dan200.computercraft.core.util.Nullability.assertNonNull;
|
||||
|
||||
/**
|
||||
* A screen which displays a series of buttons (such as a yes/no prompt).
|
||||
* <p>
|
||||
* When closed, it returns to the previous screen.
|
||||
*/
|
||||
public final class OptionScreen extends Screen {
|
||||
private static final ResourceLocation BACKGROUND = new ResourceLocation("computercraft", "textures/gui/blank_screen.png");
|
||||
|
||||
|
@@ -12,7 +12,9 @@ import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.entity.player.Inventory;
|
||||
|
||||
|
||||
/**
|
||||
* The GUI for printers.
|
||||
*/
|
||||
public class PrinterScreen extends AbstractContainerScreen<PrinterMenu> {
|
||||
private static final ResourceLocation BACKGROUND = new ResourceLocation("computercraft", "textures/gui/printer.png");
|
||||
|
||||
|
@@ -19,6 +19,11 @@ import org.lwjgl.glfw.GLFW;
|
||||
import static dan200.computercraft.client.render.PrintoutRenderer.*;
|
||||
import static dan200.computercraft.client.render.RenderTypes.FULL_BRIGHT_LIGHTMAP;
|
||||
|
||||
/**
|
||||
* The GUI for printed pages and books.
|
||||
*
|
||||
* @see dan200.computercraft.client.render.PrintoutRenderer
|
||||
*/
|
||||
public class PrintoutScreen extends AbstractContainerScreen<HeldItemMenu> {
|
||||
private final boolean book;
|
||||
private final int pages;
|
||||
@@ -46,8 +51,6 @@ public class PrintoutScreen extends AbstractContainerScreen<HeldItemMenu> {
|
||||
|
||||
@Override
|
||||
public boolean keyPressed(int key, int scancode, int modifiers) {
|
||||
if (super.keyPressed(key, scancode, modifiers)) return true;
|
||||
|
||||
if (key == GLFW.GLFW_KEY_RIGHT) {
|
||||
if (page < pages - 1) page++;
|
||||
return true;
|
||||
@@ -58,7 +61,7 @@ public class PrintoutScreen extends AbstractContainerScreen<HeldItemMenu> {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
return super.keyPressed(key, scancode, modifiers);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@@ -19,13 +19,18 @@ import net.minecraft.world.entity.player.Inventory;
|
||||
|
||||
import static dan200.computercraft.shared.turtle.inventory.TurtleMenu.*;
|
||||
|
||||
/**
|
||||
* The GUI for turtles.
|
||||
*/
|
||||
public class TurtleScreen extends AbstractComputerScreen<TurtleMenu> {
|
||||
private static final ResourceLocation BACKGROUND_NORMAL = new ResourceLocation(ComputerCraftAPI.MOD_ID, "textures/gui/turtle_normal.png");
|
||||
private static final ResourceLocation BACKGROUND_ADVANCED = new ResourceLocation(ComputerCraftAPI.MOD_ID, "textures/gui/turtle_advanced.png");
|
||||
|
||||
private static final int TEX_WIDTH = 254;
|
||||
private static final int TEX_WIDTH = 278;
|
||||
private static final int TEX_HEIGHT = 217;
|
||||
|
||||
private static final int FULL_TEX_SIZE = 512;
|
||||
|
||||
public TurtleScreen(TurtleMenu container, Inventory player, Component title) {
|
||||
super(container, player, title, BORDER);
|
||||
|
||||
@@ -42,16 +47,16 @@ public class TurtleScreen extends AbstractComputerScreen<TurtleMenu> {
|
||||
protected void renderBg(PoseStack transform, float partialTicks, int mouseX, int mouseY) {
|
||||
var advanced = family == ComputerFamily.ADVANCED;
|
||||
RenderSystem.setShaderTexture(0, advanced ? BACKGROUND_ADVANCED : BACKGROUND_NORMAL);
|
||||
blit(transform, leftPos + AbstractComputerMenu.SIDEBAR_WIDTH, topPos, 0, 0, TEX_WIDTH, TEX_HEIGHT);
|
||||
blit(transform, leftPos + AbstractComputerMenu.SIDEBAR_WIDTH, topPos, 0, 0, 0, TEX_WIDTH, TEX_HEIGHT, FULL_TEX_SIZE, FULL_TEX_SIZE);
|
||||
|
||||
// Render selected slot
|
||||
var slot = getMenu().getSelectedSlot();
|
||||
if (slot >= 0) {
|
||||
RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F);
|
||||
var slotX = slot % 4;
|
||||
var slotY = slot / 4;
|
||||
blit(transform,
|
||||
leftPos + TURTLE_START_X - 2 + slotX * 18, topPos + PLAYER_START_Y - 2 + slotY * 18,
|
||||
0, 217, 24, 24
|
||||
leftPos + TURTLE_START_X - 2 + slotX * 18, topPos + PLAYER_START_Y - 2 + slotY * 18, 0,
|
||||
0, 217, 24, 24, FULL_TEX_SIZE, FULL_TEX_SIZE
|
||||
);
|
||||
}
|
||||
|
||||
|
@@ -45,11 +45,11 @@ public final class ComputerSidebar {
|
||||
x += CORNERS_BORDER + 1;
|
||||
y += CORNERS_BORDER + ICON_MARGIN;
|
||||
|
||||
var turnOn = new HintedMessage(
|
||||
var turnOn = new HintedMessage(Component.translatable("gui.computercraft.tooltip.turn_on"), (Component) null);
|
||||
var turnOff = new HintedMessage(
|
||||
Component.translatable("gui.computercraft.tooltip.turn_off"),
|
||||
Component.translatable("gui.computercraft.tooltip.turn_off.key")
|
||||
);
|
||||
var turnOff = new HintedMessage(Component.translatable("gui.computercraft.tooltip.turn_on"), (Component) null);
|
||||
add.accept(new DynamicImageButton(
|
||||
x, y, ICON_WIDTH, ICON_HEIGHT, () -> isOn.getAsBoolean() ? 15 : 1, 1, ICON_TEX_Y_DIFF,
|
||||
TEXTURE, TEX_SIZE, TEX_SIZE, b -> toggleComputer(isOn, input),
|
||||
|
@@ -68,14 +68,11 @@ public class DynamicImageButton extends Button {
|
||||
RenderSystem.enableDepthTest();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component getMessage() {
|
||||
return message.get().message;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(PoseStack stack, int mouseX, int mouseY, float partialTicks) {
|
||||
setTooltip(message.get().tooltip());
|
||||
var message = this.message.get();
|
||||
setMessage(message.message());
|
||||
setTooltip(message.tooltip());
|
||||
super.render(stack, mouseX, mouseY, partialTicks);
|
||||
}
|
||||
|
||||
|
@@ -15,6 +15,7 @@ import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.components.AbstractWidget;
|
||||
import net.minecraft.client.gui.narration.NarratedElementType;
|
||||
import net.minecraft.client.gui.narration.NarrationElementOutput;
|
||||
import net.minecraft.client.gui.screens.Screen;
|
||||
import net.minecraft.client.renderer.MultiBufferSource;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import org.lwjgl.glfw.GLFW;
|
||||
@@ -25,6 +26,12 @@ 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 widget which renders a computer terminal and handles input events (keyboard, mouse, clipboard) and computer
|
||||
* shortcuts (terminate/shutdown/reboot).
|
||||
*
|
||||
* @see dan200.computercraft.client.gui.ClientInputHandler The input handler typically used with this class.
|
||||
*/
|
||||
public class TerminalWidget extends AbstractWidget {
|
||||
private static final Component DESCRIPTION = Component.translatable("gui.computercraft.terminal");
|
||||
|
||||
@@ -75,6 +82,11 @@ public class TerminalWidget extends AbstractWidget {
|
||||
@Override
|
||||
public boolean keyPressed(int key, int scancode, int modifiers) {
|
||||
if (key == GLFW.GLFW_KEY_ESCAPE) return false;
|
||||
if (Screen.isPaste(key)) {
|
||||
paste();
|
||||
return true;
|
||||
}
|
||||
|
||||
if ((modifiers & GLFW.GLFW_MOD_CONTROL) != 0) {
|
||||
switch (key) {
|
||||
case GLFW.GLFW_KEY_T -> {
|
||||
@@ -86,32 +98,6 @@ public class TerminalWidget extends AbstractWidget {
|
||||
case GLFW.GLFW_KEY_R -> {
|
||||
if (rebootTimer < 0) rebootTimer = 0;
|
||||
}
|
||||
case GLFW.GLFW_KEY_V -> {
|
||||
// Ctrl+V for paste
|
||||
var clipboard = Minecraft.getInstance().keyboardHandler.getClipboard();
|
||||
if (clipboard != null) {
|
||||
// Clip to the first occurrence of \r or \n
|
||||
var newLineIndex1 = clipboard.indexOf("\r");
|
||||
var newLineIndex2 = clipboard.indexOf("\n");
|
||||
if (newLineIndex1 >= 0 && newLineIndex2 >= 0) {
|
||||
clipboard = clipboard.substring(0, Math.min(newLineIndex1, newLineIndex2));
|
||||
} else if (newLineIndex1 >= 0) {
|
||||
clipboard = clipboard.substring(0, newLineIndex1);
|
||||
} else if (newLineIndex2 >= 0) {
|
||||
clipboard = clipboard.substring(0, newLineIndex2);
|
||||
}
|
||||
|
||||
// Filter the string
|
||||
clipboard = SharedConstants.filterText(clipboard);
|
||||
if (!clipboard.isEmpty()) {
|
||||
// Clip to 512 characters and queue the event
|
||||
if (clipboard.length() > 512) clipboard = clipboard.substring(0, 512);
|
||||
computer.queueEvent("paste", new Object[]{ clipboard });
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -125,6 +111,29 @@ public class TerminalWidget extends AbstractWidget {
|
||||
return true;
|
||||
}
|
||||
|
||||
private void paste() {
|
||||
var clipboard = Minecraft.getInstance().keyboardHandler.getClipboard();
|
||||
|
||||
// Clip to the first occurrence of \r or \n
|
||||
var newLineIndex1 = clipboard.indexOf('\r');
|
||||
var newLineIndex2 = clipboard.indexOf('\n');
|
||||
if (newLineIndex1 >= 0 && newLineIndex2 >= 0) {
|
||||
clipboard = clipboard.substring(0, Math.min(newLineIndex1, newLineIndex2));
|
||||
} else if (newLineIndex1 >= 0) {
|
||||
clipboard = clipboard.substring(0, newLineIndex1);
|
||||
} else if (newLineIndex2 >= 0) {
|
||||
clipboard = clipboard.substring(0, newLineIndex2);
|
||||
}
|
||||
|
||||
// Filter the string
|
||||
clipboard = SharedConstants.filterText(clipboard);
|
||||
if (!clipboard.isEmpty()) {
|
||||
// Clip to 512 characters and queue the event
|
||||
if (clipboard.length() > 512) clipboard = clipboard.substring(0, 512);
|
||||
computer.queueEvent("paste", new Object[]{ clipboard });
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean keyReleased(int key, int scancode, int modifiers) {
|
||||
// Queue the "key_up" event and remove from the down set
|
||||
|
@@ -0,0 +1,44 @@
|
||||
// SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers
|
||||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package dan200.computercraft.client.integration.emi;
|
||||
|
||||
import dan200.computercraft.api.turtle.TurtleSide;
|
||||
import dan200.computercraft.shared.ModRegistry;
|
||||
import dan200.computercraft.shared.pocket.items.PocketComputerItem;
|
||||
import dan200.computercraft.shared.turtle.items.TurtleItem;
|
||||
import dev.emi.emi.api.EmiEntrypoint;
|
||||
import dev.emi.emi.api.EmiPlugin;
|
||||
import dev.emi.emi.api.EmiRegistry;
|
||||
import dev.emi.emi.api.stack.Comparison;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
|
||||
import java.util.function.BiPredicate;
|
||||
|
||||
@EmiEntrypoint
|
||||
public class EMIComputerCraft implements EmiPlugin {
|
||||
@Override
|
||||
public void register(EmiRegistry registry) {
|
||||
registry.setDefaultComparison(ModRegistry.Items.TURTLE_NORMAL.get(), turtleComparison);
|
||||
registry.setDefaultComparison(ModRegistry.Items.TURTLE_ADVANCED.get(), turtleComparison);
|
||||
|
||||
registry.setDefaultComparison(ModRegistry.Items.POCKET_COMPUTER_NORMAL.get(), pocketComparison);
|
||||
registry.setDefaultComparison(ModRegistry.Items.POCKET_COMPUTER_ADVANCED.get(), pocketComparison);
|
||||
}
|
||||
|
||||
private static final Comparison turtleComparison = compareStacks((left, right) ->
|
||||
left.getItem() instanceof TurtleItem turtle
|
||||
&& turtle.getUpgrade(left, TurtleSide.LEFT) == turtle.getUpgrade(right, TurtleSide.LEFT)
|
||||
&& turtle.getUpgrade(left, TurtleSide.RIGHT) == turtle.getUpgrade(right, TurtleSide.RIGHT));
|
||||
|
||||
private static final Comparison pocketComparison = compareStacks((left, right) ->
|
||||
left.getItem() instanceof PocketComputerItem && PocketComputerItem.getUpgrade(left) == PocketComputerItem.getUpgrade(right));
|
||||
|
||||
private static Comparison compareStacks(BiPredicate<ItemStack, ItemStack> test) {
|
||||
return Comparison.of((left, right) -> {
|
||||
ItemStack leftStack = left.getItemStack(), rightStack = right.getItemStack();
|
||||
return leftStack.getItem() == rightStack.getItem() && test.test(leftStack, rightStack);
|
||||
});
|
||||
}
|
||||
}
|
@@ -0,0 +1,103 @@
|
||||
// SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers
|
||||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package dan200.computercraft.client.model.turtle;
|
||||
|
||||
import com.mojang.blaze3d.vertex.DefaultVertexFormat;
|
||||
import com.mojang.blaze3d.vertex.VertexFormat;
|
||||
import com.mojang.blaze3d.vertex.VertexFormatElement;
|
||||
import com.mojang.math.Transformation;
|
||||
import net.minecraft.client.renderer.block.model.BakedQuad;
|
||||
import net.minecraft.client.resources.model.BakedModel;
|
||||
import net.minecraft.core.Direction;
|
||||
import org.joml.Matrix4f;
|
||||
import org.joml.Vector4f;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Applies a {@link Transformation} (or rather a {@link Matrix4f}) to a list of {@link BakedQuad}s.
|
||||
* <p>
|
||||
* This does a little bit of magic compared with other system (i.e. Forge's {@code QuadTransformers}), as it needs to
|
||||
* handle flipping models upside down.
|
||||
* <p>
|
||||
* This is typically used with a {@link BakedModel} subclass - see the loader-specific projects.
|
||||
*/
|
||||
public class ModelTransformer {
|
||||
private static final int[] INVERSE_ORDER = new int[]{ 3, 2, 1, 0 };
|
||||
|
||||
private static final int STRIDE = DefaultVertexFormat.BLOCK.getIntegerSize();
|
||||
private static final int POS_OFFSET = findOffset(DefaultVertexFormat.BLOCK, DefaultVertexFormat.ELEMENT_POSITION);
|
||||
|
||||
protected final Matrix4f transformation;
|
||||
protected final boolean invert;
|
||||
private @Nullable TransformedQuads cache;
|
||||
|
||||
public ModelTransformer(Transformation transformation) {
|
||||
this.transformation = transformation.getMatrix();
|
||||
invert = transformation.getMatrix().determinant() < 0;
|
||||
}
|
||||
|
||||
public List<BakedQuad> transform(List<BakedQuad> quads) {
|
||||
if (quads.isEmpty()) return List.of();
|
||||
|
||||
// We do some basic caching here to avoid recomputing every frame. Most turtle models don't have culled faces,
|
||||
// so it's not worth being smarter here.
|
||||
var cache = this.cache;
|
||||
if (cache != null && quads.equals(cache.original())) return cache.transformed();
|
||||
|
||||
List<BakedQuad> transformed = new ArrayList<>(quads.size());
|
||||
for (var quad : quads) transformed.add(transformQuad(quad));
|
||||
this.cache = new TransformedQuads(quads, transformed);
|
||||
return transformed;
|
||||
}
|
||||
|
||||
private BakedQuad transformQuad(BakedQuad quad) {
|
||||
var inputData = quad.getVertices();
|
||||
var outputData = new int[inputData.length];
|
||||
for (var i = 0; i < 4; i++) {
|
||||
var inStart = STRIDE * i;
|
||||
// Reverse the order of the quads if we're inverting
|
||||
var outStart = getVertexOffset(i, invert);
|
||||
System.arraycopy(inputData, inStart, outputData, outStart, STRIDE);
|
||||
|
||||
// Apply the matrix to our position
|
||||
var inPosStart = inStart + POS_OFFSET;
|
||||
var outPosStart = outStart + POS_OFFSET;
|
||||
|
||||
var x = Float.intBitsToFloat(inputData[inPosStart]);
|
||||
var y = Float.intBitsToFloat(inputData[inPosStart + 1]);
|
||||
var z = Float.intBitsToFloat(inputData[inPosStart + 2]);
|
||||
|
||||
// Transform the position
|
||||
var pos = new Vector4f(x, y, z, 1);
|
||||
transformation.transformProject(pos);
|
||||
|
||||
outputData[outPosStart] = Float.floatToRawIntBits(pos.x());
|
||||
outputData[outPosStart + 1] = Float.floatToRawIntBits(pos.y());
|
||||
outputData[outPosStart + 2] = Float.floatToRawIntBits(pos.z());
|
||||
}
|
||||
|
||||
var direction = Direction.rotate(transformation, quad.getDirection());
|
||||
return new BakedQuad(outputData, quad.getTintIndex(), direction, quad.getSprite(), quad.isShade());
|
||||
}
|
||||
|
||||
public static int getVertexOffset(int vertex, boolean invert) {
|
||||
return (invert ? ModelTransformer.INVERSE_ORDER[vertex] : vertex) * ModelTransformer.STRIDE;
|
||||
}
|
||||
|
||||
private record TransformedQuads(List<BakedQuad> original, List<BakedQuad> transformed) {
|
||||
}
|
||||
|
||||
private static int findOffset(VertexFormat format, VertexFormatElement element) {
|
||||
var offset = 0;
|
||||
for (var other : format.getElements()) {
|
||||
if (other == element) return offset / Integer.BYTES;
|
||||
offset += element.getByteSize();
|
||||
}
|
||||
throw new IllegalArgumentException("Cannot find " + element + " in " + format);
|
||||
}
|
||||
}
|
@@ -4,11 +4,13 @@
|
||||
|
||||
package dan200.computercraft.client.model.turtle;
|
||||
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import com.mojang.math.Transformation;
|
||||
import dan200.computercraft.api.client.TransformedModel;
|
||||
import dan200.computercraft.api.turtle.ITurtleUpgrade;
|
||||
import dan200.computercraft.api.turtle.TurtleSide;
|
||||
import dan200.computercraft.api.upgrades.UpgradeData;
|
||||
import dan200.computercraft.client.platform.ClientPlatformHelper;
|
||||
import dan200.computercraft.client.render.TurtleBlockEntityRenderer;
|
||||
import dan200.computercraft.client.turtle.TurtleUpgradeModellers;
|
||||
@@ -21,15 +23,17 @@ import net.minecraft.world.item.ItemStack;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* Combines several individual models together to form a turtle.
|
||||
*
|
||||
* @param <T> The type of the resulting "baked model".
|
||||
*/
|
||||
public final class TurtleModelParts {
|
||||
public final class TurtleModelParts<T> {
|
||||
private static final Transformation identity, flip;
|
||||
|
||||
static {
|
||||
@@ -42,33 +46,67 @@ public final class TurtleModelParts {
|
||||
flip = new Transformation(stack.last().pose());
|
||||
}
|
||||
|
||||
public record Combination(
|
||||
private record Combination(
|
||||
boolean colour,
|
||||
@Nullable ITurtleUpgrade leftUpgrade,
|
||||
@Nullable ITurtleUpgrade rightUpgrade,
|
||||
@Nullable UpgradeData<ITurtleUpgrade> leftUpgrade,
|
||||
@Nullable UpgradeData<ITurtleUpgrade> rightUpgrade,
|
||||
@Nullable ResourceLocation overlay,
|
||||
boolean christmas,
|
||||
boolean flip
|
||||
) {
|
||||
Combination copy() {
|
||||
if (leftUpgrade == null && rightUpgrade == null) return this;
|
||||
return new Combination(
|
||||
colour, UpgradeData.copyOf(leftUpgrade), UpgradeData.copyOf(rightUpgrade),
|
||||
overlay, christmas, flip
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private final BakedModel familyModel;
|
||||
private final BakedModel colourModel;
|
||||
private final Function<TransformedModel, BakedModel> transformer;
|
||||
private final Function<Combination, T> buildModel;
|
||||
|
||||
/**
|
||||
* A cache of {@link TransformedModel} to the transformed {@link BakedModel}. This helps us pool the transformed
|
||||
* instances, reducing memory usage and hopefully ensuring their caches are hit more often!
|
||||
*/
|
||||
private final Map<TransformedModel, BakedModel> transformCache = new HashMap<>();
|
||||
private final Map<TransformedModel, BakedModel> transformCache = CacheBuilder.newBuilder()
|
||||
.concurrencyLevel(1)
|
||||
.expireAfterAccess(30, TimeUnit.SECONDS)
|
||||
.<TransformedModel, BakedModel>build()
|
||||
.asMap();
|
||||
|
||||
public TurtleModelParts(BakedModel familyModel, BakedModel colourModel, ModelTransformer transformer) {
|
||||
/**
|
||||
* A cache of {@link Combination}s to the combined model.
|
||||
*/
|
||||
private final Map<Combination, T> modelCache = CacheBuilder.newBuilder()
|
||||
.concurrencyLevel(1)
|
||||
.expireAfterAccess(30, TimeUnit.SECONDS)
|
||||
.<Combination, T>build()
|
||||
.asMap();
|
||||
|
||||
public TurtleModelParts(BakedModel familyModel, BakedModel colourModel, ModelTransformer transformer, Function<List<BakedModel>, T> combineModel) {
|
||||
this.familyModel = familyModel;
|
||||
this.colourModel = colourModel;
|
||||
this.transformer = x -> transformer.transform(x.getModel(), x.getMatrix());
|
||||
buildModel = x -> combineModel.apply(buildModel(x));
|
||||
}
|
||||
|
||||
public Combination getCombination(ItemStack stack) {
|
||||
public T getModel(ItemStack stack) {
|
||||
var combination = getCombination(stack);
|
||||
var existing = modelCache.get(combination);
|
||||
if (existing != null) return existing;
|
||||
|
||||
// Take a defensive copy of the upgrade data, and add it to the cache.
|
||||
var newCombination = combination.copy();
|
||||
var newModel = buildModel.apply(newCombination);
|
||||
modelCache.put(newCombination, newModel);
|
||||
return newModel;
|
||||
}
|
||||
|
||||
private Combination getCombination(ItemStack stack) {
|
||||
var christmas = Holiday.getCurrent() == Holiday.CHRISTMAS;
|
||||
|
||||
if (!(stack.getItem() instanceof TurtleItem turtle)) {
|
||||
@@ -76,8 +114,8 @@ public final class TurtleModelParts {
|
||||
}
|
||||
|
||||
var colour = turtle.getColour(stack);
|
||||
var leftUpgrade = turtle.getUpgrade(stack, TurtleSide.LEFT);
|
||||
var rightUpgrade = turtle.getUpgrade(stack, TurtleSide.RIGHT);
|
||||
var leftUpgrade = turtle.getUpgradeWithData(stack, TurtleSide.LEFT);
|
||||
var rightUpgrade = turtle.getUpgradeWithData(stack, TurtleSide.RIGHT);
|
||||
var overlay = turtle.getOverlay(stack);
|
||||
var label = turtle.getLabel(stack);
|
||||
var flip = label != null && (label.equals("Dinnerbone") || label.equals("Grumm"));
|
||||
@@ -85,7 +123,7 @@ public final class TurtleModelParts {
|
||||
return new Combination(colour != -1, leftUpgrade, rightUpgrade, overlay, christmas, flip);
|
||||
}
|
||||
|
||||
public List<BakedModel> buildModel(Combination combo) {
|
||||
private List<BakedModel> buildModel(Combination combo) {
|
||||
var mc = Minecraft.getInstance();
|
||||
var modelManager = mc.getItemRenderer().getItemModelShaper().getModelManager();
|
||||
|
||||
@@ -97,19 +135,20 @@ public final class TurtleModelParts {
|
||||
if (overlayModelLocation != null) {
|
||||
parts.add(transform(ClientPlatformHelper.get().getModel(modelManager, overlayModelLocation), transformation));
|
||||
}
|
||||
if (combo.leftUpgrade() != null) {
|
||||
var model = TurtleUpgradeModellers.getModel(combo.leftUpgrade(), null, TurtleSide.LEFT);
|
||||
parts.add(transform(model.getModel(), transformation.compose(model.getMatrix())));
|
||||
}
|
||||
if (combo.rightUpgrade() != null) {
|
||||
var model = TurtleUpgradeModellers.getModel(combo.rightUpgrade(), null, TurtleSide.RIGHT);
|
||||
parts.add(transform(model.getModel(), transformation.compose(model.getMatrix())));
|
||||
}
|
||||
|
||||
addUpgrade(parts, transformation, TurtleSide.LEFT, combo.leftUpgrade());
|
||||
addUpgrade(parts, transformation, TurtleSide.RIGHT, combo.rightUpgrade());
|
||||
|
||||
return parts;
|
||||
}
|
||||
|
||||
public BakedModel transform(BakedModel model, Transformation transformation) {
|
||||
private void addUpgrade(List<BakedModel> parts, Transformation transformation, TurtleSide side, @Nullable UpgradeData<ITurtleUpgrade> upgrade) {
|
||||
if (upgrade == null) return;
|
||||
var model = TurtleUpgradeModellers.getModel(upgrade.upgrade(), upgrade.data(), side);
|
||||
parts.add(transform(model.getModel(), transformation.compose(model.getMatrix())));
|
||||
}
|
||||
|
||||
private BakedModel transform(BakedModel model, Transformation transformation) {
|
||||
if (transformation.equals(Transformation.identity())) return model;
|
||||
return transformCache.computeIfAbsent(new TransformedModel(model, transformation), transformer);
|
||||
}
|
||||
|
@@ -4,8 +4,13 @@
|
||||
|
||||
package dan200.computercraft.client.platform;
|
||||
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import dan200.computercraft.shared.network.NetworkMessage;
|
||||
import dan200.computercraft.shared.network.server.ServerNetworkContext;
|
||||
import net.minecraft.client.renderer.MultiBufferSource;
|
||||
import net.minecraft.client.resources.model.BakedModel;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public interface ClientPlatformHelper extends dan200.computercraft.impl.client.ClientPlatformHelper {
|
||||
static ClientPlatformHelper get() {
|
||||
@@ -18,4 +23,16 @@ public interface ClientPlatformHelper extends dan200.computercraft.impl.client.C
|
||||
* @param message The message to send.
|
||||
*/
|
||||
void sendToServer(NetworkMessage<ServerNetworkContext> message);
|
||||
|
||||
/**
|
||||
* Render a {@link BakedModel}, using any loader-specific hooks.
|
||||
*
|
||||
* @param transform The current matrix transformation to apply.
|
||||
* @param buffers The current pool of render buffers.
|
||||
* @param model The model to draw.
|
||||
* @param lightmapCoord The current packed lightmap coordinate.
|
||||
* @param overlayLight The current overlay light.
|
||||
* @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);
|
||||
}
|
||||
|
@@ -13,6 +13,10 @@ import net.minecraft.client.renderer.RenderType;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import org.joml.Matrix4f;
|
||||
|
||||
/**
|
||||
* Renders the borders of computers, either for a GUI ({@link dan200.computercraft.client.gui.ComputerScreen}) or
|
||||
* {@linkplain PocketItemRenderer in-hand pocket computers}.
|
||||
*/
|
||||
public class ComputerBorderRenderer {
|
||||
public static final ResourceLocation BACKGROUND_NORMAL = new ResourceLocation(ComputerCraftAPI.MOD_ID, "textures/gui/corners_normal.png");
|
||||
public static final ResourceLocation BACKGROUND_ADVANCED = new ResourceLocation(ComputerCraftAPI.MOD_ID, "textures/gui/corners_advanced.png");
|
||||
|
@@ -9,14 +9,19 @@ import com.mojang.math.Axis;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.ItemInHandRenderer;
|
||||
import net.minecraft.client.renderer.MultiBufferSource;
|
||||
import net.minecraft.client.renderer.block.model.ItemTransforms;
|
||||
import net.minecraft.util.Mth;
|
||||
import net.minecraft.world.InteractionHand;
|
||||
import net.minecraft.world.entity.HumanoidArm;
|
||||
import net.minecraft.world.entity.LivingEntity;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.item.ItemDisplayContext;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
|
||||
/**
|
||||
* A base class for items which have map-like rendering when held in the hand.
|
||||
*
|
||||
* @see dan200.computercraft.client.ClientHooks#onRenderHeldItem(PoseStack, MultiBufferSource, int, InteractionHand, float, float, float, ItemStack)
|
||||
*/
|
||||
public abstract class ItemMapLikeRenderer {
|
||||
/**
|
||||
* The main rendering method for the item.
|
||||
@@ -25,7 +30,7 @@ public abstract class ItemMapLikeRenderer {
|
||||
* @param render The buffer to render to
|
||||
* @param stack The stack to render
|
||||
* @param light The packed lightmap coordinates.
|
||||
* @see ItemInHandRenderer#renderItem(LivingEntity, ItemStack, ItemTransforms.TransformType, boolean, PoseStack, MultiBufferSource, int)
|
||||
* @see ItemInHandRenderer#renderItem(LivingEntity, ItemStack, ItemDisplayContext, boolean, PoseStack, MultiBufferSource, int)
|
||||
*/
|
||||
protected abstract void renderItem(PoseStack transform, MultiBufferSource render, ItemStack stack, int light);
|
||||
|
||||
|
@@ -0,0 +1,103 @@
|
||||
// SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers
|
||||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package dan200.computercraft.client.render;
|
||||
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import com.mojang.blaze3d.vertex.VertexConsumer;
|
||||
import dan200.computercraft.client.model.turtle.ModelTransformer;
|
||||
import dan200.computercraft.client.platform.ClientPlatformHelper;
|
||||
import net.minecraft.client.renderer.MultiBufferSource;
|
||||
import net.minecraft.client.renderer.block.model.BakedQuad;
|
||||
import net.minecraft.client.renderer.entity.ItemRenderer;
|
||||
import net.minecraft.client.resources.model.BakedModel;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import org.joml.Vector4f;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Utilities for rendering {@link BakedModel}s and {@link BakedQuad}s.
|
||||
*/
|
||||
public final class ModelRenderer {
|
||||
private ModelRenderer() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Render a list of {@linkplain BakedQuad quads} to a buffer.
|
||||
* <p>
|
||||
* This is not intended to be used directly, but instead by {@link ClientPlatformHelper#renderBakedModel(PoseStack, MultiBufferSource, BakedModel, int, int, int[])}. The
|
||||
* implementation here is pretty similar to {@link ItemRenderer#renderQuadList(PoseStack, VertexConsumer, List, ItemStack, int, int)},
|
||||
* but supports inverted quads (i.e. those with a negative scale).
|
||||
*
|
||||
* @param transform The current matrix transformation to apply.
|
||||
* @param buffer The buffer to draw to.
|
||||
* @param quads The quads to draw.
|
||||
* @param lightmapCoord The current packed lightmap coordinate.
|
||||
* @param overlayLight The current overlay light.
|
||||
* @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) {
|
||||
var matrix = transform.last();
|
||||
var inverted = matrix.pose().determinant() < 0;
|
||||
|
||||
for (var bakedquad : quads) {
|
||||
var tint = -1;
|
||||
if (tints != null && bakedquad.isTinted()) {
|
||||
var idx = bakedquad.getTintIndex();
|
||||
if (idx >= 0 && idx < tints.length) tint = tints[bakedquad.getTintIndex()];
|
||||
}
|
||||
|
||||
var r = (float) (tint >> 16 & 255) / 255.0F;
|
||||
var g = (float) (tint >> 8 & 255) / 255.0F;
|
||||
var b = (float) (tint & 255) / 255.0F;
|
||||
putBulkQuad(buffer, matrix, bakedquad, r, g, b, lightmapCoord, overlayLight, inverted);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A version of {@link VertexConsumer#putBulkData(PoseStack.Pose, BakedQuad, float, float, float, int, int)} which
|
||||
* will reverse vertex order when the matrix is inverted.
|
||||
*
|
||||
* @param buffer The buffer to draw to.
|
||||
* @param pose The current matrix stack.
|
||||
* @param quad The quad to draw.
|
||||
* @param red The red tint of this quad.
|
||||
* @param green The green tint of this quad.
|
||||
* @param blue The blue tint of this quad.
|
||||
* @param lightmapCoord The lightmap coordinate
|
||||
* @param overlayLight The overlay light.
|
||||
* @param invert Whether to reverse the order of this quad.
|
||||
*/
|
||||
private static void putBulkQuad(VertexConsumer buffer, PoseStack.Pose pose, BakedQuad quad, float red, float green, float blue, int lightmapCoord, int overlayLight, boolean invert) {
|
||||
var matrix = pose.pose();
|
||||
// It's a little dubious to transform using this matrix rather than the normal matrix. This mirrors the logic in
|
||||
// Direction.rotate (so not out of nowhere!), but is a little suspicious.
|
||||
var dirNormal = quad.getDirection().getNormal();
|
||||
var vector = new Vector4f();
|
||||
|
||||
matrix.transform(dirNormal.getX(), dirNormal.getY(), dirNormal.getZ(), 0.0f, vector).normalize();
|
||||
float normalX = vector.x(), normalY = vector.y(), normalZ = vector.z();
|
||||
|
||||
var vertices = quad.getVertices();
|
||||
for (var vertex = 0; vertex < 4; vertex++) {
|
||||
var i = ModelTransformer.getVertexOffset(vertex, invert);
|
||||
|
||||
var x = Float.intBitsToFloat(vertices[i]);
|
||||
var y = Float.intBitsToFloat(vertices[i + 1]);
|
||||
var z = Float.intBitsToFloat(vertices[i + 2]);
|
||||
|
||||
matrix.transform(x, y, z, 1, vector);
|
||||
|
||||
var u = Float.intBitsToFloat(vertices[i + 4]);
|
||||
var v = Float.intBitsToFloat(vertices[i + 5]);
|
||||
buffer.vertex(
|
||||
vector.x(), vector.y(), vector.z(),
|
||||
red, green, blue, 1.0F, u, v, overlayLight, lightmapCoord,
|
||||
normalX, normalY, normalZ
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
@@ -15,6 +15,10 @@ import org.joml.Matrix4f;
|
||||
import static dan200.computercraft.client.render.text.FixedWidthFontRenderer.FONT_HEIGHT;
|
||||
import static dan200.computercraft.shared.media.items.PrintoutItem.LINES_PER_PAGE;
|
||||
|
||||
/**
|
||||
* Renders printed pages or books, either for a GUI ({@link dan200.computercraft.client.gui.PrintoutScreen}) or
|
||||
* {@linkplain PrintoutItemRenderer in-hand/item frame printouts}.
|
||||
*/
|
||||
public final class PrintoutRenderer {
|
||||
private static final float BG_SIZE = 256.0f;
|
||||
|
||||
|
@@ -22,6 +22,9 @@ import java.util.Objects;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* Shared {@link RenderType}s used throughout the mod.
|
||||
*/
|
||||
public class RenderTypes {
|
||||
public static final int FULL_BRIGHT_LIGHTMAP = (0xF << 4) | (0xF << 20);
|
||||
|
||||
|
@@ -5,7 +5,6 @@
|
||||
package dan200.computercraft.client.render;
|
||||
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import com.mojang.blaze3d.vertex.VertexConsumer;
|
||||
import com.mojang.math.Axis;
|
||||
import com.mojang.math.Transformation;
|
||||
import dan200.computercraft.api.ComputerCraftAPI;
|
||||
@@ -14,25 +13,20 @@ import dan200.computercraft.client.platform.ClientPlatformHelper;
|
||||
import dan200.computercraft.client.turtle.TurtleUpgradeModellers;
|
||||
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
||||
import dan200.computercraft.shared.turtle.blocks.TurtleBlockEntity;
|
||||
import dan200.computercraft.shared.util.DirectionUtil;
|
||||
import dan200.computercraft.shared.util.Holiday;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.Font;
|
||||
import net.minecraft.client.renderer.MultiBufferSource;
|
||||
import net.minecraft.client.renderer.Sheets;
|
||||
import net.minecraft.client.renderer.block.model.BakedQuad;
|
||||
import net.minecraft.client.renderer.blockentity.BlockEntityRenderDispatcher;
|
||||
import net.minecraft.client.renderer.blockentity.BlockEntityRenderer;
|
||||
import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider;
|
||||
import net.minecraft.client.resources.model.BakedModel;
|
||||
import net.minecraft.client.resources.model.ModelResourceLocation;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.util.RandomSource;
|
||||
import net.minecraft.world.phys.BlockHitResult;
|
||||
import net.minecraft.world.phys.HitResult;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.List;
|
||||
|
||||
public class TurtleBlockEntityRenderer implements BlockEntityRenderer<TurtleBlockEntity> {
|
||||
private static final ModelResourceLocation NORMAL_TURTLE_MODEL = new ModelResourceLocation(ComputerCraftAPI.MOD_ID, "turtle_normal", "inventory");
|
||||
@@ -40,8 +34,6 @@ public class TurtleBlockEntityRenderer implements BlockEntityRenderer<TurtleBloc
|
||||
private static final ResourceLocation COLOUR_TURTLE_MODEL = new ResourceLocation(ComputerCraftAPI.MOD_ID, "block/turtle_colour");
|
||||
private static final ResourceLocation ELF_OVERLAY_MODEL = new ResourceLocation(ComputerCraftAPI.MOD_ID, "block/turtle_elf_overlay");
|
||||
|
||||
private final RandomSource random = RandomSource.create(0);
|
||||
|
||||
private final BlockEntityRenderDispatcher renderer;
|
||||
private final Font font;
|
||||
|
||||
@@ -107,23 +99,22 @@ public class TurtleBlockEntityRenderer implements BlockEntityRenderer<TurtleBloc
|
||||
var family = turtle.getFamily();
|
||||
var overlay = turtle.getOverlay();
|
||||
|
||||
var buffer = buffers.getBuffer(Sheets.translucentCullBlockSheet());
|
||||
renderModel(transform, buffer, lightmapCoord, overlayLight, getTurtleModel(family, colour != -1), colour == -1 ? null : new int[]{ colour });
|
||||
renderModel(transform, buffers, lightmapCoord, overlayLight, getTurtleModel(family, colour != -1), colour == -1 ? null : new int[]{ colour });
|
||||
|
||||
// Render the overlay
|
||||
var overlayModel = getTurtleOverlayModel(overlay, Holiday.getCurrent() == Holiday.CHRISTMAS);
|
||||
if (overlayModel != null) {
|
||||
renderModel(transform, buffer, lightmapCoord, overlayLight, overlayModel, null);
|
||||
renderModel(transform, buffers, lightmapCoord, overlayLight, overlayModel, null);
|
||||
}
|
||||
|
||||
// Render the upgrades
|
||||
renderUpgrade(transform, buffer, lightmapCoord, overlayLight, turtle, TurtleSide.LEFT, partialTicks);
|
||||
renderUpgrade(transform, buffer, lightmapCoord, overlayLight, turtle, TurtleSide.RIGHT, partialTicks);
|
||||
renderUpgrade(transform, buffers, lightmapCoord, overlayLight, turtle, TurtleSide.LEFT, partialTicks);
|
||||
renderUpgrade(transform, buffers, lightmapCoord, overlayLight, turtle, TurtleSide.RIGHT, partialTicks);
|
||||
|
||||
transform.popPose();
|
||||
}
|
||||
|
||||
private void renderUpgrade(PoseStack transform, VertexConsumer renderer, int lightmapCoord, int overlayLight, TurtleBlockEntity turtle, TurtleSide side, float f) {
|
||||
private void renderUpgrade(PoseStack transform, MultiBufferSource buffers, int lightmapCoord, int overlayLight, TurtleBlockEntity turtle, TurtleSide side, float f) {
|
||||
var upgrade = turtle.getUpgrade(side);
|
||||
if (upgrade == null) return;
|
||||
transform.pushPose();
|
||||
@@ -134,46 +125,33 @@ public class TurtleBlockEntityRenderer implements BlockEntityRenderer<TurtleBloc
|
||||
transform.translate(0.0f, -0.5f, -0.5f);
|
||||
|
||||
var model = TurtleUpgradeModellers.getModel(upgrade, turtle.getAccess(), side);
|
||||
pushPoseFromTransformation(transform, model.getMatrix());
|
||||
renderModel(transform, renderer, lightmapCoord, overlayLight, model.getModel(), null);
|
||||
transform.popPose();
|
||||
applyTransformation(transform, model.getMatrix());
|
||||
renderModel(transform, buffers, lightmapCoord, overlayLight, model.getModel(), null);
|
||||
|
||||
transform.popPose();
|
||||
}
|
||||
|
||||
private void renderModel(PoseStack transform, VertexConsumer renderer, int lightmapCoord, int overlayLight, ResourceLocation modelLocation, @Nullable int[] tints) {
|
||||
private void renderModel(PoseStack transform, MultiBufferSource buffers, int lightmapCoord, int overlayLight, ResourceLocation modelLocation, @Nullable int[] tints) {
|
||||
var modelManager = Minecraft.getInstance().getItemRenderer().getItemModelShaper().getModelManager();
|
||||
renderModel(transform, renderer, lightmapCoord, overlayLight, ClientPlatformHelper.get().getModel(modelManager, modelLocation), tints);
|
||||
renderModel(transform, buffers, lightmapCoord, overlayLight, ClientPlatformHelper.get().getModel(modelManager, modelLocation), tints);
|
||||
}
|
||||
|
||||
private void renderModel(PoseStack transform, VertexConsumer renderer, int lightmapCoord, int overlayLight, BakedModel model, @Nullable int[] tints) {
|
||||
random.setSeed(0);
|
||||
renderQuads(transform, renderer, lightmapCoord, overlayLight, model.getQuads(null, null, random), tints);
|
||||
for (var facing : DirectionUtil.FACINGS) {
|
||||
renderQuads(transform, renderer, lightmapCoord, overlayLight, model.getQuads(null, facing, random), tints);
|
||||
}
|
||||
/**
|
||||
* Render a block model.
|
||||
*
|
||||
* @param transform The current matrix stack.
|
||||
* @param renderer The buffer to write to.
|
||||
* @param lightmapCoord The current lightmap coordinate.
|
||||
* @param overlayLight The overlay light.
|
||||
* @param model The model to render.
|
||||
* @param tints Tints for the quads, as an array of RGB values.
|
||||
* @see net.minecraft.client.renderer.block.ModelBlockRenderer#renderModel
|
||||
*/
|
||||
private void renderModel(PoseStack transform, MultiBufferSource renderer, int lightmapCoord, int overlayLight, BakedModel model, @Nullable int[] tints) {
|
||||
ClientPlatformHelper.get().renderBakedModel(transform, renderer, model, lightmapCoord, overlayLight, tints);
|
||||
}
|
||||
|
||||
private static void renderQuads(PoseStack transform, VertexConsumer buffer, int lightmapCoord, int overlayLight, List<BakedQuad> quads, @Nullable int[] tints) {
|
||||
var matrix = transform.last();
|
||||
|
||||
for (var bakedquad : quads) {
|
||||
var tint = -1;
|
||||
if (tints != null && bakedquad.isTinted()) {
|
||||
var idx = bakedquad.getTintIndex();
|
||||
if (idx >= 0 && idx < tints.length) tint = tints[bakedquad.getTintIndex()];
|
||||
}
|
||||
|
||||
var r = (float) (tint >> 16 & 255) / 255.0F;
|
||||
var g = (float) (tint >> 8 & 255) / 255.0F;
|
||||
var b = (float) (tint & 255) / 255.0F;
|
||||
buffer.putBulkData(matrix, bakedquad, r, g, b, lightmapCoord, overlayLight);
|
||||
}
|
||||
}
|
||||
|
||||
private static void pushPoseFromTransformation(PoseStack stack, Transformation transformation) {
|
||||
stack.pushPose();
|
||||
|
||||
private static void applyTransformation(PoseStack stack, Transformation transformation) {
|
||||
var trans = transformation.getTranslation();
|
||||
stack.translate(trans.x(), trans.y(), trans.z());
|
||||
|
||||
|
@@ -20,6 +20,13 @@ import javax.annotation.Nullable;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Holds the client-side state of a monitor. This both tracks the last place a monitor was rendered at (see the comments
|
||||
* in {@link MonitorBlockEntityRenderer}) and the current OpenGL buffers allocated for this object.
|
||||
* <p>
|
||||
* This is automatically cleared by {@link dan200.computercraft.shared.peripheral.monitor.MonitorBlockEntity} when the
|
||||
* entity is unloaded on the client side (see {@link MonitorRenderState#close()}).
|
||||
*/
|
||||
public class MonitorRenderState implements ClientMonitor.RenderState {
|
||||
@GuardedBy("allMonitors")
|
||||
private static final Set<MonitorRenderState> allMonitors = new HashSet<>();
|
||||
|
@@ -7,6 +7,7 @@ package dan200.computercraft.client.render.monitor;
|
||||
import com.mojang.blaze3d.shaders.Uniform;
|
||||
import com.mojang.blaze3d.vertex.VertexFormat;
|
||||
import dan200.computercraft.client.FrameInfo;
|
||||
import dan200.computercraft.client.render.RenderTypes;
|
||||
import dan200.computercraft.client.render.text.FixedWidthFontRenderer;
|
||||
import dan200.computercraft.core.terminal.Terminal;
|
||||
import dan200.computercraft.core.terminal.TextBuffer;
|
||||
@@ -24,6 +25,16 @@ import java.nio.ByteBuffer;
|
||||
|
||||
import static dan200.computercraft.client.render.text.FixedWidthFontRenderer.getColour;
|
||||
|
||||
/**
|
||||
* The shader used for the monitor TBO renderer.
|
||||
* <p>
|
||||
* This extends Minecraft's default shader loading code to extract out the TBO buffer and handle our custom uniforms
|
||||
* ({@code MonitorData}, {@code CursorBlink}).
|
||||
* <p>
|
||||
* See also {@code monitor_tbo.fsh} and {@code monitor_tbo.vsh} in the mod's resources.
|
||||
*
|
||||
* @see RenderTypes#getMonitorTextureBufferShader()
|
||||
*/
|
||||
public class MonitorTextureBufferShader extends ShaderInstance {
|
||||
public static final int UNIFORM_SIZE = 4 * 4 * 16 + 4 + 4 + 2 * 4 + 4;
|
||||
|
||||
|
@@ -13,13 +13,18 @@ import org.lwjgl.BufferUtils;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.sound.sampled.AudioFormat;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
/**
|
||||
* An {@link AudioStream} which decodes DFPWM streams, converting them to PCM.
|
||||
*
|
||||
* @see SpeakerPeripheral Server-side encoding of the audio.
|
||||
* @see SpeakerInstance
|
||||
*/
|
||||
class DfpwmStream implements AudioStream {
|
||||
private static final int PREC = 10;
|
||||
private static final int LPF_STRENGTH = 140;
|
||||
@@ -128,7 +133,7 @@ class DfpwmStream implements AudioStream {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
public void close() {
|
||||
buffers.clear();
|
||||
}
|
||||
|
||||
|
@@ -20,6 +20,14 @@ import net.minecraft.world.entity.Entity;
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
/**
|
||||
* A sound played by a speaker. This has two purposes:
|
||||
*
|
||||
* <ul>
|
||||
* <li>Tracks a {@link SpeakerPosition}, ensuring the sound moves around with the speaker's owner.</li>
|
||||
* <li>Provides a {@link DfpwmStream} when playing custom audio.</li>
|
||||
* </ul>
|
||||
*/
|
||||
public class SpeakerSound extends AbstractSoundInstance implements TickableSoundInstance {
|
||||
@Nullable
|
||||
DfpwmStream stream;
|
||||
|
@@ -13,6 +13,12 @@ import dan200.computercraft.shared.turtle.upgrades.TurtleModem;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A {@link TurtleUpgradeModeller} for modems, providing different models depending on if the modem is on/off.
|
||||
*/
|
||||
public class TurtleModemModeller implements TurtleUpgradeModeller<TurtleModem> {
|
||||
private final ResourceLocation leftOffModel;
|
||||
private final ResourceLocation rightOffModel;
|
||||
@@ -45,4 +51,9 @@ public class TurtleModemModeller implements TurtleUpgradeModeller<TurtleModem> {
|
||||
? TransformedModel.of(active ? leftOnModel : leftOffModel)
|
||||
: TransformedModel.of(active ? rightOnModel : rightOffModel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<ResourceLocation> getDependencies() {
|
||||
return List.of(leftOffModel, rightOffModel, leftOnModel, rightOnModel);
|
||||
}
|
||||
}
|
||||
|
@@ -14,12 +14,19 @@ import dan200.computercraft.api.turtle.TurtleUpgradeSerialiser;
|
||||
import dan200.computercraft.impl.TurtleUpgrades;
|
||||
import dan200.computercraft.impl.UpgradeManager;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Map;
|
||||
import java.util.WeakHashMap;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* A registry of {@link TurtleUpgradeModeller}s.
|
||||
*
|
||||
* @see dan200.computercraft.api.client.ComputerCraftAPIClient#registerTurtleUpgradeModeller(TurtleUpgradeSerialiser, TurtleUpgradeModeller)
|
||||
*/
|
||||
public final class TurtleUpgradeModellers {
|
||||
private static final TurtleUpgradeModeller<ITurtleUpgrade> NULL_TURTLE_MODELLER = (upgrade, turtle, side) ->
|
||||
new TransformedModel(Minecraft.getInstance().getModelManager().getMissingModel(), Transformation.identity());
|
||||
@@ -47,12 +54,18 @@ public final class TurtleUpgradeModellers {
|
||||
}
|
||||
}
|
||||
|
||||
public static TransformedModel getModel(ITurtleUpgrade upgrade, @Nullable ITurtleAccess access, TurtleSide side) {
|
||||
public static TransformedModel getModel(ITurtleUpgrade upgrade, ITurtleAccess access, TurtleSide side) {
|
||||
@SuppressWarnings("unchecked")
|
||||
var modeller = (TurtleUpgradeModeller<ITurtleUpgrade>) modelCache.computeIfAbsent(upgrade, TurtleUpgradeModellers::getModeller);
|
||||
return modeller.getModel(upgrade, access, side);
|
||||
}
|
||||
|
||||
public static TransformedModel getModel(ITurtleUpgrade upgrade, CompoundTag data, TurtleSide side) {
|
||||
@SuppressWarnings("unchecked")
|
||||
var modeller = (TurtleUpgradeModeller<ITurtleUpgrade>) modelCache.computeIfAbsent(upgrade, TurtleUpgradeModellers::getModeller);
|
||||
return modeller.getModel(upgrade, data, side);
|
||||
}
|
||||
|
||||
private static TurtleUpgradeModeller<?> getModeller(ITurtleUpgrade upgradeA) {
|
||||
var wrapper = TurtleUpgrades.instance().getWrapper(upgradeA);
|
||||
if (wrapper == null) return NULL_TURTLE_MODELLER;
|
||||
@@ -60,4 +73,8 @@ public final class TurtleUpgradeModellers {
|
||||
var modeller = turtleModels.get(wrapper.serialiser());
|
||||
return modeller == null ? NULL_TURTLE_MODELLER : modeller;
|
||||
}
|
||||
|
||||
public static Stream<ResourceLocation> getDependencies() {
|
||||
return turtleModels.values().stream().flatMap(x -> x.getDependencies().stream());
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,34 @@
|
||||
// SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers
|
||||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package dan200.computercraft.data.client;
|
||||
|
||||
import dan200.computercraft.data.DataProviders;
|
||||
import dan200.computercraft.shared.turtle.inventory.UpgradeSlot;
|
||||
import net.minecraft.client.renderer.texture.atlas.SpriteSources;
|
||||
import net.minecraft.client.renderer.texture.atlas.sources.SingleFile;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.packs.PackType;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* A version of {@link DataProviders} which relies on client-side classes.
|
||||
* <p>
|
||||
* This is called from {@link DataProviders#add(DataProviders.GeneratorSink)}.
|
||||
*/
|
||||
public final class ClientDataProviders {
|
||||
private ClientDataProviders() {
|
||||
}
|
||||
|
||||
public static void add(DataProviders.GeneratorSink generator) {
|
||||
generator.addFromCodec("Block atlases", PackType.CLIENT_RESOURCES, "atlases", SpriteSources.FILE_CODEC, out -> {
|
||||
out.accept(new ResourceLocation("blocks"), List.of(
|
||||
new SingleFile(UpgradeSlot.LEFT_UPGRADE, Optional.empty()),
|
||||
new SingleFile(UpgradeSlot.RIGHT_UPGRADE, Optional.empty())
|
||||
));
|
||||
});
|
||||
}
|
||||
}
|
@@ -37,6 +37,14 @@ import static net.minecraft.data.models.model.ModelLocationUtils.getModelLocatio
|
||||
import static net.minecraft.data.models.model.TextureMapping.getBlockTexture;
|
||||
|
||||
class BlockModelProvider {
|
||||
private static final TextureSlot CURSOR = TextureSlot.create("cursor");
|
||||
|
||||
private static final ModelTemplate COMPUTER_ON = new ModelTemplate(
|
||||
Optional.of(new ResourceLocation(ComputerCraftAPI.MOD_ID, "block/computer_on")),
|
||||
Optional.empty(),
|
||||
TextureSlot.FRONT, TextureSlot.SIDE, TextureSlot.TOP, CURSOR
|
||||
);
|
||||
|
||||
private static final ModelTemplate MONITOR_BASE = new ModelTemplate(
|
||||
Optional.of(new ResourceLocation(ComputerCraftAPI.MOD_ID, "block/monitor_base")),
|
||||
Optional.empty(),
|
||||
@@ -142,11 +150,18 @@ class BlockModelProvider {
|
||||
private static void registerComputer(BlockModelGenerators generators, ComputerBlock<?> block) {
|
||||
generators.blockStateOutput.accept(MultiVariantGenerator.multiVariant(block)
|
||||
.with(createHorizontalFacingDispatch())
|
||||
.with(createModelDispatch(ComputerBlock.STATE, state -> ModelTemplates.CUBE_ORIENTABLE.createWithSuffix(
|
||||
block, "_" + state.getSerializedName(),
|
||||
TextureMapping.orientableCube(block).put(TextureSlot.FRONT, getBlockTexture(block, "_front" + state.getTexture())),
|
||||
generators.modelOutput
|
||||
)))
|
||||
.with(createModelDispatch(ComputerBlock.STATE, state -> switch (state) {
|
||||
case OFF -> ModelTemplates.CUBE_ORIENTABLE.createWithSuffix(
|
||||
block, "_" + state.getSerializedName(),
|
||||
TextureMapping.orientableCube(block),
|
||||
generators.modelOutput
|
||||
);
|
||||
case ON, BLINKING -> COMPUTER_ON.createWithSuffix(
|
||||
block, "_" + state.getSerializedName(),
|
||||
TextureMapping.orientableCube(block).put(CURSOR, new ResourceLocation(ComputerCraftAPI.MOD_ID, "block/computer" + state.getTexture())),
|
||||
generators.modelOutput
|
||||
);
|
||||
}))
|
||||
);
|
||||
generators.delegateItemModel(block, getModelLocation(block, "_blinking"));
|
||||
}
|
||||
|
@@ -4,16 +4,20 @@
|
||||
|
||||
package dan200.computercraft.data;
|
||||
|
||||
import com.mojang.serialization.Codec;
|
||||
import net.minecraft.data.DataProvider;
|
||||
import net.minecraft.data.PackOutput;
|
||||
import net.minecraft.data.loot.LootTableProvider.SubProviderEntry;
|
||||
import net.minecraft.data.models.BlockModelGenerators;
|
||||
import net.minecraft.data.models.ItemModelGenerators;
|
||||
import net.minecraft.data.tags.TagsProvider;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.packs.PackType;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
@@ -37,11 +41,22 @@ public final class DataProviders {
|
||||
generator.models(BlockModelProvider::addBlockModels, ItemModelProvider::addItemModels);
|
||||
|
||||
generator.add(out -> new LanguageProvider(out, turtleUpgrades, pocketUpgrades));
|
||||
|
||||
// Unfortunately we rely on some client-side classes in this code. We just load in the client side data provider
|
||||
// and invoke that.
|
||||
try {
|
||||
Class.forName("dan200.computercraft.data.client.ClientDataProviders")
|
||||
.getMethod("add", GeneratorSink.class).invoke(null, generator);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
interface GeneratorSink {
|
||||
public interface GeneratorSink {
|
||||
<T extends DataProvider> T add(DataProvider.Factory<T> factory);
|
||||
|
||||
<T> void addFromCodec(String name, PackType type, String directory, Codec<T> codec, Consumer<BiConsumer<ResourceLocation, T>> output);
|
||||
|
||||
void lootTable(List<SubProviderEntry> tables);
|
||||
|
||||
TagsProvider<Block> blockTags(Consumer<TagProvider.TagConsumer<Block>> tags);
|
||||
|
@@ -6,6 +6,7 @@ package dan200.computercraft.data;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import dan200.computercraft.api.ComputerCraftAPI;
|
||||
import dan200.computercraft.api.ComputerCraftTags;
|
||||
import dan200.computercraft.api.pocket.PocketUpgradeDataProvider;
|
||||
import dan200.computercraft.api.turtle.TurtleUpgradeDataProvider;
|
||||
import dan200.computercraft.api.upgrades.UpgradeBase;
|
||||
@@ -20,6 +21,7 @@ import dan200.computercraft.shared.platform.RegistryWrappers;
|
||||
import net.minecraft.data.CachedOutput;
|
||||
import net.minecraft.data.DataProvider;
|
||||
import net.minecraft.data.PackOutput;
|
||||
import net.minecraft.tags.TagKey;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
|
||||
@@ -97,6 +99,12 @@ public final class LanguageProvider implements DataProvider {
|
||||
add(ModRegistry.Items.POCKET_COMPUTER_ADVANCED.get(), "Advanced Pocket Computer");
|
||||
add(ModRegistry.Items.POCKET_COMPUTER_ADVANCED.get().getDescriptionId() + ".upgraded", "Advanced %s Pocket Computer");
|
||||
|
||||
// Tags (for EMI)
|
||||
add(ComputerCraftTags.Items.COMPUTER, "Computers");
|
||||
add(ComputerCraftTags.Items.TURTLE, "Turtles");
|
||||
add(ComputerCraftTags.Items.WIRED_MODEM, "Wired modems");
|
||||
add(ComputerCraftTags.Items.MONITOR, "Monitors");
|
||||
|
||||
// Turtle/pocket upgrades
|
||||
add("upgrade.minecraft.diamond_sword.adjective", "Melee");
|
||||
add("upgrade.minecraft.diamond_shovel.adjective", "Digging");
|
||||
@@ -150,9 +158,6 @@ public final class LanguageProvider implements DataProvider {
|
||||
add("commands.computercraft.track.dump.desc", "Dump the latest results of computer tracking.");
|
||||
add("commands.computercraft.track.dump.no_timings", "No timings available");
|
||||
add("commands.computercraft.track.dump.computer", "Computer");
|
||||
add("commands.computercraft.reload.synopsis", "Reload the ComputerCraft config file");
|
||||
add("commands.computercraft.reload.desc", "Reload the ComputerCraft config file");
|
||||
add("commands.computercraft.reload.done", "Reloaded config");
|
||||
add("commands.computercraft.queue.synopsis", "Send a computer_command event to a command computer");
|
||||
add("commands.computercraft.queue.desc", "Send a computer_command event to a command computer, passing through the additional arguments. This is mostly designed for map makers, acting as a more computer-friendly version of /trigger. Any player can run the command, which would most likely be done through a text component's click event.");
|
||||
|
||||
@@ -208,11 +213,13 @@ public final class LanguageProvider implements DataProvider {
|
||||
// Config options
|
||||
addConfigEntry(ConfigSpec.computerSpaceLimit, "Computer space limit (bytes)");
|
||||
addConfigEntry(ConfigSpec.floppySpaceLimit, "Floppy Disk space limit (bytes)");
|
||||
addConfigEntry(ConfigSpec.uploadMaxSize, "File upload size limit (bytes)");
|
||||
addConfigEntry(ConfigSpec.maximumFilesOpen, "Maximum files open per computer");
|
||||
addConfigEntry(ConfigSpec.disableLua51Features, "Disable Lua 5.1 features");
|
||||
addConfigEntry(ConfigSpec.defaultComputerSettings, "Default Computer settings");
|
||||
addConfigEntry(ConfigSpec.logComputerErrors, "Log computer errors");
|
||||
addConfigEntry(ConfigSpec.commandRequireCreative, "Command computers require creative");
|
||||
addConfigEntry(ConfigSpec.disabledGenericMethods, "Disabled generic methods");
|
||||
|
||||
addConfigGroup(ConfigSpec.serverSpec, "execution", "Execution");
|
||||
addConfigEntry(ConfigSpec.computerThreads, "Computer threads");
|
||||
@@ -229,6 +236,11 @@ public final class LanguageProvider implements DataProvider {
|
||||
addConfigEntry(ConfigSpec.httpDownloadBandwidth, "Global download limit");
|
||||
addConfigEntry(ConfigSpec.httpUploadBandwidth, "Global upload limit");
|
||||
|
||||
addConfigGroup(ConfigSpec.serverSpec, "http.proxy", "Proxy");
|
||||
addConfigEntry(ConfigSpec.httpProxyHost, "Host name");
|
||||
addConfigEntry(ConfigSpec.httpProxyPort, "Port");
|
||||
addConfigEntry(ConfigSpec.httpProxyType, "Proxy type");
|
||||
|
||||
addConfigGroup(ConfigSpec.serverSpec, "peripheral", "Peripherals");
|
||||
addConfigEntry(ConfigSpec.commandBlockEnabled, "Enable command block peripheral");
|
||||
addConfigEntry(ConfigSpec.modemRange, "Modem range (default)");
|
||||
@@ -271,8 +283,8 @@ public final class LanguageProvider implements DataProvider {
|
||||
turtleUpgrades.getGeneratedUpgrades().stream().map(UpgradeBase::getUnlocalisedAdjective),
|
||||
pocketUpgrades.getGeneratedUpgrades().stream().map(UpgradeBase::getUnlocalisedAdjective),
|
||||
Metric.metrics().values().stream().map(x -> AggregatedMetric.TRANSLATION_PREFIX + x.name() + ".name"),
|
||||
getConfigEntries(ConfigSpec.serverSpec).map(ConfigFile.Entry::translationKey),
|
||||
getConfigEntries(ConfigSpec.clientSpec).map(ConfigFile.Entry::translationKey)
|
||||
ConfigSpec.serverSpec.entries().map(ConfigFile.Entry::translationKey),
|
||||
ConfigSpec.clientSpec.entries().map(ConfigFile.Entry::translationKey)
|
||||
).flatMap(x -> x);
|
||||
}
|
||||
|
||||
@@ -292,6 +304,10 @@ public final class LanguageProvider implements DataProvider {
|
||||
add(AggregatedMetric.TRANSLATION_PREFIX + metric.name() + ".name", text);
|
||||
}
|
||||
|
||||
private void add(TagKey<Item> tag, String text) {
|
||||
add("tag.item." + tag.location().getNamespace() + "." + tag.location().getPath(), text);
|
||||
}
|
||||
|
||||
private void addConfigGroup(ConfigFile spec, String path, String text) {
|
||||
var entry = spec.getEntry(path);
|
||||
if (!(entry instanceof ConfigFile.Group)) throw new IllegalArgumentException("Cannot find group " + path);
|
||||
@@ -302,16 +318,4 @@ public final class LanguageProvider implements DataProvider {
|
||||
add(value.translationKey(), text);
|
||||
add(value.translationKey() + ".tooltip", value.comment());
|
||||
}
|
||||
|
||||
private static Stream<ConfigFile.Entry> getConfigEntries(ConfigFile spec) {
|
||||
return spec.entries().flatMap(LanguageProvider::getConfigEntries);
|
||||
}
|
||||
|
||||
private static Stream<ConfigFile.Entry> getConfigEntries(ConfigFile.Entry entry) {
|
||||
if (entry instanceof ConfigFile.Value<?>) return Stream.of(entry);
|
||||
if (entry instanceof ConfigFile.Group group) {
|
||||
return Stream.concat(Stream.of(entry), group.children().flatMap(LanguageProvider::getConfigEntries));
|
||||
}
|
||||
throw new IllegalStateException("Invalid config entry " + entry);
|
||||
}
|
||||
}
|
||||
|
@@ -8,6 +8,7 @@ import com.google.gson.JsonObject;
|
||||
import dan200.computercraft.api.ComputerCraftAPI;
|
||||
import dan200.computercraft.api.pocket.PocketUpgradeDataProvider;
|
||||
import dan200.computercraft.api.turtle.TurtleUpgradeDataProvider;
|
||||
import dan200.computercraft.api.upgrades.UpgradeData;
|
||||
import dan200.computercraft.core.util.Colour;
|
||||
import dan200.computercraft.shared.ModRegistry;
|
||||
import dan200.computercraft.shared.common.IColouredItem;
|
||||
@@ -15,8 +16,8 @@ import dan200.computercraft.shared.computer.core.ComputerFamily;
|
||||
import dan200.computercraft.shared.platform.PlatformHelper;
|
||||
import dan200.computercraft.shared.platform.RecipeIngredients;
|
||||
import dan200.computercraft.shared.platform.RegistryWrappers;
|
||||
import dan200.computercraft.shared.pocket.items.PocketComputerItemFactory;
|
||||
import dan200.computercraft.shared.turtle.items.TurtleItemFactory;
|
||||
import dan200.computercraft.shared.pocket.items.PocketComputerItem;
|
||||
import dan200.computercraft.shared.turtle.items.TurtleItem;
|
||||
import dan200.computercraft.shared.util.ColourUtils;
|
||||
import net.minecraft.advancements.critereon.InventoryChangeTrigger;
|
||||
import net.minecraft.advancements.critereon.ItemPredicate;
|
||||
@@ -38,6 +39,7 @@ import net.minecraft.world.item.crafting.SimpleCraftingRecipeSerializer;
|
||||
import net.minecraft.world.level.ItemLike;
|
||||
import net.minecraft.world.level.block.Blocks;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
@@ -93,20 +95,23 @@ class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider {
|
||||
}
|
||||
}
|
||||
|
||||
private static List<TurtleItem> turtleItems() {
|
||||
return List.of(ModRegistry.Items.TURTLE_NORMAL.get(), ModRegistry.Items.TURTLE_ADVANCED.get());
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a crafting recipe for each turtle upgrade.
|
||||
*
|
||||
* @param add The callback to add recipes.
|
||||
*/
|
||||
private void turtleUpgrades(Consumer<FinishedRecipe> add) {
|
||||
for (var family : ComputerFamily.values()) {
|
||||
var base = TurtleItemFactory.create(-1, null, -1, family, null, null, 0, null);
|
||||
if (base.isEmpty()) continue;
|
||||
for (var turtleItem : turtleItems()) {
|
||||
var base = turtleItem.create(-1, null, -1, null, null, 0, null);
|
||||
|
||||
var nameId = family.name().toLowerCase(Locale.ROOT);
|
||||
var nameId = turtleItem.getFamily().name().toLowerCase(Locale.ROOT);
|
||||
|
||||
for (var upgrade : turtleUpgrades.getGeneratedUpgrades()) {
|
||||
var result = TurtleItemFactory.create(-1, null, -1, family, null, upgrade, -1, null);
|
||||
var result = turtleItem.create(-1, null, -1, null, UpgradeData.ofDefault(upgrade), -1, null);
|
||||
ShapedRecipeBuilder
|
||||
.shaped(RecipeCategory.REDSTONE, result.getItem())
|
||||
.group(String.format("%s:turtle_%s", ComputerCraftAPI.MOD_ID, nameId))
|
||||
@@ -125,20 +130,24 @@ class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider {
|
||||
}
|
||||
}
|
||||
|
||||
private static List<PocketComputerItem> pocketComputerItems() {
|
||||
return List.of(ModRegistry.Items.POCKET_COMPUTER_NORMAL.get(), ModRegistry.Items.POCKET_COMPUTER_ADVANCED.get());
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a crafting recipe for each pocket upgrade.
|
||||
*
|
||||
* @param add The callback to add recipes.
|
||||
*/
|
||||
private void pocketUpgrades(Consumer<FinishedRecipe> add) {
|
||||
for (var family : ComputerFamily.values()) {
|
||||
var base = PocketComputerItemFactory.create(-1, null, -1, family, null);
|
||||
for (var pocket : pocketComputerItems()) {
|
||||
var base = pocket.create(-1, null, -1, null);
|
||||
if (base.isEmpty()) continue;
|
||||
|
||||
var nameId = family.name().toLowerCase(Locale.ROOT);
|
||||
var nameId = pocket.getFamily().name().toLowerCase(Locale.ROOT);
|
||||
|
||||
for (var upgrade : pocketUpgrades.getGeneratedUpgrades()) {
|
||||
var result = PocketComputerItemFactory.create(-1, null, -1, family, upgrade);
|
||||
var result = pocket.create(-1, null, -1, UpgradeData.ofDefault(upgrade));
|
||||
ShapedRecipeBuilder
|
||||
.shaped(RecipeCategory.REDSTONE, result.getItem())
|
||||
.group(String.format("%s:pocket_%s", ComputerCraftAPI.MOD_ID, nameId))
|
||||
@@ -180,11 +189,10 @@ class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider {
|
||||
}
|
||||
|
||||
private void turtleOverlay(Consumer<FinishedRecipe> add, String overlay, Consumer<ShapelessRecipeBuilder> build) {
|
||||
for (var family : ComputerFamily.values()) {
|
||||
var base = TurtleItemFactory.create(-1, null, -1, family, null, null, 0, null);
|
||||
if (base.isEmpty()) continue;
|
||||
for (var turtleItem : turtleItems()) {
|
||||
var base = turtleItem.create(-1, null, -1, null, null, 0, null);
|
||||
|
||||
var nameId = family.name().toLowerCase(Locale.ROOT);
|
||||
var nameId = turtleItem.getFamily().name().toLowerCase(Locale.ROOT);
|
||||
var group = "%s:turtle_%s_overlay".formatted(ComputerCraftAPI.MOD_ID, nameId);
|
||||
|
||||
var builder = ShapelessRecipeBuilder.shapeless(RecipeCategory.REDSTONE, base.getItem())
|
||||
|
@@ -4,6 +4,7 @@
|
||||
|
||||
package dan200.computercraft.impl;
|
||||
|
||||
import dan200.computercraft.api.ComputerCraftAPI;
|
||||
import dan200.computercraft.api.detail.BlockReference;
|
||||
import dan200.computercraft.api.detail.DetailRegistry;
|
||||
import dan200.computercraft.api.filesystem.Mount;
|
||||
@@ -14,10 +15,10 @@ import dan200.computercraft.api.media.MediaProvider;
|
||||
import dan200.computercraft.api.network.PacketNetwork;
|
||||
import dan200.computercraft.api.network.wired.WiredElement;
|
||||
import dan200.computercraft.api.network.wired.WiredNode;
|
||||
import dan200.computercraft.api.pocket.PocketUpgradeSerialiser;
|
||||
import dan200.computercraft.api.redstone.BundledRedstoneProvider;
|
||||
import dan200.computercraft.api.turtle.TurtleRefuelHandler;
|
||||
import dan200.computercraft.core.apis.ApiFactories;
|
||||
import dan200.computercraft.core.asm.GenericMethod;
|
||||
import dan200.computercraft.api.turtle.TurtleUpgradeSerialiser;
|
||||
import dan200.computercraft.core.filesystem.WritableFileMount;
|
||||
import dan200.computercraft.impl.detail.DetailRegistryImpl;
|
||||
import dan200.computercraft.impl.network.wired.WiredNodeImpl;
|
||||
@@ -27,6 +28,8 @@ import dan200.computercraft.shared.details.BlockDetails;
|
||||
import dan200.computercraft.shared.details.ItemDetails;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
@@ -41,6 +44,9 @@ public abstract class AbstractComputerCraftAPI implements ComputerCraftAPIServic
|
||||
private final DetailRegistry<ItemStack> itemStackDetails = new DetailRegistryImpl<>(ItemDetails::fillBasic);
|
||||
private final DetailRegistry<BlockReference> blockDetails = new DetailRegistryImpl<>(BlockDetails::fillBasic);
|
||||
|
||||
protected static final ResourceKey<Registry<TurtleUpgradeSerialiser<?>>> turtleUpgradeRegistryId = ResourceKey.createRegistryKey(new ResourceLocation(ComputerCraftAPI.MOD_ID, "turtle_upgrade_serialiser"));
|
||||
protected static final ResourceKey<Registry<PocketUpgradeSerialiser<?>>> pocketUpgradeRegistryId = ResourceKey.createRegistryKey(new ResourceLocation(ComputerCraftAPI.MOD_ID, "pocket_upgrade_serialiser"));
|
||||
|
||||
public static @Nullable InputStream getResourceFile(MinecraftServer server, String domain, String subPath) {
|
||||
var manager = server.getResourceManager();
|
||||
var resource = manager.getResource(new ResourceLocation(domain, subPath)).orElse(null);
|
||||
@@ -71,7 +77,7 @@ public abstract class AbstractComputerCraftAPI implements ComputerCraftAPIServic
|
||||
|
||||
@Override
|
||||
public final void registerGenericSource(GenericSource source) {
|
||||
GenericMethod.register(source);
|
||||
GenericSources.register(source);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -105,10 +111,20 @@ public abstract class AbstractComputerCraftAPI implements ComputerCraftAPIServic
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerRefuelHandler(TurtleRefuelHandler handler) {
|
||||
public final void registerRefuelHandler(TurtleRefuelHandler handler) {
|
||||
TurtleRefuelHandlers.register(handler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final ResourceKey<Registry<TurtleUpgradeSerialiser<?>>> turtleUpgradeRegistryId() {
|
||||
return turtleUpgradeRegistryId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final ResourceKey<Registry<PocketUpgradeSerialiser<?>>> pocketUpgradeRegistryId() {
|
||||
return pocketUpgradeRegistryId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final DetailRegistry<ItemStack> getItemStackDetailRegistry() {
|
||||
return itemStackDetails;
|
||||
|
@@ -2,7 +2,7 @@
|
||||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package dan200.computercraft.core.apis;
|
||||
package dan200.computercraft.impl;
|
||||
|
||||
import dan200.computercraft.api.lua.ILuaAPIFactory;
|
||||
|
||||
@@ -11,19 +11,24 @@ import java.util.Collections;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* The global factory for {@link ILuaAPIFactory}s.
|
||||
*
|
||||
* @see dan200.computercraft.core.ComputerContext.Builder#apiFactories(Collection)
|
||||
* @see dan200.computercraft.api.ComputerCraftAPI#registerAPIFactory(ILuaAPIFactory)
|
||||
*/
|
||||
public final class ApiFactories {
|
||||
private ApiFactories() {
|
||||
}
|
||||
|
||||
private static final Collection<ILuaAPIFactory> factories = new LinkedHashSet<>();
|
||||
private static final Collection<ILuaAPIFactory> factoriesView = Collections.unmodifiableCollection(factories);
|
||||
|
||||
public static synchronized void register(ILuaAPIFactory factory) {
|
||||
static synchronized void register(ILuaAPIFactory factory) {
|
||||
Objects.requireNonNull(factory, "provider cannot be null");
|
||||
factories.add(factory);
|
||||
}
|
||||
|
||||
public static Iterable<ILuaAPIFactory> getAll() {
|
||||
return factoriesView;
|
||||
public static Collection<ILuaAPIFactory> getAll() {
|
||||
return Collections.unmodifiableCollection(factories);
|
||||
}
|
||||
}
|
@@ -0,0 +1,41 @@
|
||||
// SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers
|
||||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package dan200.computercraft.impl;
|
||||
|
||||
import dan200.computercraft.api.lua.GenericSource;
|
||||
import dan200.computercraft.core.asm.GenericMethod;
|
||||
import dan200.computercraft.shared.config.ConfigSpec;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* The global registry for {@link GenericSource}s.
|
||||
*
|
||||
* @see dan200.computercraft.core.ComputerContext.Builder#genericMethods(Collection)
|
||||
* @see dan200.computercraft.api.ComputerCraftAPI#registerGenericSource(GenericSource)
|
||||
*/
|
||||
public final class GenericSources {
|
||||
private GenericSources() {
|
||||
}
|
||||
|
||||
private static final Collection<GenericSource> sources = new LinkedHashSet<>();
|
||||
|
||||
static synchronized void register(GenericSource source) {
|
||||
Objects.requireNonNull(source, "provider cannot be null");
|
||||
sources.add(source);
|
||||
}
|
||||
|
||||
public static Collection<GenericMethod> getAllMethods() {
|
||||
var disabledMethods = Set.copyOf(ConfigSpec.disabledGenericMethods.get());
|
||||
return sources.stream()
|
||||
.filter(x -> !disabledMethods.contains(x.id()))
|
||||
.flatMap(GenericMethod::getMethods)
|
||||
.filter(x -> !disabledMethods.contains(x.id()))
|
||||
.toList();
|
||||
}
|
||||
}
|
@@ -12,7 +12,7 @@ import java.util.stream.Stream;
|
||||
|
||||
public final class PocketUpgrades {
|
||||
private static final UpgradeManager<PocketUpgradeSerialiser<?>, IPocketUpgrade> registry = new UpgradeManager<>(
|
||||
"pocket computer upgrade", "computercraft/pocket_upgrades", PocketUpgradeSerialiser.REGISTRY_ID
|
||||
"pocket computer upgrade", "computercraft/pocket_upgrades", PocketUpgradeSerialiser.registryId()
|
||||
);
|
||||
|
||||
private PocketUpgrades() {
|
||||
|
@@ -12,7 +12,7 @@ import java.util.stream.Stream;
|
||||
|
||||
public final class TurtleUpgrades {
|
||||
private static final UpgradeManager<TurtleUpgradeSerialiser<?>, ITurtleUpgrade> registry = new UpgradeManager<>(
|
||||
"turtle upgrade", "computercraft/turtle_upgrades", TurtleUpgradeSerialiser.REGISTRY_ID
|
||||
"turtle upgrade", "computercraft/turtle_upgrades", TurtleUpgradeSerialiser.registryId()
|
||||
);
|
||||
|
||||
private TurtleUpgrades() {
|
||||
|
@@ -7,6 +7,7 @@ package dan200.computercraft.impl;
|
||||
import com.google.gson.*;
|
||||
import dan200.computercraft.api.ComputerCraftAPI;
|
||||
import dan200.computercraft.api.upgrades.UpgradeBase;
|
||||
import dan200.computercraft.api.upgrades.UpgradeData;
|
||||
import dan200.computercraft.api.upgrades.UpgradeSerialiser;
|
||||
import dan200.computercraft.shared.platform.PlatformHelper;
|
||||
import net.minecraft.core.Registry;
|
||||
@@ -74,13 +75,13 @@ public class UpgradeManager<R extends UpgradeSerialiser<? extends T>, T extends
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public T get(ItemStack stack) {
|
||||
public UpgradeData<T> get(ItemStack stack) {
|
||||
if (stack.isEmpty()) return null;
|
||||
|
||||
for (var wrapper : current.values()) {
|
||||
var craftingStack = wrapper.upgrade().getCraftingItem();
|
||||
if (!craftingStack.isEmpty() && craftingStack.getItem() == stack.getItem() && wrapper.upgrade().isItemSuitable(stack)) {
|
||||
return wrapper.upgrade();
|
||||
return UpgradeData.of(wrapper.upgrade, wrapper.upgrade.getUpgradeData(stack));
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -1,19 +0,0 @@
|
||||
// SPDX-FileCopyrightText: 2022 The CC: Tweaked Developers
|
||||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package dan200.computercraft.mixin;
|
||||
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.level.Explosion;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
@Mixin(Explosion.class)
|
||||
public interface ExplosionAccessor {
|
||||
@Nullable
|
||||
@Accessor("source")
|
||||
Entity computercraft$getExploder();
|
||||
}
|
@@ -11,6 +11,7 @@ import dan200.computercraft.api.detail.VanillaDetailRegistries;
|
||||
import dan200.computercraft.api.media.IMedia;
|
||||
import dan200.computercraft.api.pocket.PocketUpgradeSerialiser;
|
||||
import dan200.computercraft.api.turtle.TurtleUpgradeSerialiser;
|
||||
import dan200.computercraft.api.upgrades.UpgradeData;
|
||||
import dan200.computercraft.core.util.Colour;
|
||||
import dan200.computercraft.impl.PocketUpgrades;
|
||||
import dan200.computercraft.impl.TurtleUpgrades;
|
||||
@@ -255,7 +256,7 @@ public final class ModRegistry {
|
||||
}
|
||||
|
||||
public static class TurtleSerialisers {
|
||||
static final RegistrationHelper<TurtleUpgradeSerialiser<?>> REGISTRY = PlatformHelper.get().createRegistrationHelper(TurtleUpgradeSerialiser.REGISTRY_ID);
|
||||
static final RegistrationHelper<TurtleUpgradeSerialiser<?>> REGISTRY = PlatformHelper.get().createRegistrationHelper(TurtleUpgradeSerialiser.registryId());
|
||||
|
||||
public static final RegistryEntry<TurtleUpgradeSerialiser<TurtleSpeaker>> SPEAKER =
|
||||
REGISTRY.register("speaker", () -> TurtleUpgradeSerialiser.simpleWithCustomItem(TurtleSpeaker::new));
|
||||
@@ -270,7 +271,7 @@ public final class ModRegistry {
|
||||
}
|
||||
|
||||
public static class PocketUpgradeSerialisers {
|
||||
static final RegistrationHelper<PocketUpgradeSerialiser<?>> REGISTRY = PlatformHelper.get().createRegistrationHelper(PocketUpgradeSerialiser.REGISTRY_ID);
|
||||
static final RegistrationHelper<PocketUpgradeSerialiser<?>> REGISTRY = PlatformHelper.get().createRegistrationHelper(PocketUpgradeSerialiser.registryId());
|
||||
|
||||
public static final RegistryEntry<PocketUpgradeSerialiser<PocketSpeaker>> SPEAKER =
|
||||
REGISTRY.register("speaker", () -> PocketUpgradeSerialiser.simpleWithCustomItem(PocketSpeaker::new));
|
||||
@@ -446,12 +447,12 @@ public final class ModRegistry {
|
||||
private static void addTurtle(CreativeModeTab.Output out, TurtleItem turtle) {
|
||||
out.accept(turtle.create(-1, null, -1, null, null, 0, null));
|
||||
TurtleUpgrades.getVanillaUpgrades()
|
||||
.map(x -> turtle.create(-1, null, -1, null, x, 0, null))
|
||||
.map(x -> turtle.create(-1, null, -1, null, UpgradeData.ofDefault(x), 0, null))
|
||||
.forEach(out::accept);
|
||||
}
|
||||
|
||||
private static void addPocket(CreativeModeTab.Output out, PocketComputerItem pocket) {
|
||||
out.accept(pocket.create(-1, null, -1, null));
|
||||
PocketUpgrades.getVanillaUpgrades().map(x -> pocket.create(-1, null, -1, x)).forEach(out::accept);
|
||||
PocketUpgrades.getVanillaUpgrades().map(x -> pocket.create(-1, null, -1, UpgradeData.ofDefault(x))).forEach(out::accept);
|
||||
}
|
||||
}
|
||||
|
@@ -6,9 +6,12 @@ package dan200.computercraft.shared.command;
|
||||
|
||||
import com.mojang.brigadier.CommandDispatcher;
|
||||
import com.mojang.brigadier.arguments.StringArgumentType;
|
||||
import com.mojang.brigadier.builder.RequiredArgumentBuilder;
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
import com.mojang.brigadier.suggestion.Suggestions;
|
||||
import dan200.computercraft.core.computer.ComputerSide;
|
||||
import dan200.computercraft.core.metrics.Metrics;
|
||||
import dan200.computercraft.shared.command.arguments.ComputersArgumentType;
|
||||
import dan200.computercraft.shared.command.text.TableBuilder;
|
||||
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
||||
import dan200.computercraft.shared.computer.core.ServerComputer;
|
||||
@@ -64,7 +67,6 @@ public final class CommandComputerCraft {
|
||||
var source = context.getSource();
|
||||
List<ServerComputer> computers = new ArrayList<>(ServerContext.get(source.getServer()).registry().getComputers());
|
||||
|
||||
// Unless we're on a server, limit the number of rows we can send.
|
||||
Level world = source.getLevel();
|
||||
var pos = BlockPos.containing(source.getPosition());
|
||||
|
||||
@@ -125,7 +127,7 @@ public final class CommandComputerCraft {
|
||||
if (computer.isOn()) shutdown++;
|
||||
computer.shutdown();
|
||||
}
|
||||
context.getSource().sendSuccess(translate("commands.computercraft.shutdown.done", shutdown, computers.size()), false);
|
||||
context.getSource().sendSuccess(Component.translatable("commands.computercraft.shutdown.done", shutdown, computers.size()), false);
|
||||
return shutdown;
|
||||
}))
|
||||
|
||||
@@ -139,7 +141,7 @@ public final class CommandComputerCraft {
|
||||
if (!computer.isOn()) on++;
|
||||
computer.turnOn();
|
||||
}
|
||||
context.getSource().sendSuccess(translate("commands.computercraft.turn_on.done", on, computers.size()), false);
|
||||
context.getSource().sendSuccess(Component.translatable("commands.computercraft.turn_on.done", on, computers.size()), false);
|
||||
return on;
|
||||
}))
|
||||
|
||||
@@ -170,7 +172,10 @@ public final class CommandComputerCraft {
|
||||
|
||||
.then(command("queue")
|
||||
.requires(UserLevel.ANYONE)
|
||||
.arg("computer", manyComputers())
|
||||
.arg(
|
||||
RequiredArgumentBuilder.<CommandSourceStack, ComputersArgumentType.ComputersSupplier>argument("computer", manyComputers())
|
||||
.suggests((context, builder) -> Suggestions.empty())
|
||||
)
|
||||
.argManyValue("args", StringArgumentType.string(), Collections.emptyList())
|
||||
.executes((ctx, args) -> {
|
||||
var computers = getComputersArgument(ctx, "computer");
|
||||
@@ -214,8 +219,10 @@ public final class CommandComputerCraft {
|
||||
getMetricsInstance(context.getSource()).start();
|
||||
|
||||
var stopCommand = "/computercraft track stop";
|
||||
context.getSource().sendSuccess(translate("commands.computercraft.track.start.stop",
|
||||
link(text(stopCommand), stopCommand, translate("commands.computercraft.track.stop.action"))), false);
|
||||
context.getSource().sendSuccess(Component.translatable(
|
||||
"commands.computercraft.track.start.stop",
|
||||
link(text(stopCommand), stopCommand, Component.translatable("commands.computercraft.track.stop.action"))
|
||||
), false);
|
||||
return 1;
|
||||
}))
|
||||
|
||||
@@ -255,7 +262,7 @@ public final class CommandComputerCraft {
|
||||
out.append(link(
|
||||
text(Integer.toString(serverComputer.getInstanceID())),
|
||||
"/computercraft dump " + serverComputer.getInstanceID(),
|
||||
translate("commands.computercraft.dump.action")
|
||||
Component.translatable("commands.computercraft.dump.action")
|
||||
));
|
||||
}
|
||||
|
||||
@@ -269,13 +276,13 @@ public final class CommandComputerCraft {
|
||||
.append(link(
|
||||
text("\u261b"),
|
||||
"/computercraft tp " + serverComputer.getInstanceID(),
|
||||
translate("commands.computercraft.tp.action")
|
||||
Component.translatable("commands.computercraft.tp.action")
|
||||
))
|
||||
.append(" ")
|
||||
.append(link(
|
||||
text("\u20e2"),
|
||||
"/computercraft view " + serverComputer.getInstanceID(),
|
||||
translate("commands.computercraft.view.action")
|
||||
Component.translatable("commands.computercraft.view.action")
|
||||
));
|
||||
}
|
||||
|
||||
@@ -292,7 +299,7 @@ public final class CommandComputerCraft {
|
||||
return link(
|
||||
position(computer.getPosition()),
|
||||
"/computercraft tp " + computer.getInstanceID(),
|
||||
translate("commands.computercraft.tp.action")
|
||||
Component.translatable("commands.computercraft.tp.action")
|
||||
);
|
||||
} else {
|
||||
return position(computer.getPosition());
|
||||
@@ -306,7 +313,7 @@ public final class CommandComputerCraft {
|
||||
return link(
|
||||
text("\u270E"),
|
||||
"/" + OPEN_COMPUTER + id,
|
||||
translate("commands.computercraft.dump.open_path")
|
||||
Component.translatable("commands.computercraft.dump.open_path")
|
||||
);
|
||||
}
|
||||
|
||||
@@ -331,7 +338,7 @@ public final class CommandComputerCraft {
|
||||
timings.sort(Comparator.<ComputerMetrics, Long>comparing(x -> x.get(sortField.metric(), sortField.aggregate())).reversed());
|
||||
|
||||
var headers = new Component[1 + fields.size()];
|
||||
headers[0] = translate("commands.computercraft.track.dump.computer");
|
||||
headers[0] = Component.translatable("commands.computercraft.track.dump.computer");
|
||||
for (var i = 0; i < fields.size(); i++) headers[i + 1] = fields.get(i).displayName();
|
||||
var table = new TableBuilder("Metrics", headers);
|
||||
|
||||
|
@@ -49,6 +49,29 @@ public enum UserLevel implements Predicate<CommandSourceStack> {
|
||||
return source.hasPermission(toLevel());
|
||||
}
|
||||
|
||||
/**
|
||||
* Take the union of two {@link UserLevel}s.
|
||||
* <p>
|
||||
* This satisfies the property that for all sources {@code s}, {@code a.test(s) || b.test(s) == (a ∪ b).test(s)}.
|
||||
*
|
||||
* @param left The first user level to take the union of.
|
||||
* @param right The second user level to take the union of.
|
||||
* @return The union of two levels.
|
||||
*/
|
||||
public static UserLevel union(UserLevel left, UserLevel right) {
|
||||
if (left == right) return left;
|
||||
|
||||
// x ∪ ANYONE = ANYONE
|
||||
if (left == ANYONE || right == ANYONE) return ANYONE;
|
||||
|
||||
// x ∪ OWNER = OWNER
|
||||
if (left == OWNER) return right;
|
||||
if (right == OWNER) return left;
|
||||
|
||||
// At this point, we have x != y and x, y ∈ { OP, OWNER_OP }.
|
||||
return OWNER_OP;
|
||||
}
|
||||
|
||||
private static boolean isOwner(CommandSourceStack source) {
|
||||
var server = source.getServer();
|
||||
var sender = source.getEntity();
|
||||
|
@@ -44,12 +44,12 @@ public class ArgumentUtils {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static <A extends ArgumentType<?>, T extends ArgumentTypeInfo.Template<A>> void serializeToNetwork(FriendlyByteBuf buffer, ArgumentTypeInfo<A, T> type, ArgumentTypeInfo.Template<A> template) {
|
||||
RegistryWrappers.writeId(buffer, RegistryWrappers.COMMAND_ARGUMENT_TYPES, type);
|
||||
buffer.writeId(RegistryWrappers.COMMAND_ARGUMENT_TYPES, type);
|
||||
type.serializeToNetwork((T) template, buffer);
|
||||
}
|
||||
|
||||
public static ArgumentTypeInfo.Template<?> deserialize(FriendlyByteBuf buffer) {
|
||||
var type = RegistryWrappers.readId(buffer, RegistryWrappers.COMMAND_ARGUMENT_TYPES);
|
||||
var type = buffer.readById(RegistryWrappers.COMMAND_ARGUMENT_TYPES);
|
||||
Objects.requireNonNull(type, "Unknown argument type");
|
||||
return type.deserializeFromNetwork(buffer);
|
||||
}
|
||||
|
@@ -18,7 +18,6 @@ import net.minecraft.commands.CommandBuildContext;
|
||||
import net.minecraft.commands.CommandSourceStack;
|
||||
import net.minecraft.commands.synchronization.ArgumentTypeInfo;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
@@ -159,14 +158,14 @@ public final class ComputersArgumentType implements ArgumentType<ComputersArgume
|
||||
}
|
||||
|
||||
@Override
|
||||
public ComputersArgumentType.Template unpack(@NotNull ComputersArgumentType argumentType) {
|
||||
public ComputersArgumentType.Template unpack(ComputersArgumentType argumentType) {
|
||||
return new ComputersArgumentType.Template(this, argumentType.requireSome);
|
||||
}
|
||||
}
|
||||
|
||||
public record Template(Info info, boolean requireSome) implements ArgumentTypeInfo.Template<ComputersArgumentType> {
|
||||
@Override
|
||||
public ComputersArgumentType instantiate(@NotNull CommandBuildContext context) {
|
||||
public ComputersArgumentType instantiate(CommandBuildContext context) {
|
||||
return requireSome ? SOME : MANY;
|
||||
}
|
||||
|
||||
|
@@ -17,7 +17,6 @@ import net.minecraft.commands.synchronization.ArgumentTypeInfo;
|
||||
import net.minecraft.commands.synchronization.ArgumentTypeInfos;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
@@ -144,7 +143,7 @@ public final class RepeatArgumentType<T, U> implements ArgumentType<List<T>> {
|
||||
) implements ArgumentTypeInfo.Template<RepeatArgumentType<?, ?>> {
|
||||
@Override
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
public RepeatArgumentType<?, ?> instantiate(@NotNull CommandBuildContext commandBuildContext) {
|
||||
public RepeatArgumentType<?, ?> instantiate(CommandBuildContext commandBuildContext) {
|
||||
var child = child().instantiate(commandBuildContext);
|
||||
return flatten ? RepeatArgumentType.someFlat((ArgumentType) child, some()) : RepeatArgumentType.some(child, some());
|
||||
}
|
||||
|
@@ -48,11 +48,15 @@ public class CommandBuilder<S> implements CommandNodeBuilder<S, Command<S>> {
|
||||
return this;
|
||||
}
|
||||
|
||||
public CommandBuilder<S> arg(String name, ArgumentType<?> type) {
|
||||
args.add(RequiredArgumentBuilder.argument(name, type));
|
||||
public CommandBuilder<S> arg(ArgumentBuilder<S, ?> arg) {
|
||||
args.add(arg);
|
||||
return this;
|
||||
}
|
||||
|
||||
public CommandBuilder<S> arg(String name, ArgumentType<?> type) {
|
||||
return arg(RequiredArgumentBuilder.argument(name, type));
|
||||
}
|
||||
|
||||
public <T> CommandNodeBuilder<S, ArgCommand<S, List<T>>> argManyValue(String name, ArgumentType<T> type, List<T> empty) {
|
||||
return argMany(name, type, () -> empty);
|
||||
}
|
||||
@@ -74,7 +78,7 @@ public class CommandBuilder<S> implements CommandNodeBuilder<S, Command<S>> {
|
||||
|
||||
return command -> {
|
||||
// The node for no arguments
|
||||
var tail = tail(ctx -> command.run(ctx, empty.get()));
|
||||
var tail = setupTail(ctx -> command.run(ctx, empty.get()));
|
||||
|
||||
// The node for one or more arguments
|
||||
ArgumentBuilder<S, ?> moreArg = RequiredArgumentBuilder
|
||||
@@ -83,7 +87,7 @@ public class CommandBuilder<S> implements CommandNodeBuilder<S, Command<S>> {
|
||||
|
||||
// Chain all of them together!
|
||||
tail.then(moreArg);
|
||||
return link(tail);
|
||||
return buildTail(tail);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -94,20 +98,16 @@ public class CommandBuilder<S> implements CommandNodeBuilder<S, Command<S>> {
|
||||
|
||||
@Override
|
||||
public CommandNode<S> executes(Command<S> command) {
|
||||
if (args.isEmpty()) throw new IllegalStateException("Cannot have empty arg chain builder");
|
||||
|
||||
return link(tail(command));
|
||||
return buildTail(setupTail(command));
|
||||
}
|
||||
|
||||
private ArgumentBuilder<S, ?> tail(Command<S> command) {
|
||||
var defaultTail = args.get(args.size() - 1);
|
||||
defaultTail.executes(command);
|
||||
if (requires != null) defaultTail.requires(requires);
|
||||
return defaultTail;
|
||||
private ArgumentBuilder<S, ?> setupTail(Command<S> command) {
|
||||
return args.get(args.size() - 1).executes(command);
|
||||
}
|
||||
|
||||
private CommandNode<S> link(ArgumentBuilder<S, ?> tail) {
|
||||
private CommandNode<S> buildTail(ArgumentBuilder<S, ?> tail) {
|
||||
for (var i = args.size() - 2; i >= 0; i--) tail = args.get(i).then(tail);
|
||||
if (requires != null) tail.requires(requires);
|
||||
return tail.build();
|
||||
}
|
||||
}
|
||||
|
@@ -10,6 +10,7 @@ import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||
import com.mojang.brigadier.context.CommandContext;
|
||||
import com.mojang.brigadier.tree.CommandNode;
|
||||
import com.mojang.brigadier.tree.LiteralCommandNode;
|
||||
import dan200.computercraft.shared.command.UserLevel;
|
||||
import net.minecraft.ChatFormatting;
|
||||
import net.minecraft.commands.CommandSourceStack;
|
||||
import net.minecraft.network.chat.ClickEvent;
|
||||
@@ -18,10 +19,11 @@ import net.minecraft.network.chat.Component;
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static dan200.computercraft.core.util.Nullability.assertNonNull;
|
||||
import static dan200.computercraft.shared.command.text.ChatHelpers.coloured;
|
||||
import static dan200.computercraft.shared.command.text.ChatHelpers.translate;
|
||||
|
||||
/**
|
||||
* An alternative to {@link LiteralArgumentBuilder} which also provides a {@code /... help} command, and defaults
|
||||
@@ -38,6 +40,29 @@ public final class HelpingArgumentBuilder extends LiteralArgumentBuilder<Command
|
||||
return new HelpingArgumentBuilder(literal);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LiteralArgumentBuilder<CommandSourceStack> requires(Predicate<CommandSourceStack> requirement) {
|
||||
throw new IllegalStateException("Cannot use requires on a HelpingArgumentBuilder");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Predicate<CommandSourceStack> getRequirement() {
|
||||
// The requirement of this node is the union of all child's requirements.
|
||||
var requirements = Stream.concat(
|
||||
children.stream().map(ArgumentBuilder::getRequirement),
|
||||
getArguments().stream().map(CommandNode::getRequirement)
|
||||
).toList();
|
||||
|
||||
// If all requirements are a UserLevel, take the union of those instead.
|
||||
var userLevel = UserLevel.OWNER;
|
||||
for (var requirement : requirements) {
|
||||
if (!(requirement instanceof UserLevel level)) return x -> requirements.stream().anyMatch(y -> y.test(x));
|
||||
userLevel = UserLevel.union(userLevel, level);
|
||||
}
|
||||
|
||||
return userLevel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LiteralArgumentBuilder<CommandSourceStack> executes(final Command<CommandSourceStack> command) {
|
||||
throw new IllegalStateException("Cannot use executes on a HelpingArgumentBuilder");
|
||||
@@ -77,13 +102,11 @@ public final class HelpingArgumentBuilder extends LiteralArgumentBuilder<Command
|
||||
|
||||
private LiteralCommandNode<CommandSourceStack> buildImpl(String id, String command) {
|
||||
var helpCommand = new HelpCommand(id, command);
|
||||
var node = new LiteralCommandNode<CommandSourceStack>(getLiteral(), helpCommand, getRequirement(), getRedirect(), getRedirectModifier(), isFork());
|
||||
var node = new LiteralCommandNode<>(getLiteral(), helpCommand, getRequirement(), getRedirect(), getRedirectModifier(), isFork());
|
||||
helpCommand.node = node;
|
||||
|
||||
// Set up a /... help command
|
||||
var helpNode = LiteralArgumentBuilder.<CommandSourceStack>literal("help")
|
||||
.requires(x -> getArguments().stream().anyMatch(y -> y.getRequirement().test(x)))
|
||||
.executes(helpCommand);
|
||||
var helpNode = LiteralArgumentBuilder.<CommandSourceStack>literal("help").executes(helpCommand);
|
||||
|
||||
// Add all normal command children to this and the help node
|
||||
for (var child : getArguments()) {
|
||||
@@ -153,9 +176,9 @@ public final class HelpingArgumentBuilder extends LiteralArgumentBuilder<Command
|
||||
var output = Component.literal("")
|
||||
.append(coloured("/" + command + usage, HEADER))
|
||||
.append(" ")
|
||||
.append(coloured(translate("commands." + id + ".synopsis"), SYNOPSIS))
|
||||
.append(Component.translatable("commands." + id + ".synopsis").withStyle(SYNOPSIS))
|
||||
.append("\n")
|
||||
.append(translate("commands." + id + ".desc"));
|
||||
.append(Component.translatable("commands." + id + ".desc"));
|
||||
|
||||
for (var child : node.getChildren()) {
|
||||
if (!child.getRequirement().test(context.getSource()) || !(child instanceof LiteralCommandNode)) {
|
||||
@@ -171,7 +194,7 @@ public final class HelpingArgumentBuilder extends LiteralArgumentBuilder<Command
|
||||
));
|
||||
output.append(component);
|
||||
|
||||
output.append(" - ").append(translate("commands." + id + "." + child.getName() + ".synopsis"));
|
||||
output.append(" - ").append(Component.translatable("commands." + id + "." + child.getName() + ".synopsis"));
|
||||
}
|
||||
|
||||
return output;
|
||||
|
@@ -23,28 +23,15 @@ public final class ChatHelpers {
|
||||
}
|
||||
|
||||
public static MutableComponent coloured(@Nullable String text, ChatFormatting colour) {
|
||||
return Component.literal(text == null ? "" : text).withStyle(colour);
|
||||
}
|
||||
|
||||
public static <T extends MutableComponent> T coloured(T component, ChatFormatting colour) {
|
||||
component.withStyle(colour);
|
||||
return component;
|
||||
return text(text).withStyle(colour);
|
||||
}
|
||||
|
||||
public static MutableComponent text(@Nullable String text) {
|
||||
return Component.literal(text == null ? "" : text);
|
||||
}
|
||||
|
||||
public static MutableComponent translate(@Nullable String text) {
|
||||
return Component.translatable(text == null ? "" : text);
|
||||
}
|
||||
|
||||
public static MutableComponent translate(@Nullable String text, Object... args) {
|
||||
return Component.translatable(text == null ? "" : text, args);
|
||||
}
|
||||
|
||||
public static MutableComponent list(Component... children) {
|
||||
var component = Component.literal("");
|
||||
var component = Component.empty();
|
||||
for (var child : children) {
|
||||
component.append(child);
|
||||
}
|
||||
@@ -52,14 +39,14 @@ public final class ChatHelpers {
|
||||
}
|
||||
|
||||
public static MutableComponent position(@Nullable BlockPos pos) {
|
||||
if (pos == null) return translate("commands.computercraft.generic.no_position");
|
||||
return translate("commands.computercraft.generic.position", pos.getX(), pos.getY(), pos.getZ());
|
||||
if (pos == null) return Component.translatable("commands.computercraft.generic.no_position");
|
||||
return Component.translatable("commands.computercraft.generic.position", pos.getX(), pos.getY(), pos.getZ());
|
||||
}
|
||||
|
||||
public static MutableComponent bool(boolean value) {
|
||||
return value
|
||||
? coloured(translate("commands.computercraft.generic.yes"), ChatFormatting.GREEN)
|
||||
: coloured(translate("commands.computercraft.generic.no"), ChatFormatting.RED);
|
||||
? Component.translatable("commands.computercraft.generic.yes").withStyle(ChatFormatting.GREEN)
|
||||
: Component.translatable("commands.computercraft.generic.no").withStyle(ChatFormatting.RED);
|
||||
}
|
||||
|
||||
public static Component link(MutableComponent component, String command, Component toolTip) {
|
||||
@@ -81,10 +68,9 @@ public final class ChatHelpers {
|
||||
}
|
||||
|
||||
public static MutableComponent copy(String text) {
|
||||
var name = Component.literal(text);
|
||||
var style = name.getStyle()
|
||||
return Component.literal(text).withStyle(s -> s
|
||||
.withClickEvent(new ClickEvent(ClickEvent.Action.COPY_TO_CLIPBOARD, text))
|
||||
.withHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, Component.translatable("gui.computercraft.tooltip.copy")));
|
||||
return name.withStyle(style);
|
||||
.withHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, Component.translatable("gui.computercraft.tooltip.copy")))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -11,7 +11,6 @@ import org.apache.commons.lang3.StringUtils;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import static dan200.computercraft.shared.command.text.ChatHelpers.coloured;
|
||||
import static dan200.computercraft.shared.command.text.ChatHelpers.translate;
|
||||
|
||||
public interface TableFormatter {
|
||||
Component SEPARATOR = coloured("| ", ChatFormatting.GRAY);
|
||||
@@ -99,7 +98,7 @@ public interface TableFormatter {
|
||||
}
|
||||
|
||||
if (table.getAdditional() > 0) {
|
||||
writeLine(id, coloured(translate("commands.computercraft.generic.additional_rows", table.getAdditional()), ChatFormatting.AQUA));
|
||||
writeLine(id, Component.translatable("commands.computercraft.generic.additional_rows", table.getAdditional()).withStyle(ChatFormatting.AQUA));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -130,6 +130,7 @@ public abstract class AbstractComputerBlock<T extends AbstractComputerBlockEntit
|
||||
|
||||
@Override
|
||||
public void playerWillDestroy(Level world, BlockPos pos, BlockState state, Player player) {
|
||||
super.playerWillDestroy(world, pos, state, player);
|
||||
if (!(world instanceof ServerLevel serverWorld)) return;
|
||||
|
||||
// We drop the item here instead of doing it in the harvest method, as we should
|
||||
|
@@ -199,6 +199,11 @@ public abstract class AbstractComputerBlockEntity extends BlockEntity implements
|
||||
return localSide;
|
||||
}
|
||||
|
||||
private void updateRedstoneInputs(ServerComputer computer) {
|
||||
var pos = getBlockPos();
|
||||
for (var dir : DirectionUtil.FACINGS) updateRedstoneInput(computer, dir, pos.relative(dir));
|
||||
}
|
||||
|
||||
private void updateRedstoneInput(ServerComputer computer, Direction dir, BlockPos targetPos) {
|
||||
var offsetSide = dir.getOpposite();
|
||||
var localDir = remapToLocalSide(dir);
|
||||
@@ -254,8 +259,7 @@ public abstract class AbstractComputerBlockEntity extends BlockEntity implements
|
||||
|
||||
// If the position is not any adjacent one, update all inputs. This is pretty terrible, but some redstone mods
|
||||
// handle this incorrectly.
|
||||
var pos = getBlockPos();
|
||||
for (var dir : DirectionUtil.FACINGS) updateRedstoneInput(computer, dir, pos.relative(dir));
|
||||
updateRedstoneInputs(computer);
|
||||
invalidSides = (1 << 6) - 1; // Mark all peripherals as dirty.
|
||||
}
|
||||
|
||||
@@ -264,9 +268,10 @@ public abstract class AbstractComputerBlockEntity extends BlockEntity implements
|
||||
*/
|
||||
public void updateOutput() {
|
||||
BlockEntityHelpers.updateBlock(this);
|
||||
for (var dir : DirectionUtil.FACINGS) {
|
||||
RedstoneUtil.propagateRedstoneOutput(getLevel(), getBlockPos(), dir);
|
||||
}
|
||||
for (var dir : DirectionUtil.FACINGS) RedstoneUtil.propagateRedstoneOutput(getLevel(), getBlockPos(), dir);
|
||||
|
||||
var computer = getServerComputer();
|
||||
if (computer != null) updateRedstoneInputs(computer);
|
||||
}
|
||||
|
||||
protected abstract ServerComputer createComputer(int id);
|
||||
|
@@ -9,12 +9,16 @@ import dan200.computercraft.api.ComputerCraftAPI;
|
||||
import dan200.computercraft.api.filesystem.Mount;
|
||||
import dan200.computercraft.api.network.PacketNetwork;
|
||||
import dan200.computercraft.core.ComputerContext;
|
||||
import dan200.computercraft.core.computer.ComputerThread;
|
||||
import dan200.computercraft.core.computer.GlobalEnvironment;
|
||||
import dan200.computercraft.core.computer.mainthread.MainThread;
|
||||
import dan200.computercraft.core.computer.mainthread.MainThreadConfig;
|
||||
import dan200.computercraft.core.lua.CobaltLuaMachine;
|
||||
import dan200.computercraft.core.lua.ILuaMachine;
|
||||
import dan200.computercraft.core.methods.MethodSupplier;
|
||||
import dan200.computercraft.core.methods.PeripheralMethod;
|
||||
import dan200.computercraft.impl.AbstractComputerCraftAPI;
|
||||
import dan200.computercraft.impl.ApiFactories;
|
||||
import dan200.computercraft.impl.GenericSources;
|
||||
import dan200.computercraft.shared.CommonHooks;
|
||||
import dan200.computercraft.shared.computer.metrics.GlobalMetrics;
|
||||
import dan200.computercraft.shared.config.ConfigSpec;
|
||||
@@ -65,12 +69,14 @@ public final class ServerContext {
|
||||
private ServerContext(MinecraftServer server) {
|
||||
this.server = server;
|
||||
storageDir = server.getWorldPath(FOLDER);
|
||||
mainThread = new MainThread();
|
||||
context = new ComputerContext(
|
||||
new Environment(server),
|
||||
new ComputerThread(ConfigSpec.computerThreads.get()),
|
||||
mainThread, luaMachine
|
||||
);
|
||||
mainThread = new MainThread(mainThreadConfig);
|
||||
context = ComputerContext.builder(new Environment(server))
|
||||
.computerThreads(ConfigSpec.computerThreads.get())
|
||||
.mainThreadScheduler(mainThread)
|
||||
.luaFactory(luaMachine)
|
||||
.apiFactories(ApiFactories.getAll())
|
||||
.genericMethods(GenericSources.getAllMethods())
|
||||
.build();
|
||||
idAssigner = new IDAssigner(storageDir.resolve("ids.json"));
|
||||
}
|
||||
|
||||
@@ -132,6 +138,16 @@ public final class ServerContext {
|
||||
return context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@link MethodSupplier} used to find methods on peripherals.
|
||||
*
|
||||
* @return The {@link PeripheralMethod} method supplier.
|
||||
* @see ComputerContext#peripheralMethods()
|
||||
*/
|
||||
public MethodSupplier<PeripheralMethod> peripheralMethods() {
|
||||
return context.peripheralMethods();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tick all components of this server context. This should <em>NOT</em> be called outside of {@link CommonHooks}.
|
||||
*/
|
||||
@@ -215,4 +231,16 @@ public final class ServerContext {
|
||||
return ComputerCraftAPI.MOD_ID + "/" + ComputerCraftAPI.getInstalledVersion();
|
||||
}
|
||||
}
|
||||
|
||||
private static final MainThreadConfig mainThreadConfig = new MainThreadConfig() {
|
||||
@Override
|
||||
public long maxGlobalTime() {
|
||||
return TimeUnit.MILLISECONDS.toNanos(ConfigSpec.maxMainGlobalTime.get());
|
||||
}
|
||||
|
||||
@Override
|
||||
public long maxComputerTime() {
|
||||
return TimeUnit.MILLISECONDS.toNanos(ConfigSpec.maxMainComputerTime.get());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user