1
0
mirror of https://github.com/SquidDev-CC/CC-Tweaked synced 2025-11-08 09:23:00 +00:00

Compare commits

..

7 Commits

Author SHA1 Message Date
Jonathan Coates
7b9a156abc Register our block entities with DFU
This ensures that data fixers will be applied to items in these
inventories.
2024-03-22 21:47:11 +00:00
Jonathan Coates
0a9e5c78f3 Remove some deprecated code
Some of this is technically an API break, but 1.20.4 is pretty unstable.

 - Remove WiredNetwork from the public API
 - Remove legacy computer selectors
2024-03-22 21:36:52 +00:00
Jonathan Coates
da5885ef35 Merge branch 'mc-1.20.x' into mc-1.20.y 2024-03-22 21:23:49 +00:00
Jonathan Coates
240528cce5 Update NF and NG
Trying to rebuild after this OOM killed all my windows. I hate NeoGradle
so much.
2024-02-18 19:02:23 +00:00
Jonathan Coates
83f1f86888 Merge branch 'mc-1.20.x' into mc-1.20.y 2024-02-18 18:45:20 +00:00
Jonathan Coates
9c202bd1c2 Fix incorrect cast for JEI 2024-02-05 18:52:20 +00:00
Jonathan Coates
fc834cd97f Update to 1.20.4 2024-01-31 20:55:14 +00:00
976 changed files with 7648 additions and 9346 deletions

View File

@@ -8,8 +8,10 @@ body:
label: Minecraft Version label: Minecraft Version
description: What version of Minecraft are you using? description: What version of Minecraft are you using?
options: options:
- 1.20.1 - 1.16.x
- 1.21.x - 1.18.x
- 1.19.x
- 1.20.x
validations: validations:
required: true required: true
- type: input - type: input

View File

@@ -9,16 +9,16 @@ jobs:
steps: steps:
- name: 📥 Clone repository - name: 📥 Clone repository
uses: actions/checkout@v4 uses: actions/checkout@v3
- name: 📥 Set up Java - name: 📥 Set up Java
uses: actions/setup-java@v4 uses: actions/setup-java@v3
with: with:
java-version: 17 java-version: 17
distribution: 'temurin' distribution: 'temurin'
- name: 📥 Setup Gradle - name: 📥 Setup Gradle
uses: gradle/actions/setup-gradle@v3 uses: gradle/gradle-build-action@v2
with: with:
cache-read-only: ${{ !startsWith(github.ref, 'refs/heads/mc-') }} cache-read-only: ${{ !startsWith(github.ref, 'refs/heads/mc-') }}
@@ -30,16 +30,8 @@ jobs:
- name: ⚒️ Build - name: ⚒️ Build
run: ./gradlew assemble || ./gradlew assemble run: ./gradlew assemble || ./gradlew assemble
- name: Cache pre-commit
uses: actions/cache@v4
with:
path: ~/.cache/pre-commit
key: pre-commit-3|${{ env.pythonLocation }}|${{ hashFiles('.pre-commit-config.yaml') }}
- name: 💡 Lint - name: 💡 Lint
run: | uses: pre-commit/action@v3.0.0
pipx install pre-commit
pre-commit run --show-diff-on-failure --color=always
- name: 🧪 Run tests - name: 🧪 Run tests
run: ./gradlew test validateMixinNames checkChangelog run: ./gradlew test validateMixinNames checkChangelog
@@ -66,13 +58,13 @@ jobs:
find projects/forge/build/libs projects/fabric/build/libs -type f -regex '.*[0-9.]+\(-SNAPSHOT\)?\.jar$' -exec bash -c 'cp {} "jars/$(basename {} .jar)-$(git rev-parse HEAD).jar"' \; find projects/forge/build/libs projects/fabric/build/libs -type f -regex '.*[0-9.]+\(-SNAPSHOT\)?\.jar$' -exec bash -c 'cp {} "jars/$(basename {} .jar)-$(git rev-parse HEAD).jar"' \;
- name: 📤 Upload Jar - name: 📤 Upload Jar
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v3
with: with:
name: CC-Tweaked name: CC-Tweaked
path: ./jars path: ./jars
- name: 📤 Upload coverage - name: 📤 Upload coverage
uses: codecov/codecov-action@v4 uses: codecov/codecov-action@v3
build-core: build-core:
strategy: strategy:
@@ -89,28 +81,24 @@ jobs:
runs-on: ${{ matrix.uses }} runs-on: ${{ matrix.uses }}
steps: steps:
- name: 📥 Clone repository - name: Clone repository
uses: actions/checkout@v4 uses: actions/checkout@v3
- name: 📥 Set up Java - name: Set up Java
uses: actions/setup-java@v4 uses: actions/setup-java@v3
with: with:
java-version: 17 java-version: 17
distribution: 'temurin' distribution: 'temurin'
- name: 📥 Setup Gradle - name: Setup Gradle
uses: gradle/actions/setup-gradle@v3 uses: gradle/gradle-build-action@v2
with: with:
cache-read-only: ${{ !startsWith(github.ref, 'refs/heads/mc-') }} cache-read-only: ${{ !startsWith(github.ref, 'refs/heads/mc-') }}
- name: ⚒️ Build - name: Run tests
run: |
./gradlew --configure-on-demand :core:assemble
- name: 🧪 Run tests
run: | run: |
./gradlew --configure-on-demand :core:test ./gradlew --configure-on-demand :core:test
- name: 🧪 Parse test reports - name: Parse test reports
run: python3 ./tools/parse-reports.py run: python3 ./tools/parse-reports.py
if: ${{ failure() }} if: ${{ failure() }}

19
.github/workflows/make-doc.sh vendored Executable file
View File

@@ -0,0 +1,19 @@
#!/usr/bin/env bash
set -eu
DEST="${GITHUB_REF#refs/*/}"
echo "Uploading docs to https://tweaked.cc/$DEST"
# Setup ssh key
mkdir -p "$HOME/.ssh/"
echo "$SSH_KEY" > "$HOME/.ssh/key"
chmod 600 "$HOME/.ssh/key"
# And upload
rsync -avc -e "ssh -i $HOME/.ssh/key -o StrictHostKeyChecking=no -p $SSH_PORT" \
"$GITHUB_WORKSPACE/projects/web/build/site/" \
"$SSH_USER@$SSH_HOST:/$DEST"
rsync -avc -e "ssh -i $HOME/.ssh/key -o StrictHostKeyChecking=no -p $SSH_PORT" \
"$GITHUB_WORKSPACE/projects/common-api/build/docs/javadoc/" \
"$SSH_USER@$SSH_HOST:/$DEST/javadoc"

View File

@@ -3,7 +3,8 @@ name: Build documentation
on: on:
push: push:
branches: branches:
- mc-* - mc-1.19.x
- mc-1.20.x
jobs: jobs:
make_doc: make_doc:
@@ -11,25 +12,30 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: 📥 Clone repository - name: Clone repository
uses: actions/checkout@v4 uses: actions/checkout@v3
- name: 📥 Set up Java - name: Set up Java
uses: actions/setup-java@v4 uses: actions/setup-java@v1
with: with:
java-version: 17 java-version: 17
distribution: 'temurin' distribution: 'temurin'
- name: 📥 Setup Gradle - name: Setup Gradle
uses: gradle/actions/setup-gradle@v3 uses: gradle/gradle-build-action@v2
with: with:
cache-read-only: ${{ !startsWith(github.ref, 'refs/heads/mc-') }} cache-read-only: ${{ !startsWith(github.ref, 'refs/heads/mc-') }}
- name: ⚒️ Generate documentation - name: Build with Gradle
run: ./gradlew docWebsite --no-daemon run: ./gradlew compileJava --no-daemon || ./gradlew compileJava --no-daemon
- name: 📤 Upload Jar - name: Generate documentation
uses: actions/upload-artifact@v4 run: ./gradlew docWebsite :common-api:javadoc --no-daemon
with:
name: Documentation - name: Upload documentation
path: ./projects/web/build/site/ run: .github/workflows/make-doc.sh 2> /dev/null
env:
SSH_KEY: ${{ secrets.SSH_KEY }}
SSH_USER: ${{ secrets.SSH_USER }}
SSH_HOST: ${{ secrets.SSH_HOST }}
SSH_PORT: ${{ secrets.SSH_PORT }}

26
.gitpod.yml Normal file
View File

@@ -0,0 +1,26 @@
# SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
#
# SPDX-License-Identifier: MPL-2.0
image:
file: config/gitpod/Dockerfile
ports:
- port: 25565
onOpen: notify
vscode:
extensions:
- eamodio.gitlens
- github.vscode-pull-request-github
- ms-azuretools.vscode-docker
- redhat.java
- richardwillis.vscode-gradle
- vscjava.vscode-java-debug
- vscode.github
tasks:
- name: Setup pre-commit hool
init: pre-commit install --allow-missing-config
- name: Install npm packages
init: npm ci

View File

@@ -27,7 +27,7 @@ repos:
exclude: "^(.*\\.(bat)|LICENSE)$" exclude: "^(.*\\.(bat)|LICENSE)$"
- repo: https://github.com/fsfe/reuse-tool - repo: https://github.com/fsfe/reuse-tool
rev: v4.0.3 rev: v2.1.0
hooks: hooks:
- id: reuse - id: reuse

100
.reuse/dep5 Normal file
View File

@@ -0,0 +1,100 @@
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Source: https://github.com/cc-tweaked/cc-tweaked
Upstream-Name: CC: Tweaked
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/*
projects/web/src/htmlTransform/export/index.json
projects/web/src/htmlTransform/export/items/minecraft/*
Comment: Generated/data files are CC0.
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
projects/common/src/main/resources/assets/minecraft/shaders/core/computercraft/monitor_tbo.json
projects/common/src/main/resources/computercraft.mixins.json
projects/common/src/testMod/resources/computercraft-gametest.mixins.json
projects/common/src/testMod/resources/data/computercraft/loot_tables/treasure_disk.json
projects/common/src/testMod/resources/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
projects/fabric/src/main/resources/fabric.mod.json
projects/fabric/src/testMod/resources/computercraft-gametest.fabric.mixins.json
projects/fabric/src/testMod/resources/fabric.mod.json
projects/forge/src/client/resources/computercraft-client.forge.mixins.json
projects/web/src/frontend/mount/.settings
projects/web/src/frontend/mount/example.nfp
projects/web/src/frontend/mount/example.nft
projects/web/src/frontend/mount/expr_template.lua
projects/web/tsconfig.json
Comment: Several assets where it's inconvenient to create a .license file.
Copyright: The CC: Tweaked Developers
License: MPL-2.0
Files:
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/assets/computercraft/textures/gui/term_font.png
projects/core/src/main/resources/data/computercraft/lua/rom/autorun/.ignoreme
projects/core/src/main/resources/data/computercraft/lua/rom/help/*
projects/core/src/main/resources/data/computercraft/lua/rom/programs/fun/advanced/levels/*
projects/web/src/htmlTransform/export/items/computercraft/*
Comment: Bulk-license original assets as CCPL.
Copyright: 2011 Daniel Ratcliffe
License: LicenseRef-CCPL
Files:
projects/common/src/main/resources/assets/computercraft/lang/cs_cz.json
projects/common/src/main/resources/assets/computercraft/lang/ko_kr.json
projects/common/src/main/resources/assets/computercraft/lang/pl_pl.json
projects/common/src/main/resources/assets/computercraft/lang/pt_br.json
projects/common/src/main/resources/assets/computercraft/lang/ru_ru.json
projects/common/src/main/resources/assets/computercraft/lang/uk_ua.json
projects/common/src/main/resources/assets/computercraft/lang/zh_cn.json
Comment: Community-contributed license files
Copyright: 2017 The CC: Tweaked Developers
License: LicenseRef-CCPL
Files:
projects/common/src/main/resources/assets/computercraft/lang/*
Comment: Community-contributed license files
Copyright: 2017 The CC: Tweaked Developers
License: MPL-2.0
Files:
.github/*
Comment:
GitHub build scripts are CC0. While we could add a header to each file,
it's unclear if it will break actions or issue templates in some way.
Copyright: Jonathan Coates <git@squiddev.cc>
License: CC0-1.0
Files:
gradle/wrapper/*
gradlew
gradlew.bat
Copyright: Gradle Inc
License: Apache-2.0
Files: projects/core/src/test/resources/test-rom/data/json-parsing/*
Copyright: 2016 Nicolas Seriot
License: MIT

View File

@@ -22,16 +22,16 @@ If you have a bug, suggestion, or other feedback, the best thing to do is [file
use the issue templates - they provide a useful hint on what information to provide. use the issue templates - they provide a useful hint on what information to provide.
## Translations ## Translations
Translations are managed through [CrowdIn], an online interface for managing language strings. Translations may either Translations are managed through [Weblate], an online interface for managing language strings. This is synced
be contributed there, or directly via a pull request. automatically with GitHub, so please don't submit PRs adding/changing translations!
## Setting up a development environment ## Setting up a development environment
In order to develop CC: Tweaked, you'll need to download the source code and then run it. In order to develop CC: Tweaked, you'll need to download the source code and then run it.
- Make sure you've got the following software installed: - Make sure you've got the following software installed:
- Java Development Kit 17 (JDK). This can be downloaded from [Adoptium]. - Java Development Kit (JDK) installed. This can be downloaded from [Adoptium].
- [Git](https://git-scm.com/). - [Git](https://git-scm.com/).
- [NodeJS 20 or later][node]. - If you want to work on documentation, [NodeJS][node].
- Download CC: Tweaked's source code: - Download CC: Tweaked's source code:
``` ```
@@ -101,10 +101,10 @@ about how you can build on that until you've covered everything!
[community]: README.md#community "Get in touch with the community." [community]: README.md#community "Get in touch with the community."
[Adoptium]: https://adoptium.net/temurin/releases?version=17 "Download OpenJDK 17" [Adoptium]: https://adoptium.net/temurin/releases?version=17 "Download OpenJDK 17"
[illuaminate]: https://github.com/SquidDev/illuaminate/ "Illuaminate on GitHub" [illuaminate]: https://github.com/SquidDev/illuaminate/ "Illuaminate on GitHub"
[weblate]: https://i18n.tweaked.cc/projects/cc-tweaked/minecraft/ "CC: Tweaked weblate instance"
[docs]: https://tweaked.cc/ "CC: Tweaked documentation" [docs]: https://tweaked.cc/ "CC: Tweaked documentation"
[ldoc]: http://stevedonovan.github.io/ldoc/ "ldoc, a Lua documentation generator." [ldoc]: http://stevedonovan.github.io/ldoc/ "ldoc, a Lua documentation generator."
[mc-test]: https://www.youtube.com/watch?v=vXaWOJTCYNg [mc-test]: https://www.youtube.com/watch?v=vXaWOJTCYNg
[busted]: https://github.com/Olivine-Labs/busted "busted: Elegant Lua unit testing." [busted]: https://github.com/Olivine-Labs/busted "busted: Elegant Lua unit testing."
[node]: https://nodejs.org/en/ "Node.js" [node]: https://nodejs.org/en/ "Node.js"
[architecture]: projects/ARCHITECTURE.md [architecture]: projects/ARCHITECTURE.md
[Crowdin]: https://crowdin.com/project/cc-tweaked/

View File

@@ -26,9 +26,8 @@ developing the mod, [check out the instructions here](CONTRIBUTING.md#developing
## Community ## Community
If you need help getting started with CC: Tweaked, want to show off your latest project, or just want to chat about If you need help getting started with CC: Tweaked, want to show off your latest project, or just want to chat about
ComputerCraft, do check out our [GitHub discussions page][GitHub discussions]! There's also a fairly populated, ComputerCraft, do check out our [forum] and [GitHub discussions page][GitHub discussions]! There's also a fairly
albeit quiet IRC channel on [EsperNet], if that's more your cup of tea. You can join `#computercraft` through your populated, albeit quiet [IRC channel][irc], if that's more your cup of tea.
desktop client, or online using [KiwiIRC].
We also host fairly comprehensive documentation at [tweaked.cc](https://tweaked.cc/ "The CC: Tweaked website"). We also host fairly comprehensive documentation at [tweaked.cc](https://tweaked.cc/ "The CC: Tweaked website").
@@ -40,7 +39,7 @@ on is present.
```groovy ```groovy
repositories { repositories {
maven { maven {
url "https://maven.squiddev.cc" url "https://squiddev.cc/maven/"
content { content {
includeGroup("cc.tweaked") includeGroup("cc.tweaked")
} }
@@ -52,9 +51,8 @@ dependencies {
compileOnly("cc.tweaked:cc-tweaked-$mcVersion-common-api:$cctVersion") compileOnly("cc.tweaked:cc-tweaked-$mcVersion-common-api:$cctVersion")
// Forge Gradle // Forge Gradle
compileOnly("cc.tweaked:cc-tweaked-$mcVersion-core-api:$cctVersion") compileOnly("cc.tweaked:cc-tweaked-$mcVersion-forge-api:$cctVersion")
compileOnly(fg.deobf("cc.tweaked:cc-tweaked-$mcVersion-forge-api:$cctVersion")) runtimeOnly("cc.tweaked:cc-tweaked-$mcVersion-forge:$cctVersion")
runtimeOnly(fg.deobf("cc.tweaked:cc-tweaked-$mcVersion-forge:$cctVersion"))
// Fabric Loom // Fabric Loom
modCompileOnly("cc.tweaked:cc-tweaked-$mcVersion-fabric-api:$cctVersion") modCompileOnly("cc.tweaked:cc-tweaked-$mcVersion-fabric-api:$cctVersion")
@@ -87,6 +85,6 @@ the generated documentation [can be browsed online](https://tweaked.cc/javadoc/)
[modrinth]: https://modrinth.com/mod/gu7yAYhd "Download CC: Tweaked from Modrinth" [modrinth]: https://modrinth.com/mod/gu7yAYhd "Download CC: Tweaked from Modrinth"
[Minecraft Forge]: https://files.minecraftforge.net/ "Download Minecraft Forge." [Minecraft Forge]: https://files.minecraftforge.net/ "Download Minecraft Forge."
[Fabric]: https://fabricmc.net/use/installer/ "Download Fabric." [Fabric]: https://fabricmc.net/use/installer/ "Download Fabric."
[forum]: https://forums.computercraft.cc/
[GitHub Discussions]: https://github.com/cc-tweaked/CC-Tweaked/discussions [GitHub Discussions]: https://github.com/cc-tweaked/CC-Tweaked/discussions
[EsperNet]: https://www.esper.net/ [IRC]: https://webchat.esper.net/?channels=computercraft "#computercraft on EsperNet"
[KiwiIRC]: https://kiwiirc.com/nextclient/#irc://irc.esper.net:+6697/#computercraft "#computercraft on EsperNet"

View File

@@ -1,110 +0,0 @@
# SPDX-FileCopyrightText: 2017 The CC: Tweaked Developers
#
# SPDX-License-Identifier: MPL-2.0
version = 1
SPDX-PackageName = "CC: Tweaked"
SPDX-PackageSupplier = "Jonathan Coates <git@squiddev.cc>"
SPDX-PackageDownloadLocation = "https://github.com/cc-tweaked/cc-tweaked"
[[annotations]]
# Generated/data files are CC0.
SPDX-FileCopyrightText = "The CC: Tweaked Developers"
SPDX-License-Identifier = "CC0-1.0"
path = [
"gradle/gradle-daemon-jvm.properties",
"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/**/src/generated/**",
"projects/web/src/htmlTransform/export/index.json",
"projects/web/src/htmlTransform/export/items/minecraft/**",
]
[[annotations]]
# Several assets where it's inconvenient to create a .license file.
SPDX-FileCopyrightText = "The CC: Tweaked Developers"
SPDX-License-Identifier = "MPL-2.0"
path = [
"doc/images/**",
"package.json",
"package-lock.json",
"projects/common/src/client/resources/computercraft-client.mixins.json",
"projects/common/src/main/resources/assets/minecraft/shaders/core/computercraft/monitor_tbo.json",
"projects/common/src/main/resources/computercraft.mixins.json",
"projects/common/src/testMod/resources/computercraft-gametest.mixins.json",
"projects/common/src/testMod/resources/data/computercraft/loot_tables/treasure_disk.json",
"projects/common/src/testMod/resources/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",
"projects/fabric/src/main/resources/fabric.mod.json",
"projects/fabric/src/testMod/resources/computercraft-gametest.fabric.mixins.json",
"projects/fabric/src/testMod/resources/fabric.mod.json",
"projects/forge/src/client/resources/computercraft-client.forge.mixins.json",
"projects/web/src/frontend/mount/.settings",
"projects/web/src/frontend/mount/example.nfp",
"projects/web/src/frontend/mount/example.nft",
"projects/web/src/frontend/mount/expr_template.lua",
"projects/web/tsconfig.json",
]
[[annotations]]
# Bulk-license original assets as CCPL.
SPDX-FileCopyrightText = "2011 Daniel Ratcliffe"
SPDX-License-Identifier = "LicenseRef-CCPL"
path = [
"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/assets/computercraft/textures/gui/term_font.png",
"projects/core/src/main/resources/data/computercraft/lua/rom/autorun/.ignoreme",
"projects/core/src/main/resources/data/computercraft/lua/rom/help/**",
"projects/core/src/main/resources/data/computercraft/lua/rom/programs/fun/advanced/levels/**",
"projects/web/src/htmlTransform/export/items/computercraft/**",
]
[[annotations]]
# Community-contributed license files
SPDX-FileCopyrightText = "2017 The CC: Tweaked Developers"
SPDX-License-Identifier = "LicenseRef-CCPL"
path = [
"projects/common/src/main/resources/assets/computercraft/lang/cs_cz.json",
"projects/common/src/main/resources/assets/computercraft/lang/ko_kr.json",
"projects/common/src/main/resources/assets/computercraft/lang/pl_pl.json",
"projects/common/src/main/resources/assets/computercraft/lang/pt_br.json",
"projects/common/src/main/resources/assets/computercraft/lang/ru_ru.json",
"projects/common/src/main/resources/assets/computercraft/lang/uk_ua.json",
"projects/common/src/main/resources/assets/computercraft/lang/zh_cn.json",
]
[[annotations]]
# Community-contributed license files
SPDX-FileCopyrightText = "2017 The CC: Tweaked Developers"
SPDX-License-Identifier = "MPL-2.0"
path = "projects/common/src/main/resources/assets/computercraft/lang/**"
[[annotations]]
# GitHub build scripts are CC0. While we could add a header to each file,
# it's unclear if it will break actions or issue templates in some way.
SPDX-FileCopyrightText = "Jonathan Coates <git@squiddev.cc>"
SPDX-License-Identifier = "CC0-1.0"
path = ".github/**"
[[annotations]]
path = ["gradle/wrapper/**"]
SPDX-FileCopyrightText = "Gradle Inc"
SPDX-License-Identifier = "Apache-2.0"
[[annotations]]
path = "projects/core/src/test/resources/test-rom/data/json-parsing/**"
SPDX-FileCopyrightText = "2016 Nicolas Seriot"
SPDX-License-Identifier = "MIT"

View File

@@ -5,8 +5,9 @@
import cc.tweaked.gradle.JUnitExt import cc.tweaked.gradle.JUnitExt
import net.fabricmc.loom.api.LoomGradleExtensionAPI import net.fabricmc.loom.api.LoomGradleExtensionAPI
import net.fabricmc.loom.util.gradle.SourceSetHelper import net.fabricmc.loom.util.gradle.SourceSetHelper
import org.jetbrains.gradle.ext.* import org.jetbrains.gradle.ext.compiler
import org.jetbrains.gradle.ext.Application import org.jetbrains.gradle.ext.runConfigurations
import org.jetbrains.gradle.ext.settings
plugins { plugins {
publishing publishing
@@ -85,19 +86,6 @@ idea.project.settings.runConfigurations {
moduleName = "${idea.project.name}.forge.test" moduleName = "${idea.project.name}.forge.test"
packageName = "" packageName = ""
} }
register<Application>("Standalone") {
moduleName = "${idea.project.name}.standalone.main"
mainClass = "cc.tweaked.standalone.Main"
programParameters = "--resources=projects/core/src/main/resources --term=80x30 --allow-local-domains"
}
}
// Build with the IntelliJ, rather than through Gradle. This may require setting the "Compiler Output" option in
// "Project Structure".
idea.project.settings.delegateActions {
delegateBuildRunToGradle = false
testRunner = ActionDelegationConfig.TestRunner.PLATFORM
} }
idea.project.settings.compiler.javac { idea.project.settings.compiler.javac {

View File

@@ -14,18 +14,14 @@ repositories {
mavenCentral() mavenCentral()
gradlePluginPortal() gradlePluginPortal()
maven("https://maven.minecraftforge.net") { maven("https://maven.neoforged.net/releases") {
name = "Forge" name = "NeoForge"
content { content {
includeGroup("net.minecraftforge") includeGroup("net.minecraftforge")
includeGroup("net.minecraftforge.gradle") includeGroup("net.neoforged")
} includeGroup("net.neoforged.gradle")
} includeModule("codechicken", "DiffPatch")
includeModule("net.covers1624", "Quack")
maven("https://maven.parchmentmc.org") {
name = "Librarian"
content {
includeGroupByRegex("^org\\.parchmentmc.*")
} }
} }
@@ -36,7 +32,7 @@ repositories {
} }
} }
maven("https://maven.squiddev.cc") { maven("https://squiddev.cc/maven") {
name = "SquidDev" name = "SquidDev"
content { content {
includeGroup("cc.tweaked.vanilla-extract") includeGroup("cc.tweaked.vanilla-extract")
@@ -51,10 +47,9 @@ dependencies {
implementation(libs.curseForgeGradle) implementation(libs.curseForgeGradle)
implementation(libs.fabric.loom) implementation(libs.fabric.loom)
implementation(libs.forgeGradle)
implementation(libs.ideaExt) implementation(libs.ideaExt)
implementation(libs.librarian)
implementation(libs.minotaur) implementation(libs.minotaur)
implementation(libs.neoGradle.userdev)
implementation(libs.vanillaExtract) implementation(libs.vanillaExtract)
} }

View File

@@ -10,10 +10,8 @@ import cc.tweaked.gradle.IdeaRunConfigurations
import cc.tweaked.gradle.MinecraftConfigurations import cc.tweaked.gradle.MinecraftConfigurations
plugins { plugins {
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("cc-tweaked.java-convention")
id("org.parchmentmc.librarian.forgegradle") id("net.neoforged.gradle.userdev")
} }
plugins.apply(CCTweakedPlugin::class.java) plugins.apply(CCTweakedPlugin::class.java)
@@ -21,10 +19,20 @@ plugins.apply(CCTweakedPlugin::class.java)
val mcVersion: String by extra val mcVersion: String by extra
minecraft { minecraft {
val libs = project.extensions.getByType<VersionCatalogsExtension>().named("libs") modIdentifier("computercraft")
mappings("parchment", "${libs.findVersion("parchmentMc").get()}-${libs.findVersion("parchment").get()}-$mcVersion") }
accessTransformer(project(":forge").file("src/main/resources/META-INF/accesstransformer.cfg")) subsystems {
parchment {
val libs = project.extensions.getByType<VersionCatalogsExtension>().named("libs")
minecraftVersion = libs.findVersion("parchmentMc").get().toString()
mappingsVersion = libs.findVersion("parchment").get().toString()
}
}
dependencies {
val libs = project.extensions.getByType<VersionCatalogsExtension>().named("libs")
implementation("net.neoforged:neoforge:${libs.findVersion("neoForge").get()}")
} }
MinecraftConfigurations.setup(project) MinecraftConfigurations.setup(project)
@@ -32,13 +40,3 @@ MinecraftConfigurations.setup(project)
extensions.configure(CCTweakedExtension::class.java) { extensions.configure(CCTweakedExtension::class.java) {
linters(minecraft = true, loader = "forge") linters(minecraft = true, loader = "forge")
} }
dependencies {
val libs = project.extensions.getByType<VersionCatalogsExtension>().named("libs")
"minecraft"("net.minecraftforge:forge:$mcVersion-${libs.findVersion("forge").get()}")
}
tasks.configureEach {
// genIntellijRuns isn't registered until much later, so we need this silly hijinks.
if (name == "genIntellijRuns") doLast { IdeaRunConfigurations(project).patch() }
}

View File

@@ -38,23 +38,15 @@ java {
repositories { repositories {
mavenCentral() mavenCentral()
val mainMaven = maven("https://maven.squiddev.cc/mirror") { val mainMaven = maven("https://squiddev.cc/maven") {
name = "SquidDev" name = "SquidDev"
} }
exclusiveContent { exclusiveContent {
forRepositories(mainMaven) 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 { filter {
includeGroup("cc.tweaked") includeGroup("cc.tweaked")
// Things we mirror // Things we mirror
includeGroup("com.simibubi.create")
includeGroup("commoble.morered") includeGroup("commoble.morered")
includeGroup("dev.architectury") includeGroup("dev.architectury")
includeGroup("dev.emi") includeGroup("dev.emi")
@@ -95,8 +87,9 @@ sourceSets.all {
check("InlineMeSuggester", CheckSeverity.OFF) // Minecraft uses @Deprecated liberally check("InlineMeSuggester", CheckSeverity.OFF) // Minecraft uses @Deprecated liberally
// Too many false positives right now. Maybe we need an indirection for it later on. // Too many false positives right now. Maybe we need an indirection for it later on.
check("ReferenceEquality", CheckSeverity.OFF) check("ReferenceEquality", CheckSeverity.OFF)
check("EnumOrdinal", CheckSeverity.OFF) // For now. We could replace most of these with EnumMap. check("UnusedVariable", CheckSeverity.OFF) // Too many false positives with records.
check("OperatorPrecedence", CheckSeverity.OFF) // For now. check("OperatorPrecedence", CheckSeverity.OFF) // For now.
check("AlreadyChecked", CheckSeverity.OFF) // Seems to be broken?
check("NonOverridingEquals", CheckSeverity.OFF) // Peripheral.equals makes this hard to avoid check("NonOverridingEquals", CheckSeverity.OFF) // Peripheral.equals makes this hard to avoid
check("FutureReturnValueIgnored", CheckSeverity.OFF) // Too many false positives with Netty check("FutureReturnValueIgnored", CheckSeverity.OFF) // Too many false positives with Netty
@@ -134,8 +127,8 @@ tasks.processResources {
tasks.withType(AbstractArchiveTask::class.java).configureEach { tasks.withType(AbstractArchiveTask::class.java).configureEach {
isPreserveFileTimestamps = false isPreserveFileTimestamps = false
isReproducibleFileOrder = true isReproducibleFileOrder = true
filePermissions {} dirMode = Integer.valueOf("755", 8)
dirPermissions {} fileMode = Integer.valueOf("664", 8)
} }
tasks.jar { tasks.jar {

View File

@@ -38,7 +38,7 @@ publishing {
} }
repositories { repositories {
maven("https://maven.squiddev.cc") { maven("https://squiddev.cc/maven") {
name = "SquidDev" name = "SquidDev"
credentials(PasswordCredentials::class) credentials(PasswordCredentials::class)

View File

@@ -22,6 +22,7 @@ import org.gradle.api.tasks.SourceSet
import org.gradle.api.tasks.bundling.Jar import org.gradle.api.tasks.bundling.Jar
import org.gradle.api.tasks.compile.JavaCompile import org.gradle.api.tasks.compile.JavaCompile
import org.gradle.api.tasks.javadoc.Javadoc import org.gradle.api.tasks.javadoc.Javadoc
import org.gradle.configurationcache.extensions.capitalized
import org.gradle.language.base.plugins.LifecycleBasePlugin import org.gradle.language.base.plugins.LifecycleBasePlugin
import org.gradle.language.jvm.tasks.ProcessResources import org.gradle.language.jvm.tasks.ProcessResources
import org.gradle.process.JavaForkOptions import org.gradle.process.JavaForkOptions
@@ -34,6 +35,7 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
import java.io.File import java.io.File
import java.io.IOException import java.io.IOException
import java.net.URI import java.net.URI
import java.net.URL
import java.util.regex.Pattern import java.util.regex.Pattern
abstract class CCTweakedExtension( abstract class CCTweakedExtension(
@@ -180,7 +182,7 @@ abstract class CCTweakedExtension(
fun <T> jacoco(task: NamedDomainObjectProvider<T>) where T : Task, T : JavaForkOptions { fun <T> jacoco(task: NamedDomainObjectProvider<T>) where T : Task, T : JavaForkOptions {
val classDump = project.layout.buildDirectory.dir("jacocoClassDump/${task.name}") val classDump = project.layout.buildDirectory.dir("jacocoClassDump/${task.name}")
val reportTaskName = "jacoco${task.name.capitalise()}Report" val reportTaskName = "jacoco${task.name.capitalized()}Report"
val jacoco = project.extensions.getByType(JacocoPluginExtension::class.java) val jacoco = project.extensions.getByType(JacocoPluginExtension::class.java)
task.configure { task.configure {
@@ -224,12 +226,12 @@ abstract class CCTweakedExtension(
* where possible. * where possible.
*/ */
fun downloadFile(label: String, url: String): File { fun downloadFile(label: String, url: String): File {
val uri = URI(url) val url = URL(url)
val path = File(uri.path) val path = File(url.path)
project.repositories.ivy { project.repositories.ivy {
name = label name = label
setUrl(URI(uri.scheme, uri.userInfo, uri.host, uri.port, path.parent, null, null)) setUrl(URI(url.protocol, url.userInfo, url.host, url.port, path.parent, null, null))
patternLayout { patternLayout {
artifact("[artifact].[ext]") artifact("[artifact].[ext]")
} }

View File

@@ -143,7 +143,7 @@ fun getNextVersion(version: String): String {
val lastIndex = mainVersion.lastIndexOf('.') val lastIndex = mainVersion.lastIndexOf('.')
if (lastIndex < 0) throw IllegalArgumentException("Cannot parse version format \"$version\"") if (lastIndex < 0) throw IllegalArgumentException("Cannot parse version format \"$version\"")
val lastVersion = try { val lastVersion = try {
mainVersion.substring(lastIndex + 1).toInt() version.substring(lastIndex + 1).toInt()
} catch (e: NumberFormatException) { } catch (e: NumberFormatException) {
throw IllegalArgumentException("Cannot parse version format \"$version\"", e) throw IllegalArgumentException("Cannot parse version format \"$version\"", e)
} }
@@ -155,15 +155,3 @@ fun getNextVersion(version: String): String {
if (dashIndex >= 0) out.append(version, dashIndex, version.length) if (dashIndex >= 0) out.append(version, dashIndex, version.length)
return out.toString() return out.toString()
} }
/**
* Capitalise the first letter of the string.
*
* This is a replacement for the now deprecated [String.capitalize].
*/
fun String.capitalise(): String {
if (isEmpty()) return this
val first = this[0]
val firstTitle = first.titlecaseChar()
return if (first == firstTitle) this else firstTitle + substring(1)
}

View File

@@ -4,23 +4,59 @@
package cc.tweaked.gradle package cc.tweaked.gradle
import net.minecraftforge.gradle.common.util.RunConfig import net.neoforged.gradle.common.runs.run.RunImpl
import net.minecraftforge.gradle.common.util.runs.setRunConfigInternal import net.neoforged.gradle.common.runs.tasks.RunExec
import net.neoforged.gradle.dsl.common.extensions.RunnableSourceSet
import net.neoforged.gradle.dsl.common.runs.run.Run
import org.gradle.api.plugins.JavaPluginExtension import org.gradle.api.plugins.JavaPluginExtension
import org.gradle.api.tasks.JavaExec import org.gradle.api.tasks.JavaExec
import org.gradle.api.tasks.SourceSet
import org.gradle.jvm.toolchain.JavaToolchainService import org.gradle.jvm.toolchain.JavaToolchainService
import org.gradle.kotlin.dsl.assign
import org.gradle.kotlin.dsl.create
import org.gradle.kotlin.dsl.findByType
import java.nio.file.Files import java.nio.file.Files
/** /**
* Set [JavaExec] task to run a given [RunConfig]. * Set [JavaExec] task to run a given [RunConfig].
*
* See also [RunExec].
*/ */
fun JavaExec.setRunConfig(config: RunConfig) { fun JavaExec.setRunConfig(config: Run) {
dependsOn("prepareRuns") mainClass.set(config.mainClass)
setRunConfigInternal(project, this, config) workingDir = config.workingDirectory.get().asFile
doFirst("Create working directory") { Files.createDirectories(workingDir.toPath()) } argumentProviders.add { config.programArguments.get() }
jvmArgumentProviders.add { config.jvmArguments.get() }
environment(config.environmentVariables.get())
systemProperties(config.systemProperties.get())
config.modSources.get().forEach { classpath(it.runtimeClasspath) }
classpath(config.classpath)
classpath(config.dependencies.get().configuration)
(config as RunImpl).taskDependencies.forEach { dependsOn(it) }
javaLauncher.set( javaLauncher.set(
project.extensions.getByType(JavaToolchainService::class.java) project.extensions.getByType(JavaToolchainService::class.java)
.launcherFor(project.extensions.getByType(JavaPluginExtension::class.java).toolchain), .launcherFor(project.extensions.getByType(JavaPluginExtension::class.java).toolchain),
) )
doFirst("Create working directory") { Files.createDirectories(workingDir.toPath()) }
}
/**
* Add a new [Run.modSource] with a specific mod id.
*/
fun Run.modSourceAs(sourceSet: SourceSet, mod: String) {
// NeoGradle requires a RunnableSourceSet to be present, so we inject it into other project's source sets.
val runnable = sourceSet.extensions.findByType<RunnableSourceSet>() ?: run {
val extension = sourceSet.extensions.create<RunnableSourceSet>(RunnableSourceSet.NAME, project)
extension.modIdentifier = mod
extension.modIdentifier.finalizeValueOnRead()
extension
}
if (runnable.modIdentifier.get() != mod) throw IllegalArgumentException("Multiple mod identifiers")
modSource(sourceSet)
} }

View File

@@ -1,120 +0,0 @@
// SPDX-FileCopyrightText: 2024 The CC: Tweaked Developers
//
// SPDX-License-Identifier: MPL-2.0
package cc.tweaked.gradle
import cc.tweaked.vanillaextract.core.util.MoreFiles
import org.gradle.api.Action
import org.gradle.api.DefaultTask
import org.gradle.api.GradleException
import org.gradle.api.file.*
import org.gradle.api.model.ObjectFactory
import org.gradle.api.provider.ListProperty
import org.gradle.api.tasks.*
import javax.inject.Inject
/**
* Merge common files across multiple directories into one destination directory.
*
* This is intended for merging the generated resources from the Forge and Fabric projects. Files common between the two
* are written to the global [output] directory, while distinct files are written to the per-source
* [MergeTrees.Source.output] directory.
*/
abstract class MergeTrees : DefaultTask() {
/**
* A source directory to read from.
*/
interface Source {
/**
* The folder contianing all input files.
*/
@get:InputFiles
@get:PathSensitive(PathSensitivity.RELATIVE)
val input: ConfigurableFileTree
fun input(configure: Action<ConfigurableFileTree>) {
configure.execute(input)
}
/**
* The folder to write files unique to this folder to.
*/
@get:OutputDirectory
val output: DirectoryProperty
}
/**
* The list of sources.
*/
@get:Nested
abstract val sources: ListProperty<Source>
/**
* Add and configure a new source.
*/
fun source(configure: Action<Source>) {
val instance = objectFactory.newInstance(Source::class.java)
configure.execute(instance)
instance.output.disallowChanges()
sources.add(instance)
}
/**
* The directory to write common files to.
*/
@get:OutputDirectory
abstract val output: DirectoryProperty
@get:Inject
protected abstract val objectFactory: ObjectFactory
@get:Inject
protected abstract val fsOperations: FileSystemOperations
@TaskAction
fun run() {
val sources = this.sources.get()
if (sources.isEmpty()) throw GradleException("Cannot have an empty list of sources")
val files = mutableMapOf<String, SharedFile>()
for (source in sources) {
source.input.visit(
object : FileVisitor {
override fun visitDir(dirDetails: FileVisitDetails) = Unit
override fun visitFile(fileDetails: FileVisitDetails) {
val path = fileDetails.file.toRelativeString(source.input.dir)
val hash = MoreFiles.computeSha1(fileDetails.file.toPath())
val existing = files[path]
if (existing == null) {
files[path] = SharedFile(hash, 1)
} else if (existing.hash == hash) {
existing.found++
}
}
},
)
}
val sharedFiles = files.entries.asSequence().filter { (_, v) -> v.found == sources.size }.map { (k, _) -> k }.toList()
// Copy shared files to the common directory
fsOperations.sync {
from(sources[0].input)
into(output)
include(sharedFiles)
}
// And all other files to their per-source directory
for (source in sources) {
fsOperations.sync {
from(source.input)
into(source.output)
exclude(sharedFiles)
}
}
}
class SharedFile(val hash: String, var found: Int)
}

View File

@@ -4,7 +4,6 @@
package cc.tweaked.gradle package cc.tweaked.gradle
import net.minecraftforge.gradle.common.util.RunConfig
import org.gradle.api.GradleException import org.gradle.api.GradleException
import org.gradle.api.file.FileSystemOperations import org.gradle.api.file.FileSystemOperations
import org.gradle.api.invocation.Gradle import org.gradle.api.invocation.Gradle
@@ -65,14 +64,6 @@ abstract class ClientJavaExec : JavaExec() {
setTestProperties() setTestProperties()
} }
/**
* Set this task to run a given [RunConfig].
*/
fun setRunConfig(config: RunConfig) {
(this as JavaExec).setRunConfig(config)
setTestProperties() // setRunConfig may clobber some properties, ensure everything is set.
}
/** /**
* Copy configuration from a task with the given name. * Copy configuration from a task with the given name.
*/ */

View File

@@ -46,7 +46,7 @@ abstract class NpmInstall : DefaultTask() {
@TaskAction @TaskAction
fun install() { fun install() {
project.exec { project.exec {
commandLine(ProcessHelpers.getExecutable("npm"), "ci") commandLine("npm", "ci")
workingDir = projectRoot.get().asFile workingDir = projectRoot.get().asFile
} }
} }
@@ -59,6 +59,6 @@ abstract class NpmInstall : DefaultTask() {
abstract class NpxExecToDir : ExecToDir() { abstract class NpxExecToDir : ExecToDir() {
init { init {
dependsOn(NpmInstall.TASK_NAME) dependsOn(NpmInstall.TASK_NAME)
executable = ProcessHelpers.getExecutable("npx") executable = "npx"
} }
} }

View File

@@ -9,7 +9,6 @@ import org.gradle.api.GradleException
import java.io.BufferedReader import java.io.BufferedReader
import java.io.File import java.io.File
import java.io.InputStreamReader import java.io.InputStreamReader
import java.nio.charset.StandardCharsets
internal object ProcessHelpers { internal object ProcessHelpers {
fun startProcess(vararg command: String): Process { fun startProcess(vararg command: String): Process {
@@ -35,7 +34,7 @@ internal object ProcessHelpers {
val process = startProcess(*command) val process = startProcess(*command)
process.outputStream.close() process.outputStream.close()
val out = BufferedReader(InputStreamReader(process.inputStream, StandardCharsets.UTF_8)).use { reader -> val out = BufferedReader(InputStreamReader(process.inputStream)).use { reader ->
reader.lines().filter { it.isNotEmpty() }.toList() reader.lines().filter { it.isNotEmpty() }.toList()
} }
ProcessGroovyMethods.closeStreams(process) ProcessGroovyMethods.closeStreams(process)
@@ -47,28 +46,6 @@ internal object ProcessHelpers {
val path = System.getenv("PATH") ?: return false val path = System.getenv("PATH") ?: return false
return path.splitToSequence(File.pathSeparator).any { File(it, name).exists() } return path.splitToSequence(File.pathSeparator).any { File(it, name).exists() }
} }
/**
* Search for an executable on the `PATH` if required.
*
* [Process]/[ProcessBuilder] does not handle all executable file extensions on Windows (such as `.com). When on
* Windows, this function searches `PATH` and `PATHEXT` for an executable matching [name].
*/
fun getExecutable(name: String): String {
if (!System.getProperty("os.name").lowercase().contains("windows")) return name
val path = (System.getenv("PATH") ?: return name).split(File.pathSeparator)
val pathExt = (System.getenv("PATHEXT") ?: return name).split(File.pathSeparator)
for (pathEntry in path) {
for (ext in pathExt) {
val resolved = File(pathEntry, name + ext)
if (resolved.exists()) return resolved.getAbsolutePath()
}
}
return name
}
} }
internal fun Process.waitForOrThrow(message: String) { internal fun Process.waitForOrThrow(message: String) {

View File

@@ -1,51 +0,0 @@
// 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))
}

12
config/gitpod/Dockerfile Normal file
View File

@@ -0,0 +1,12 @@
# SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
#
# SPDX-License-Identifier: MPL-2.0
FROM gitpod/workspace-base
USER gitpod
RUN sudo apt-get -q update \
&& sudo apt-get install -yq openjdk-16-jdk python3-pip npm \
&& sudo pip3 install pre-commit \
&& sudo update-java-alternatives --set java-1.16.0-openjdk-amd64

View File

@@ -1,27 +0,0 @@
# SPDX-FileCopyrightText: 2017 The CC: Tweaked Developers
#
# SPDX-License-Identifier: MPL-2.0
files:
- source: projects/common/src/generated/resources/assets/computercraft/lang/en_us.json
translation: /projects/common/src/main/resources/assets/computercraft/lang/%locale_with_underscore%.json
languages_mapping:
locale_with_underscore:
cs: cs_cs # Czech
da: da_dk # Danish
de: de_de # German
es-ES: es_es # Spanish
fr: fr_fr # French
it: it_it # Italian
ja: ja_jp # Japanese
ko: ko_kr # Korean
nb: nb_no # Norwegian Bokmal
nl: nl_nl # Dutch
pl: pl_pl # Polish
pt-BR: pt_br # Portuguese, Brazilian
ru: ru_ru # Russian
sv-SE: sv_se # Sweedish
tok: tok # Toki Pona
tr: tr_tr # Turkish
uk: uk_ua # Ukraine
vi: vi_vn # Vietnamese
zh-CN: zh_cn # Chinese Simplified

View File

@@ -131,7 +131,7 @@ different.
First, we require the dfpwm module and call [`cc.audio.dfpwm.make_decoder`] to construct a new decoder. This decoder First, we require the dfpwm module and call [`cc.audio.dfpwm.make_decoder`] to construct a new decoder. This decoder
accepts blocks of DFPWM data and converts it to a list of 8-bit amplitudes, which we can then play with our speaker. accepts blocks of DFPWM data and converts it to a list of 8-bit amplitudes, which we can then play with our speaker.
As mentioned above, [`speaker.playAudio`] accepts at most 128×1024 samples in one go. DFPWM uses a single bit for each As mentioned above, [`speaker.playAudio`] accepts at most 128×1024 samples in one go. DFPMW uses a single bit for each
sample, which means we want to process our audio in chunks of 16×1024 bytes (16KiB). In order to do this, we use sample, which means we want to process our audio in chunks of 16×1024 bytes (16KiB). In order to do this, we use
[`io.lines`], which provides a nice way to loop over chunks of a file. You can of course just use [`fs.open`] and [`io.lines`], which provides a nice way to loop over chunks of a file. You can of course just use [`fs.open`] and
[`fs.ReadHandle.read`] if you prefer. [`fs.ReadHandle.read`] if you prefer.
@@ -191,7 +191,7 @@ end
> [Confused?][!NOTE] > [Confused?][!NOTE]
> Don't worry if you don't understand this example. It's quite advanced, and does use some ideas that this guide doesn't > Don't worry if you don't understand this example. It's quite advanced, and does use some ideas that this guide doesn't
> cover. That said, don't be afraid to ask [the community for help][community]. > cover. That said, don't be afraid to ask on [GitHub Discussions] or [IRC] either!
It's worth noting that the examples of audio processing we've mentioned here are about manipulating the _amplitude_ of It's worth noting that the examples of audio processing we've mentioned here are about manipulating the _amplitude_ of
the wave. If you wanted to modify the _frequency_ (for instance, shifting the pitch), things get rather more complex. the wave. If you wanted to modify the _frequency_ (for instance, shifting the pitch), things get rather more complex.
@@ -205,4 +205,5 @@ This is, I'm afraid, left as an exercise to the reader.
[PCM]: https://en.wikipedia.org/wiki/Pulse-code_modulation "Pulse-code Modulation - Wikipedia" [PCM]: https://en.wikipedia.org/wiki/Pulse-code_modulation "Pulse-code Modulation - Wikipedia"
[Ring Buffer]: https://en.wikipedia.org/wiki/Circular_buffer "Circular buffer - Wikipedia" [Ring Buffer]: https://en.wikipedia.org/wiki/Circular_buffer "Circular buffer - Wikipedia"
[Sine Wave]: https://en.wikipedia.org/wiki/Sine_wave "Sine wave - Wikipedia" [Sine Wave]: https://en.wikipedia.org/wiki/Sine_wave "Sine wave - Wikipedia"
[Community]: /#community [GitHub Discussions]: https://github.com/cc-tweaked/CC-Tweaked/discussions
[IRC]: https://webchat.esper.net/?channels=computercraft "#computercraft on EsperNet"

View File

@@ -4,14 +4,7 @@ SPDX-FileCopyrightText: 2020 The CC: Tweaked Developers
SPDX-License-Identifier: MPL-2.0 SPDX-License-Identifier: MPL-2.0
--> -->
<h1> # ![CC: Tweaked](logo.png)
<picture>
<source media="(prefers-color-scheme: dark)" srcset="logo-darkmode.png">
<source media="(prefers-color-scheme: light)" srcset="logo.png">
<img alt="CC: Tweaked" src="logo.png">
</picture>
</h1>
CC: Tweaked is a mod for Minecraft which adds programmable computers, turtles and more to the game. A fork of the 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 much-beloved [ComputerCraft], it continues its legacy with improved performance and stability, along with a wealth of
new features. new features.
@@ -45,16 +38,12 @@ little daunting getting started. Thankfully, there's several fantastic tutorials
- [Direwolf20's ComputerCraft tutorials](https://www.youtube.com/watch?v=wrUHUhfCY5A "ComputerCraft Tutorial Episode 1 - HELP! and Hello World") - [Direwolf20's ComputerCraft tutorials](https://www.youtube.com/watch?v=wrUHUhfCY5A "ComputerCraft Tutorial Episode 1 - HELP! and Hello World")
- [Sethbling's ComputerCraft series](https://www.youtube.com/watch?v=DSsx4VSe-Uk "Programming Tutorial with Minecraft Turtles -- Ep. 1: Intro to Turtles and If-Then-Else_End") - [Sethbling's ComputerCraft series](https://www.youtube.com/watch?v=DSsx4VSe-Uk "Programming Tutorial with Minecraft Turtles -- Ep. 1: Intro to Turtles and If-Then-Else_End")
- [Lyqyd's Computer Basics 1](https://ccf.squiddev.cc/forums2/index.php?/topic/15033-computer-basics-i/ "Computer Basics I") - [Lyqyd's Computer Basics 1](http://www.computercraft.info/forums2/index.php?/topic/15033-computer-basics-i/ "Computer Basics I")
Once you're a little more familiar with the mod, the sidebar and links below provide more detailed documentation on the Once you're a little more familiar with the mod, the sidebar and links below provide more detailed documentation on the
various APIs and peripherals provided by the mod. various APIs and peripherals provided by the mod.
<h2 id="community">Community</h2> If you get stuck, do [ask a question on GitHub][GitHub Discussions] or pop in to the ComputerCraft's [IRC channel][IRC].
If you need help getting started with CC: Tweaked, want to show off your latest project, or just want to chat about
ComputerCraft, do check out our [GitHub discussions page][GitHub discussions]! There's also a fairly populated,
albeit quiet IRC channel on [EsperNet], if that's more your cup of tea. You can join `#computercraft` through your
desktop client, or online using [KiwiIRC].
## Get Involved ## Get Involved
CC: Tweaked lives on [GitHub]. If you've got any ideas, feedback or bugs please do [create an issue][bug]. CC: Tweaked lives on [GitHub]. If you've got any ideas, feedback or bugs please do [create an issue][bug].
@@ -69,5 +58,4 @@ CC: Tweaked lives on [GitHub]. If you've got any ideas, feedback or bugs please
[Fabric]: https://fabricmc.net/use/installer/ "Download Fabric." [Fabric]: https://fabricmc.net/use/installer/ "Download Fabric."
[lua]: https://www.lua.org/ "Lua's main website" [lua]: https://www.lua.org/ "Lua's main website"
[GitHub Discussions]: https://github.com/cc-tweaked/CC-Tweaked/discussions [GitHub Discussions]: https://github.com/cc-tweaked/CC-Tweaked/discussions
[EsperNet]: https://www.esper.net/ [IRC]: https://webchat.esper.net/?channels=computercraft "#computercraft on EsperNet"
[KiwiIRC]: https://kiwiirc.com/nextclient/#irc://irc.esper.net:+6697/#computercraft "#computercraft on EsperNet"

View File

@@ -45,16 +45,12 @@ little daunting getting started. Thankfully, there's several fantastic tutorials
- [Direwolf20's ComputerCraft tutorials](https://www.youtube.com/watch?v=wrUHUhfCY5A "ComputerCraft Tutorial Episode 1 - HELP! and Hello World") - [Direwolf20's ComputerCraft tutorials](https://www.youtube.com/watch?v=wrUHUhfCY5A "ComputerCraft Tutorial Episode 1 - HELP! and Hello World")
- [Sethbling's ComputerCraft series](https://www.youtube.com/watch?v=DSsx4VSe-Uk "Programming Tutorial with Minecraft Turtles -- Ep. 1: Intro to Turtles and If-Then-Else_End") - [Sethbling's ComputerCraft series](https://www.youtube.com/watch?v=DSsx4VSe-Uk "Programming Tutorial with Minecraft Turtles -- Ep. 1: Intro to Turtles and If-Then-Else_End")
- [Lyqyd's Computer Basics 1](https://ccf.squiddev.cc/forums2/index.php?/topic/15033-computer-basics-i/ "Computer Basics I") - [Lyqyd's Computer Basics 1](http://www.computercraft.info/forums2/index.php?/topic/15033-computer-basics-i/ "Computer Basics I")
Once you're a little more familiar with the mod, the [wiki](https://tweaked.cc/) provides more detailed documentation on the Once you're a little more familiar with the mod, the [wiki](https://tweaked.cc/) provides more detailed documentation on the
various APIs and peripherals provided by the mod. various APIs and peripherals provided by the mod.
## Community If you get stuck, do [ask a question on GitHub][GitHub Discussions] or pop in to the ComputerCraft's [IRC channel][IRC].
If you need help getting started with CC: Tweaked, want to show off your latest project, or just want to chat about
ComputerCraft, do check out our [GitHub discussions page][GitHub discussions]! There's also a fairly populated,
albeit quiet IRC channel on [EsperNet], if that's more your cup of tea. You can join `#computercraft` through your
desktop client, or online using [KiwiIRC].
## Get Involved ## Get Involved
CC: Tweaked lives on [GitHub]. If you've got any ideas, feedback or bugs please do [create an issue][bug]. CC: Tweaked lives on [GitHub]. If you've got any ideas, feedback or bugs please do [create an issue][bug].
@@ -64,5 +60,4 @@ CC: Tweaked lives on [GitHub]. If you've got any ideas, feedback or bugs please
[computercraft]: https://github.com/dan200/ComputerCraft "ComputerCraft on GitHub" [computercraft]: https://github.com/dan200/ComputerCraft "ComputerCraft on GitHub"
[lua]: https://www.lua.org/ "Lua's main website" [lua]: https://www.lua.org/ "Lua's main website"
[GitHub Discussions]: https://github.com/cc-tweaked/CC-Tweaked/discussions [GitHub Discussions]: https://github.com/cc-tweaked/CC-Tweaked/discussions
[EsperNet]: https://www.esper.net/ [IRC]: http://webchat.esper.net/?channels=computercraft "#computercraft on EsperNet"
[KiwiIRC]: https://kiwiirc.com/nextclient/#irc://irc.esper.net:+6697/#computercraft "#computercraft on EsperNet"

View File

@@ -25,13 +25,13 @@ as documentation for breaking changes and "gotchas" one should look out for betw
- Update to Lua 5.2: - Update to Lua 5.2:
- Support for Lua 5.0's pseudo-argument `arg` has been removed. You should always use `...` for varargs. - Support for Lua 5.0's pseudo-argument `arg` has been removed. You should always use `...` for varargs.
- Environments are no longer baked into the runtime, and instead use the `_ENV` local or upvalue. [`getfenv`]/[`setfenv`] - Environments are no longer baked into the runtime, and instead use the `_ENV` local or upvalue. `getfenv`/`setfenv`
now only work on Lua functions with an `_ENV` upvalue. [`getfenv`] will return the global environment when called now only work on Lua functions with an `_ENV` upvalue. `getfenv` will return the global environment when called
with other functions, and [`setfenv`] will have no effect. with other functions, and `setfenv` will have no effect.
- [`load`]/[`loadstring`] defaults to using the global environment (`_G`) rather than the current coroutine's - `load`/`loadstring` defaults to using the global environment (`_G`) rather than the current coroutine's
environment. environment.
- Support for dumping functions ([`string.dump`]) and loading binary chunks has been removed. - Support for dumping functions (`string.dump`) and loading binary chunks has been removed.
- [`math.random`] now uses Lua 5.4's random number generator. - `math.random` now uses Lua 5.4's random number generator.
- File handles, HTTP requests and websockets now always use the original bytes rather than encoding/decoding to UTF-8. - File handles, HTTP requests and websockets now always use the original bytes rather than encoding/decoding to UTF-8.
@@ -44,7 +44,7 @@ as documentation for breaking changes and "gotchas" one should look out for betw
`keys.enter` constant was queued when the key was pressed) `keys.enter` constant was queued when the key was pressed)
- Minecraft 1.13 removed the concept of item damage and block metadata (see ["The Flattening"][flattening]). As a - Minecraft 1.13 removed the concept of item damage and block metadata (see ["The Flattening"][flattening]). As a
result [`turtle.inspect`] no longer provides block metadata, and [`turtle.getItemDetail`] no longer provides damage. result `turtle.inspect` no longer provides block metadata, and `turtle.getItemDetail` no longer provides damage.
- Block states (`turtle.inspect().state`) should provide all the same information as block metadata, but in a much - Block states (`turtle.inspect().state`) should provide all the same information as block metadata, but in a much
more understandable format. more understandable format.
@@ -70,12 +70,12 @@ as documentation for breaking changes and "gotchas" one should look out for betw
- Unlabelled computers and turtles now keep their ID when broken, meaning that unlabelled computers/items do not stack. - Unlabelled computers and turtles now keep their ID when broken, meaning that unlabelled computers/items do not stack.
## ComputerCraft 1.80pr1 {#cc-1.80} ## ComputerCraft 1.80pr1 {#cc-1.80}
- Programs run via [`shell.run`] are now started in their own isolated environment. This means globals set by programs - Programs run via `shell.run` are now started in their own isolated environment. This means globals set by programs
will not be accessible outside of this program. will not be accessible outside of this program.
- Programs containing `/` are looked up in the current directory and are no longer looked up on the path. For instance, - Programs containing `/` are looked up in the current directory and are no longer looked up on the path. For instance,
you can no longer type `turtle/excavate` to run `/rom/programs/turtle/excavate.lua`. you can no longer type `turtle/excavate` to run `/rom/programs/turtle/excavate.lua`.
[flattening]: https://minecraft.wiki/w/Java_Edition_1.13/Flattening [flattening]: https://minecraft.wiki/w/Java_Edition_1.13/Flattening
[legal_data_pack]: https://minecraft.wiki/w/Tutorials/Creating_a_data_pack#Legal_characters [legal_data_pack]: https://minecraft.gamepedia.com/Tutorials/Creating_a_data_pack#Legal_characters
[datapack-example]: https://github.com/cc-tweaked/datapack-example "An example datapack for CC: Tweaked" [datapack-example]: https://github.com/cc-tweaked/datapack-example "An example datapack for CC: Tweaked"

View File

@@ -81,7 +81,7 @@ compatibility for these newer versions.
| `string.dump` strip argument | ✔ | | | `string.dump` strip argument | ✔ | |
| `string.pack`/`string.unpack`/`string.packsize` | ✔ | | | `string.pack`/`string.unpack`/`string.packsize` | ✔ | |
| `table.move` | ✔ | | | `table.move` | ✔ | |
| `math.atan2` -> `math.atan` | 🔶 | `math.atan` supports its two argument form. | | `math.atan2` -> `math.atan` | | |
| Removed `math.frexp`, `math.ldexp`, `math.pow`, `math.cosh`, `math.sinh`, `math.tanh` | ❌ | | | Removed `math.frexp`, `math.ldexp`, `math.pow`, `math.cosh`, `math.sinh`, `math.tanh` | ❌ | |
| `math.maxinteger`/`math.mininteger` | ❌ | | | `math.maxinteger`/`math.mininteger` | ❌ | |
| `math.tointeger` | ❌ | | | `math.tointeger` | ❌ | |

View File

@@ -2,15 +2,15 @@
# #
# SPDX-License-Identifier: MPL-2.0 # SPDX-License-Identifier: MPL-2.0
org.gradle.jvmargs=-Xmx3G -Dfile.encoding=UTF-8 org.gradle.jvmargs=-Xmx3G
org.gradle.parallel=true org.gradle.parallel=true
kotlin.stdlib.default.dependency=false kotlin.stdlib.default.dependency=false
kotlin.jvm.target.validation.mode=error kotlin.jvm.target.validation.mode=error
# Mod properties # Mod properties
isUnstable=false isUnstable=true
modVersion=1.113.1 modVersion=1.110.0
# Minecraft properties: We want to configure this here so we can read it in settings.gradle # Minecraft properties: We want to configure this here so we can read it in settings.gradle
mcVersion=1.20.1 mcVersion=1.20.4

View File

@@ -1,2 +0,0 @@
#This file is generated by updateDaemonJvm
toolchainVersion=17

View File

@@ -7,26 +7,26 @@
# Minecraft # Minecraft
# MC version is specified in gradle.properties, as we need that in settings.gradle. # MC version is specified in gradle.properties, as we need that in settings.gradle.
# Remember to update corresponding versions in fabric.mod.json/mods.toml # Remember to update corresponding versions in fabric.mod.json/mods.toml
fabric-api = "0.86.1+1.20.1" fabric-api = "0.93.1+1.20.4"
fabric-loader = "0.14.21" fabric-loader = "0.15.3"
forge = "47.1.0" neoForge = "20.4.210"
forgeSpi = "7.0.1" neoForgeSpi = "8.0.1"
mixin = "0.8.5" mixin = "0.8.5"
parchment = "2023.08.20" parchment = "2023.12.31"
parchmentMc = "1.20.1" parchmentMc = "1.20.3"
yarn = "1.20.1+build.10" yarn = "1.20.4+build.3"
# Core dependencies (these versions are tied to the version Minecraft uses) # Core dependencies (these versions are tied to the version Minecraft uses)
fastutil = "8.5.9" fastutil = "8.5.12"
guava = "31.1-jre" guava = "32.1.2-jre"
netty = "4.1.82.Final" netty = "4.1.97.Final"
slf4j = "2.0.1" slf4j = "2.0.7"
# Core dependencies (independent of Minecraft) # Core dependencies (independent of Minecraft)
asm = "9.6" asm = "9.6"
autoService = "1.1.1" autoService = "1.1.1"
checkerFramework = "3.42.0" checkerFramework = "3.42.0"
cobalt = "0.9.4" cobalt = "0.9.2"
commonsCli = "1.6.0" commonsCli = "1.6.0"
jetbrainsAnnotations = "24.1.0" jetbrainsAnnotations = "24.1.0"
jsr305 = "3.0.2" jsr305 = "3.0.2"
@@ -36,18 +36,16 @@ kotlin-coroutines = "1.7.3"
nightConfig = "3.6.7" nightConfig = "3.6.7"
# Minecraft mods # Minecraft mods
emi = "1.0.8+1.20.1" emi = "1.0.30+1.20.4"
fabricPermissions = "0.3.20230723" fabricPermissions = "0.3.20230723"
iris = "1.6.4+1.20" iris = "1.6.14+1.20.4"
jei = "15.2.0.22" jei = "17.3.0.48"
modmenu = "7.1.0" modmenu = "9.0.0"
moreRed = "4.0.0.4" moreRed = "4.0.0.4"
oculus = "1.2.5" oculus = "1.2.5"
rei = "12.0.626" rei = "14.0.688"
rubidium = "0.6.1" rubidium = "0.6.1"
sodium = "mc1.20-0.4.10" sodium = "mc1.20-0.4.10"
create-forge = "0.5.1.f-33"
create-fabric = "0.5.1-f-build.1467+mc1.20.1"
# Testing # Testing
hamcrest = "2.2" hamcrest = "2.2"
@@ -58,24 +56,22 @@ jmh = "1.37"
# Build tools # Build tools
cctJavadoc = "1.8.2" cctJavadoc = "1.8.2"
checkstyle = "10.14.1" checkstyle = "10.14.1"
curseForgeGradle = "1.0.14" curseForgeGradle = "1.1.18"
errorProne-core = "2.27.0" errorProne-core = "2.23.0"
errorProne-plugin = "3.1.0" errorProne-plugin = "3.1.0"
fabric-loom = "1.7.1" fabric-loom = "1.5.7"
forgeGradle = "6.0.21"
githubRelease = "2.5.2" githubRelease = "2.5.2"
gradleVersions = "0.50.0" gradleVersions = "0.50.0"
ideaExt = "1.1.7" ideaExt = "1.1.7"
illuaminate = "0.1.0-73-g43ee16c" illuaminate = "0.1.0-69-gf294ab2"
librarian = "1.+"
lwjgl = "3.3.3" lwjgl = "3.3.3"
minotaur = "2.+" minotaur = "2.8.7"
nullAway = "0.10.25" neoGradle = "7.0.100"
shadow = "8.3.1" nullAway = "0.9.9"
spotless = "6.23.3" spotless = "6.23.3"
taskTree = "2.1.1" taskTree = "2.1.1"
teavm = "0.11.0-SQUID.1" teavm = "0.10.0-SQUID.3"
vanillaExtract = "0.1.3" vanillaExtract = "0.1.2"
versionCatalogUpdate = "0.8.1" versionCatalogUpdate = "0.8.1"
[libraries] [libraries]
@@ -87,7 +83,7 @@ checkerFramework = { module = "org.checkerframework:checker-qual", version.ref =
cobalt = { module = "cc.tweaked:cobalt", version.ref = "cobalt" } cobalt = { module = "cc.tweaked:cobalt", version.ref = "cobalt" }
commonsCli = { module = "commons-cli:commons-cli", version.ref = "commonsCli" } commonsCli = { module = "commons-cli:commons-cli", version.ref = "commonsCli" }
fastutil = { module = "it.unimi.dsi:fastutil", version.ref = "fastutil" } fastutil = { module = "it.unimi.dsi:fastutil", version.ref = "fastutil" }
forgeSpi = { module = "net.minecraftforge:forgespi", version.ref = "forgeSpi" } neoForgeSpi = { module = "net.neoforged:neoforgespi", version.ref = "neoForgeSpi" }
guava = { module = "com.google.guava:guava", version.ref = "guava" } guava = { module = "com.google.guava:guava", version.ref = "guava" }
jetbrainsAnnotations = { module = "org.jetbrains:annotations", version.ref = "jetbrainsAnnotations" } jetbrainsAnnotations = { module = "org.jetbrains:annotations", version.ref = "jetbrainsAnnotations" }
jsr305 = { module = "com.google.code.findbugs:jsr305", version.ref = "jsr305" } jsr305 = { module = "com.google.code.findbugs:jsr305", version.ref = "jsr305" }
@@ -95,26 +91,23 @@ jzlib = { module = "com.jcraft:jzlib", version.ref = "jzlib" }
kotlin-coroutines = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kotlin-coroutines" } kotlin-coroutines = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kotlin-coroutines" }
kotlin-platform = { module = "org.jetbrains.kotlin:kotlin-bom", version.ref = "kotlin" } kotlin-platform = { module = "org.jetbrains.kotlin:kotlin-bom", version.ref = "kotlin" }
kotlin-stdlib = { module = "org.jetbrains.kotlin:kotlin-stdlib", version.ref = "kotlin" } kotlin-stdlib = { module = "org.jetbrains.kotlin:kotlin-stdlib", version.ref = "kotlin" }
netty-codec = { module = "io.netty:netty-codec", version.ref = "netty" }
netty-http = { module = "io.netty:netty-codec-http", version.ref = "netty" } netty-http = { module = "io.netty:netty-codec-http", version.ref = "netty" }
netty-proxy = { module = "io.netty:netty-handler-proxy", version.ref = "netty" }
netty-socks = { module = "io.netty:netty-codec-socks", 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-core = { module = "com.electronwill.night-config:core", version.ref = "nightConfig" }
nightConfig-toml = { module = "com.electronwill.night-config:toml", version.ref = "nightConfig" } nightConfig-toml = { module = "com.electronwill.night-config:toml", version.ref = "nightConfig" }
slf4j = { module = "org.slf4j:slf4j-api", version.ref = "slf4j" } slf4j = { module = "org.slf4j:slf4j-api", version.ref = "slf4j" }
# Minecraft mods # Minecraft mods
create-fabric = { module = "com.simibubi.create:create-fabric-1.20.1", version.ref = "create-fabric" }
create-forge = { module = "com.simibubi.create:create-1.20.1", version.ref = "create-forge" }
emi = { module = "dev.emi:emi-xplat-mojmap", version.ref = "emi" }
fabric-api = { module = "net.fabricmc.fabric-api:fabric-api", version.ref = "fabric-api" } fabric-api = { module = "net.fabricmc.fabric-api:fabric-api", version.ref = "fabric-api" }
fabric-junit = { module = "net.fabricmc:fabric-loader-junit", version.ref = "fabric-loader" }
fabric-loader = { module = "net.fabricmc:fabric-loader", version.ref = "fabric-loader" } fabric-loader = { module = "net.fabricmc:fabric-loader", version.ref = "fabric-loader" }
fabric-junit = { module = "net.fabricmc:fabric-loader-junit", version.ref = "fabric-loader" }
fabricPermissions = { module = "me.lucko:fabric-permissions-api", version.ref = "fabricPermissions" } fabricPermissions = { module = "me.lucko:fabric-permissions-api", version.ref = "fabricPermissions" }
emi = { module = "dev.emi:emi-xplat-mojmap", version.ref = "emi" }
iris = { module = "maven.modrinth:iris", version.ref = "iris" } iris = { module = "maven.modrinth:iris", version.ref = "iris" }
jei-api = { module = "mezz.jei:jei-1.20.1-common-api", version.ref = "jei" } jei-api = { module = "mezz.jei:jei-1.20.4-common-api", version.ref = "jei" }
jei-fabric = { module = "mezz.jei:jei-1.20.1-fabric", version.ref = "jei" } jei-fabric = { module = "mezz.jei:jei-1.20.4-fabric", version.ref = "jei" }
jei-forge = { module = "mezz.jei:jei-1.20.1-forge", version.ref = "jei" } jei-forge = { module = "mezz.jei:jei-1.20.4-forge", version.ref = "jei" }
mixin = { module = "org.spongepowered:mixin", version.ref = "mixin" } mixin = { module = "org.spongepowered:mixin", version.ref = "mixin" }
modmenu = { module = "com.terraformersmc:modmenu", version.ref = "modmenu" } modmenu = { module = "com.terraformersmc:modmenu", version.ref = "modmenu" }
moreRed = { module = "commoble.morered:morered-1.20.1", version.ref = "moreRed" } moreRed = { module = "commoble.morered:morered-1.20.1", version.ref = "moreRed" }
@@ -152,15 +145,13 @@ errorProne-core = { module = "com.google.errorprone:error_prone_core", version.r
errorProne-plugin = { module = "net.ltgt.gradle:gradle-errorprone-plugin", version.ref = "errorProne-plugin" } errorProne-plugin = { module = "net.ltgt.gradle:gradle-errorprone-plugin", version.ref = "errorProne-plugin" }
errorProne-testHelpers = { module = "com.google.errorprone:error_prone_test_helpers", version.ref = "errorProne-core" } errorProne-testHelpers = { module = "com.google.errorprone:error_prone_test_helpers", version.ref = "errorProne-core" }
fabric-loom = { module = "net.fabricmc:fabric-loom", version.ref = "fabric-loom" } fabric-loom = { module = "net.fabricmc:fabric-loom", version.ref = "fabric-loom" }
forgeGradle = { module = "net.minecraftforge.gradle:ForgeGradle", version.ref = "forgeGradle" }
ideaExt = { module = "gradle.plugin.org.jetbrains.gradle.plugin.idea-ext:gradle-idea-ext", version.ref = "ideaExt" } ideaExt = { module = "gradle.plugin.org.jetbrains.gradle.plugin.idea-ext:gradle-idea-ext", version.ref = "ideaExt" }
kotlin-plugin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" } kotlin-plugin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" }
librarian = { module = "org.parchmentmc:librarian", version.ref = "librarian" }
minotaur = { module = "com.modrinth.minotaur:Minotaur", version.ref = "minotaur" } minotaur = { module = "com.modrinth.minotaur:Minotaur", version.ref = "minotaur" }
neoGradle-userdev = { module = "net.neoforged.gradle:userdev", version.ref = "neoGradle" }
nullAway = { module = "com.uber.nullaway:nullaway", version.ref = "nullAway" } nullAway = { module = "com.uber.nullaway:nullaway", version.ref = "nullAway" }
spotless = { module = "com.diffplug.spotless:spotless-plugin-gradle", version.ref = "spotless" } spotless = { module = "com.diffplug.spotless:spotless-plugin-gradle", version.ref = "spotless" }
teavm-classlib = { module = "org.teavm:teavm-classlib", version.ref = "teavm" } teavm-classlib = { module = "org.teavm:teavm-classlib", version.ref = "teavm" }
teavm-core = { module = "org.teavm:teavm-core", version.ref = "teavm" }
teavm-jso = { module = "org.teavm:teavm-jso", version.ref = "teavm" } teavm-jso = { module = "org.teavm:teavm-jso", version.ref = "teavm" }
teavm-jso-apis = { module = "org.teavm:teavm-jso-apis", version.ref = "teavm" } teavm-jso-apis = { module = "org.teavm:teavm-jso-apis", version.ref = "teavm" }
teavm-jso-impl = { module = "org.teavm:teavm-jso-impl", version.ref = "teavm" } teavm-jso-impl = { module = "org.teavm:teavm-jso-impl", version.ref = "teavm" }
@@ -172,12 +163,9 @@ vanillaExtract = { module = "cc.tweaked.vanilla-extract:plugin", version.ref = "
yarn = { module = "net.fabricmc:yarn", version.ref = "yarn" } yarn = { module = "net.fabricmc:yarn", version.ref = "yarn" }
[plugins] [plugins]
forgeGradle = { id = "net.minecraftforge.gradle", version.ref = "forgeGradle" }
githubRelease = { id = "com.github.breadmoirai.github-release", version.ref = "githubRelease" } githubRelease = { id = "com.github.breadmoirai.github-release", version.ref = "githubRelease" }
gradleVersions = { id = "com.github.ben-manes.versions", version.ref = "gradleVersions" } gradleVersions = { id = "com.github.ben-manes.versions", version.ref = "gradleVersions" }
kotlin = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" } kotlin = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
librarian = { id = "org.parchmentmc.librarian.forgegradle", version.ref = "librarian" }
shadow = { id = "com.gradleup.shadow", version.ref = "shadow" }
taskTree = { id = "com.dorongold.task-tree", version.ref = "taskTree" } taskTree = { id = "com.dorongold.task-tree", version.ref = "taskTree" }
versionCatalogUpdate = { id = "nl.littlerobots.version-catalog-update", version.ref = "versionCatalogUpdate" } versionCatalogUpdate = { id = "nl.littlerobots.version-catalog-update", version.ref = "versionCatalogUpdate" }
@@ -188,7 +176,7 @@ kotlin = ["kotlin-stdlib", "kotlin-coroutines"]
# Minecraft # Minecraft
externalMods-common = ["jei-api", "nightConfig-core", "nightConfig-toml"] externalMods-common = ["jei-api", "nightConfig-core", "nightConfig-toml"]
externalMods-forge-compile = ["moreRed", "oculus", "jei-api"] externalMods-forge-compile = ["moreRed", "oculus", "jei-api"]
externalMods-forge-runtime = ["jei-forge"] externalMods-forge-runtime = []
externalMods-fabric-compile = ["fabricPermissions", "iris", "jei-api", "rei-api", "rei-builtin"] externalMods-fabric-compile = ["fabricPermissions", "iris", "jei-api", "rei-api", "rei-builtin"]
externalMods-fabric-runtime = ["jei-fabric", "modmenu"] externalMods-fabric-runtime = ["jei-fabric", "modmenu"]

Binary file not shown.

View File

@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip
networkTimeout=10000 networkTimeout=10000
validateDistributionUrl=true validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME

7
gradlew vendored
View File

@@ -15,8 +15,6 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
# #
# SPDX-License-Identifier: Apache-2.0
#
############################################################################## ##############################################################################
# #
@@ -57,7 +55,7 @@
# Darwin, MinGW, and NonStop. # Darwin, MinGW, and NonStop.
# #
# (3) This script is generated from the Groovy template # (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project. # within the Gradle project.
# #
# You can find Gradle at https://github.com/gradle/gradle/. # You can find Gradle at https://github.com/gradle/gradle/.
@@ -86,8 +84,7 @@ done
# shellcheck disable=SC2034 # shellcheck disable=SC2034
APP_BASE_NAME=${0##*/} APP_BASE_NAME=${0##*/}
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
' "$PWD" ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value. # Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum MAX_FD=maximum

2
gradlew.bat vendored
View File

@@ -13,8 +13,6 @@
@rem See the License for the specific language governing permissions and @rem See the License for the specific language governing permissions and
@rem limitations under the License. @rem limitations under the License.
@rem @rem
@rem SPDX-License-Identifier: Apache-2.0
@rem
@if "%DEBUG%"=="" @echo off @if "%DEBUG%"=="" @echo off
@rem ########################################################################## @rem ##########################################################################

View File

@@ -1,43 +0,0 @@
// SPDX-FileCopyrightText: 2022 The CC: Tweaked Developers
//
// SPDX-License-Identifier: MPL-2.0
package dan200.computercraft.api.client;
import dan200.computercraft.api.client.turtle.TurtleUpgradeModeller;
import dan200.computercraft.api.turtle.ITurtleUpgrade;
import dan200.computercraft.api.turtle.TurtleUpgradeSerialiser;
import dan200.computercraft.impl.client.ComputerCraftAPIClientService;
/**
* The public API for client-only code.
*
* @see dan200.computercraft.api.ComputerCraftAPI The main API
*/
public final class ComputerCraftAPIClient {
private ComputerCraftAPIClient() {
}
/**
* Register a {@link TurtleUpgradeModeller} for a class of turtle upgrades.
* <p>
* This may be called at any point after registry creation, though it is recommended to call it within your client
* setup step.
*
* @param serialiser The turtle upgrade serialiser.
* @param modeller The upgrade modeller.
* @param <T> The type of the turtle upgrade.
* @deprecated This method can lead to confusing load behaviour on Forge. Use
* {@code dan200.computercraft.api.client.FabricComputerCraftAPIClient#registerTurtleUpgradeModeller} on Fabric, or
* {@code dan200.computercraft.api.client.turtle.RegisterTurtleModellersEvent} on Forge.
*/
@Deprecated(forRemoval = true)
public static <T extends ITurtleUpgrade> void registerTurtleUpgradeModeller(TurtleUpgradeSerialiser<T> serialiser, TurtleUpgradeModeller<T> modeller) {
// TODO(1.20.4): Remove this
getInstance().registerTurtleUpgradeModeller(serialiser, modeller);
}
private static ComputerCraftAPIClientService getInstance() {
return ComputerCraftAPIClientService.get();
}
}

View File

@@ -5,7 +5,7 @@
package dan200.computercraft.api.client.turtle; package dan200.computercraft.api.client.turtle;
import dan200.computercraft.api.turtle.ITurtleUpgrade; import dan200.computercraft.api.turtle.ITurtleUpgrade;
import dan200.computercraft.api.turtle.TurtleUpgradeSerialiser; import dan200.computercraft.api.upgrades.UpgradeSerialiser;
/** /**
* A functional interface to register a {@link TurtleUpgradeModeller} for a class of turtle upgrades. * A functional interface to register a {@link TurtleUpgradeModeller} for a class of turtle upgrades.
@@ -22,5 +22,5 @@ public interface RegisterTurtleUpgradeModeller {
* @param modeller The upgrade modeller. * @param modeller The upgrade modeller.
* @param <T> The type of the turtle upgrade. * @param <T> The type of the turtle upgrade.
*/ */
<T extends ITurtleUpgrade> void register(TurtleUpgradeSerialiser<T> serialiser, TurtleUpgradeModeller<T> modeller); <T extends ITurtleUpgrade> void register(UpgradeSerialiser<T> serialiser, TurtleUpgradeModeller<T> modeller);
} }

View File

@@ -8,7 +8,6 @@ import dan200.computercraft.api.client.TransformedModel;
import dan200.computercraft.api.turtle.ITurtleAccess; import dan200.computercraft.api.turtle.ITurtleAccess;
import dan200.computercraft.api.turtle.ITurtleUpgrade; import dan200.computercraft.api.turtle.ITurtleUpgrade;
import dan200.computercraft.api.turtle.TurtleSide; import dan200.computercraft.api.turtle.TurtleSide;
import net.minecraft.client.resources.model.ModelResourceLocation;
import net.minecraft.client.resources.model.UnbakedModel; import net.minecraft.client.resources.model.UnbakedModel;
import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.CompoundTag;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
@@ -31,30 +30,15 @@ public interface TurtleUpgradeModeller<T extends ITurtleUpgrade> {
/** /**
* Obtain the model to be used when rendering a turtle peripheral. * Obtain the model to be used when rendering a turtle peripheral.
* <p> * <p>
* When the current turtle is {@literal null}, this function should be constant for a given upgrade and side. * When the current turtle is {@literal null}, this function should be constant for a given upgrade, side and data.
* *
* @param upgrade The upgrade that you're getting the model for. * @param upgrade The upgrade that you're getting the model for.
* @param turtle Access to the turtle that the upgrade resides on. This will be null when getting item models, unless * @param turtle Access to the turtle that the upgrade resides on. This will be null when getting item models.
* {@link #getModel(ITurtleUpgrade, CompoundTag, TurtleSide)} is overriden.
* @param side Which side of the turtle (left or right) the upgrade resides on. * @param side Which side of the turtle (left or right) the upgrade resides on.
* @return The model that you wish to be used to render your upgrade.
*/
TransformedModel getModel(T upgrade, @Nullable ITurtleAccess turtle, TurtleSide side);
/**
* 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 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. * @return The model that you wish to be used to render your upgrade.
*/ */
default TransformedModel getModel(T upgrade, CompoundTag data, TurtleSide side) { TransformedModel getModel(T upgrade, @Nullable ITurtleAccess turtle, TurtleSide side, CompoundTag data);
return getModel(upgrade, (ITurtleAccess) null, side);
}
/** /**
* Get a list of models that this turtle modeller depends on. * Get a list of models that this turtle modeller depends on.
@@ -85,19 +69,6 @@ public interface TurtleUpgradeModeller<T extends ITurtleUpgrade> {
return (TurtleUpgradeModeller<T>) TurtleUpgradeModellers.UPGRADE_ITEM; return (TurtleUpgradeModeller<T>) TurtleUpgradeModellers.UPGRADE_ITEM;
} }
/**
* Construct a {@link TurtleUpgradeModeller} which has a single model for the left and right side.
*
* @param left The model to use on the left.
* @param right The model to use on the right.
* @param <T> The type of the turtle upgrade.
* @return The constructed modeller.
*/
static <T extends ITurtleUpgrade> TurtleUpgradeModeller<T> sided(ModelResourceLocation left, ModelResourceLocation right) {
// TODO(1.21.0): Remove this.
return sided((ResourceLocation) left, right);
}
/** /**
* Construct a {@link TurtleUpgradeModeller} which has a single model for the left and right side. * Construct a {@link TurtleUpgradeModeller} which has a single model for the left and right side.
* *
@@ -109,7 +80,7 @@ public interface TurtleUpgradeModeller<T extends ITurtleUpgrade> {
static <T extends ITurtleUpgrade> TurtleUpgradeModeller<T> sided(ResourceLocation left, ResourceLocation right) { static <T extends ITurtleUpgrade> TurtleUpgradeModeller<T> sided(ResourceLocation left, ResourceLocation right) {
return new TurtleUpgradeModeller<>() { return new TurtleUpgradeModeller<>() {
@Override @Override
public TransformedModel getModel(T upgrade, @Nullable ITurtleAccess turtle, TurtleSide side) { public TransformedModel getModel(T upgrade, @Nullable ITurtleAccess turtle, TurtleSide side, CompoundTag data) {
return TransformedModel.of(side == TurtleSide.LEFT ? left : right); return TransformedModel.of(side == TurtleSide.LEFT ? left : right);
} }

View File

@@ -12,7 +12,6 @@ import dan200.computercraft.api.turtle.TurtleSide;
import dan200.computercraft.impl.client.ClientPlatformHelper; import dan200.computercraft.impl.client.ClientPlatformHelper;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.CompoundTag;
import net.minecraft.world.item.ItemStack;
import org.joml.Matrix4f; import org.joml.Matrix4f;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@@ -37,16 +36,8 @@ final class TurtleUpgradeModellers {
private static final class UpgradeItemModeller implements TurtleUpgradeModeller<ITurtleUpgrade> { private static final class UpgradeItemModeller implements TurtleUpgradeModeller<ITurtleUpgrade> {
@Override @Override
public TransformedModel getModel(ITurtleUpgrade upgrade, @Nullable ITurtleAccess turtle, TurtleSide side) { public TransformedModel getModel(ITurtleUpgrade upgrade, @Nullable ITurtleAccess turtle, TurtleSide side, CompoundTag data) {
return getModel(turtle == null ? upgrade.getCraftingItem() : upgrade.getUpgradeItem(turtle.getUpgradeNBTData(side)), side); var stack = upgrade.getUpgradeItem(data);
}
@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); var model = Minecraft.getInstance().getItemRenderer().getItemModelShaper().getItemModel(stack);
if (stack.hasFoil()) model = ClientPlatformHelper.get().createdFoiledModel(model); if (stack.hasFoil()) model = ClientPlatformHelper.get().createdFoiledModel(model);
return new TransformedModel(model, side == TurtleSide.LEFT ? leftTransform : rightTransform); return new TransformedModel(model, side == TurtleSide.LEFT ? leftTransform : rightTransform);

View File

@@ -4,11 +4,9 @@
package dan200.computercraft.api; package dan200.computercraft.api;
import dan200.computercraft.api.component.ComputerComponent;
import dan200.computercraft.api.filesystem.Mount; import dan200.computercraft.api.filesystem.Mount;
import dan200.computercraft.api.filesystem.WritableMount; import dan200.computercraft.api.filesystem.WritableMount;
import dan200.computercraft.api.lua.GenericSource; import dan200.computercraft.api.lua.GenericSource;
import dan200.computercraft.api.lua.IComputerSystem;
import dan200.computercraft.api.lua.ILuaAPI; import dan200.computercraft.api.lua.ILuaAPI;
import dan200.computercraft.api.lua.ILuaAPIFactory; import dan200.computercraft.api.lua.ILuaAPIFactory;
import dan200.computercraft.api.media.IMedia; import dan200.computercraft.api.media.IMedia;
@@ -167,20 +165,7 @@ public final class ComputerCraftAPI {
* Register a custom {@link ILuaAPI}, which may be added onto all computers without requiring a peripheral. * Register a custom {@link ILuaAPI}, which may be added onto all computers without requiring a peripheral.
* <p> * <p>
* Before implementing this interface, consider alternative methods of providing methods. It is generally preferred * Before implementing this interface, consider alternative methods of providing methods. It is generally preferred
* to use peripherals to provide functionality to users. If an API is <em>required</em>, you may want to consider * to use peripherals to provide functionality to users.
* using {@link ILuaAPI#getModuleName()} to expose this library as a module instead of as a global.
* <p>
* This may be used with {@link IComputerSystem#getComponent(ComputerComponent)} to only attach APIs to specific
* computers. For example, one can add an additional API just to turtles with the following code:
*
* <pre>{@code
* ComputerCraftAPI.registerAPIFactory(computer -> {
* // Read the turtle component.
* var turtle = computer.getComponent(ComputerComponents.TURTLE);
* // If present then add our API.
* return turtle == null ? null : new MyCustomTurtleApi(turtle);
* });
* }</pre>
* *
* @param factory The factory for your API subclass. * @param factory The factory for your API subclass.
* @see ILuaAPIFactory * @see ILuaAPIFactory

View File

@@ -1,24 +0,0 @@
// SPDX-FileCopyrightText: 2024 The CC: Tweaked Developers
//
// SPDX-License-Identifier: MPL-2.0
package dan200.computercraft.api.component;
import net.minecraft.commands.CommandSourceStack;
import org.jetbrains.annotations.ApiStatus;
/**
* A computer which has permission to perform administrative/op commands, such as the command computer.
*/
@ApiStatus.NonExtendable
public interface AdminComputer {
/**
* The permission level that this computer can operate at.
*
* @return The permission level for this computer.
* @see CommandSourceStack#hasPermission(int)
*/
default int permissionLevel() {
return 2;
}
}

View File

@@ -1,48 +0,0 @@
// SPDX-FileCopyrightText: 2024 The CC: Tweaked Developers
//
// SPDX-License-Identifier: MPL-2.0
package dan200.computercraft.api.component;
import dan200.computercraft.api.lua.IComputerSystem;
import dan200.computercraft.api.lua.ILuaAPIFactory;
/**
* A component attached to a computer.
* <p>
* Components provide a mechanism to attach additional data to a computer, that can then be queried with
* {@link IComputerSystem#getComponent(ComputerComponent)}.
* <p>
* This is largely designed for {@linkplain ILuaAPIFactory custom APIs}, allowing APIs to read additional properties
* of the computer, such as its position.
*
* @param <T> The type of this component.
* @see ComputerComponents The built-in components.
*/
@SuppressWarnings("UnusedTypeParameter")
public final class ComputerComponent<T> {
private final String id;
private ComputerComponent(String id) {
this.id = id;
}
/**
* Create a new computer component.
* <p>
* Mods typically will not need to create their own components.
*
* @param namespace The namespace of this component. This should be the mod id.
* @param id The unique id of this component.
* @param <T> The component
* @return The newly created component.
*/
public static <T> ComputerComponent<T> create(String namespace, String id) {
return new ComputerComponent<>(namespace + ":" + id);
}
@Override
public String toString() {
return "ComputerComponent(" + id + ")";
}
}

View File

@@ -1,29 +0,0 @@
// SPDX-FileCopyrightText: 2024 The CC: Tweaked Developers
//
// SPDX-License-Identifier: MPL-2.0
package dan200.computercraft.api.component;
import dan200.computercraft.api.ComputerCraftAPI;
import dan200.computercraft.api.pocket.IPocketAccess;
import dan200.computercraft.api.turtle.ITurtleAccess;
/**
* The {@link ComputerComponent}s provided by ComputerCraft.
*/
public class ComputerComponents {
/**
* The {@link ITurtleAccess} associated with a turtle.
*/
public static final ComputerComponent<ITurtleAccess> TURTLE = ComputerComponent.create(ComputerCraftAPI.MOD_ID, "turtle");
/**
* The {@link IPocketAccess} associated with a pocket computer.
*/
public static final ComputerComponent<IPocketAccess> POCKET = ComputerComponent.create(ComputerCraftAPI.MOD_ID, "pocket");
/**
* This component is only present on "command computers", and other computers with admin capabilities.
*/
public static final ComputerComponent<AdminComputer> ADMIN_COMPUTER = ComputerComponent.create(ComputerCraftAPI.MOD_ID, "admin_computer");
}

View File

@@ -13,7 +13,7 @@ import java.util.Map;
import java.util.Objects; import java.util.Objects;
/** /**
* An item detail provider for {@link ItemStack}s whose {@link Item} has a specific type. * An item detail provider for {@link ItemStack}'s whose {@link Item} has a specific type.
* *
* @param <T> The type the stack's item must have. * @param <T> The type the stack's item must have.
*/ */
@@ -22,7 +22,7 @@ public abstract class BasicItemDetailProvider<T> implements DetailProvider<ItemS
private final @Nullable String namespace; private final @Nullable String namespace;
/** /**
* Create a new item detail provider. Details will be inserted into a new sub-map named as per {@code namespace}. * Create a new item detail provider. Meta will be inserted into a new sub-map named as per {@code namespace}.
* *
* @param itemType The type the stack's item must have. * @param itemType The type the stack's item must have.
* @param namespace The namespace to use for this provider. * @param namespace The namespace to use for this provider.
@@ -34,7 +34,7 @@ public abstract class BasicItemDetailProvider<T> implements DetailProvider<ItemS
} }
/** /**
* Create a new item detail provider. Details will be inserted directly into the results. * Create a new item detail provider. Meta will be inserted directly into the results.
* *
* @param itemType The type the stack's item must have. * @param itemType The type the stack's item must have.
*/ */
@@ -53,18 +53,21 @@ public abstract class BasicItemDetailProvider<T> implements DetailProvider<ItemS
* @param stack The item stack to provide details for. * @param stack The item stack to provide details for.
* @param item The item to provide details for. * @param item The item to provide details for.
*/ */
public abstract void provideDetails(Map<? super String, Object> data, ItemStack stack, T item); public abstract void provideDetails(
Map<? super String, Object> data, ItemStack stack, T item
);
@Override @Override
public final void provideDetails(Map<? super String, Object> data, ItemStack stack) { public void provideDetails(Map<? super String, Object> data, ItemStack stack) {
var item = stack.getItem(); var item = stack.getItem();
if (!itemType.isInstance(item)) return; if (!itemType.isInstance(item)) return;
if (namespace == null) { // If `namespace` is specified, insert into a new data map instead of the existing one.
provideDetails(data, stack, itemType.cast(item)); Map<? super String, Object> child = namespace == null ? data : new HashMap<>();
} else {
Map<? super String, Object> child = new HashMap<>(); provideDetails(child, stack, itemType.cast(item));
provideDetails(child, stack, itemType.cast(item));
if (namespace != null) {
data.put(namespace, child); data.put(namespace, child);
} }
} }

View File

@@ -26,7 +26,7 @@ public interface DetailRegistry<T> {
* @param provider The detail provider to register. * @param provider The detail provider to register.
* @see DetailProvider * @see DetailProvider
*/ */
void addProvider(DetailProvider<? super T> provider); void addProvider(DetailProvider<T> provider);
/** /**
* Compute basic details about an object. This is cheaper than computing all details operation, and so is suitable * Compute basic details about an object. This is cheaper than computing all details operation, and so is suitable

View File

@@ -1,59 +0,0 @@
// SPDX-FileCopyrightText: 2017 The CC: Tweaked Developers
//
// SPDX-License-Identifier: MPL-2.0
package dan200.computercraft.api.lua;
import dan200.computercraft.api.component.ComputerComponent;
import dan200.computercraft.api.peripheral.IComputerAccess;
import net.minecraft.core.BlockPos;
import net.minecraft.server.level.ServerLevel;
import org.jetbrains.annotations.ApiStatus;
import javax.annotation.Nullable;
/**
* An interface passed to {@link ILuaAPIFactory} in order to provide additional information
* about a computer.
*/
@ApiStatus.NonExtendable
public interface IComputerSystem extends IComputerAccess {
/**
* Get the level this computer is currently in.
* <p>
* This method is not guaranteed to remain the same (even for stationary computers).
*
* @return The computer's current level.
*/
ServerLevel getLevel();
/**
* Get the position this computer is currently at.
* <p>
* This method is not guaranteed to remain the same (even for stationary computers).
*
* @return The computer's current position.
*/
BlockPos getPosition();
/**
* Get the label for this computer.
*
* @return This computer's label, or {@code null} if it is not set.
*/
@Nullable
String getLabel();
/**
* Get a component attached to this computer.
* <p>
* No component is guaranteed to be on a computer, and so this method should always be guarded with a null check.
* <p>
* This method will always return the same value for a given component, and so may be cached.
*
* @param component The component to query.
* @param <T> The type of the component.
* @return The component, if present.
*/
<T> @Nullable T getComponent(ComputerComponent<T> component);
}

View File

@@ -1,92 +0,0 @@
// SPDX-FileCopyrightText: 2018 The CC: Tweaked Developers
//
// SPDX-License-Identifier: MPL-2.0
package dan200.computercraft.api.network.wired;
import dan200.computercraft.api.peripheral.IPeripheral;
import org.jetbrains.annotations.ApiStatus;
import java.util.Map;
/**
* A wired network is composed of one of more {@link WiredNode}s, a set of connections between them, and a series
* of peripherals.
* <p>
* Networks from a connected graph. This means there is some path between all nodes on the network. Further more, if
* there is some path between two nodes then they must be on the same network. {@link WiredNetwork} will automatically
* handle the merging and splitting of networks (and thus changing of available nodes and peripherals) as connections
* change.
* <p>
* This does mean one can not rely on the network remaining consistent between subsequent operations. Consequently,
* it is generally preferred to use the methods provided by {@link WiredNode}.
*
* @see WiredNode#getNetwork()
*/
@ApiStatus.NonExtendable
public interface WiredNetwork {
/**
* Create a connection between two nodes.
* <p>
* This should only be used on the server thread.
*
* @param left The first node to connect
* @param right The second node to connect
* @return {@code true} if a connection was created or {@code false} if the connection already exists.
* @throws IllegalStateException If neither node is on the network.
* @throws IllegalArgumentException If {@code left} and {@code right} are equal.
* @see WiredNode#connectTo(WiredNode)
* @see WiredNetwork#connect(WiredNode, WiredNode)
* @deprecated Use {@link WiredNode#connectTo(WiredNode)}
*/
@Deprecated
boolean connect(WiredNode left, WiredNode right);
/**
* Destroy a connection between this node and another.
* <p>
* This should only be used on the server thread.
*
* @param left The first node in the connection.
* @param right The second node in the connection.
* @return {@code true} if a connection was destroyed or {@code false} if no connection exists.
* @throws IllegalArgumentException If either node is not on the network.
* @throws IllegalArgumentException If {@code left} and {@code right} are equal.
* @see WiredNode#disconnectFrom(WiredNode)
* @see WiredNetwork#connect(WiredNode, WiredNode)
* @deprecated Use {@link WiredNode#disconnectFrom(WiredNode)}
*/
@Deprecated
boolean disconnect(WiredNode left, WiredNode right);
/**
* Sever all connections this node has, removing it from this network.
* <p>
* This should only be used on the server thread. You should only call this on nodes
* that your network element owns.
*
* @param node The node to remove
* @return Whether this node was removed from the network. One cannot remove a node from a network where it is the
* only element.
* @throws IllegalArgumentException If the node is not in the network.
* @see WiredNode#remove()
* @deprecated Use {@link WiredNode#remove()}
*/
@Deprecated
boolean remove(WiredNode node);
/**
* Update the peripherals a node provides.
* <p>
* This should only be used on the server thread. You should only call this on nodes
* that your network element owns.
*
* @param node The node to attach peripherals for.
* @param peripherals The new peripherals for this node.
* @throws IllegalArgumentException If the node is not in the network.
* @see WiredNode#updatePeripherals(Map)
* @deprecated Use {@link WiredNode#updatePeripherals(Map)}
*/
@Deprecated
void updatePeripherals(WiredNode node, Map<String, IPeripheral> peripherals);
}

View File

@@ -11,7 +11,7 @@ import org.jetbrains.annotations.ApiStatus;
import java.util.Map; import java.util.Map;
/** /**
* Wired nodes act as a layer between {@link WiredElement}s and {@link WiredNetwork}s. * A single node on a wired network.
* <p> * <p>
* Firstly, a node acts as a packet network, capable of sending and receiving modem messages to connected nodes. These * Firstly, a node acts as a packet network, capable of sending and receiving modem messages to connected nodes. These
* methods may be safely used on any thread. * methods may be safely used on any thread.
@@ -32,18 +32,6 @@ public interface WiredNode extends PacketNetwork {
*/ */
WiredElement getElement(); WiredElement getElement();
/**
* The network this node is currently connected to. Note that this may change
* after any network operation, so it should not be cached.
* <p>
* This should only be used on the server thread.
*
* @return This node's network.
* @deprecated Use the connect/disconnect/remove methods on {@link WiredNode}.
*/
@Deprecated
WiredNetwork getNetwork();
/** /**
* Create a connection from this node to another. * Create a connection from this node to another.
* <p> * <p>

View File

@@ -8,10 +8,12 @@ import dan200.computercraft.api.network.PacketSender;
/** /**
* An object on a {@link WiredNetwork} capable of sending packets. * An object on a wired network capable of sending packets.
* <p> * <p>
* Unlike a regular {@link PacketSender}, this must be associated with the node you are attempting to * Unlike a regular {@link PacketSender}, this must be associated with the node you are attempting to
* to send the packet from. * to send the packet from.
*
* @see WiredElement
*/ */
public interface WiredSender extends PacketSender { public interface WiredSender extends PacketSender {
/** /**

View File

@@ -4,39 +4,17 @@
package dan200.computercraft.api.pocket; package dan200.computercraft.api.pocket;
import dan200.computercraft.api.peripheral.IPeripheral;
import dan200.computercraft.api.upgrades.UpgradeBase; import dan200.computercraft.api.upgrades.UpgradeBase;
import dan200.computercraft.api.upgrades.UpgradeData;
import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.CompoundTag;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.Entity;
import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.ItemStack;
import net.minecraft.world.phys.Vec3;
import org.jetbrains.annotations.ApiStatus;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import java.util.Map;
/** /**
* Wrapper class for pocket computers. * Wrapper class for pocket computers.
*/ */
@ApiStatus.NonExtendable
public interface IPocketAccess { public interface IPocketAccess {
/**
* Get the level in which the pocket computer exists.
*
* @return The pocket computer's level.
*/
ServerLevel getLevel();
/**
* Get the position of the pocket computer.
*
* @return The pocket computer's position.
*/
Vec3 getPosition();
/** /**
* Gets the entity holding this item. * Gets the entity holding this item.
* <p> * <p>
@@ -83,26 +61,6 @@ public interface IPocketAccess {
*/ */
void setLight(int colour); void setLight(int colour);
/**
* Get the currently equipped upgrade.
*
* @return The currently equipped upgrade.
* @see #getUpgradeNBTData()
* @see #setUpgrade(UpgradeData)
*/
@Nullable
UpgradeData<IPocketUpgrade> getUpgrade();
/**
* Set the upgrade for this pocket computer, also updating the item stack.
* <p>
* Note this method is not thread safe - it must be called from the server thread.
*
* @param upgrade The new upgrade to set it to, may be {@code null}.
* @see #getUpgrade()
*/
void setUpgrade(@Nullable UpgradeData<IPocketUpgrade> upgrade);
/** /**
* Get the upgrade-specific NBT. * Get the upgrade-specific NBT.
* <p> * <p>
@@ -112,7 +70,6 @@ public interface IPocketAccess {
* @see #updateUpgradeNBTData() * @see #updateUpgradeNBTData()
* @see UpgradeBase#getUpgradeItem(CompoundTag) * @see UpgradeBase#getUpgradeItem(CompoundTag)
* @see UpgradeBase#getUpgradeData(ItemStack) * @see UpgradeBase#getUpgradeData(ItemStack)
* @see #getUpgrade()
*/ */
CompoundTag getUpgradeNBTData(); CompoundTag getUpgradeNBTData();
@@ -130,13 +87,4 @@ public interface IPocketAccess {
* entity} changes. * entity} changes.
*/ */
void invalidatePeripheral(); void invalidatePeripheral();
/**
* 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();
} }

View File

@@ -6,6 +6,10 @@ package dan200.computercraft.api.pocket;
import dan200.computercraft.api.peripheral.IPeripheral; import dan200.computercraft.api.peripheral.IPeripheral;
import dan200.computercraft.api.upgrades.UpgradeBase; import dan200.computercraft.api.upgrades.UpgradeBase;
import dan200.computercraft.api.upgrades.UpgradeSerialiser;
import dan200.computercraft.impl.ComputerCraftAPIService;
import net.minecraft.core.Registry;
import net.minecraft.resources.ResourceKey;
import net.minecraft.world.level.Level; import net.minecraft.world.level.Level;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@@ -13,16 +17,46 @@ import javax.annotation.Nullable;
/** /**
* A peripheral which can be equipped to the back side of a pocket computer. * A peripheral which can be equipped to the back side of a pocket computer.
* <p> * <p>
* Pocket upgrades are defined in two stages. First, on creates a {@link IPocketUpgrade} subclass and corresponding * Pocket upgrades are defined in two stages. First, one creates a {@link IPocketUpgrade} subclass and corresponding
* {@link PocketUpgradeSerialiser} instance, which are then registered in a Forge registry. * {@link UpgradeSerialiser} instance, which are then registered in a registry.
* <p> * <p>
* You then write a JSON file in your mod's {@literal data/} folder. This is then parsed when the world is loaded, and * You then write a JSON file in your mod's {@literal data/} folder. This is then parsed when the world is loaded, and
* the upgrade registered internally. See the documentation in {@link PocketUpgradeSerialiser} for details on this process * the upgrade automatically registered. It is recommended this is done via {@linkplain PocketUpgradeDataProvider data
* and where files should be located. * generators}.
* *
* @see PocketUpgradeSerialiser For how to register a pocket computer upgrade. * <h2>Example</h2>
* <pre>{@code
* // We use Forge's DeferredRegister to register our serialiser. Fabric mods may register their serialiser directly.
* static final DeferredRegister<UpgradeSerialiser<? extends IPocketUpgrade>> SERIALISERS = DeferredRegister.create(IPocketUpgrade.serialiserRegistryKey(), "my_mod");
*
* // Register a new upgrade serialiser called "my_upgrade".
* public static final RegistryObject<UpgradeSerialiser<MyUpgrade>> MY_UPGRADE =
* SERIALISERS.register("my_upgrade", () -> UpgradeSerialiser.simple(MyUpgrade::new));
*
* // Then in your constructor
* SERIALISERS.register(bus);
* }</pre>
* <p>
* We can then define a new upgrade using JSON by placing the following in
* {@code data/<my_mod>/computercraft/pocket_upgrades/<my_upgrade_id>.json}.
* <pre>{@code
* {
* "type": my_mod:my_upgrade",
* }
* }</pre>
* <p>
* {@link PocketUpgradeDataProvider} provides a data provider to aid with generating these JSON files.
*/ */
public interface IPocketUpgrade extends UpgradeBase { public interface IPocketUpgrade extends UpgradeBase {
/**
* The registry key for upgrade serialisers.
*
* @return The registry key.
*/
static ResourceKey<Registry<UpgradeSerialiser<? extends IPocketUpgrade>>> serialiserRegistryKey() {
return ComputerCraftAPIService.get().pocketUpgradeRegistryId();
}
/** /**
* Creates a peripheral for the pocket computer. * Creates a peripheral for the pocket computer.
* <p> * <p>

View File

@@ -5,6 +5,7 @@
package dan200.computercraft.api.pocket; package dan200.computercraft.api.pocket;
import dan200.computercraft.api.upgrades.UpgradeDataProvider; import dan200.computercraft.api.upgrades.UpgradeDataProvider;
import dan200.computercraft.api.upgrades.UpgradeSerialiser;
import net.minecraft.data.DataGenerator; import net.minecraft.data.DataGenerator;
import net.minecraft.data.PackOutput; import net.minecraft.data.PackOutput;
@@ -17,10 +18,11 @@ import java.util.function.Consumer;
* {@link #addUpgrades(Consumer)} function, construct each upgrade, and pass them off to the provided consumer to * {@link #addUpgrades(Consumer)} function, construct each upgrade, and pass them off to the provided consumer to
* generate them. * generate them.
* *
* @see PocketUpgradeSerialiser * @see IPocketUpgrade
* @see UpgradeSerialiser
*/ */
public abstract class PocketUpgradeDataProvider extends UpgradeDataProvider<IPocketUpgrade, PocketUpgradeSerialiser<?>> { public abstract class PocketUpgradeDataProvider extends UpgradeDataProvider<IPocketUpgrade> {
public PocketUpgradeDataProvider(PackOutput output) { public PocketUpgradeDataProvider(PackOutput output) {
super(output, "Pocket Computer Upgrades", "computercraft/pocket_upgrades", PocketUpgradeSerialiser.registryId()); super(output, "Pocket Computer Upgrades", "computercraft/pocket_upgrades", IPocketUpgrade.serialiserRegistryKey());
} }
} }

View File

@@ -1,79 +0,0 @@
// SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
//
// SPDX-License-Identifier: MPL-2.0
package dan200.computercraft.api.pocket;
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;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.crafting.SimpleCraftingRecipeSerializer;
import java.util.function.BiFunction;
import java.util.function.Function;
/**
* Reads a {@link IPocketUpgrade} from disk and reads/writes it to a network packet.
* <p>
* This follows the same format as {@link dan200.computercraft.api.turtle.TurtleUpgradeSerialiser} - consult the
* documentation there for more information.
*
* @param <T> The type of pocket computer upgrade this is responsible for serialising.
* @see IPocketUpgrade
* @see PocketUpgradeDataProvider
*/
public interface PocketUpgradeSerialiser<T extends IPocketUpgrade> extends UpgradeSerialiser<T> {
/**
* 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.
* <p>
* If you might want to vary the item, it's suggested you use {@link #simpleWithCustomItem(BiFunction)} instead.
*
* @param factory Generate a new upgrade with a specific ID.
* @param <T> The type of the generated upgrade.
* @return The serialiser for this upgrade
*/
static <T extends IPocketUpgrade> PocketUpgradeSerialiser<T> simple(Function<ResourceLocation, T> factory) {
final class Impl extends SimpleSerialiser<T> implements PocketUpgradeSerialiser<T> {
private Impl(Function<ResourceLocation, T> constructor) {
super(constructor);
}
}
return new Impl(factory);
}
/**
* Create an upgrade serialiser for a simple upgrade whose crafting item can be specified.
*
* @param factory Generate a new upgrade with a specific ID and crafting item. The returned upgrade's
* {@link UpgradeBase#getCraftingItem()} <strong>MUST</strong> equal the provided item.
* @param <T> The type of the generated upgrade.
* @return The serialiser for this upgrade.
* @see #simple(Function) For upgrades whose crafting stack should not vary.
*/
static <T extends IPocketUpgrade> PocketUpgradeSerialiser<T> simpleWithCustomItem(BiFunction<ResourceLocation, ItemStack, T> factory) {
final class Impl extends SerialiserWithCraftingItem<T> implements PocketUpgradeSerialiser<T> {
private Impl(BiFunction<ResourceLocation, ItemStack, T> factory) {
super(factory);
}
}
return new Impl(factory);
}
}

View File

@@ -6,8 +6,12 @@ package dan200.computercraft.api.turtle;
import dan200.computercraft.api.peripheral.IPeripheral; import dan200.computercraft.api.peripheral.IPeripheral;
import dan200.computercraft.api.upgrades.UpgradeBase; import dan200.computercraft.api.upgrades.UpgradeBase;
import dan200.computercraft.api.upgrades.UpgradeSerialiser;
import dan200.computercraft.impl.ComputerCraftAPIService;
import net.minecraft.core.Direction; import net.minecraft.core.Direction;
import net.minecraft.core.Registry;
import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.CompoundTag;
import net.minecraft.resources.ResourceKey;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@@ -16,15 +20,54 @@ import javax.annotation.Nullable;
* peripheral. * peripheral.
* <p> * <p>
* Turtle upgrades are defined in two stages. First, one creates a {@link ITurtleUpgrade} subclass and corresponding * Turtle upgrades are defined in two stages. First, one creates a {@link ITurtleUpgrade} subclass and corresponding
* {@link TurtleUpgradeSerialiser} instance, which are then registered in a Forge registry. * {@link UpgradeSerialiser} instance, which are then registered in a registry.
* <p> * <p>
* You then write a JSON file in your mod's {@literal data/} folder. This is then parsed when the world is loaded, and * You then write a JSON file in your mod's {@literal data/} folder. This is then parsed when the world is loaded, and
* the upgrade registered internally. See the documentation in {@link TurtleUpgradeSerialiser} for details on this process * the upgrade automatically registered. It is recommended this is done via {@linkplain TurtleUpgradeDataProvider data
* and where files should be located. * generators}.
* *
* @see TurtleUpgradeSerialiser For how to register a turtle upgrade. * <h2>Example</h2>
* <pre>{@code
* // We use Forge's DeferredRegister to register our serialiser. Fabric mods may register their serialiser directly.
* static final DeferredRegister<UpgradeSerialiser<? extends ITurtleUpgrade>> SERIALISERS = DeferredRegister.create(ITurtleUpgrade.serialiserRegistryKey(), "my_mod");
*
* // Register a new upgrade serialiser called "my_upgrade".
* public static final RegistryObject<UpgradeSerialiser<MyUpgrade>> MY_UPGRADE =
* SERIALISERS.register( "my_upgrade", () -> TurtleUpgradeSerialiser.simple( MyUpgrade::new ) );
*
* // Then in your constructor
* SERIALISERS.register( bus );
* }</pre>
* <p>
* We can then define a new upgrade using JSON by placing the following in
* {@literal data/<my_mod>/computercraft/turtle_upgrades/<my_upgrade_id>.json}}.
*
* <pre>{@code
* {
* "type": my_mod:my_upgrade",
* }
* }</pre>
* <p>
* {@link TurtleUpgradeDataProvider} provides a data provider to aid with generating these JSON files.
* <p>
* Finally, we need to register a model for our upgrade, see
* {@link dan200.computercraft.api.client.turtle.TurtleUpgradeModeller} for more information.
*
* <pre>{@code
* // Register our model inside FMLClientSetupEvent
* ComputerCraftAPIClient.registerTurtleUpgradeModeller(MY_UPGRADE.get(), TurtleUpgradeModeller.flatItem())
* }</pre>
*/ */
public interface ITurtleUpgrade extends UpgradeBase { public interface ITurtleUpgrade extends UpgradeBase {
/**
* The registry key for upgrade serialisers.
*
* @return The registry key.
*/
static ResourceKey<Registry<UpgradeSerialiser<? extends ITurtleUpgrade>>> serialiserRegistryKey() {
return ComputerCraftAPIService.get().turtleUpgradeRegistryId();
}
/** /**
* Return whether this turtle adds a tool or a peripheral to the turtle. * Return whether this turtle adds a tool or a peripheral to the turtle.
* *

View File

@@ -5,7 +5,7 @@
package dan200.computercraft.api.turtle; package dan200.computercraft.api.turtle;
/** /**
* An enum representing the two sides of the turtle that a turtle upgrade might reside. * An enum representing the two sides of the turtle that a turtle turtle might reside.
*/ */
public enum TurtleSide { public enum TurtleSide {
/** /**

View File

@@ -7,8 +7,9 @@ package dan200.computercraft.api.turtle;
import dan200.computercraft.api.ComputerCraftAPI; import dan200.computercraft.api.ComputerCraftAPI;
import dan200.computercraft.api.ComputerCraftTags; import dan200.computercraft.api.ComputerCraftTags;
import dan200.computercraft.api.upgrades.UpgradeDataProvider; import dan200.computercraft.api.upgrades.UpgradeDataProvider;
import dan200.computercraft.impl.PlatformHelper; import dan200.computercraft.api.upgrades.UpgradeSerialiser;
import net.minecraft.core.registries.Registries; import dan200.computercraft.impl.RegistryHelper;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.data.DataGenerator; import net.minecraft.data.DataGenerator;
import net.minecraft.data.PackOutput; import net.minecraft.data.PackOutput;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
@@ -29,13 +30,13 @@ import java.util.function.Consumer;
* {@link #addUpgrades(Consumer)} function, construct each upgrade, and pass them off to the provided consumer to * {@link #addUpgrades(Consumer)} function, construct each upgrade, and pass them off to the provided consumer to
* generate them. * generate them.
* *
* @see TurtleUpgradeSerialiser * @see ITurtleUpgrade
*/ */
public abstract class TurtleUpgradeDataProvider extends UpgradeDataProvider<ITurtleUpgrade, TurtleUpgradeSerialiser<?>> { public abstract class TurtleUpgradeDataProvider extends UpgradeDataProvider<ITurtleUpgrade> {
private static final ResourceLocation TOOL_ID = new ResourceLocation(ComputerCraftAPI.MOD_ID, "tool"); private static final ResourceLocation TOOL_ID = new ResourceLocation(ComputerCraftAPI.MOD_ID, "tool");
public TurtleUpgradeDataProvider(PackOutput output) { public TurtleUpgradeDataProvider(PackOutput output) {
super(output, "Turtle Upgrades", "computercraft/turtle_upgrades", TurtleUpgradeSerialiser.registryId()); super(output, "Turtle Upgrades", "computercraft/turtle_upgrades", ITurtleUpgrade.serialiserRegistryKey());
} }
/** /**
@@ -57,7 +58,7 @@ public abstract class TurtleUpgradeDataProvider extends UpgradeDataProvider<ITur
*/ */
public static class ToolBuilder { public static class ToolBuilder {
private final ResourceLocation id; private final ResourceLocation id;
private final TurtleUpgradeSerialiser<?> serialiser; private final UpgradeSerialiser<? extends ITurtleUpgrade> serialiser;
private final Item toolItem; private final Item toolItem;
private @Nullable String adjective; private @Nullable String adjective;
private @Nullable Item craftingItem; private @Nullable Item craftingItem;
@@ -66,7 +67,7 @@ public abstract class TurtleUpgradeDataProvider extends UpgradeDataProvider<ITur
private boolean allowEnchantments = false; private boolean allowEnchantments = false;
private TurtleToolDurability consumeDurability = TurtleToolDurability.NEVER; private TurtleToolDurability consumeDurability = TurtleToolDurability.NEVER;
ToolBuilder(ResourceLocation id, TurtleUpgradeSerialiser<?> serialiser, Item toolItem) { ToolBuilder(ResourceLocation id, UpgradeSerialiser<? extends ITurtleUpgrade> serialiser, Item toolItem) {
this.id = id; this.id = id;
this.serialiser = serialiser; this.serialiser = serialiser;
this.toolItem = toolItem; this.toolItem = toolItem;
@@ -149,12 +150,12 @@ public abstract class TurtleUpgradeDataProvider extends UpgradeDataProvider<ITur
* *
* @param add The callback given to {@link #addUpgrades(Consumer)}. * @param add The callback given to {@link #addUpgrades(Consumer)}.
*/ */
public void add(Consumer<Upgrade<TurtleUpgradeSerialiser<?>>> add) { public void add(Consumer<Upgrade<UpgradeSerialiser<? extends ITurtleUpgrade>>> add) {
add.accept(new Upgrade<>(id, serialiser, s -> { add.accept(new Upgrade<>(id, serialiser, s -> {
s.addProperty("item", PlatformHelper.get().getRegistryKey(Registries.ITEM, toolItem).toString()); s.addProperty("item", RegistryHelper.getKeyOrThrow(BuiltInRegistries.ITEM, toolItem).toString());
if (adjective != null) s.addProperty("adjective", adjective); if (adjective != null) s.addProperty("adjective", adjective);
if (craftingItem != null) { if (craftingItem != null) {
s.addProperty("craftItem", PlatformHelper.get().getRegistryKey(Registries.ITEM, craftingItem).toString()); s.addProperty("craftItem", RegistryHelper.getKeyOrThrow(BuiltInRegistries.ITEM, craftingItem).toString());
} }
if (damageMultiplier != null) s.addProperty("damageMultiplier", damageMultiplier); if (damageMultiplier != null) s.addProperty("damageMultiplier", damageMultiplier);
if (breakable != null) s.addProperty("breakable", breakable.location().toString()); if (breakable != null) s.addProperty("breakable", breakable.location().toString());

View File

@@ -1,109 +0,0 @@
// SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
//
// SPDX-License-Identifier: MPL-2.0
package dan200.computercraft.api.turtle;
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;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.crafting.RecipeSerializer;
import net.minecraft.world.item.crafting.SimpleCraftingRecipeSerializer;
import java.util.function.BiFunction;
import java.util.function.Function;
/**
* Reads a {@link ITurtleUpgrade} from disk and reads/writes it to a network packet.
* <p>
* These should be registered in a {@link Registry} while the game is loading, much like {@link RecipeSerializer}s.
* <p>
* If your turtle upgrade doesn't have any associated configurable parameters (like most upgrades), you can use
* {@link #simple(Function)} or {@link #simpleWithCustomItem(BiFunction)} to create a basic upgrade serialiser.
*
* <h2>Example (Forge)</h2>
* <pre>{@code
* static final DeferredRegister<TurtleUpgradeSerialiser<?>> SERIALISERS = DeferredRegister.create( TurtleUpgradeSerialiser.TYPE, "my_mod" );
*
* // Register a new upgrade serialiser called "my_upgrade".
* public static final RegistryObject<TurtleUpgradeSerialiser<MyUpgrade>> MY_UPGRADE =
* SERIALISERS.register( "my_upgrade", () -> TurtleUpgradeSerialiser.simple( MyUpgrade::new ) );
*
* // Then in your constructor
* SERIALISERS.register( bus );
* }</pre>
* <p>
* We can then define a new upgrade using JSON by placing the following in
* {@literal data/<my_mod>/computercraft/turtle_upgrades/<my_upgrade_id>.json}}.
*
* <pre>{@code
* {
* "type": my_mod:my_upgrade",
* }
* }</pre>
* <p>
* Finally, we need to register a model for our upgrade. The way to do this varies on mod loader, see
* {@link dan200.computercraft.api.client.turtle.TurtleUpgradeModeller} for more information.
* <p>
* {@link TurtleUpgradeDataProvider} provides a data provider to aid with generating these JSON files.
*
* @param <T> The type of turtle upgrade this is responsible for serialising.
* @see ITurtleUpgrade
* @see TurtleUpgradeDataProvider
* @see dan200.computercraft.api.client.turtle.TurtleUpgradeModeller
*/
public interface TurtleUpgradeSerialiser<T extends ITurtleUpgrade> extends UpgradeSerialiser<T> {
/**
* 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.
* <p>
* If you might want to vary the item, it's suggested you use {@link #simpleWithCustomItem(BiFunction)} instead.
*
* @param factory Generate a new upgrade with a specific ID.
* @param <T> The type of the generated upgrade.
* @return The serialiser for this upgrade
*/
static <T extends ITurtleUpgrade> TurtleUpgradeSerialiser<T> simple(Function<ResourceLocation, T> factory) {
final class Impl extends SimpleSerialiser<T> implements TurtleUpgradeSerialiser<T> {
private Impl(Function<ResourceLocation, T> constructor) {
super(constructor);
}
}
return new Impl(factory);
}
/**
* Create an upgrade serialiser for a simple upgrade whose crafting item can be specified.
*
* @param factory Generate a new upgrade with a specific ID and crafting item. The returned upgrade's
* {@link UpgradeBase#getCraftingItem()} <strong>MUST</strong> equal the provided item.
* @param <T> The type of the generated upgrade.
* @return The serialiser for this upgrade.
* @see #simple(Function) For upgrades whose crafting stack should not vary.
*/
static <T extends ITurtleUpgrade> TurtleUpgradeSerialiser<T> simpleWithCustomItem(BiFunction<ResourceLocation, ItemStack, T> factory) {
final class Impl extends SerialiserWithCraftingItem<T> implements TurtleUpgradeSerialiser<T> {
private Impl(BiFunction<ResourceLocation, ItemStack, T> factory) {
super(factory);
}
}
return new Impl(factory);
}
}

View File

@@ -9,7 +9,6 @@ import dan200.computercraft.api.pocket.IPocketUpgrade;
import dan200.computercraft.api.turtle.ITurtleAccess; import dan200.computercraft.api.turtle.ITurtleAccess;
import dan200.computercraft.api.turtle.ITurtleUpgrade; import dan200.computercraft.api.turtle.ITurtleUpgrade;
import dan200.computercraft.api.turtle.TurtleSide; import dan200.computercraft.api.turtle.TurtleSide;
import dan200.computercraft.impl.PlatformHelper;
import net.minecraft.Util; import net.minecraft.Util;
import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.CompoundTag;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
@@ -100,7 +99,7 @@ public interface UpgradeBase {
* The default check requires that any non-capability NBT is exactly the same as the * The default check requires that any non-capability NBT is exactly the same as the
* crafting item, but this may be relaxed for your upgrade. * crafting item, but this may be relaxed for your upgrade.
* <p> * <p>
* This is based on {@code net.minecraftforge.common.crafting.StrictNBTIngredient}'s check. * This is based on {@code net.neoforged.common.crafting.StrictNBTIngredient}'s check.
* *
* @param stack The stack to check. This is guaranteed to be non-empty and have the same item as * @param stack The stack to check. This is guaranteed to be non-empty and have the same item as
* {@link #getCraftingItem()}. * {@link #getCraftingItem()}.
@@ -111,12 +110,12 @@ public interface UpgradeBase {
// A more expanded form of ItemStack.areShareTagsEqual, but allowing an empty tag to be equal to a // A more expanded form of ItemStack.areShareTagsEqual, but allowing an empty tag to be equal to a
// null one. // null one.
var shareTag = PlatformHelper.get().getShareTag(stack); var tag = stack.getTag();
var craftingShareTag = PlatformHelper.get().getShareTag(crafting); var craftingTag = crafting.getTag();
if (shareTag == craftingShareTag) return true; if (tag == craftingTag) return true;
if (shareTag == null) return Objects.requireNonNull(craftingShareTag).isEmpty(); if (tag == null) return Objects.requireNonNull(craftingTag).isEmpty();
if (craftingShareTag == null) return shareTag.isEmpty(); if (craftingTag == null) return tag.isEmpty();
return shareTag.equals(craftingShareTag); return tag.equals(craftingTag);
} }
/** /**

View File

@@ -6,19 +6,20 @@ package dan200.computercraft.api.upgrades;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
import com.google.gson.JsonParseException; import com.google.gson.JsonParseException;
import dan200.computercraft.api.turtle.TurtleUpgradeSerialiser;
import dan200.computercraft.impl.PlatformHelper; import dan200.computercraft.impl.PlatformHelper;
import dan200.computercraft.impl.RegistryHelper;
import dan200.computercraft.impl.upgrades.SerialiserWithCraftingItem; import dan200.computercraft.impl.upgrades.SerialiserWithCraftingItem;
import dan200.computercraft.impl.upgrades.SimpleSerialiser; import dan200.computercraft.impl.upgrades.SimpleSerialiser;
import net.minecraft.Util; import net.minecraft.Util;
import net.minecraft.core.Registry; import net.minecraft.core.Registry;
import net.minecraft.core.registries.Registries; import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.data.CachedOutput; import net.minecraft.data.CachedOutput;
import net.minecraft.data.DataProvider; import net.minecraft.data.DataProvider;
import net.minecraft.data.PackOutput; import net.minecraft.data.PackOutput;
import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.Item; import net.minecraft.world.item.Item;
import org.jetbrains.annotations.ApiStatus;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import java.util.*; import java.util.*;
@@ -31,31 +32,31 @@ import java.util.function.Function;
* the other subclasses. * the other subclasses.
* *
* @param <T> The base class of upgrades. * @param <T> The base class of upgrades.
* @param <R> The upgrade serialiser to register for.
*/ */
public abstract class UpgradeDataProvider<T extends UpgradeBase, R extends UpgradeSerialiser<? extends T>> implements DataProvider { public abstract class UpgradeDataProvider<T extends UpgradeBase> implements DataProvider {
private final PackOutput output; private final PackOutput output;
private final String name; private final String name;
private final String folder; private final String folder;
private final ResourceKey<Registry<R>> registry; private final Registry<UpgradeSerialiser<? extends T>> registry;
private @Nullable List<T> upgrades; private @Nullable List<T> upgrades;
protected UpgradeDataProvider(PackOutput output, String name, String folder, ResourceKey<Registry<R>> registry) { @ApiStatus.Internal
protected UpgradeDataProvider(PackOutput output, String name, String folder, ResourceKey<Registry<UpgradeSerialiser<? extends T>>> registry) {
this.output = output; this.output = output;
this.name = name; this.name = name;
this.folder = folder; this.folder = folder;
this.registry = registry; this.registry = RegistryHelper.getRegistry(registry);
} }
/** /**
* Register an upgrade using a "simple" serialiser (e.g. {@link TurtleUpgradeSerialiser#simple(Function)}). * Register an upgrade using a {@linkplain UpgradeSerialiser#simple(Function) "simple" serialiser}.
* *
* @param id The ID of the upgrade to create. * @param id The ID of the upgrade to create.
* @param serialiser The simple serialiser. * @param serialiser The simple serialiser.
* @return The constructed upgrade, ready to be passed off to {@link #addUpgrades(Consumer)}'s consumer. * @return The constructed upgrade, ready to be passed off to {@link #addUpgrades(Consumer)}'s consumer.
*/ */
public final Upgrade<R> simple(ResourceLocation id, R serialiser) { public final Upgrade<UpgradeSerialiser<? extends T>> simple(ResourceLocation id, UpgradeSerialiser<? extends T> serialiser) {
if (!(serialiser instanceof SimpleSerialiser)) { if (!(serialiser instanceof SimpleSerialiser)) {
throw new IllegalStateException(serialiser + " must be a simple() seriaiser."); throw new IllegalStateException(serialiser + " must be a simple() seriaiser.");
} }
@@ -65,20 +66,20 @@ public abstract class UpgradeDataProvider<T extends UpgradeBase, R extends Upgra
} }
/** /**
* Register an upgrade using a "simple" serialiser (e.g. {@link TurtleUpgradeSerialiser#simple(Function)}). * Register an upgrade using a {@linkplain UpgradeSerialiser#simple(Function) simple serialiser}.
* *
* @param id The ID of the upgrade to create. * @param id The ID of the upgrade to create.
* @param serialiser The simple serialiser. * @param serialiser The simple serialiser.
* @param item The crafting upgrade for this item. * @param item The crafting upgrade for this item.
* @return The constructed upgrade, ready to be passed off to {@link #addUpgrades(Consumer)}'s consumer. * @return The constructed upgrade, ready to be passed off to {@link #addUpgrades(Consumer)}'s consumer.
*/ */
public final Upgrade<R> simpleWithCustomItem(ResourceLocation id, R serialiser, Item item) { public final Upgrade<UpgradeSerialiser<? extends T>> simpleWithCustomItem(ResourceLocation id, UpgradeSerialiser<? extends T> serialiser, Item item) {
if (!(serialiser instanceof SerialiserWithCraftingItem)) { if (!(serialiser instanceof SerialiserWithCraftingItem)) {
throw new IllegalStateException(serialiser + " must be a simpleWithCustomItem() serialiser."); throw new IllegalStateException(serialiser + " must be a simpleWithCustomItem() serialiser.");
} }
return new Upgrade<>(id, serialiser, s -> return new Upgrade<>(id, serialiser, s ->
s.addProperty("item", PlatformHelper.get().getRegistryKey(Registries.ITEM, item).toString()) s.addProperty("item", RegistryHelper.getKeyOrThrow(BuiltInRegistries.ITEM, item).toString())
); );
} }
@@ -94,7 +95,7 @@ public abstract class UpgradeDataProvider<T extends UpgradeBase, R extends Upgra
* *
* @param addUpgrade A callback used to register an upgrade. * @param addUpgrade A callback used to register an upgrade.
*/ */
protected abstract void addUpgrades(Consumer<Upgrade<R>> addUpgrade); protected abstract void addUpgrades(Consumer<Upgrade<UpgradeSerialiser<? extends T>>> addUpgrade);
@Override @Override
public CompletableFuture<?> run(CachedOutput cache) { public CompletableFuture<?> run(CachedOutput cache) {
@@ -107,7 +108,7 @@ public abstract class UpgradeDataProvider<T extends UpgradeBase, R extends Upgra
if (!seen.add(upgrade.id())) throw new IllegalStateException("Duplicate upgrade " + upgrade.id()); if (!seen.add(upgrade.id())) throw new IllegalStateException("Duplicate upgrade " + upgrade.id());
var json = new JsonObject(); var json = new JsonObject();
json.addProperty("type", PlatformHelper.get().getRegistryKey(registry, upgrade.serialiser()).toString()); json.addProperty("type", RegistryHelper.getKeyOrThrow(registry, upgrade.serialiser()).toString());
upgrade.serialise().accept(json); upgrade.serialise().accept(json);
futures.add(DataProvider.saveStable(cache, json, base.resolve(upgrade.id().getNamespace() + "/" + folder + "/" + upgrade.id().getPath() + ".json"))); futures.add(DataProvider.saveStable(cache, json, base.resolve(upgrade.id().getNamespace() + "/" + folder + "/" + upgrade.id().getPath() + ".json")));
@@ -129,9 +130,9 @@ public abstract class UpgradeDataProvider<T extends UpgradeBase, R extends Upgra
return name; return name;
} }
public final R existingSerialiser(ResourceLocation id) { public final UpgradeSerialiser<? extends T> existingSerialiser(ResourceLocation id) {
var result = PlatformHelper.get().getRegistryObject(registry, id); var result = registry.get(id);
if (result == null) throw new IllegalArgumentException("No such serialiser " + registry); if (result == null) throw new IllegalArgumentException("No such serialiser " + id);
return result; return result;
} }

View File

@@ -5,21 +5,40 @@
package dan200.computercraft.api.upgrades; package dan200.computercraft.api.upgrades;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
import dan200.computercraft.api.pocket.PocketUpgradeSerialiser; import dan200.computercraft.api.pocket.IPocketUpgrade;
import dan200.computercraft.api.turtle.TurtleUpgradeSerialiser; import dan200.computercraft.api.pocket.PocketUpgradeDataProvider;
import dan200.computercraft.api.turtle.ITurtleUpgrade;
import dan200.computercraft.api.turtle.TurtleUpgradeDataProvider;
import dan200.computercraft.impl.upgrades.SerialiserWithCraftingItem;
import dan200.computercraft.impl.upgrades.SimpleSerialiser;
import net.minecraft.core.Registry;
import net.minecraft.network.FriendlyByteBuf; import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.crafting.RecipeSerializer;
import net.minecraft.world.item.crafting.SimpleCraftingRecipeSerializer;
import java.util.function.BiFunction;
import java.util.function.Function;
/** /**
* Base interface for upgrade serialisers. This should generally not be implemented directly, instead implementing one * A serialiser for {@link ITurtleUpgrade} or {@link IPocketUpgrade}s.
* of {@link TurtleUpgradeSerialiser} or {@link PocketUpgradeSerialiser}.
* <p> * <p>
* However, it may sometimes be useful to implement this if you have some shared logic between upgrade types. * These should be registered in a {@link Registry} while the game is loading, much like {@link RecipeSerializer}s.
* <p>
* This interface is very similar to {@link RecipeSerializer}; each serialiser should correspond to a specific upgrade
* class. Upgrades are then read from JSON files in datapacks, allowing multiple instances of the upgrade to be
* registered.
* <p>
* If your upgrade doesn't have any associated configurable parameters (like most upgrades), you can use
* {@link #simple(Function)} or {@link #simpleWithCustomItem(BiFunction)} to create a basic upgrade serialiser.
* <p>
* Upgrades may be data generated via a {@link UpgradeDataProvider} (see {@link TurtleUpgradeDataProvider} and
* {@link PocketUpgradeDataProvider}).
* *
* @param <T> The upgrade that this class can serialise and deserialise. * @param <T> The upgrade that this class can serialise and deserialise.
* @see TurtleUpgradeSerialiser * @see ITurtleUpgrade
* @see PocketUpgradeSerialiser * @see IPocketUpgrade
*/ */
public interface UpgradeSerialiser<T extends UpgradeBase> { public interface UpgradeSerialiser<T extends UpgradeBase> {
/** /**
@@ -49,4 +68,30 @@ public interface UpgradeSerialiser<T extends UpgradeBase> {
*/ */
void toNetwork(FriendlyByteBuf buffer, T upgrade); void toNetwork(FriendlyByteBuf buffer, T upgrade);
/**
* Create an upgrade serialiser for a simple upgrade. This is similar to a {@link SimpleCraftingRecipeSerializer},
* but for upgrades.
* <p>
* If you might want to vary the item, it's suggested you use {@link #simpleWithCustomItem(BiFunction)} instead.
*
* @param factory Generate a new upgrade with a specific ID.
* @param <T> The type of the generated upgrade.
* @return The serialiser for this upgrade
*/
static <T extends UpgradeBase> UpgradeSerialiser<T> simple(Function<ResourceLocation, T> factory) {
return new SimpleSerialiser<>(factory);
}
/**
* Create an upgrade serialiser for a simple upgrade whose crafting item can be specified.
*
* @param factory Generate a new upgrade with a specific ID and crafting item. The returned upgrade's
* {@link UpgradeBase#getCraftingItem()} <strong>MUST</strong> equal the provided item.
* @param <T> The type of the generated upgrade.
* @return The serialiser for this upgrade.
* @see #simple(Function) For upgrades whose crafting stack should not vary.
*/
static <T extends UpgradeBase> UpgradeSerialiser<T> simpleWithCustomItem(BiFunction<ResourceLocation, ItemStack, T> factory) {
return new SerialiserWithCraftingItem<>(factory);
}
} }

View File

@@ -15,10 +15,11 @@ import dan200.computercraft.api.media.MediaProvider;
import dan200.computercraft.api.network.PacketNetwork; import dan200.computercraft.api.network.PacketNetwork;
import dan200.computercraft.api.network.wired.WiredElement; import dan200.computercraft.api.network.wired.WiredElement;
import dan200.computercraft.api.network.wired.WiredNode; import dan200.computercraft.api.network.wired.WiredNode;
import dan200.computercraft.api.pocket.PocketUpgradeSerialiser; import dan200.computercraft.api.pocket.IPocketUpgrade;
import dan200.computercraft.api.redstone.BundledRedstoneProvider; import dan200.computercraft.api.redstone.BundledRedstoneProvider;
import dan200.computercraft.api.turtle.ITurtleUpgrade;
import dan200.computercraft.api.turtle.TurtleRefuelHandler; import dan200.computercraft.api.turtle.TurtleRefuelHandler;
import dan200.computercraft.api.turtle.TurtleUpgradeSerialiser; import dan200.computercraft.api.upgrades.UpgradeSerialiser;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction; import net.minecraft.core.Direction;
import net.minecraft.core.Registry; import net.minecraft.core.Registry;
@@ -67,9 +68,9 @@ public interface ComputerCraftAPIService {
void registerRefuelHandler(TurtleRefuelHandler handler); void registerRefuelHandler(TurtleRefuelHandler handler);
ResourceKey<Registry<TurtleUpgradeSerialiser<?>>> turtleUpgradeRegistryId(); ResourceKey<Registry<UpgradeSerialiser<? extends ITurtleUpgrade>>> turtleUpgradeRegistryId();
ResourceKey<Registry<PocketUpgradeSerialiser<?>>> pocketUpgradeRegistryId(); ResourceKey<Registry<UpgradeSerialiser<? extends IPocketUpgrade>>> pocketUpgradeRegistryId();
DetailRegistry<ItemStack> getItemStackDetailRegistry(); DetailRegistry<ItemStack> getItemStackDetailRegistry();

View File

@@ -6,11 +6,6 @@ package dan200.computercraft.impl;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
import dan200.computercraft.api.upgrades.UpgradeDataProvider; import dan200.computercraft.api.upgrades.UpgradeDataProvider;
import net.minecraft.core.Registry;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.ItemStack;
import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.ApiStatus;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@@ -32,39 +27,6 @@ public interface PlatformHelper {
return instance == null ? Services.raise(PlatformHelper.class, Instance.ERROR) : instance; return instance == null ? Services.raise(PlatformHelper.class, Instance.ERROR) : instance;
} }
/**
* Get the unique ID for a registered object.
*
* @param registry The registry to look up this object in.
* @param object The object to look up.
* @param <T> The type of object the registry stores.
* @return The registered object's ID.
* @throws IllegalArgumentException If the registry or object are not registered.
*/
<T> ResourceLocation getRegistryKey(ResourceKey<Registry<T>> registry, T object);
/**
* Look up an ID in a registry, returning the registered object.
*
* @param registry The registry to look up this object in.
* @param id The ID to look up.
* @param <T> The type of object the registry stores.
* @return The resolved registry object.
* @throws IllegalArgumentException If the registry or object are not registered.
*/
<T> T getRegistryObject(ResourceKey<Registry<T>> registry, ResourceLocation id);
/**
* Get the subset of an {@link ItemStack}'s {@linkplain ItemStack#getTag() tag} which is synced to the client.
*
* @param item The stack.
* @return The item's tag.
*/
@Nullable
default CompoundTag getShareTag(ItemStack item) {
return item.getTag();
}
/** /**
* Add a resource condition which requires a mod to be loaded. This should be used by data providers such as * Add a resource condition which requires a mod to be loaded. This should be used by data providers such as
* {@link UpgradeDataProvider}. * {@link UpgradeDataProvider}.

View File

@@ -0,0 +1,49 @@
// SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers
//
// SPDX-License-Identifier: MPL-2.0
package dan200.computercraft.impl;
import net.minecraft.core.Registry;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import org.jetbrains.annotations.ApiStatus;
/**
* Additioanl functions for working with {@linkplain Registry registries}.
*/
@ApiStatus.Internal
public final class RegistryHelper {
private RegistryHelper() {
}
/**
* Find a registry from a {@link ResourceKey}, throwing if it does not exist.
*
* @param id The id of the registry.
* @param <T> The contents of the registry
* @return The associated registry.
*/
@SuppressWarnings("unchecked")
public static <T> Registry<T> getRegistry(ResourceKey<Registry<T>> id) {
var registry = (Registry<T>) BuiltInRegistries.REGISTRY.get(id.location());
if (registry == null) throw new IllegalArgumentException("Unknown registry " + id);
return registry;
}
/**
* Get the key of a registry entry, throwing if it is not registered.
*
* @param registry The registry to look up in.
* @param object The object to look up.
* @param <T> The type of this registry.
* @return The ID of this object
* @see Registry#getResourceKey(Object)
*/
public static <T> ResourceLocation getKeyOrThrow(Registry<T> registry, T object) {
var key = registry.getResourceKey(object);
if (key.isEmpty()) throw new IllegalArgumentException(object + " was not registered in " + registry.key());
return key.get().location();
}
}

View File

@@ -23,27 +23,27 @@ import java.util.function.BiFunction;
* @param <T> The upgrade that this class can serialise and deserialise. * @param <T> The upgrade that this class can serialise and deserialise.
*/ */
@ApiStatus.Internal @ApiStatus.Internal
public abstract class SerialiserWithCraftingItem<T extends UpgradeBase> implements UpgradeSerialiser<T> { public final class SerialiserWithCraftingItem<T extends UpgradeBase> implements UpgradeSerialiser<T> {
private final BiFunction<ResourceLocation, ItemStack, T> factory; private final BiFunction<ResourceLocation, ItemStack, T> factory;
protected SerialiserWithCraftingItem(BiFunction<ResourceLocation, ItemStack, T> factory) { public SerialiserWithCraftingItem(BiFunction<ResourceLocation, ItemStack, T> factory) {
this.factory = factory; this.factory = factory;
} }
@Override @Override
public final T fromJson(ResourceLocation id, JsonObject object) { public T fromJson(ResourceLocation id, JsonObject object) {
var item = GsonHelper.getAsItem(object, "item"); var item = GsonHelper.getAsItem(object, "item");
return factory.apply(id, new ItemStack(item)); return factory.apply(id, new ItemStack(item));
} }
@Override @Override
public final T fromNetwork(ResourceLocation id, FriendlyByteBuf buffer) { public T fromNetwork(ResourceLocation id, FriendlyByteBuf buffer) {
var item = buffer.readItem(); var item = buffer.readItem();
return factory.apply(id, item); return factory.apply(id, item);
} }
@Override @Override
public final void toNetwork(FriendlyByteBuf buffer, T upgrade) { public void toNetwork(FriendlyByteBuf buffer, T upgrade) {
buffer.writeItem(upgrade.getCraftingItem()); buffer.writeItem(upgrade.getCraftingItem());
} }
} }

View File

@@ -21,7 +21,7 @@ import java.util.function.Function;
* @param <T> The upgrade that this class can serialise and deserialise. * @param <T> The upgrade that this class can serialise and deserialise.
*/ */
@ApiStatus.Internal @ApiStatus.Internal
public abstract class SimpleSerialiser<T extends UpgradeBase> implements UpgradeSerialiser<T> { public final class SimpleSerialiser<T extends UpgradeBase> implements UpgradeSerialiser<T> {
private final Function<ResourceLocation, T> constructor; private final Function<ResourceLocation, T> constructor;
public SimpleSerialiser(Function<ResourceLocation, T> constructor) { public SimpleSerialiser(Function<ResourceLocation, T> constructor) {
@@ -29,16 +29,16 @@ public abstract class SimpleSerialiser<T extends UpgradeBase> implements Upgrade
} }
@Override @Override
public final T fromJson(ResourceLocation id, JsonObject object) { public T fromJson(ResourceLocation id, JsonObject object) {
return constructor.apply(id); return constructor.apply(id);
} }
@Override @Override
public final T fromNetwork(ResourceLocation id, FriendlyByteBuf buffer) { public T fromNetwork(ResourceLocation id, FriendlyByteBuf buffer) {
return constructor.apply(id); return constructor.apply(id);
} }
@Override @Override
public final void toNetwork(FriendlyByteBuf buffer, T upgrade) { public void toNetwork(FriendlyByteBuf buffer, T upgrade) {
} }
} }

View File

@@ -11,12 +11,6 @@ plugins {
id("cc-tweaked.publishing") id("cc-tweaked.publishing")
} }
sourceSets {
main {
resources.srcDir("src/generated/resources")
}
}
minecraft { minecraft {
accessWideners( accessWideners(
"src/main/resources/computercraft.accesswidener", "src/main/resources/computercraft.accesswidener",
@@ -38,12 +32,12 @@ repositories {
dependencies { dependencies {
// Pull in our other projects. See comments in MinecraftConfigurations on this nastiness. // Pull in our other projects. See comments in MinecraftConfigurations on this nastiness.
api(project(":core")) implementation(project(":core"))
api(commonClasses(project(":common-api"))) implementation(commonClasses(project(":common-api")))
clientApi(clientClasses(project(":common-api"))) clientImplementation(clientClasses(project(":common-api")))
compileOnly(libs.mixin)
compileOnly(libs.bundles.externalMods.common) compileOnly(libs.bundles.externalMods.common)
compileOnly(variantOf(libs.create.forge) { classifier("slim") }) { isTransitive = false }
clientCompileOnly(variantOf(libs.emi) { classifier("api") }) clientCompileOnly(variantOf(libs.emi) { classifier("api") })
annotationProcessorEverywhere(libs.autoService) annotationProcessorEverywhere(libs.autoService)
@@ -112,21 +106,3 @@ val lintLua by tasks.registering(IlluaminateExec::class) {
doFirst { if (System.getenv("GITHUB_ACTIONS") != null) println("::add-matcher::.github/matchers/illuaminate.json") } doFirst { if (System.getenv("GITHUB_ACTIONS") != null) println("::add-matcher::.github/matchers/illuaminate.json") }
doLast { if (System.getenv("GITHUB_ACTIONS") != null) println("::remove-matcher owner=illuaminate::") } doLast { if (System.getenv("GITHUB_ACTIONS") != null) println("::remove-matcher owner=illuaminate::") }
} }
val runData by tasks.registering(MergeTrees::class) {
output = layout.projectDirectory.dir("src/generated/resources")
for (loader in listOf("forge", "fabric")) {
mustRunAfter(":$loader:runData")
source {
input {
from(project(":$loader").layout.buildDirectory.dir("generatedResources"))
exclude(".cache")
}
output = project(":$loader").layout.projectDirectory.dir("src/generated/resources")
}
}
}
tasks.withType(GenerateModuleMetadata::class).configureEach { isEnabled = false }

View File

@@ -108,7 +108,7 @@ public final class ClientHooks {
*/ */
public static void addBlockDebugInfo(Consumer<String> addText) { public static void addBlockDebugInfo(Consumer<String> addText) {
var minecraft = Minecraft.getInstance(); var minecraft = Minecraft.getInstance();
if (!minecraft.options.renderDebug || minecraft.level == null) return; if (!minecraft.getDebugOverlay().showDebugScreen() || minecraft.level == null) return;
if (minecraft.hitResult == null || minecraft.hitResult.getType() != HitResult.Type.BLOCK) return; if (minecraft.hitResult == null || minecraft.hitResult.getType() != HitResult.Type.BLOCK) return;
var tile = minecraft.level.getBlockEntity(((BlockHitResult) minecraft.hitResult).getBlockPos()); var tile = minecraft.level.getBlockEntity(((BlockHitResult) minecraft.hitResult).getBlockPos());
@@ -138,7 +138,7 @@ public final class ClientHooks {
* @param addText A callback which adds a single line of text. * @param addText A callback which adds a single line of text.
*/ */
public static void addGameDebugInfo(Consumer<String> addText) { public static void addGameDebugInfo(Consumer<String> addText) {
if (MonitorBlockEntityRenderer.hasRenderedThisFrame() && Minecraft.getInstance().options.renderDebug) { if (MonitorBlockEntityRenderer.hasRenderedThisFrame() && Minecraft.getInstance().getDebugOverlay().showDebugScreen()) {
addText.accept("[CC:T] Monitor renderer: " + MonitorBlockEntityRenderer.currentRenderer()); addText.accept("[CC:T] Monitor renderer: " + MonitorBlockEntityRenderer.currentRenderer());
} }
} }

View File

@@ -13,7 +13,6 @@ import dan200.computercraft.api.client.turtle.RegisterTurtleUpgradeModeller;
import dan200.computercraft.api.client.turtle.TurtleUpgradeModeller; import dan200.computercraft.api.client.turtle.TurtleUpgradeModeller;
import dan200.computercraft.client.gui.*; import dan200.computercraft.client.gui.*;
import dan200.computercraft.client.pocket.ClientPocketComputers; import dan200.computercraft.client.pocket.ClientPocketComputers;
import dan200.computercraft.client.render.CustomLecternRenderer;
import dan200.computercraft.client.render.RenderTypes; import dan200.computercraft.client.render.RenderTypes;
import dan200.computercraft.client.render.TurtleBlockEntityRenderer; import dan200.computercraft.client.render.TurtleBlockEntityRenderer;
import dan200.computercraft.client.render.monitor.MonitorBlockEntityRenderer; import dan200.computercraft.client.render.monitor.MonitorBlockEntityRenderer;
@@ -26,12 +25,15 @@ import dan200.computercraft.shared.common.IColouredItem;
import dan200.computercraft.shared.computer.core.ComputerState; import dan200.computercraft.shared.computer.core.ComputerState;
import dan200.computercraft.shared.computer.core.ServerContext; import dan200.computercraft.shared.computer.core.ServerContext;
import dan200.computercraft.shared.computer.inventory.AbstractComputerMenu; import dan200.computercraft.shared.computer.inventory.AbstractComputerMenu;
import dan200.computercraft.shared.computer.inventory.ViewComputerMenu;
import dan200.computercraft.shared.media.items.DiskItem; import dan200.computercraft.shared.media.items.DiskItem;
import dan200.computercraft.shared.media.items.TreasureDiskItem; import dan200.computercraft.shared.media.items.TreasureDiskItem;
import net.minecraft.Util; import net.minecraft.Util;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.client.color.item.ItemColor; import net.minecraft.client.color.item.ItemColor;
import net.minecraft.client.gui.screens.MenuScreens; import net.minecraft.client.gui.screens.MenuScreens;
import net.minecraft.client.gui.screens.Screen;
import net.minecraft.client.gui.screens.inventory.MenuAccess;
import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.client.renderer.ShaderInstance; import net.minecraft.client.renderer.ShaderInstance;
import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider; import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider;
@@ -43,6 +45,8 @@ import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.packs.resources.PreparableReloadListener; import net.minecraft.server.packs.resources.PreparableReloadListener;
import net.minecraft.server.packs.resources.ResourceProvider; import net.minecraft.server.packs.resources.ResourceProvider;
import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.inventory.AbstractContainerMenu;
import net.minecraft.world.inventory.MenuType;
import net.minecraft.world.item.Item; import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.ItemLike; import net.minecraft.world.level.ItemLike;
@@ -74,7 +78,6 @@ public final class ClientRegistry {
BlockEntityRenderers.register(ModRegistry.BlockEntities.MONITOR_ADVANCED.get(), MonitorBlockEntityRenderer::new); BlockEntityRenderers.register(ModRegistry.BlockEntities.MONITOR_ADVANCED.get(), MonitorBlockEntityRenderer::new);
BlockEntityRenderers.register(ModRegistry.BlockEntities.TURTLE_NORMAL.get(), TurtleBlockEntityRenderer::new); BlockEntityRenderers.register(ModRegistry.BlockEntities.TURTLE_NORMAL.get(), TurtleBlockEntityRenderer::new);
BlockEntityRenderers.register(ModRegistry.BlockEntities.TURTLE_ADVANCED.get(), TurtleBlockEntityRenderer::new); BlockEntityRenderers.register(ModRegistry.BlockEntities.TURTLE_ADVANCED.get(), TurtleBlockEntityRenderer::new);
BlockEntityRenderers.register(ModRegistry.BlockEntities.LECTERN.get(), CustomLecternRenderer::new);
} }
/** /**
@@ -83,14 +86,6 @@ public final class ClientRegistry {
* @param itemProperties Callback to register item properties. * @param itemProperties Callback to register item properties.
*/ */
public static void registerMainThread(RegisterItemProperty itemProperties) { public static void registerMainThread(RegisterItemProperty itemProperties) {
MenuScreens.<AbstractComputerMenu, ComputerScreen<AbstractComputerMenu>>register(ModRegistry.Menus.COMPUTER.get(), ComputerScreen::new);
MenuScreens.<AbstractComputerMenu, NoTermComputerScreen<AbstractComputerMenu>>register(ModRegistry.Menus.POCKET_COMPUTER_NO_TERM.get(), NoTermComputerScreen::new);
MenuScreens.register(ModRegistry.Menus.TURTLE.get(), TurtleScreen::new);
MenuScreens.register(ModRegistry.Menus.PRINTER.get(), PrinterScreen::new);
MenuScreens.register(ModRegistry.Menus.DISK_DRIVE.get(), DiskDriveScreen::new);
MenuScreens.register(ModRegistry.Menus.PRINTOUT.get(), PrintoutScreen::new);
registerItemProperty(itemProperties, "state", registerItemProperty(itemProperties, "state",
new UnclampedPropertyFunction((stack, world, player, random) -> { new UnclampedPropertyFunction((stack, world, player, random) -> {
var computer = ClientPocketComputers.get(stack); var computer = ClientPocketComputers.get(stack);
@@ -104,6 +99,23 @@ public final class ClientRegistry {
); );
} }
public static void registerMenuScreens(RegisterMenuScreen register) {
register.<AbstractComputerMenu, ComputerScreen<AbstractComputerMenu>>register(ModRegistry.Menus.COMPUTER.get(), ComputerScreen::new);
register.<AbstractComputerMenu, ComputerScreen<AbstractComputerMenu>>register(ModRegistry.Menus.POCKET_COMPUTER.get(), ComputerScreen::new);
register.<AbstractComputerMenu, NoTermComputerScreen<AbstractComputerMenu>>register(ModRegistry.Menus.POCKET_COMPUTER_NO_TERM.get(), NoTermComputerScreen::new);
register.register(ModRegistry.Menus.TURTLE.get(), TurtleScreen::new);
register.register(ModRegistry.Menus.PRINTER.get(), PrinterScreen::new);
register.register(ModRegistry.Menus.DISK_DRIVE.get(), DiskDriveScreen::new);
register.register(ModRegistry.Menus.PRINTOUT.get(), PrintoutScreen::new);
register.<ViewComputerMenu, ComputerScreen<ViewComputerMenu>>register(ModRegistry.Menus.VIEW_COMPUTER.get(), ComputerScreen::new);
}
public interface RegisterMenuScreen {
<M extends AbstractContainerMenu, U extends Screen & MenuAccess<M>> void register(MenuType<? extends M> type, MenuScreens.ScreenConstructor<M, U> factory);
}
public static void registerTurtleModellers(RegisterTurtleUpgradeModeller register) { public static void registerTurtleModellers(RegisterTurtleUpgradeModeller register) {
register.register(ModRegistry.TurtleSerialisers.SPEAKER.get(), TurtleUpgradeModeller.sided( register.register(ModRegistry.TurtleSerialisers.SPEAKER.get(), TurtleUpgradeModeller.sided(
new ResourceLocation(ComputerCraftAPI.MOD_ID, "block/turtle_speaker_left"), new ResourceLocation(ComputerCraftAPI.MOD_ID, "block/turtle_speaker_left"),

View File

@@ -9,7 +9,6 @@ import dan200.computercraft.client.gui.widgets.DynamicImageButton;
import dan200.computercraft.client.gui.widgets.TerminalWidget; import dan200.computercraft.client.gui.widgets.TerminalWidget;
import dan200.computercraft.client.network.ClientNetworking; import dan200.computercraft.client.network.ClientNetworking;
import dan200.computercraft.core.terminal.Terminal; import dan200.computercraft.core.terminal.Terminal;
import dan200.computercraft.core.util.Nullability;
import dan200.computercraft.shared.computer.core.ComputerFamily; import dan200.computercraft.shared.computer.core.ComputerFamily;
import dan200.computercraft.shared.computer.core.InputHandler; import dan200.computercraft.shared.computer.core.InputHandler;
import dan200.computercraft.shared.computer.inventory.AbstractComputerMenu; import dan200.computercraft.shared.computer.inventory.AbstractComputerMenu;
@@ -19,7 +18,6 @@ import dan200.computercraft.shared.config.Config;
import dan200.computercraft.shared.network.server.UploadFileMessage; import dan200.computercraft.shared.network.server.UploadFileMessage;
import net.minecraft.ChatFormatting; import net.minecraft.ChatFormatting;
import net.minecraft.Util; import net.minecraft.Util;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiGraphics; import net.minecraft.client.gui.GuiGraphics;
import net.minecraft.client.gui.components.events.GuiEventListener; import net.minecraft.client.gui.components.events.GuiEventListener;
import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen; import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen;
@@ -98,8 +96,8 @@ public abstract class AbstractComputerScreen<T extends AbstractComputerMenu> ext
getTerminal().update(); getTerminal().update();
if (uploadNagDeadline != Long.MAX_VALUE && Util.getNanos() >= uploadNagDeadline) { if (uploadNagDeadline != Long.MAX_VALUE && Util.getNanos() >= uploadNagDeadline) {
new ItemToast(minecraft(), displayStack, NO_RESPONSE_TITLE, NO_RESPONSE_MSG, ItemToast.TRANSFER_NO_RESPONSE_TOKEN) new ItemToast(minecraft, displayStack, NO_RESPONSE_TITLE, NO_RESPONSE_MSG, ItemToast.TRANSFER_NO_RESPONSE_TOKEN)
.showOrReplace(minecraft().getToasts()); .showOrReplace(minecraft.getToasts());
uploadNagDeadline = Long.MAX_VALUE; uploadNagDeadline = Long.MAX_VALUE;
} }
} }
@@ -127,7 +125,6 @@ public abstract class AbstractComputerScreen<T extends AbstractComputerMenu> ext
@Override @Override
public void render(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) { public void render(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) {
renderBackground(graphics);
super.render(graphics, mouseX, mouseY, partialTicks); super.render(graphics, mouseX, mouseY, partialTicks);
renderTooltip(graphics, mouseX, mouseY); renderTooltip(graphics, mouseX, mouseY);
} }
@@ -209,7 +206,7 @@ public abstract class AbstractComputerScreen<T extends AbstractComputerMenu> ext
return; return;
} }
if (!toUpload.isEmpty()) UploadFileMessage.send(menu, toUpload, ClientNetworking::sendToServer); if (toUpload.size() > 0) UploadFileMessage.send(menu, toUpload, ClientNetworking::sendToServer);
} }
public void uploadResult(UploadResult result, @Nullable Component message) { public void uploadResult(UploadResult result, @Nullable Component message) {
@@ -225,13 +222,9 @@ public abstract class AbstractComputerScreen<T extends AbstractComputerMenu> ext
} }
private void alert(Component title, Component message) { private void alert(Component title, Component message) {
OptionScreen.show(minecraft(), title, message, OptionScreen.show(minecraft, title, message,
List.of(OptionScreen.newButton(OK, b -> minecraft().setScreen(this))), List.of(OptionScreen.newButton(OK, b -> minecraft.setScreen(this))),
() -> minecraft().setScreen(this) () -> minecraft.setScreen(this)
); );
} }
private Minecraft minecraft() {
return Nullability.assertNonNull(minecraft);
}
} }

View File

@@ -49,31 +49,31 @@ public final class ClientInputHandler implements InputHandler {
@Override @Override
public void keyDown(int key, boolean repeat) { public void keyDown(int key, boolean repeat) {
ClientNetworking.sendToServer(new KeyEventServerMessage(menu, repeat ? KeyEventServerMessage.Action.REPEAT : KeyEventServerMessage.Action.DOWN, key)); ClientNetworking.sendToServer(new KeyEventServerMessage(menu, repeat ? KeyEventServerMessage.TYPE_REPEAT : KeyEventServerMessage.TYPE_DOWN, key));
} }
@Override @Override
public void keyUp(int key) { public void keyUp(int key) {
ClientNetworking.sendToServer(new KeyEventServerMessage(menu, KeyEventServerMessage.Action.UP, key)); ClientNetworking.sendToServer(new KeyEventServerMessage(menu, KeyEventServerMessage.TYPE_UP, key));
} }
@Override @Override
public void mouseClick(int button, int x, int y) { public void mouseClick(int button, int x, int y) {
ClientNetworking.sendToServer(new MouseEventServerMessage(menu, MouseEventServerMessage.Action.CLICK, button, x, y)); ClientNetworking.sendToServer(new MouseEventServerMessage(menu, MouseEventServerMessage.TYPE_CLICK, button, x, y));
} }
@Override @Override
public void mouseUp(int button, int x, int y) { public void mouseUp(int button, int x, int y) {
ClientNetworking.sendToServer(new MouseEventServerMessage(menu, MouseEventServerMessage.Action.UP, button, x, y)); ClientNetworking.sendToServer(new MouseEventServerMessage(menu, MouseEventServerMessage.TYPE_UP, button, x, y));
} }
@Override @Override
public void mouseDrag(int button, int x, int y) { public void mouseDrag(int button, int x, int y) {
ClientNetworking.sendToServer(new MouseEventServerMessage(menu, MouseEventServerMessage.Action.DRAG, button, x, y)); ClientNetworking.sendToServer(new MouseEventServerMessage(menu, MouseEventServerMessage.TYPE_DRAG, button, x, y));
} }
@Override @Override
public void mouseScroll(int direction, int x, int y) { public void mouseScroll(int direction, int x, int y) {
ClientNetworking.sendToServer(new MouseEventServerMessage(menu, MouseEventServerMessage.Action.SCROLL, direction, x, y)); ClientNetworking.sendToServer(new MouseEventServerMessage(menu, MouseEventServerMessage.TYPE_SCROLL, direction, x, y));
} }
} }

View File

@@ -28,7 +28,6 @@ public class DiskDriveScreen extends AbstractContainerScreen<DiskDriveMenu> {
@Override @Override
public void render(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) { public void render(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) {
renderBackground(graphics);
super.render(graphics, mouseX, mouseY, partialTicks); super.render(graphics, mouseX, mouseY, partialTicks);
renderTooltip(graphics, mouseX, mouseY); renderTooltip(graphics, mouseX, mouseY);
} }

View File

@@ -9,6 +9,7 @@ import net.minecraft.client.gui.GuiGraphics;
import net.minecraft.client.gui.components.toasts.Toast; import net.minecraft.client.gui.components.toasts.Toast;
import net.minecraft.client.gui.components.toasts.ToastComponent; import net.minecraft.client.gui.components.toasts.ToastComponent;
import net.minecraft.network.chat.Component; import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.FormattedCharSequence; import net.minecraft.util.FormattedCharSequence;
import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.ItemStack;
@@ -18,6 +19,7 @@ import java.util.List;
* A {@link Toast} implementation which displays an arbitrary message along with an optional {@link ItemStack}. * A {@link Toast} implementation which displays an arbitrary message along with an optional {@link ItemStack}.
*/ */
public class ItemToast implements Toast { public class ItemToast implements Toast {
private static final ResourceLocation TEXTURE = new ResourceLocation("toast/recipe");
public static final Object TRANSFER_NO_RESPONSE_TOKEN = new Object(); public static final Object TRANSFER_NO_RESPONSE_TOKEN = new Object();
private static final long DISPLAY_TIME = 7000L; private static final long DISPLAY_TIME = 7000L;
@@ -79,7 +81,7 @@ public class ItemToast implements Toast {
} }
if (width == 160 && message.size() <= 1) { if (width == 160 && message.size() <= 1) {
graphics.blit(TEXTURE, 0, 0, 0, 64, width, height()); graphics.blitSprite(TEXTURE, 0, 0, width, height());
} else { } else {
var height = height(); var height = height();
@@ -109,14 +111,14 @@ public class ItemToast implements Toast {
} }
private static void renderBackgroundRow(GuiGraphics graphics, int x, int u, int y, int height) { private static void renderBackgroundRow(GuiGraphics graphics, int x, int u, int y, int height) {
var leftOffset = 5; var leftOffset = u == 0 ? 20 : 5;
var rightOffset = Math.min(60, x - leftOffset); var rightOffset = Math.min(60, x - leftOffset);
graphics.blit(TEXTURE, 0, y, 0, 32 + u, leftOffset, height); graphics.blitSprite(TEXTURE, 160, 32, 0, u, 0, y, leftOffset, height);
for (var k = leftOffset; k < x - rightOffset; k += 64) { for (var k = leftOffset; k < x - rightOffset; k += 64) {
graphics.blit(TEXTURE, k, y, 32, 32 + u, Math.min(64, x - k - rightOffset), height); graphics.blitSprite(TEXTURE, 160, 32, 32, u, k, y, Math.min(64, x - k - rightOffset), height);
} }
graphics.blit(TEXTURE, x - rightOffset, y, 160 - rightOffset, 32 + u, rightOffset, height); graphics.blitSprite(TEXTURE, 160, 32, 160 - rightOffset, u, x - rightOffset, y, rightOffset, height);
} }
} }

View File

@@ -6,10 +6,8 @@ package dan200.computercraft.client.gui;
import dan200.computercraft.client.gui.widgets.TerminalWidget; import dan200.computercraft.client.gui.widgets.TerminalWidget;
import dan200.computercraft.core.terminal.Terminal; import dan200.computercraft.core.terminal.Terminal;
import dan200.computercraft.core.util.Nullability;
import dan200.computercraft.shared.computer.inventory.AbstractComputerMenu; import dan200.computercraft.shared.computer.inventory.AbstractComputerMenu;
import net.minecraft.client.KeyMapping; import net.minecraft.client.KeyMapping;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiGraphics; import net.minecraft.client.gui.GuiGraphics;
import net.minecraft.client.gui.screens.Screen; import net.minecraft.client.gui.screens.Screen;
import net.minecraft.client.gui.screens.inventory.MenuAccess; import net.minecraft.client.gui.screens.inventory.MenuAccess;
@@ -18,7 +16,6 @@ import net.minecraft.world.entity.player.Inventory;
import org.lwjgl.glfw.GLFW; import org.lwjgl.glfw.GLFW;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import java.util.Objects;
import static dan200.computercraft.core.util.Nullability.assertNonNull; import static dan200.computercraft.core.util.Nullability.assertNonNull;
@@ -47,8 +44,8 @@ public class NoTermComputerScreen<T extends AbstractComputerMenu> extends Screen
protected void init() { protected void init() {
// First ensure we're still grabbing the mouse, so the user can look around. Then reset bits of state that // First ensure we're still grabbing the mouse, so the user can look around. Then reset bits of state that
// grabbing unsets. // grabbing unsets.
minecraft().mouseHandler.grabMouse(); minecraft.mouseHandler.grabMouse();
minecraft().screen = this; minecraft.screen = this;
KeyMapping.releaseAll(); KeyMapping.releaseAll();
super.init(); super.init();
@@ -66,14 +63,14 @@ public class NoTermComputerScreen<T extends AbstractComputerMenu> extends Screen
} }
@Override @Override
public boolean mouseScrolled(double pMouseX, double pMouseY, double pDelta) { public boolean mouseScrolled(double mouseX, double mouseY, double scrollX, double scrollY) {
Objects.requireNonNull(minecraft().player).getInventory().swapPaint(pDelta); minecraft.player.getInventory().swapPaint(scrollX);
return super.mouseScrolled(pMouseX, pMouseY, pDelta); return super.mouseScrolled(mouseX, mouseY, scrollX, scrollY);
} }
@Override @Override
public void onClose() { public void onClose() {
Objects.requireNonNull(minecraft().player).closeContainer(); minecraft.player.closeContainer();
super.onClose(); super.onClose();
} }
@@ -96,16 +93,12 @@ public class NoTermComputerScreen<T extends AbstractComputerMenu> extends Screen
public void render(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) { public void render(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) {
super.render(graphics, mouseX, mouseY, partialTicks); super.render(graphics, mouseX, mouseY, partialTicks);
var font = minecraft().font; var font = minecraft.font;
var lines = font.split(Component.translatable("gui.computercraft.pocket_computer_overlay"), (int) (width * 0.8)); var lines = font.split(Component.translatable("gui.computercraft.pocket_computer_overlay"), (int) (width * 0.8));
var y = 10; var y = 10;
for (var line : lines) { for (var line : lines) {
graphics.drawString(font, line, (width / 2) - (font.width(line) / 2), y, 0xFFFFFF, true); graphics.drawString(font, line, (width / 2) - (minecraft.font.width(line) / 2), y, 0xFFFFFF, true);
y += 9; y += 9;
} }
} }
private Minecraft minecraft() {
return Nullability.assertNonNull(minecraft);
}
} }

View File

@@ -86,8 +86,6 @@ public final class OptionScreen extends Screen {
@Override @Override
public void render(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) { public void render(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) {
renderBackground(graphics);
// Render the actual texture. // Render the actual texture.
graphics.blit(BACKGROUND, x, y, 0, 0, innerWidth, PADDING); graphics.blit(BACKGROUND, x, y, 0, 0, innerWidth, PADDING);
graphics.blit(BACKGROUND, graphics.blit(BACKGROUND,

View File

@@ -30,7 +30,6 @@ public class PrinterScreen extends AbstractContainerScreen<PrinterMenu> {
@Override @Override
public void render(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) { public void render(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) {
renderBackground(graphics);
super.render(graphics, mouseX, mouseY, partialTicks); super.render(graphics, mouseX, mouseY, partialTicks);
renderTooltip(graphics, mouseX, mouseY); renderTooltip(graphics, mouseX, mouseY);
} }

View File

@@ -6,22 +6,15 @@ package dan200.computercraft.client.gui;
import com.mojang.blaze3d.vertex.Tesselator; import com.mojang.blaze3d.vertex.Tesselator;
import dan200.computercraft.core.terminal.TextBuffer; import dan200.computercraft.core.terminal.TextBuffer;
import dan200.computercraft.shared.ModRegistry; import dan200.computercraft.shared.common.HeldItemMenu;
import dan200.computercraft.shared.media.PrintoutMenu;
import dan200.computercraft.shared.media.items.PrintoutItem; import dan200.computercraft.shared.media.items.PrintoutItem;
import net.minecraft.client.gui.GuiGraphics; import net.minecraft.client.gui.GuiGraphics;
import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen; import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen;
import net.minecraft.client.renderer.MultiBufferSource; import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.network.chat.Component; import net.minecraft.network.chat.Component;
import net.minecraft.world.entity.player.Inventory; import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.inventory.AbstractContainerMenu;
import net.minecraft.world.inventory.ContainerListener;
import net.minecraft.world.item.ItemStack;
import org.lwjgl.glfw.GLFW; import org.lwjgl.glfw.GLFW;
import java.util.Arrays;
import java.util.Objects;
import static dan200.computercraft.client.render.PrintoutRenderer.*; import static dan200.computercraft.client.render.PrintoutRenderer.*;
import static dan200.computercraft.client.render.RenderTypes.FULL_BRIGHT_LIGHTMAP; import static dan200.computercraft.client.render.RenderTypes.FULL_BRIGHT_LIGHTMAP;
@@ -30,75 +23,40 @@ import static dan200.computercraft.client.render.RenderTypes.FULL_BRIGHT_LIGHTMA
* *
* @see dan200.computercraft.client.render.PrintoutRenderer * @see dan200.computercraft.client.render.PrintoutRenderer
*/ */
public final class PrintoutScreen extends AbstractContainerScreen<PrintoutMenu> implements ContainerListener { public class PrintoutScreen extends AbstractContainerScreen<HeldItemMenu> {
private PrintoutInfo printout = PrintoutInfo.DEFAULT; private final boolean book;
private int page = 0; private final int pages;
private final TextBuffer[] text;
private final TextBuffer[] colours;
private int page;
public PrintoutScreen(PrintoutMenu container, Inventory player, Component title) { public PrintoutScreen(HeldItemMenu container, Inventory player, Component title) {
super(container, player, title); super(container, player, title);
imageHeight = Y_SIZE; imageHeight = Y_SIZE;
}
private void setPrintout(ItemStack stack) { var text = PrintoutItem.getText(container.getStack());
var text = PrintoutItem.getText(stack); this.text = new TextBuffer[text.length];
var textBuffers = new TextBuffer[text.length]; for (var i = 0; i < this.text.length; i++) this.text[i] = new TextBuffer(text[i]);
for (var i = 0; i < textBuffers.length; i++) textBuffers[i] = new TextBuffer(text[i]);
var colours = PrintoutItem.getColours(stack); var colours = PrintoutItem.getColours(container.getStack());
var colourBuffers = new TextBuffer[colours.length]; this.colours = new TextBuffer[colours.length];
for (var i = 0; i < colours.length; i++) colourBuffers[i] = new TextBuffer(colours[i]); for (var i = 0; i < this.colours.length; i++) this.colours[i] = new TextBuffer(colours[i]);
var pages = Math.max(text.length / PrintoutItem.LINES_PER_PAGE, 1); page = 0;
var book = stack.is(ModRegistry.Items.PRINTED_BOOK.get()); pages = Math.max(this.text.length / PrintoutItem.LINES_PER_PAGE, 1);
book = ((PrintoutItem) container.getStack().getItem()).getType() == PrintoutItem.Type.BOOK;
printout = new PrintoutInfo(pages, book, textBuffers, colourBuffers);
}
@Override
protected void init() {
super.init();
menu.addSlotListener(this);
}
@Override
public void removed() {
menu.removeSlotListener(this);
}
@Override
public void slotChanged(AbstractContainerMenu menu, int slot, ItemStack stack) {
if (slot == 0) setPrintout(stack);
}
@Override
public void dataChanged(AbstractContainerMenu menu, int slot, int data) {
if (slot == PrintoutMenu.DATA_CURRENT_PAGE) page = data;
}
private void setPage(int page) {
this.page = page;
var gameMode = Objects.requireNonNull(Objects.requireNonNull(minecraft).gameMode);
gameMode.handleInventoryButtonClick(menu.containerId, PrintoutMenu.PAGE_BUTTON_OFFSET + page);
}
private void previousPage() {
if (page > 0) setPage(page - 1);
}
private void nextPage() {
if (page < printout.pages() - 1) setPage(page + 1);
} }
@Override @Override
public boolean keyPressed(int key, int scancode, int modifiers) { public boolean keyPressed(int key, int scancode, int modifiers) {
if (key == GLFW.GLFW_KEY_RIGHT) { if (key == GLFW.GLFW_KEY_RIGHT) {
nextPage(); if (page < pages - 1) page++;
return true; return true;
} }
if (key == GLFW.GLFW_KEY_LEFT) { if (key == GLFW.GLFW_KEY_LEFT) {
previousPage(); if (page > 0) page--;
return true; return true;
} }
@@ -106,17 +64,17 @@ public final class PrintoutScreen extends AbstractContainerScreen<PrintoutMenu>
} }
@Override @Override
public boolean mouseScrolled(double x, double y, double delta) { public boolean mouseScrolled(double x, double y, double deltaX, double deltaY) {
if (super.mouseScrolled(x, y, delta)) return true; if (super.mouseScrolled(x, y, deltaX, deltaY)) return true;
if (delta < 0) { if (deltaX < 0) {
// Scroll up goes to the next page // Scroll up goes to the next page
nextPage(); if (page < pages - 1) page++;
return true; return true;
} }
if (delta > 0) { if (deltaX > 0) {
// Scroll down goes to the previous page // Scroll down goes to the previous page
previousPage(); if (page > 0) page--;
return true; return true;
} }
@@ -127,39 +85,22 @@ public final class PrintoutScreen extends AbstractContainerScreen<PrintoutMenu>
protected void renderBg(GuiGraphics graphics, float partialTicks, int mouseX, int mouseY) { protected void renderBg(GuiGraphics graphics, float partialTicks, int mouseX, int mouseY) {
// Draw the printout // Draw the printout
var renderer = MultiBufferSource.immediate(Tesselator.getInstance().getBuilder()); var renderer = MultiBufferSource.immediate(Tesselator.getInstance().getBuilder());
drawBorder(graphics.pose(), renderer, leftPos, topPos, 0, page, pages, book, FULL_BRIGHT_LIGHTMAP);
drawBorder(graphics.pose(), renderer, leftPos, topPos, 0, page, printout.pages(), printout.book(), FULL_BRIGHT_LIGHTMAP); drawText(graphics.pose(), renderer, leftPos + X_TEXT_MARGIN, topPos + Y_TEXT_MARGIN, PrintoutItem.LINES_PER_PAGE * page, FULL_BRIGHT_LIGHTMAP, text, colours);
drawText(graphics.pose(), renderer, leftPos + X_TEXT_MARGIN, topPos + Y_TEXT_MARGIN, PrintoutItem.LINES_PER_PAGE * page, FULL_BRIGHT_LIGHTMAP, printout.text(), printout.colour());
renderer.endBatch(); renderer.endBatch();
} }
@Override @Override
public void render(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) { public void renderBackground(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) {
// We must take the background further back in order to not overlap with our printed pages. // We must take the background further back in order to not overlap with our printed pages.
graphics.pose().pushPose(); graphics.pose().pushPose();
graphics.pose().translate(0, 0, -1); graphics.pose().translate(0, 0, -1);
renderBackground(graphics); super.renderBackground(graphics, mouseX, mouseY, partialTicks);
graphics.pose().popPose(); graphics.pose().popPose();
super.render(graphics, mouseX, mouseY, partialTicks);
} }
@Override @Override
protected void renderLabels(GuiGraphics graphics, int mouseX, int mouseY) { protected void renderLabels(GuiGraphics graphics, int mouseX, int mouseY) {
// Skip rendering labels. // Skip rendering labels.
} }
record PrintoutInfo(int pages, boolean book, TextBuffer[] text, TextBuffer[] colour) {
public static final PrintoutInfo DEFAULT;
static {
var textLines = new TextBuffer[PrintoutItem.LINES_PER_PAGE];
Arrays.fill(textLines, new TextBuffer(" ".repeat(PrintoutItem.LINE_MAX_LENGTH)));
var colourLines = new TextBuffer[PrintoutItem.LINES_PER_PAGE];
Arrays.fill(colourLines, new TextBuffer("f".repeat(PrintoutItem.LINE_MAX_LENGTH)));
DEFAULT = new PrintoutInfo(1, false, textLines, colourLines);
}
}
} }

View File

@@ -43,6 +43,10 @@ public class DynamicImageButton extends Button {
@Override @Override
public void renderWidget(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) { public void renderWidget(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) {
var message = this.message.get();
setMessage(message.message());
setTooltip(message.tooltip());
var texture = this.texture.get(isHoveredOrFocused()); var texture = this.texture.get(isHoveredOrFocused());
RenderSystem.disableDepthTest(); RenderSystem.disableDepthTest();
@@ -50,14 +54,6 @@ public class DynamicImageButton extends Button {
RenderSystem.enableDepthTest(); RenderSystem.enableDepthTest();
} }
@Override
public void render(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks) {
var message = this.message.get();
setMessage(message.message());
setTooltip(message.tooltip());
super.render(graphics, mouseX, mouseY, partialTicks);
}
public record HintedMessage(Component message, Tooltip tooltip) { public record HintedMessage(Component message, Tooltip tooltip) {
public HintedMessage(Component message, @Nullable Component hint) { public HintedMessage(Component message, @Nullable Component hint) {
this( this(

View File

@@ -195,7 +195,7 @@ public class TerminalWidget extends AbstractWidget {
} }
@Override @Override
public boolean mouseScrolled(double mouseX, double mouseY, double delta) { public boolean mouseScrolled(double mouseX, double mouseY, double delta, double deltaY) {
if (!inTermRegion(mouseX, mouseY)) return false; if (!inTermRegion(mouseX, mouseY)) return false;
if (!hasMouseSupport() || delta == 0) return false; if (!hasMouseSupport() || delta == 0) return false;

View File

@@ -1,117 +0,0 @@
// SPDX-FileCopyrightText: 2024 The CC: Tweaked Developers
//
// SPDX-License-Identifier: MPL-2.0
package dan200.computercraft.client.model;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer;
import dan200.computercraft.api.ComputerCraftAPI;
import dan200.computercraft.client.render.CustomLecternRenderer;
import dan200.computercraft.shared.media.items.PrintoutItem;
import net.minecraft.client.model.geom.ModelPart;
import net.minecraft.client.model.geom.PartPose;
import net.minecraft.client.model.geom.builders.CubeListBuilder;
import net.minecraft.client.model.geom.builders.MeshDefinition;
import net.minecraft.client.resources.model.Material;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.inventory.InventoryMenu;
import java.util.List;
/**
* A model for {@linkplain PrintoutItem printouts} placed on a lectern.
* <p>
* This provides two models, {@linkplain #renderPages(PoseStack, VertexConsumer, int, int, int) one for a variable
* number of pages}, and {@linkplain #renderBook(PoseStack, VertexConsumer, int, int) one for books}.
*
* @see CustomLecternRenderer
*/
public class LecternPrintoutModel {
public static final ResourceLocation TEXTURE = new ResourceLocation(ComputerCraftAPI.MOD_ID, "entity/printout");
public static final Material MATERIAL = new Material(InventoryMenu.BLOCK_ATLAS, TEXTURE);
private static final int TEXTURE_WIDTH = 32;
private static final int TEXTURE_HEIGHT = 32;
private static final String PAGE_1 = "page_1";
private static final String PAGE_2 = "page_2";
private static final String PAGE_3 = "page_3";
private static final List<String> PAGES = List.of(PAGE_1, PAGE_2, PAGE_3);
private final ModelPart pagesRoot;
private final ModelPart bookRoot;
private final ModelPart[] pages;
public LecternPrintoutModel() {
pagesRoot = buildPages();
bookRoot = buildBook();
pages = PAGES.stream().map(pagesRoot::getChild).toArray(ModelPart[]::new);
}
private static ModelPart buildPages() {
var mesh = new MeshDefinition();
var parts = mesh.getRoot();
parts.addOrReplaceChild(
PAGE_1,
CubeListBuilder.create().texOffs(0, 0).addBox(-0.005f, -4.0f, -2.5f, 1f, 8.0f, 5.0f),
PartPose.ZERO
);
parts.addOrReplaceChild(
PAGE_2,
CubeListBuilder.create().texOffs(12, 0).addBox(-0.005f, -4.0f, -2.5f, 1f, 8.0f, 5.0f),
PartPose.offsetAndRotation(-0.125f, 0, 1.5f, (float) Math.PI * (1f / 16), 0, 0)
);
parts.addOrReplaceChild(
PAGE_3,
CubeListBuilder.create().texOffs(12, 0).addBox(-0.005f, -4.0f, -2.5f, 1f, 8.0f, 5.0f),
PartPose.offsetAndRotation(-0.25f, 0, -1.5f, (float) -Math.PI * (2f / 16), 0, 0)
);
return mesh.getRoot().bake(TEXTURE_WIDTH, TEXTURE_HEIGHT);
}
private static ModelPart buildBook() {
var mesh = new MeshDefinition();
var parts = mesh.getRoot();
parts.addOrReplaceChild(
"spine",
CubeListBuilder.create().texOffs(12, 15).addBox(-0.005f, -5.0f, -0.5f, 0, 10, 1.0f),
PartPose.ZERO
);
var angle = (float) Math.toRadians(5);
parts.addOrReplaceChild(
"left",
CubeListBuilder.create()
.texOffs(0, 10).addBox(0, -5.0f, -6.0f, 0, 10, 6.0f)
.texOffs(0, 0).addBox(0.005f, -4.0f, -5.0f, 1.0f, 8.0f, 5.0f),
PartPose.offsetAndRotation(-0.005f, 0, -0.5f, 0, -angle, 0)
);
parts.addOrReplaceChild(
"right",
CubeListBuilder.create()
.texOffs(14, 10).addBox(0, -5.0f, 0, 0, 10, 6.0f)
.texOffs(0, 0).addBox(0.005f, -4.0f, 0, 1.0f, 8.0f, 5.0f),
PartPose.offsetAndRotation(-0.005f, 0, 0.5f, 0, angle, 0)
);
return mesh.getRoot().bake(TEXTURE_WIDTH, TEXTURE_HEIGHT);
}
public void renderBook(PoseStack poseStack, VertexConsumer buffer, int packedLight, int packedOverlay) {
bookRoot.render(poseStack, buffer, packedLight, packedOverlay, 1, 1, 1, 1);
}
public void renderPages(PoseStack poseStack, VertexConsumer buffer, int packedLight, int packedOverlay, int pageCount) {
if (pageCount > pages.length) pageCount = pages.length;
var i = 0;
for (; i < pageCount; i++) pages[i].visible = true;
for (; i < pages.length; i++) pages[i].visible = false;
pagesRoot.render(poseStack, buffer, packedLight, packedOverlay, 1, 1, 1, 1);
}
}

View File

@@ -49,7 +49,7 @@ public final class ClientNetworkContextImpl implements ClientNetworkContext {
} }
@Override @Override
public void handleMonitorData(BlockPos pos, @Nullable TerminalState terminal) { public void handleMonitorData(BlockPos pos, TerminalState terminal) {
var player = Minecraft.getInstance().player; var player = Minecraft.getInstance().player;
if (player == null) return; if (player == null) return;
@@ -67,7 +67,7 @@ public final class ClientNetworkContextImpl implements ClientNetworkContext {
} }
@Override @Override
public void handlePocketComputerData(UUID instanceId, ComputerState state, int lightState, @Nullable TerminalState terminal) { public void handlePocketComputerData(UUID instanceId, ComputerState state, int lightState, TerminalState terminal) {
ClientPocketComputers.setState(instanceId, state, lightState, terminal); ClientPocketComputers.setState(instanceId, state, lightState, terminal);
} }

View File

@@ -11,7 +11,7 @@ import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.resources.model.BakedModel; import net.minecraft.client.resources.model.BakedModel;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.network.protocol.Packet; import net.minecraft.network.protocol.Packet;
import net.minecraft.network.protocol.game.ServerGamePacketListener; import net.minecraft.network.protocol.common.ServerCommonPacketListener;
import net.minecraft.sounds.SoundEvent; import net.minecraft.sounds.SoundEvent;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@@ -27,7 +27,7 @@ public interface ClientPlatformHelper extends dan200.computercraft.impl.client.C
* @param message The messsge to convert. * @param message The messsge to convert.
* @return The converted message. * @return The converted message.
*/ */
Packet<ServerGamePacketListener> createPacket(NetworkMessage<ServerNetworkContext> message); Packet<ServerCommonPacketListener> createPacket(NetworkMessage<ServerNetworkContext> message);
/** /**
* Render a {@link BakedModel}, using any loader-specific hooks. * Render a {@link BakedModel}, using any loader-specific hooks.

View File

@@ -6,6 +6,7 @@ package dan200.computercraft.client.pocket;
import dan200.computercraft.shared.computer.core.ComputerState; import dan200.computercraft.shared.computer.core.ComputerState;
import dan200.computercraft.shared.computer.core.ServerComputer; import dan200.computercraft.shared.computer.core.ServerComputer;
import dan200.computercraft.shared.computer.terminal.NetworkedTerminal;
import dan200.computercraft.shared.computer.terminal.TerminalState; import dan200.computercraft.shared.computer.terminal.TerminalState;
import dan200.computercraft.shared.network.client.PocketComputerDataMessage; import dan200.computercraft.shared.network.client.PocketComputerDataMessage;
import dan200.computercraft.shared.pocket.items.PocketComputerItem; import dan200.computercraft.shared.pocket.items.PocketComputerItem;
@@ -43,21 +44,20 @@ public final class ClientPocketComputers {
* @param lightColour The current colour of the modem light. * @param lightColour The current colour of the modem light.
* @param terminalData The current terminal contents. * @param terminalData The current terminal contents.
*/ */
public static void setState(UUID instanceId, ComputerState state, int lightColour, @Nullable TerminalState terminalData) { public static void setState(UUID instanceId, ComputerState state, int lightColour, TerminalState terminalData) {
var computer = instances.get(instanceId); var computer = instances.get(instanceId);
if (computer == null) { if (computer == null) {
instances.put(instanceId, new PocketComputerData(state, lightColour, terminalData)); var terminal = new NetworkedTerminal(terminalData.width, terminalData.height, terminalData.colour);
instances.put(instanceId, computer = new PocketComputerData(state, lightColour, terminal));
} else { } else {
computer.setState(state, lightColour, terminalData); computer.setState(state, lightColour);
} }
if (terminalData.hasTerminal()) terminalData.apply(computer.getTerminal());
} }
public static @Nullable PocketComputerData get(ItemStack stack) { public static @Nullable PocketComputerData get(ItemStack stack) {
var id = PocketComputerItem.getInstanceID(stack); var id = PocketComputerItem.getInstanceID(stack);
return id == null ? null : instances.get(id); return id == null ? null : instances.get(id);
} }
static @Nullable PocketComputerData get(UUID id) {
return instances.get(id);
}
} }

View File

@@ -1,101 +0,0 @@
// SPDX-FileCopyrightText: 2024 The CC: Tweaked Developers
//
// SPDX-License-Identifier: MPL-2.0
package dan200.computercraft.client.pocket;
import com.mojang.blaze3d.vertex.PoseStack;
import dan200.computercraft.client.gui.GuiSprites;
import dan200.computercraft.client.render.ComputerBorderRenderer;
import dan200.computercraft.client.render.RenderTypes;
import dan200.computercraft.client.render.SpriteRenderer;
import dan200.computercraft.client.render.text.FixedWidthFontRenderer;
import dan200.computercraft.core.terminal.Terminal;
import dan200.computercraft.shared.computer.core.ComputerFamily;
import dan200.computercraft.shared.computer.terminal.NetworkedTerminal;
import dan200.computercraft.shared.pocket.items.PocketTooltipComponent;
import net.minecraft.client.gui.Font;
import net.minecraft.client.gui.GuiGraphics;
import net.minecraft.client.gui.screens.inventory.tooltip.ClientTooltipComponent;
import net.minecraft.client.renderer.MultiBufferSource;
import javax.annotation.Nullable;
import java.util.UUID;
import static dan200.computercraft.client.render.ComputerBorderRenderer.BORDER;
import static dan200.computercraft.client.render.ComputerBorderRenderer.MARGIN;
import static dan200.computercraft.client.render.text.FixedWidthFontRenderer.FONT_HEIGHT;
import static dan200.computercraft.client.render.text.FixedWidthFontRenderer.FONT_WIDTH;
/**
* Renders the pocket computer's terminal in the item's tooltip.
* <p>
* The rendered terminal is downscaled by a factor of {@link #SCALE}.
*/
public class PocketClientTooltipComponent implements ClientTooltipComponent {
private static final float SCALE = 0.5f;
private final UUID id;
private final ComputerFamily family;
public PocketClientTooltipComponent(PocketTooltipComponent component) {
this.id = component.id();
this.family = component.family();
}
private @Nullable PocketComputerData computer() {
return ClientPocketComputers.get(id);
}
private @Nullable NetworkedTerminal terminal() {
var computer = computer();
return computer == null ? null : computer.getTerminal();
}
@Override
public int getHeight() {
var terminal = terminal();
if (terminal == null) return 0;
return (int) Math.ceil(
(terminal.getHeight() * FixedWidthFontRenderer.FONT_HEIGHT + ComputerBorderRenderer.BORDER * 2 + ComputerBorderRenderer.MARGIN * 2) * SCALE
);
}
@Override
public int getWidth(Font font) {
var terminal = terminal();
if (terminal == null) return 0;
return (int) Math.ceil(
(terminal.getWidth() * FixedWidthFontRenderer.FONT_WIDTH + ComputerBorderRenderer.BORDER * 2 + ComputerBorderRenderer.MARGIN * 2) * SCALE
);
}
@Override
public void renderImage(Font font, int x, int y, GuiGraphics guiGraphics) {
var terminal = terminal();
if (terminal == null) return;
var pose = guiGraphics.pose();
pose.pushPose();
pose.translate(x, y, 0);
pose.scale(SCALE, SCALE, 1);
render(pose, guiGraphics.bufferSource(), terminal);
pose.popPose();
}
private void render(PoseStack stack, MultiBufferSource buffers, Terminal terminal) {
var width = terminal.getWidth() * FONT_WIDTH + MARGIN * 2;
var height = terminal.getHeight() * FONT_HEIGHT + MARGIN * 2;
var renderer = SpriteRenderer.createForGui(stack.last().pose(), buffers.getBuffer(RenderTypes.GUI_SPRITES));
ComputerBorderRenderer.render(renderer, GuiSprites.getComputerTextures(family), BORDER, BORDER, width, height, false);
var quadEmitter = FixedWidthFontRenderer.toVertexConsumer(stack, buffers.getBuffer(RenderTypes.TERMINAL));
FixedWidthFontRenderer.drawTerminal(quadEmitter, BORDER + MARGIN, BORDER + MARGIN, terminal, MARGIN, MARGIN, MARGIN, MARGIN);
}
}

View File

@@ -6,11 +6,8 @@ package dan200.computercraft.client.pocket;
import dan200.computercraft.shared.computer.core.ComputerState; import dan200.computercraft.shared.computer.core.ComputerState;
import dan200.computercraft.shared.computer.terminal.NetworkedTerminal; import dan200.computercraft.shared.computer.terminal.NetworkedTerminal;
import dan200.computercraft.shared.computer.terminal.TerminalState;
import dan200.computercraft.shared.pocket.core.PocketServerComputer; import dan200.computercraft.shared.pocket.core.PocketServerComputer;
import javax.annotation.Nullable;
/** /**
* Clientside data about a pocket computer. * Clientside data about a pocket computer.
* <p> * <p>
@@ -22,21 +19,21 @@ import javax.annotation.Nullable;
* @see PocketServerComputer The server-side pocket computer. * @see PocketServerComputer The server-side pocket computer.
*/ */
public final class PocketComputerData { public final class PocketComputerData {
private @Nullable NetworkedTerminal terminal; private final NetworkedTerminal terminal;
private ComputerState state; private ComputerState state;
private int lightColour; private int lightColour;
PocketComputerData(ComputerState state, int lightColour, @Nullable TerminalState terminalData) { PocketComputerData(ComputerState state, int lightColour, NetworkedTerminal terminal) {
this.state = state; this.state = state;
this.lightColour = lightColour; this.lightColour = lightColour;
if (terminalData != null) terminal = terminalData.create(); this.terminal = terminal;
} }
public int getLightState() { public int getLightState() {
return state != ComputerState.OFF ? lightColour : -1; return state != ComputerState.OFF ? lightColour : -1;
} }
public @Nullable NetworkedTerminal getTerminal() { public NetworkedTerminal getTerminal() {
return terminal; return terminal;
} }
@@ -44,16 +41,8 @@ public final class PocketComputerData {
return state; return state;
} }
void setState(ComputerState state, int lightColour, @Nullable TerminalState terminalData) { void setState(ComputerState state, int lightColour) {
this.state = state; this.state = state;
this.lightColour = lightColour; this.lightColour = lightColour;
if (terminalData != null) {
if (terminal == null) {
terminal = terminalData.create();
} else {
terminalData.apply(terminal);
}
}
} }
} }

View File

@@ -1,51 +0,0 @@
// SPDX-FileCopyrightText: 2024 The CC: Tweaked Developers
//
// SPDX-License-Identifier: MPL-2.0
package dan200.computercraft.client.render;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.math.Axis;
import dan200.computercraft.client.model.LecternPrintoutModel;
import dan200.computercraft.shared.lectern.CustomLecternBlockEntity;
import dan200.computercraft.shared.media.items.PrintoutItem;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.blockentity.BlockEntityRenderer;
import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider;
import net.minecraft.client.renderer.blockentity.LecternRenderer;
import net.minecraft.world.level.block.LecternBlock;
/**
* A block entity renderer for our {@linkplain CustomLecternBlockEntity lectern}.
* <p>
* This largely follows {@link LecternRenderer}, but with support for multiple types of item.
*/
public class CustomLecternRenderer implements BlockEntityRenderer<CustomLecternBlockEntity> {
private final LecternPrintoutModel printoutModel;
public CustomLecternRenderer(BlockEntityRendererProvider.Context context) {
printoutModel = new LecternPrintoutModel();
}
@Override
public void render(CustomLecternBlockEntity lectern, float partialTick, PoseStack poseStack, MultiBufferSource buffer, int packedLight, int packedOverlay) {
poseStack.pushPose();
poseStack.translate(0.5f, 1.0625f, 0.5f);
poseStack.mulPose(Axis.YP.rotationDegrees(-lectern.getBlockState().getValue(LecternBlock.FACING).getClockWise().toYRot()));
poseStack.mulPose(Axis.ZP.rotationDegrees(67.5f));
poseStack.translate(0, -0.125f, 0);
var item = lectern.getItem();
if (item.getItem() instanceof PrintoutItem printout) {
var vertexConsumer = LecternPrintoutModel.MATERIAL.buffer(buffer, RenderType::entitySolid);
if (printout.getType() == PrintoutItem.Type.BOOK) {
printoutModel.renderBook(poseStack, vertexConsumer, packedLight, packedOverlay);
} else {
printoutModel.renderPages(poseStack, vertexConsumer, packedLight, packedOverlay, PrintoutItem.getPageCount(item));
}
}
poseStack.popPose();
}
}

View File

@@ -17,8 +17,6 @@ import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemDisplayContext; import net.minecraft.world.item.ItemDisplayContext;
import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.ItemStack;
import java.util.Objects;
/** /**
* A base class for items which have map-like rendering when held in the hand. * A base class for items which have map-like rendering when held in the hand.
* *
@@ -37,7 +35,7 @@ public abstract class ItemMapLikeRenderer {
protected abstract void renderItem(PoseStack transform, MultiBufferSource render, ItemStack stack, int light); protected abstract void renderItem(PoseStack transform, MultiBufferSource render, ItemStack stack, int light);
public void renderItemFirstPerson(PoseStack transform, MultiBufferSource render, int lightTexture, InteractionHand hand, float pitch, float equipProgress, float swingProgress, ItemStack stack) { public void renderItemFirstPerson(PoseStack transform, MultiBufferSource render, int lightTexture, InteractionHand hand, float pitch, float equipProgress, float swingProgress, ItemStack stack) {
Player player = Objects.requireNonNull(Minecraft.getInstance().player); Player player = Minecraft.getInstance().player;
transform.pushPose(); transform.pushPose();
if (hand == InteractionHand.MAIN_HAND && player.getOffhandItem().isEmpty()) { if (hand == InteractionHand.MAIN_HAND && player.getOffhandItem().isEmpty()) {

View File

@@ -13,7 +13,6 @@ import dan200.computercraft.core.util.Colour;
import dan200.computercraft.shared.computer.core.ComputerFamily; import dan200.computercraft.shared.computer.core.ComputerFamily;
import dan200.computercraft.shared.config.Config; import dan200.computercraft.shared.config.Config;
import dan200.computercraft.shared.pocket.items.PocketComputerItem; import dan200.computercraft.shared.pocket.items.PocketComputerItem;
import dan200.computercraft.shared.util.ARGB32;
import net.minecraft.client.renderer.MultiBufferSource; import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.ItemStack;
import org.joml.Matrix4f; import org.joml.Matrix4f;
@@ -93,11 +92,16 @@ public final class PocketItemRenderer extends ItemMapLikeRenderer {
} }
private static void renderLight(PoseStack transform, MultiBufferSource render, int colour, int width, int height) { private static void renderLight(PoseStack transform, MultiBufferSource render, int colour, int width, int height) {
var r = (byte) ((colour >>> 16) & 0xFF);
var g = (byte) ((colour >>> 8) & 0xFF);
var b = (byte) (colour & 0xFF);
var c = new byte[]{ r, g, b, (byte) 255 };
var buffer = render.getBuffer(RenderTypes.TERMINAL); var buffer = render.getBuffer(RenderTypes.TERMINAL);
FixedWidthFontRenderer.drawQuad( FixedWidthFontRenderer.drawQuad(
FixedWidthFontRenderer.toVertexConsumer(transform, buffer), FixedWidthFontRenderer.toVertexConsumer(transform, buffer),
width - LIGHT_HEIGHT * 2, height + BORDER / 2.0f, 0.001f, LIGHT_HEIGHT * 2, LIGHT_HEIGHT, width - LIGHT_HEIGHT * 2, height + BORDER / 2.0f, 0.001f, LIGHT_HEIGHT * 2, LIGHT_HEIGHT,
ARGB32.opaque(colour), RenderTypes.FULL_BRIGHT_LIGHTMAP c, RenderTypes.FULL_BRIGHT_LIGHTMAP
); );
} }
} }

View File

@@ -34,12 +34,11 @@ public class SpriteRenderer {
this.b = b; this.b = b;
} }
public static SpriteRenderer createForGui(Matrix4f transform, VertexConsumer builder) {
return new SpriteRenderer(transform, builder, 0, RenderTypes.FULL_BRIGHT_LIGHTMAP, 255, 255, 255);
}
public static SpriteRenderer createForGui(GuiGraphics graphics, RenderType renderType) { public static SpriteRenderer createForGui(GuiGraphics graphics, RenderType renderType) {
return createForGui(graphics.pose().last().pose(), graphics.bufferSource().getBuffer(renderType)); return new SpriteRenderer(
graphics.pose().last().pose(), graphics.bufferSource().getBuffer(renderType),
0, RenderTypes.FULL_BRIGHT_LIGHTMAP, 255, 255, 255
);
} }
/** /**
@@ -126,10 +125,10 @@ public class SpriteRenderer {
} }
public static float u(TextureAtlasSprite sprite, int x, int width) { public static float u(TextureAtlasSprite sprite, int x, int width) {
return sprite.getU((double) x / width * 16); return sprite.getU((float) x / width);
} }
public static float v(TextureAtlasSprite sprite, int y, int height) { public static float v(TextureAtlasSprite sprite, int y, int height) {
return sprite.getV((double) y / height * 16); return sprite.getV((float) y / height);
} }
} }

View File

@@ -55,7 +55,7 @@ public class TurtleBlockEntityRenderer implements BlockEntityRenderer<TurtleBloc
// Render the label // Render the label
var label = turtle.getLabel(); var label = turtle.getLabel();
var hit = renderer.cameraHitResult; var hit = renderer.cameraHitResult;
if (label != null && hit != null && hit.getType() == HitResult.Type.BLOCK && turtle.getBlockPos().equals(((BlockHitResult) hit).getBlockPos())) { if (label != null && hit.getType() == HitResult.Type.BLOCK && turtle.getBlockPos().equals(((BlockHitResult) hit).getBlockPos())) {
var mc = Minecraft.getInstance(); var mc = Minecraft.getInstance();
var font = this.font; var font = this.font;

View File

@@ -9,6 +9,7 @@ import com.mojang.blaze3d.platform.MemoryTracker;
import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.vertex.*; import com.mojang.blaze3d.vertex.*;
import com.mojang.math.Axis; import com.mojang.math.Axis;
import dan200.computercraft.annotations.ForgeOverride;
import dan200.computercraft.client.FrameInfo; import dan200.computercraft.client.FrameInfo;
import dan200.computercraft.client.integration.ShaderMod; import dan200.computercraft.client.integration.ShaderMod;
import dan200.computercraft.client.render.RenderTypes; import dan200.computercraft.client.render.RenderTypes;
@@ -25,6 +26,7 @@ import dan200.computercraft.shared.util.DirectionUtil;
import net.minecraft.client.renderer.MultiBufferSource; import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.blockentity.BlockEntityRenderer; import net.minecraft.client.renderer.blockentity.BlockEntityRenderer;
import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider; import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider;
import net.minecraft.world.phys.AABB;
import org.joml.Matrix3f; import org.joml.Matrix3f;
import org.joml.Matrix4f; import org.joml.Matrix4f;
import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL11;
@@ -255,6 +257,11 @@ public class MonitorBlockEntityRenderer implements BlockEntityRenderer<MonitorBl
return Config.monitorDistance; return Config.monitorDistance;
} }
@ForgeOverride
public AABB getRenderBoundingBox(MonitorBlockEntity monitor) {
return monitor.getRenderBoundingBox();
}
/** /**
* Determine if any monitors were rendered this frame. * Determine if any monitors were rendered this frame.
* *

View File

@@ -13,11 +13,9 @@ import dan200.computercraft.core.terminal.Palette;
import dan200.computercraft.core.terminal.Terminal; import dan200.computercraft.core.terminal.Terminal;
import dan200.computercraft.core.terminal.TextBuffer; import dan200.computercraft.core.terminal.TextBuffer;
import dan200.computercraft.core.util.Colour; import dan200.computercraft.core.util.Colour;
import dan200.computercraft.shared.util.ARGB32;
import org.lwjgl.system.MemoryUtil; import org.lwjgl.system.MemoryUtil;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import static dan200.computercraft.client.render.text.FixedWidthFontRenderer.*; import static dan200.computercraft.client.render.text.FixedWidthFontRenderer.*;
import static org.lwjgl.system.MemoryUtil.*; import static org.lwjgl.system.MemoryUtil.*;
@@ -40,12 +38,10 @@ import static org.lwjgl.system.MemoryUtil.*;
* {@link FixedWidthFontRenderer}. * {@link FixedWidthFontRenderer}.
*/ */
public final class DirectFixedWidthFontRenderer { public final class DirectFixedWidthFontRenderer {
private static final boolean IS_LITTLE_ENDIAN = ByteOrder.nativeOrder() == ByteOrder.LITTLE_ENDIAN;
private DirectFixedWidthFontRenderer() { private DirectFixedWidthFontRenderer() {
} }
private static void drawChar(QuadEmitter emitter, float x, float y, int index, int colour) { private static void drawChar(QuadEmitter emitter, float x, float y, int index, byte[] colour) {
// Short circuit to avoid the common case - the texture should be blank here after all. // Short circuit to avoid the common case - the texture should be blank here after all.
if (index == '\0' || index == ' ') return; if (index == '\0' || index == ' ') return;
@@ -162,8 +158,8 @@ public final class DirectFixedWidthFontRenderer {
return (terminal.getHeight() + 2) * (terminal.getWidth() + 2) * 2; return (terminal.getHeight() + 2) * (terminal.getWidth() + 2) * 2;
} }
private static void quad(QuadEmitter buffer, float x1, float y1, float x2, float y2, float z, int colour, float u1, float v1, float u2, float v2) { private static void quad(QuadEmitter buffer, float x1, float y1, float x2, float y2, float z, byte[] rgba, float u1, float v1, float u2, float v2) {
buffer.quad(x1, y1, x2, y2, z, colour, u1, v1, u2, v2); buffer.quad(x1, y1, x2, y2, z, rgba, u1, v1, u2, v2);
} }
public interface QuadEmitter { public interface QuadEmitter {
@@ -171,7 +167,7 @@ public final class DirectFixedWidthFontRenderer {
ByteBuffer buffer(); ByteBuffer buffer();
void quad(float x1, float y1, float x2, float y2, float z, int colour, float u1, float v1, float u2, float v2); void quad(float x1, float y1, float x2, float y2, float z, byte[] rgba, float u1, float v1, float u2, float v2);
} }
public record ByteBufferEmitter(ByteBuffer buffer) implements QuadEmitter { public record ByteBufferEmitter(ByteBuffer buffer) implements QuadEmitter {
@@ -181,12 +177,12 @@ public final class DirectFixedWidthFontRenderer {
} }
@Override @Override
public void quad(float x1, float y1, float x2, float y2, float z, int colour, float u1, float v1, float u2, float v2) { public void quad(float x1, float y1, float x2, float y2, float z, byte[] rgba, float u1, float v1, float u2, float v2) {
DirectFixedWidthFontRenderer.quad(buffer, x1, y1, x2, y2, z, colour, u1, v1, u2, v2); DirectFixedWidthFontRenderer.quad(buffer, x1, y1, x2, y2, z, rgba, u1, v1, u2, v2);
} }
} }
static void quad(ByteBuffer buffer, float x1, float y1, float x2, float y2, float z, int colour, float u1, float v1, float u2, float v2) { static void quad(ByteBuffer buffer, float x1, float y1, float x2, float y2, float z, byte[] rgba, float u1, float v1, float u2, float v2) {
// Emit a single quad to our buffer. This uses Unsafe (well, LWJGL's MemoryUtil) to directly blit bytes to the // Emit a single quad to our buffer. This uses Unsafe (well, LWJGL's MemoryUtil) to directly blit bytes to the
// underlying buffer. This allows us to have a single bounds check up-front, rather than one for every write. // underlying buffer. This allows us to have a single bounds check up-front, rather than one for every write.
// This provides significant performance gains, at the cost of well, using Unsafe. // This provides significant performance gains, at the cost of well, using Unsafe.
@@ -200,15 +196,16 @@ public final class DirectFixedWidthFontRenderer {
if (position < 0 || 112 > buffer.limit() - position) throw new IndexOutOfBoundsException(); if (position < 0 || 112 > buffer.limit() - position) throw new IndexOutOfBoundsException();
// Require the pointer to be aligned to a 32-bit boundary. // Require the pointer to be aligned to a 32-bit boundary.
if ((addr & 3) != 0) throw new IllegalStateException("Memory is not aligned"); if ((addr & 3) != 0) throw new IllegalStateException("Memory is not aligned");
// Also assert the length of the array. This appears to help elide bounds checks on the array in some circumstances.
// Pack colour so it is equivalent to rgba:BBBB. if (rgba.length != 4) throw new IllegalStateException();
var colourAbgr = ARGB32.toABGR32(colour);
var nativeColour = IS_LITTLE_ENDIAN ? colourAbgr : Integer.reverseBytes(colourAbgr);
memPutFloat(addr + 0, x1); memPutFloat(addr + 0, x1);
memPutFloat(addr + 4, y1); memPutFloat(addr + 4, y1);
memPutFloat(addr + 8, z); memPutFloat(addr + 8, z);
memPutInt(addr + 12, nativeColour); memPutByte(addr + 12, rgba[0]);
memPutByte(addr + 13, rgba[1]);
memPutByte(addr + 14, rgba[2]);
memPutByte(addr + 15, (byte) 255);
memPutFloat(addr + 16, u1); memPutFloat(addr + 16, u1);
memPutFloat(addr + 20, v1); memPutFloat(addr + 20, v1);
memPutShort(addr + 24, (short) 0xF0); memPutShort(addr + 24, (short) 0xF0);
@@ -217,7 +214,10 @@ public final class DirectFixedWidthFontRenderer {
memPutFloat(addr + 28, x1); memPutFloat(addr + 28, x1);
memPutFloat(addr + 32, y2); memPutFloat(addr + 32, y2);
memPutFloat(addr + 36, z); memPutFloat(addr + 36, z);
memPutInt(addr + 40, nativeColour); memPutByte(addr + 40, rgba[0]);
memPutByte(addr + 41, rgba[1]);
memPutByte(addr + 42, rgba[2]);
memPutByte(addr + 43, (byte) 255);
memPutFloat(addr + 44, u1); memPutFloat(addr + 44, u1);
memPutFloat(addr + 48, v2); memPutFloat(addr + 48, v2);
memPutShort(addr + 52, (short) 0xF0); memPutShort(addr + 52, (short) 0xF0);
@@ -226,7 +226,10 @@ public final class DirectFixedWidthFontRenderer {
memPutFloat(addr + 56, x2); memPutFloat(addr + 56, x2);
memPutFloat(addr + 60, y2); memPutFloat(addr + 60, y2);
memPutFloat(addr + 64, z); memPutFloat(addr + 64, z);
memPutInt(addr + 68, nativeColour); memPutByte(addr + 68, rgba[0]);
memPutByte(addr + 69, rgba[1]);
memPutByte(addr + 70, rgba[2]);
memPutByte(addr + 71, (byte) 255);
memPutFloat(addr + 72, u2); memPutFloat(addr + 72, u2);
memPutFloat(addr + 76, v2); memPutFloat(addr + 76, v2);
memPutShort(addr + 80, (short) 0xF0); memPutShort(addr + 80, (short) 0xF0);
@@ -235,7 +238,10 @@ public final class DirectFixedWidthFontRenderer {
memPutFloat(addr + 84, x2); memPutFloat(addr + 84, x2);
memPutFloat(addr + 88, y1); memPutFloat(addr + 88, y1);
memPutFloat(addr + 92, z); memPutFloat(addr + 92, z);
memPutInt(addr + 96, nativeColour); memPutByte(addr + 96, rgba[0]);
memPutByte(addr + 97, rgba[1]);
memPutByte(addr + 98, rgba[2]);
memPutByte(addr + 99, (byte) 255);
memPutFloat(addr + 100, u2); memPutFloat(addr + 100, u2);
memPutFloat(addr + 104, v1); memPutFloat(addr + 104, v1);
memPutShort(addr + 108, (short) 0xF0); memPutShort(addr + 108, (short) 0xF0);

View File

@@ -12,7 +12,6 @@ import dan200.computercraft.core.terminal.Terminal;
import dan200.computercraft.core.terminal.TextBuffer; import dan200.computercraft.core.terminal.TextBuffer;
import dan200.computercraft.core.util.Colour; import dan200.computercraft.core.util.Colour;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.FastColor;
import org.joml.Matrix4f; import org.joml.Matrix4f;
import org.joml.Vector3f; import org.joml.Vector3f;
@@ -42,7 +41,7 @@ public final class FixedWidthFontRenderer {
static final float BACKGROUND_START = (WIDTH - 6.0f) / WIDTH; static final float BACKGROUND_START = (WIDTH - 6.0f) / WIDTH;
static final float BACKGROUND_END = (WIDTH - 4.0f) / WIDTH; static final float BACKGROUND_END = (WIDTH - 4.0f) / WIDTH;
private static final int BLACK = FastColor.ARGB32.color(255, byteColour(Colour.BLACK.getR()), byteColour(Colour.BLACK.getR()), byteColour(Colour.BLACK.getR())); private static final byte[] BLACK = new byte[]{ byteColour(Colour.BLACK.getR()), byteColour(Colour.BLACK.getR()), byteColour(Colour.BLACK.getR()), (byte) 255 };
private static final float Z_OFFSET = 1e-3f; private static final float Z_OFFSET = 1e-3f;
private FixedWidthFontRenderer() { private FixedWidthFontRenderer() {
@@ -60,7 +59,7 @@ public final class FixedWidthFontRenderer {
return 15 - Terminal.getColour(c, def); return 15 - Terminal.getColour(c, def);
} }
private static void drawChar(QuadEmitter emitter, float x, float y, int index, int colour, int light) { private static void drawChar(QuadEmitter emitter, float x, float y, int index, byte[] colour, int light) {
// Short circuit to avoid the common case - the texture should be blank here after all. // Short circuit to avoid the common case - the texture should be blank here after all.
if (index == '\0' || index == ' ') return; if (index == '\0' || index == ' ') return;
@@ -76,7 +75,7 @@ public final class FixedWidthFontRenderer {
); );
} }
public static void drawQuad(QuadEmitter emitter, float x, float y, float z, float width, float height, int colour, int light) { public static void drawQuad(QuadEmitter emitter, float x, float y, float z, float width, float height, byte[] colour, int light) {
quad(emitter, x, y, x + width, y + height, z, colour, BACKGROUND_START, BACKGROUND_START, BACKGROUND_END, BACKGROUND_END, light); quad(emitter, x, y, x + width, y + height, z, colour, BACKGROUND_START, BACKGROUND_START, BACKGROUND_END, BACKGROUND_END, light);
} }
@@ -217,10 +216,10 @@ public final class FixedWidthFontRenderer {
return new QuadEmitter(transform.last().pose(), consumer); return new QuadEmitter(transform.last().pose(), consumer);
} }
private static void quad(QuadEmitter c, float x1, float y1, float x2, float y2, float z, int colour, float u1, float v1, float u2, float v2, int light) { private static void quad(QuadEmitter c, float x1, float y1, float x2, float y2, float z, byte[] rgba, float u1, float v1, float u2, float v2, int light) {
var poseMatrix = c.poseMatrix(); var poseMatrix = c.poseMatrix();
var consumer = c.consumer(); var consumer = c.consumer();
int r = FastColor.ARGB32.red(colour), g = FastColor.ARGB32.green(colour), b = FastColor.ARGB32.blue(colour), a = FastColor.ARGB32.alpha(colour); byte r = rgba[0], g = rgba[1], b = rgba[2], a = rgba[3];
consumer.vertex(poseMatrix, x1, y1, z).color(r, g, b, a).uv(u1, v1).uv2(light).endVertex(); consumer.vertex(poseMatrix, x1, y1, z).color(r, g, b, a).uv(u1, v1).uv2(light).endVertex();
consumer.vertex(poseMatrix, x1, y2, z).color(r, g, b, a).uv(u1, v2).uv2(light).endVertex(); consumer.vertex(poseMatrix, x1, y2, z).color(r, g, b, a).uv(u1, v2).uv2(light).endVertex();

View File

@@ -10,6 +10,7 @@ import dan200.computercraft.api.client.turtle.TurtleUpgradeModeller;
import dan200.computercraft.api.turtle.ITurtleAccess; import dan200.computercraft.api.turtle.ITurtleAccess;
import dan200.computercraft.api.turtle.TurtleSide; import dan200.computercraft.api.turtle.TurtleSide;
import dan200.computercraft.shared.turtle.upgrades.TurtleModem; import dan200.computercraft.shared.turtle.upgrades.TurtleModem;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
@@ -40,7 +41,7 @@ public class TurtleModemModeller implements TurtleUpgradeModeller<TurtleModem> {
} }
@Override @Override
public TransformedModel getModel(TurtleModem upgrade, @Nullable ITurtleAccess turtle, TurtleSide side) { public TransformedModel getModel(TurtleModem upgrade, @Nullable ITurtleAccess turtle, TurtleSide side, CompoundTag data) {
var active = false; var active = false;
if (turtle != null) { if (turtle != null) {
var turtleNBT = turtle.getUpgradeNBTData(side); var turtleNBT = turtle.getUpgradeNBTData(side);

View File

@@ -10,15 +10,13 @@ import dan200.computercraft.api.client.turtle.TurtleUpgradeModeller;
import dan200.computercraft.api.turtle.ITurtleAccess; import dan200.computercraft.api.turtle.ITurtleAccess;
import dan200.computercraft.api.turtle.ITurtleUpgrade; import dan200.computercraft.api.turtle.ITurtleUpgrade;
import dan200.computercraft.api.turtle.TurtleSide; import dan200.computercraft.api.turtle.TurtleSide;
import dan200.computercraft.api.turtle.TurtleUpgradeSerialiser; import dan200.computercraft.api.upgrades.UpgradeSerialiser;
import dan200.computercraft.impl.PlatformHelper; import dan200.computercraft.impl.RegistryHelper;
import dan200.computercraft.impl.TurtleUpgrades; import dan200.computercraft.impl.TurtleUpgrades;
import dan200.computercraft.impl.UpgradeManager; import dan200.computercraft.impl.UpgradeManager;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.CompoundTag;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Map; import java.util.Map;
import java.util.WeakHashMap; import java.util.WeakHashMap;
@@ -29,12 +27,10 @@ import java.util.stream.Stream;
* A registry of {@link TurtleUpgradeModeller}s. * A registry of {@link TurtleUpgradeModeller}s.
*/ */
public final class TurtleUpgradeModellers { public final class TurtleUpgradeModellers {
private static final Logger LOG = LoggerFactory.getLogger(TurtleUpgradeModellers.class); private static final TurtleUpgradeModeller<ITurtleUpgrade> NULL_TURTLE_MODELLER = (upgrade, turtle, side, data) ->
private static final TurtleUpgradeModeller<ITurtleUpgrade> NULL_TURTLE_MODELLER = (upgrade, turtle, side) ->
new TransformedModel(Minecraft.getInstance().getModelManager().getMissingModel(), Transformation.identity()); new TransformedModel(Minecraft.getInstance().getModelManager().getMissingModel(), Transformation.identity());
private static final Map<TurtleUpgradeSerialiser<?>, TurtleUpgradeModeller<?>> turtleModels = new ConcurrentHashMap<>(); private static final Map<UpgradeSerialiser<? extends ITurtleUpgrade>, TurtleUpgradeModeller<?>> turtleModels = new ConcurrentHashMap<>();
private static volatile boolean fetchedModels; private static volatile boolean fetchedModels;
/** /**
@@ -48,15 +44,12 @@ public final class TurtleUpgradeModellers {
private TurtleUpgradeModellers() { private TurtleUpgradeModellers() {
} }
public static <T extends ITurtleUpgrade> void register(TurtleUpgradeSerialiser<T> serialiser, TurtleUpgradeModeller<T> modeller) { public static <T extends ITurtleUpgrade> void register(UpgradeSerialiser<T> serialiser, TurtleUpgradeModeller<T> modeller) {
if (fetchedModels) { if (fetchedModels) {
// TODO(1.20.4): Replace with an error. throw new IllegalStateException(String.format(
LOG.warn( "Turtle upgrade serialiser %s must be registered before models are baked.",
"Turtle upgrade serialiser {} was registered too late, its models may not be loaded correctly. If you are " + RegistryHelper.getKeyOrThrow(RegistryHelper.getRegistry(ITurtleUpgrade.serialiserRegistryKey()), serialiser)
"the mod author, you may be using a deprecated API - see https://github.com/cc-tweaked/CC-Tweaked/pull/1684 " + ));
"for further information.",
PlatformHelper.get().getRegistryKey(TurtleUpgradeSerialiser.registryId(), serialiser)
);
} }
if (turtleModels.putIfAbsent(serialiser, modeller) != null) { if (turtleModels.putIfAbsent(serialiser, modeller) != null) {
@@ -67,13 +60,13 @@ public final class TurtleUpgradeModellers {
public static TransformedModel getModel(ITurtleUpgrade upgrade, ITurtleAccess access, TurtleSide side) { public static TransformedModel getModel(ITurtleUpgrade upgrade, ITurtleAccess access, TurtleSide side) {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
var modeller = (TurtleUpgradeModeller<ITurtleUpgrade>) modelCache.computeIfAbsent(upgrade, TurtleUpgradeModellers::getModeller); var modeller = (TurtleUpgradeModeller<ITurtleUpgrade>) modelCache.computeIfAbsent(upgrade, TurtleUpgradeModellers::getModeller);
return modeller.getModel(upgrade, access, side); return modeller.getModel(upgrade, access, side, access.getUpgradeNBTData(side));
} }
public static TransformedModel getModel(ITurtleUpgrade upgrade, CompoundTag data, TurtleSide side) { public static TransformedModel getModel(ITurtleUpgrade upgrade, CompoundTag data, TurtleSide side) {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
var modeller = (TurtleUpgradeModeller<ITurtleUpgrade>) modelCache.computeIfAbsent(upgrade, TurtleUpgradeModellers::getModeller); var modeller = (TurtleUpgradeModeller<ITurtleUpgrade>) modelCache.computeIfAbsent(upgrade, TurtleUpgradeModellers::getModeller);
return modeller.getModel(upgrade, data, side); return modeller.getModel(upgrade, null, side, data);
} }
private static TurtleUpgradeModeller<?> getModeller(ITurtleUpgrade upgradeA) { private static TurtleUpgradeModeller<?> getModeller(ITurtleUpgrade upgradeA) {

View File

@@ -5,7 +5,6 @@
package dan200.computercraft.data.client; package dan200.computercraft.data.client;
import dan200.computercraft.client.gui.GuiSprites; import dan200.computercraft.client.gui.GuiSprites;
import dan200.computercraft.client.model.LecternPrintoutModel;
import dan200.computercraft.data.DataProviders; import dan200.computercraft.data.DataProviders;
import dan200.computercraft.shared.turtle.inventory.UpgradeSlot; import dan200.computercraft.shared.turtle.inventory.UpgradeSlot;
import net.minecraft.client.renderer.texture.atlas.SpriteSource; import net.minecraft.client.renderer.texture.atlas.SpriteSource;
@@ -31,8 +30,7 @@ public final class ClientDataProviders {
generator.addFromCodec("Block atlases", PackType.CLIENT_RESOURCES, "atlases", SpriteSources.FILE_CODEC, out -> { generator.addFromCodec("Block atlases", PackType.CLIENT_RESOURCES, "atlases", SpriteSources.FILE_CODEC, out -> {
out.accept(new ResourceLocation("blocks"), List.of( out.accept(new ResourceLocation("blocks"), List.of(
new SingleFile(UpgradeSlot.LEFT_UPGRADE, Optional.empty()), new SingleFile(UpgradeSlot.LEFT_UPGRADE, Optional.empty()),
new SingleFile(UpgradeSlot.RIGHT_UPGRADE, Optional.empty()), new SingleFile(UpgradeSlot.RIGHT_UPGRADE, Optional.empty())
new SingleFile(LecternPrintoutModel.TEXTURE, Optional.empty())
)); ));
out.accept(GuiSprites.SPRITE_SHEET, Stream.of( out.accept(GuiSprites.SPRITE_SHEET, Stream.of(
// Buttons // Buttons

View File

@@ -1,8 +0,0 @@
{
"variants": {
"facing=east": {"model": "minecraft:block/lectern", "y": 90},
"facing=north": {"model": "minecraft:block/lectern", "y": 0},
"facing=south": {"model": "minecraft:block/lectern", "y": 180},
"facing=west": {"model": "minecraft:block/lectern", "y": 270}
}
}

View File

@@ -1,12 +0,0 @@
{
"parent": "computercraft:block/turtle_base",
"textures": {
"back": "computercraft:block/turtle_advanced_back",
"backpack": "computercraft:block/turtle_advanced_backpack",
"bottom": "computercraft:block/turtle_advanced_bottom",
"front": "computercraft:block/turtle_advanced_front",
"left": "computercraft:block/turtle_advanced_left",
"right": "computercraft:block/turtle_advanced_right",
"top": "computercraft:block/turtle_advanced_top"
}
}

View File

@@ -1,12 +0,0 @@
{
"parent": "computercraft:block/turtle_base",
"textures": {
"back": "computercraft:block/turtle_normal_back",
"backpack": "computercraft:block/turtle_normal_backpack",
"bottom": "computercraft:block/turtle_normal_bottom",
"front": "computercraft:block/turtle_normal_front",
"left": "computercraft:block/turtle_normal_left",
"right": "computercraft:block/turtle_normal_right",
"top": "computercraft:block/turtle_normal_top"
}
}

Some files were not shown because too many files have changed in this diff Show More