Compare commits
56 Commits
v1.20.1-1.
...
v1.20.1-1.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
08d4f91c8b | ||
|
|
b9eac4e509 | ||
|
|
dc3d8ea198 | ||
|
|
cbe075b001 | ||
|
|
ed0b156e05 | ||
|
|
4dd0735066 | ||
|
|
38e516d7c7 | ||
|
|
70a31855ac | ||
|
|
6c8e64ffcd | ||
|
|
7285c32d58 | ||
|
|
99c60ac54b | ||
|
|
63e40cf3cb | ||
|
|
1d45935a25 | ||
|
|
f80373e7a2 | ||
|
|
63185629b7 | ||
|
|
4bfb9ac323 | ||
|
|
5926b6c994 | ||
|
|
f5ed43584d | ||
|
|
d77f5f135f | ||
|
|
7744d2663b | ||
|
|
4566cb8273 | ||
|
|
052e7a7ae5 | ||
|
|
0895200681 | ||
|
|
09d0f563b7 | ||
|
|
e188f1d3fa | ||
|
|
819a4f7231 | ||
|
|
898cb2a95d | ||
|
|
03a8f83191 | ||
|
|
aef92c8ebc | ||
|
|
571ea794a8 | ||
|
|
e81af93043 | ||
|
|
25b8a65c5c | ||
|
|
e4236824d7 | ||
|
|
cfd11ffa92 | ||
|
|
ce133a5e66 | ||
|
|
038fbc1ed1 | ||
|
|
c582fb521c | ||
|
|
af21792844 | ||
|
|
9fbb1070ef | ||
|
|
1944995c33 | ||
|
|
ac851a795b | ||
|
|
334761788a | ||
|
|
5af3e15dd5 | ||
|
|
209b1ddbf9 | ||
|
|
0c9f9a8652 | ||
|
|
862d92785e | ||
|
|
d48b85d50c | ||
|
|
4d619de357 | ||
|
|
57c289f173 | ||
|
|
f63f85921f | ||
|
|
c7e49d1929 | ||
|
|
d9b0cc7075 | ||
|
|
1e214f329e | ||
|
|
de930c8d09 | ||
|
|
735e7ce09b | ||
|
|
6e9799316a |
6
.github/ISSUE_TEMPLATE/bug_report.yaml
vendored
@@ -8,10 +8,8 @@ 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.16.x
|
- 1.20.1
|
||||||
- 1.18.x
|
- 1.21.x
|
||||||
- 1.19.x
|
|
||||||
- 1.20.x
|
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: input
|
- type: input
|
||||||
|
|||||||
19
.github/workflows/make-doc.sh
vendored
@@ -1,19 +0,0 @@
|
|||||||
#!/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"
|
|
||||||
28
.github/workflows/make-doc.yml
vendored
@@ -3,8 +3,7 @@ name: Build documentation
|
|||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- mc-1.19.x
|
- mc-*
|
||||||
- mc-1.20.x
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
make_doc:
|
make_doc:
|
||||||
@@ -12,30 +11,25 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Clone repository
|
- name: 📥 Clone repository
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Set up Java
|
- name: 📥 Set up Java
|
||||||
uses: actions/setup-java@v4
|
uses: actions/setup-java@v4
|
||||||
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/actions/setup-gradle@v3
|
||||||
with:
|
with:
|
||||||
cache-read-only: ${{ !startsWith(github.ref, 'refs/heads/mc-') }}
|
cache-read-only: ${{ !startsWith(github.ref, 'refs/heads/mc-') }}
|
||||||
|
|
||||||
- name: Build with Gradle
|
- name: ⚒️ Generate documentation
|
||||||
run: ./gradlew compileJava --no-daemon || ./gradlew compileJava --no-daemon
|
run: ./gradlew docWebsite --no-daemon
|
||||||
|
|
||||||
- name: Generate documentation
|
- name: 📤 Upload Jar
|
||||||
run: ./gradlew docWebsite :common-api:javadoc --no-daemon
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
- name: Upload documentation
|
name: Documentation
|
||||||
run: .github/workflows/make-doc.sh 2> /dev/null
|
path: ./projects/web/build/site/
|
||||||
env:
|
|
||||||
SSH_KEY: ${{ secrets.SSH_KEY }}
|
|
||||||
SSH_USER: ${{ secrets.SSH_USER }}
|
|
||||||
SSH_HOST: ${{ secrets.SSH_HOST }}
|
|
||||||
SSH_PORT: ${{ secrets.SSH_PORT }}
|
|
||||||
|
|||||||
@@ -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: v2.1.0
|
rev: v4.0.3
|
||||||
hooks:
|
hooks:
|
||||||
- id: reuse
|
- id: reuse
|
||||||
|
|
||||||
|
|||||||
99
.reuse/dep5
@@ -1,99 +0,0 @@
|
|||||||
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/*/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
|
|
||||||
@@ -12,7 +12,6 @@ If you've any other questions, [just ask the community][community] or [open an i
|
|||||||
|
|
||||||
## Table of Contents
|
## Table of Contents
|
||||||
- [Reporting issues](#reporting-issues)
|
- [Reporting issues](#reporting-issues)
|
||||||
- [Translations](#translations)
|
|
||||||
- [Setting up a development environment](#setting-up-a-development-environment)
|
- [Setting up a development environment](#setting-up-a-development-environment)
|
||||||
- [Developing CC: Tweaked](#developing-cc-tweaked)
|
- [Developing CC: Tweaked](#developing-cc-tweaked)
|
||||||
- [Writing documentation](#writing-documentation)
|
- [Writing documentation](#writing-documentation)
|
||||||
@@ -21,17 +20,13 @@ If you've any other questions, [just ask the community][community] or [open an i
|
|||||||
If you have a bug, suggestion, or other feedback, the best thing to do is [file an issue][new-issue]. When doing so, do
|
If you have a bug, suggestion, or other feedback, the best thing to do is [file an issue][new-issue]. When doing so, do
|
||||||
use the issue templates - they provide a useful hint on what information to provide.
|
use the issue templates - they provide a useful hint on what information to provide.
|
||||||
|
|
||||||
## Translations
|
|
||||||
Translations are managed through [Weblate], an online interface for managing language strings. This is synced
|
|
||||||
automatically with GitHub, so please don't submit PRs adding/changing translations!
|
|
||||||
|
|
||||||
## 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 (JDK). This can be downloaded from [Adoptium].
|
- Java Development Kit 17 (JDK). This can be downloaded from [Adoptium].
|
||||||
- [Git](https://git-scm.com/).
|
- [Git](https://git-scm.com/).
|
||||||
- [NodeJS][node].
|
- [NodeJS 20 or later][node].
|
||||||
|
|
||||||
- Download CC: Tweaked's source code:
|
- Download CC: Tweaked's source code:
|
||||||
```
|
```
|
||||||
@@ -101,7 +96,6 @@ 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
|
||||||
|
|||||||
11
README.md
@@ -26,8 +26,9 @@ 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 [forum] and [GitHub discussions page][GitHub discussions]! There's also a fairly
|
ComputerCraft, do check out our [GitHub discussions page][GitHub discussions]! There's also a fairly populated,
|
||||||
populated, albeit quiet [IRC channel][irc], if that's more your cup of tea.
|
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].
|
||||||
|
|
||||||
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").
|
||||||
|
|
||||||
@@ -39,7 +40,7 @@ on is present.
|
|||||||
```groovy
|
```groovy
|
||||||
repositories {
|
repositories {
|
||||||
maven {
|
maven {
|
||||||
url "https://squiddev.cc/maven/"
|
url "https://maven.squiddev.cc"
|
||||||
content {
|
content {
|
||||||
includeGroup("cc.tweaked")
|
includeGroup("cc.tweaked")
|
||||||
}
|
}
|
||||||
@@ -86,6 +87,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
|
||||||
[IRC]: https://webchat.esper.net/?channels=computercraft "#computercraft on EsperNet"
|
[EsperNet]: https://www.esper.net/
|
||||||
|
[KiwiIRC]: https://kiwiirc.com/nextclient/#irc://irc.esper.net:+6697/#computercraft "#computercraft on EsperNet"
|
||||||
|
|||||||
110
REUSE.toml
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
# 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/**", "gradlew", "gradlew.bat"]
|
||||||
|
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"
|
||||||
@@ -36,7 +36,7 @@ repositories {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
maven("https://squiddev.cc/maven") {
|
maven("https://maven.squiddev.cc") {
|
||||||
name = "SquidDev"
|
name = "SquidDev"
|
||||||
content {
|
content {
|
||||||
includeGroup("cc.tweaked.vanilla-extract")
|
includeGroup("cc.tweaked.vanilla-extract")
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ java {
|
|||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
|
|
||||||
val mainMaven = maven("https://squiddev.cc/maven") {
|
val mainMaven = maven("https://maven.squiddev.cc/mirror") {
|
||||||
name = "SquidDev"
|
name = "SquidDev"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,6 +54,7 @@ repositories {
|
|||||||
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")
|
||||||
@@ -94,9 +95,8 @@ 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("UnusedVariable", CheckSeverity.OFF) // Too many false positives with records.
|
check("EnumOrdinal", CheckSeverity.OFF) // For now. We could replace most of these with EnumMap.
|
||||||
check("OperatorPrecedence", CheckSeverity.OFF) // For now.
|
check("OperatorPrecedence", CheckSeverity.OFF) // For now.
|
||||||
check("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 +134,8 @@ tasks.processResources {
|
|||||||
tasks.withType(AbstractArchiveTask::class.java).configureEach {
|
tasks.withType(AbstractArchiveTask::class.java).configureEach {
|
||||||
isPreserveFileTimestamps = false
|
isPreserveFileTimestamps = false
|
||||||
isReproducibleFileOrder = true
|
isReproducibleFileOrder = true
|
||||||
dirMode = Integer.valueOf("755", 8)
|
filePermissions {}
|
||||||
fileMode = Integer.valueOf("664", 8)
|
dirPermissions {}
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.jar {
|
tasks.jar {
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ publishing {
|
|||||||
}
|
}
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
maven("https://squiddev.cc/maven") {
|
maven("https://maven.squiddev.cc") {
|
||||||
name = "SquidDev"
|
name = "SquidDev"
|
||||||
|
|
||||||
credentials(PasswordCredentials::class)
|
credentials(PasswordCredentials::class)
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ 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
|
||||||
@@ -35,7 +34,6 @@ 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(
|
||||||
@@ -182,7 +180,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.capitalized()}Report"
|
val reportTaskName = "jacoco${task.name.capitalise()}Report"
|
||||||
|
|
||||||
val jacoco = project.extensions.getByType(JacocoPluginExtension::class.java)
|
val jacoco = project.extensions.getByType(JacocoPluginExtension::class.java)
|
||||||
task.configure {
|
task.configure {
|
||||||
@@ -226,12 +224,12 @@ abstract class CCTweakedExtension(
|
|||||||
* where possible.
|
* where possible.
|
||||||
*/
|
*/
|
||||||
fun downloadFile(label: String, url: String): File {
|
fun downloadFile(label: String, url: String): File {
|
||||||
val url = URL(url)
|
val uri = URI(url)
|
||||||
val path = File(url.path)
|
val path = File(uri.path)
|
||||||
|
|
||||||
project.repositories.ivy {
|
project.repositories.ivy {
|
||||||
name = label
|
name = label
|
||||||
setUrl(URI(url.protocol, url.userInfo, url.host, url.port, path.parent, null, null))
|
setUrl(URI(uri.scheme, uri.userInfo, uri.host, uri.port, path.parent, null, null))
|
||||||
patternLayout {
|
patternLayout {
|
||||||
artifact("[artifact].[ext]")
|
artifact("[artifact].[ext]")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -155,3 +155,15 @@ 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)
|
||||||
|
}
|
||||||
|
|||||||
@@ -98,7 +98,6 @@ abstract class MergeTrees : DefaultTask() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
val sharedFiles = files.entries.asSequence().filter { (_, v) -> v.found == sources.size }.map { (k, _) -> k }.toList()
|
val sharedFiles = files.entries.asSequence().filter { (_, v) -> v.found == sources.size }.map { (k, _) -> k }.toList()
|
||||||
println(sharedFiles)
|
|
||||||
|
|
||||||
// Copy shared files to the common directory
|
// Copy shared files to the common directory
|
||||||
fsOperations.sync {
|
fsOperations.sync {
|
||||||
|
|||||||
@@ -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 on [GitHub Discussions] or [IRC] either!
|
> cover. That said, don't be afraid to ask [the community for help][community].
|
||||||
|
|
||||||
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,5 +205,4 @@ 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"
|
||||||
[GitHub Discussions]: https://github.com/cc-tweaked/CC-Tweaked/discussions
|
[Community]: /#community
|
||||||
[IRC]: https://webchat.esper.net/?channels=computercraft "#computercraft on EsperNet"
|
|
||||||
|
|||||||
20
doc/index.md
@@ -4,7 +4,14 @@ SPDX-FileCopyrightText: 2020 The CC: Tweaked Developers
|
|||||||
SPDX-License-Identifier: MPL-2.0
|
SPDX-License-Identifier: MPL-2.0
|
||||||
-->
|
-->
|
||||||
|
|
||||||
# 
|
<h1>
|
||||||
|
<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.
|
||||||
@@ -38,12 +45,16 @@ 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](http://www.computercraft.info/forums2/index.php?/topic/15033-computer-basics-i/ "Computer Basics I")
|
- [Lyqyd's Computer Basics 1](https://ccf.squiddev.cc/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.
|
||||||
|
|
||||||
If you get stuck, do [ask a question on GitHub][GitHub Discussions] or pop in to the ComputerCraft's [IRC channel][IRC].
|
<h2 id="community">Community</h2>
|
||||||
|
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].
|
||||||
@@ -58,4 +69,5 @@ 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
|
||||||
[IRC]: https://webchat.esper.net/?channels=computercraft "#computercraft on EsperNet"
|
[EsperNet]: https://www.esper.net/
|
||||||
|
[KiwiIRC]: https://kiwiirc.com/nextclient/#irc://irc.esper.net:+6697/#computercraft "#computercraft on EsperNet"
|
||||||
|
|||||||
@@ -45,12 +45,16 @@ 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](http://www.computercraft.info/forums2/index.php?/topic/15033-computer-basics-i/ "Computer Basics I")
|
- [Lyqyd's Computer Basics 1](https://ccf.squiddev.cc/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.
|
||||||
|
|
||||||
If you get stuck, do [ask a question on GitHub][GitHub Discussions] or pop in to the ComputerCraft's [IRC channel][IRC].
|
## Community
|
||||||
|
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].
|
||||||
@@ -60,4 +64,5 @@ 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
|
||||||
[IRC]: http://webchat.esper.net/?channels=computercraft "#computercraft on EsperNet"
|
[EsperNet]: https://www.esper.net/
|
||||||
|
[KiwiIRC]: https://kiwiirc.com/nextclient/#irc://irc.esper.net:+6697/#computercraft "#computercraft on EsperNet"
|
||||||
|
|||||||
@@ -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.gamepedia.com/Tutorials/Creating_a_data_pack#Legal_characters
|
[legal_data_pack]: https://minecraft.wiki/w/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"
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ kotlin.jvm.target.validation.mode=error
|
|||||||
|
|
||||||
# Mod properties
|
# Mod properties
|
||||||
isUnstable=false
|
isUnstable=false
|
||||||
modVersion=1.110.3
|
modVersion=1.112.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.1
|
||||||
|
|||||||
2
gradle/gradle-daemon-jvm.properties
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
#This file is generated by updateDaemonJvm
|
||||||
|
toolchainVersion=17
|
||||||
@@ -46,6 +46,8 @@ oculus = "1.2.5"
|
|||||||
rei = "12.0.626"
|
rei = "12.0.626"
|
||||||
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"
|
||||||
@@ -57,21 +59,21 @@ jmh = "1.37"
|
|||||||
cctJavadoc = "1.8.2"
|
cctJavadoc = "1.8.2"
|
||||||
checkstyle = "10.14.1"
|
checkstyle = "10.14.1"
|
||||||
curseForgeGradle = "1.0.14"
|
curseForgeGradle = "1.0.14"
|
||||||
errorProne-core = "2.23.0"
|
errorProne-core = "2.27.0"
|
||||||
errorProne-plugin = "3.1.0"
|
errorProne-plugin = "3.1.0"
|
||||||
fabric-loom = "1.6.7"
|
fabric-loom = "1.7.1"
|
||||||
forgeGradle = "6.0.21"
|
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-71-g378d86e"
|
illuaminate = "0.1.0-73-g43ee16c"
|
||||||
librarian = "1.+"
|
librarian = "1.+"
|
||||||
lwjgl = "3.3.3"
|
lwjgl = "3.3.3"
|
||||||
minotaur = "2.+"
|
minotaur = "2.+"
|
||||||
nullAway = "0.10.25"
|
nullAway = "0.10.25"
|
||||||
spotless = "6.23.3"
|
spotless = "6.23.3"
|
||||||
taskTree = "2.1.1"
|
taskTree = "2.1.1"
|
||||||
teavm = "0.10.0-SQUID.4"
|
teavm = "0.11.0-SQUID.1"
|
||||||
vanillaExtract = "0.1.3"
|
vanillaExtract = "0.1.3"
|
||||||
versionCatalogUpdate = "0.8.1"
|
versionCatalogUpdate = "0.8.1"
|
||||||
|
|
||||||
@@ -100,11 +102,13 @@ nightConfig-toml = { module = "com.electronwill.night-config:toml", version.ref
|
|||||||
slf4j = { module = "org.slf4j:slf4j-api", version.ref = "slf4j" }
|
slf4j = { module = "org.slf4j:slf4j-api", version.ref = "slf4j" }
|
||||||
|
|
||||||
# Minecraft mods
|
# Minecraft mods
|
||||||
fabric-api = { module = "net.fabricmc.fabric-api:fabric-api", version.ref = "fabric-api" }
|
create-fabric = { module = "com.simibubi.create:create-fabric-1.20.1", version.ref = "create-fabric" }
|
||||||
fabric-loader = { module = "net.fabricmc:fabric-loader", version.ref = "fabric-loader" }
|
create-forge = { module = "com.simibubi.create:create-1.20.1", version.ref = "create-forge" }
|
||||||
fabric-junit = { module = "net.fabricmc:fabric-loader-junit", version.ref = "fabric-loader" }
|
|
||||||
fabricPermissions = { module = "me.lucko:fabric-permissions-api", version.ref = "fabricPermissions" }
|
|
||||||
emi = { module = "dev.emi:emi-xplat-mojmap", version.ref = "emi" }
|
emi = { module = "dev.emi:emi-xplat-mojmap", version.ref = "emi" }
|
||||||
|
fabric-api = { module = "net.fabricmc.fabric-api:fabric-api", version.ref = "fabric-api" }
|
||||||
|
fabric-junit = { module = "net.fabricmc:fabric-loader-junit", version.ref = "fabric-loader" }
|
||||||
|
fabric-loader = { module = "net.fabricmc:fabric-loader", version.ref = "fabric-loader" }
|
||||||
|
fabricPermissions = { module = "me.lucko:fabric-permissions-api", version.ref = "fabricPermissions" }
|
||||||
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.1-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.1-fabric", version.ref = "jei" }
|
||||||
|
|||||||
2
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,6 +1,6 @@
|
|||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip
|
||||||
networkTimeout=10000
|
networkTimeout=10000
|
||||||
validateDistributionUrl=true
|
validateDistributionUrl=true
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
|||||||
2
gradlew
vendored
@@ -55,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/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/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/.
|
||||||
|
|||||||
@@ -4,9 +4,11 @@
|
|||||||
|
|
||||||
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;
|
||||||
@@ -165,7 +167,20 @@ 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.
|
* to use peripherals to provide functionality to users. If an API is <em>required</em>, you may want to consider
|
||||||
|
* 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
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
// 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
// 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 + ")";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
// 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");
|
||||||
|
}
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
// 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);
|
||||||
|
}
|
||||||
@@ -4,13 +4,15 @@
|
|||||||
|
|
||||||
package dan200.computercraft.api.lua;
|
package dan200.computercraft.api.lua;
|
||||||
|
|
||||||
|
import dan200.computercraft.api.ComputerCraftAPI;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct an {@link ILuaAPI} for a specific computer.
|
* Construct an {@link ILuaAPI} for a computer.
|
||||||
*
|
*
|
||||||
* @see ILuaAPI
|
* @see ILuaAPI
|
||||||
* @see dan200.computercraft.api.ComputerCraftAPI#registerAPIFactory(ILuaAPIFactory)
|
* @see ComputerCraftAPI#registerAPIFactory(ILuaAPIFactory)
|
||||||
*/
|
*/
|
||||||
@FunctionalInterface
|
@FunctionalInterface
|
||||||
public interface ILuaAPIFactory {
|
public interface ILuaAPIFactory {
|
||||||
@@ -6,10 +6,14 @@ 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.UpgradeData;
|
||||||
import net.minecraft.nbt.CompoundTag;
|
import net.minecraft.nbt.CompoundTag;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
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;
|
import java.util.Map;
|
||||||
@@ -17,7 +21,22 @@ 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>
|
||||||
@@ -64,6 +83,26 @@ 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>
|
||||||
@@ -73,6 +112,7 @@ 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();
|
||||||
|
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ dependencies {
|
|||||||
clientImplementation(clientClasses(project(":common-api")))
|
clientImplementation(clientClasses(project(":common-api")))
|
||||||
|
|
||||||
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)
|
||||||
@@ -127,3 +128,5 @@ val runData by tasks.registering(MergeTrees::class) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tasks.withType(GenerateModuleMetadata::class).configureEach { isEnabled = false }
|
||||||
|
|||||||
@@ -25,7 +25,6 @@ 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;
|
||||||
@@ -83,7 +82,6 @@ public final class ClientRegistry {
|
|||||||
*/
|
*/
|
||||||
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, ComputerScreen<AbstractComputerMenu>>register(ModRegistry.Menus.COMPUTER.get(), ComputerScreen::new);
|
||||||
MenuScreens.<AbstractComputerMenu, ComputerScreen<AbstractComputerMenu>>register(ModRegistry.Menus.POCKET_COMPUTER.get(), ComputerScreen::new);
|
|
||||||
MenuScreens.<AbstractComputerMenu, NoTermComputerScreen<AbstractComputerMenu>>register(ModRegistry.Menus.POCKET_COMPUTER_NO_TERM.get(), NoTermComputerScreen::new);
|
MenuScreens.<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.TURTLE.get(), TurtleScreen::new);
|
||||||
|
|
||||||
@@ -91,8 +89,6 @@ public final class ClientRegistry {
|
|||||||
MenuScreens.register(ModRegistry.Menus.DISK_DRIVE.get(), DiskDriveScreen::new);
|
MenuScreens.register(ModRegistry.Menus.DISK_DRIVE.get(), DiskDriveScreen::new);
|
||||||
MenuScreens.register(ModRegistry.Menus.PRINTOUT.get(), PrintoutScreen::new);
|
MenuScreens.register(ModRegistry.Menus.PRINTOUT.get(), PrintoutScreen::new);
|
||||||
|
|
||||||
MenuScreens.<ViewComputerMenu, ComputerScreen<ViewComputerMenu>>register(ModRegistry.Menus.VIEW_COMPUTER.get(), ComputerScreen::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);
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
//
|
//
|
||||||
// SPDX-License-Identifier: MPL-2.0
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
package dan200.computercraft.shared.integration.jei;
|
package dan200.computercraft.client.integration.jei;
|
||||||
|
|
||||||
import dan200.computercraft.api.ComputerCraftAPI;
|
import dan200.computercraft.api.ComputerCraftAPI;
|
||||||
import dan200.computercraft.api.turtle.TurtleSide;
|
import dan200.computercraft.api.turtle.TurtleSide;
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
//
|
//
|
||||||
// SPDX-License-Identifier: MPL-2.0
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
package dan200.computercraft.shared.integration.jei;
|
package dan200.computercraft.client.integration.jei;
|
||||||
|
|
||||||
import dan200.computercraft.shared.integration.UpgradeRecipeGenerator;
|
import dan200.computercraft.shared.integration.UpgradeRecipeGenerator;
|
||||||
import dan200.computercraft.shared.pocket.items.PocketComputerItem;
|
import dan200.computercraft.shared.pocket.items.PocketComputerItem;
|
||||||
@@ -1 +1,12 @@
|
|||||||
{"parent": "computercraft:block/turtle_base", "textures": {"texture": "computercraft:block/turtle_advanced"}}
|
{
|
||||||
|
"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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1 +1,12 @@
|
|||||||
{"parent": "computercraft:block/turtle_base", "textures": {"texture": "computercraft:block/turtle_normal"}}
|
{
|
||||||
|
"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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -38,6 +38,9 @@ import static net.minecraft.data.models.model.TextureMapping.getBlockTexture;
|
|||||||
|
|
||||||
class BlockModelProvider {
|
class BlockModelProvider {
|
||||||
private static final TextureSlot CURSOR = TextureSlot.create("cursor");
|
private static final TextureSlot CURSOR = TextureSlot.create("cursor");
|
||||||
|
private static final TextureSlot LEFT = TextureSlot.create("left");
|
||||||
|
private static final TextureSlot RIGHT = TextureSlot.create("right");
|
||||||
|
private static final TextureSlot BACKPACK = TextureSlot.create("backpack");
|
||||||
|
|
||||||
private static final ModelTemplate COMPUTER_ON = new ModelTemplate(
|
private static final ModelTemplate COMPUTER_ON = new ModelTemplate(
|
||||||
Optional.of(new ResourceLocation(ComputerCraftAPI.MOD_ID, "block/computer_on")),
|
Optional.of(new ResourceLocation(ComputerCraftAPI.MOD_ID, "block/computer_on")),
|
||||||
@@ -58,7 +61,7 @@ class BlockModelProvider {
|
|||||||
private static final ModelTemplate TURTLE = new ModelTemplate(
|
private static final ModelTemplate TURTLE = new ModelTemplate(
|
||||||
Optional.of(new ResourceLocation(ComputerCraftAPI.MOD_ID, "block/turtle_base")),
|
Optional.of(new ResourceLocation(ComputerCraftAPI.MOD_ID, "block/turtle_base")),
|
||||||
Optional.empty(),
|
Optional.empty(),
|
||||||
TextureSlot.TEXTURE
|
TextureSlot.FRONT, TextureSlot.BACK, TextureSlot.TOP, TextureSlot.BOTTOM, LEFT, RIGHT, BACKPACK
|
||||||
);
|
);
|
||||||
private static final ModelTemplate TURTLE_UPGRADE_LEFT = new ModelTemplate(
|
private static final ModelTemplate TURTLE_UPGRADE_LEFT = new ModelTemplate(
|
||||||
Optional.of(new ResourceLocation(ComputerCraftAPI.MOD_ID, "block/turtle_upgrade_base_left")),
|
Optional.of(new ResourceLocation(ComputerCraftAPI.MOD_ID, "block/turtle_upgrade_base_left")),
|
||||||
@@ -167,7 +170,16 @@ class BlockModelProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static void registerTurtle(BlockModelGenerators generators, TurtleBlock block) {
|
private static void registerTurtle(BlockModelGenerators generators, TurtleBlock block) {
|
||||||
var model = TURTLE.create(block, TextureMapping.defaultTexture(block), generators.modelOutput);
|
var model = TURTLE.create(block, new TextureMapping()
|
||||||
|
.put(TextureSlot.FRONT, getBlockTexture(block, "_front"))
|
||||||
|
.put(TextureSlot.BACK, getBlockTexture(block, "_back"))
|
||||||
|
.put(TextureSlot.TOP, getBlockTexture(block, "_top"))
|
||||||
|
.put(TextureSlot.BOTTOM, getBlockTexture(block, "_bottom"))
|
||||||
|
.put(LEFT, getBlockTexture(block, "_left"))
|
||||||
|
.put(RIGHT, getBlockTexture(block, "_right"))
|
||||||
|
.put(BACKPACK, getBlockTexture(block, "_backpack")),
|
||||||
|
generators.modelOutput
|
||||||
|
);
|
||||||
generators.blockStateOutput.accept(
|
generators.blockStateOutput.accept(
|
||||||
MultiVariantGenerator.multiVariant(block, Variant.variant().with(VariantProperties.MODEL, model))
|
MultiVariantGenerator.multiVariant(block, Variant.variant().with(VariantProperties.MODEL, model))
|
||||||
.with(createHorizontalFacingDispatch())
|
.with(createHorizontalFacingDispatch())
|
||||||
|
|||||||
@@ -36,13 +36,13 @@ public final class ItemModelProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static void registerPocketComputer(ItemModelGenerators generators, ResourceLocation id, boolean off) {
|
private static void registerPocketComputer(ItemModelGenerators generators, ResourceLocation id, boolean off) {
|
||||||
createFlatItem(generators, addSuffix(id, "_blinking"),
|
createFlatItem(generators, id.withSuffix("_blinking"),
|
||||||
new ResourceLocation(ComputerCraftAPI.MOD_ID, "item/pocket_computer_blink"),
|
new ResourceLocation(ComputerCraftAPI.MOD_ID, "item/pocket_computer_blink"),
|
||||||
id,
|
id,
|
||||||
new ResourceLocation(ComputerCraftAPI.MOD_ID, "item/pocket_computer_light")
|
new ResourceLocation(ComputerCraftAPI.MOD_ID, "item/pocket_computer_light")
|
||||||
);
|
);
|
||||||
|
|
||||||
createFlatItem(generators, addSuffix(id, "_on"),
|
createFlatItem(generators, id.withSuffix("_on"),
|
||||||
new ResourceLocation(ComputerCraftAPI.MOD_ID, "item/pocket_computer_on"),
|
new ResourceLocation(ComputerCraftAPI.MOD_ID, "item/pocket_computer_on"),
|
||||||
id,
|
id,
|
||||||
new ResourceLocation(ComputerCraftAPI.MOD_ID, "item/pocket_computer_light")
|
new ResourceLocation(ComputerCraftAPI.MOD_ID, "item/pocket_computer_light")
|
||||||
@@ -94,8 +94,4 @@ public final class ItemModelProvider {
|
|||||||
new ModelTemplate(Optional.of(new ResourceLocation("item/generated")), Optional.empty(), slots)
|
new ModelTemplate(Optional.of(new ResourceLocation("item/generated")), Optional.empty(), slots)
|
||||||
.create(model, mapping, generators.output);
|
.create(model, mapping, generators.output);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ResourceLocation addSuffix(ResourceLocation location, String suffix) {
|
|
||||||
return new ResourceLocation(location.getNamespace(), location.getPath() + suffix);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ import java.util.Objects;
|
|||||||
/**
|
/**
|
||||||
* The global factory for {@link ILuaAPIFactory}s.
|
* The global factory for {@link ILuaAPIFactory}s.
|
||||||
*
|
*
|
||||||
* @see dan200.computercraft.core.ComputerContext.Builder#apiFactories(Collection)
|
|
||||||
* @see dan200.computercraft.api.ComputerCraftAPI#registerAPIFactory(ILuaAPIFactory)
|
* @see dan200.computercraft.api.ComputerCraftAPI#registerAPIFactory(ILuaAPIFactory)
|
||||||
*/
|
*/
|
||||||
public final class ApiFactories {
|
public final class ApiFactories {
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ package dan200.computercraft.shared;
|
|||||||
|
|
||||||
import com.mojang.brigadier.arguments.ArgumentType;
|
import com.mojang.brigadier.arguments.ArgumentType;
|
||||||
import dan200.computercraft.api.ComputerCraftAPI;
|
import dan200.computercraft.api.ComputerCraftAPI;
|
||||||
|
import dan200.computercraft.api.component.ComputerComponents;
|
||||||
import dan200.computercraft.api.detail.DetailProvider;
|
import dan200.computercraft.api.detail.DetailProvider;
|
||||||
import dan200.computercraft.api.detail.VanillaDetailRegistries;
|
import dan200.computercraft.api.detail.VanillaDetailRegistries;
|
||||||
import dan200.computercraft.api.media.IMedia;
|
import dan200.computercraft.api.media.IMedia;
|
||||||
@@ -23,13 +24,12 @@ import dan200.computercraft.shared.common.ClearColourRecipe;
|
|||||||
import dan200.computercraft.shared.common.ColourableRecipe;
|
import dan200.computercraft.shared.common.ColourableRecipe;
|
||||||
import dan200.computercraft.shared.common.DefaultBundledRedstoneProvider;
|
import dan200.computercraft.shared.common.DefaultBundledRedstoneProvider;
|
||||||
import dan200.computercraft.shared.common.HeldItemMenu;
|
import dan200.computercraft.shared.common.HeldItemMenu;
|
||||||
|
import dan200.computercraft.shared.computer.apis.CommandAPI;
|
||||||
import dan200.computercraft.shared.computer.blocks.CommandComputerBlock;
|
import dan200.computercraft.shared.computer.blocks.CommandComputerBlock;
|
||||||
import dan200.computercraft.shared.computer.blocks.CommandComputerBlockEntity;
|
|
||||||
import dan200.computercraft.shared.computer.blocks.ComputerBlock;
|
import dan200.computercraft.shared.computer.blocks.ComputerBlock;
|
||||||
import dan200.computercraft.shared.computer.blocks.ComputerBlockEntity;
|
import dan200.computercraft.shared.computer.blocks.ComputerBlockEntity;
|
||||||
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
||||||
import dan200.computercraft.shared.computer.inventory.ComputerMenuWithoutInventory;
|
import dan200.computercraft.shared.computer.inventory.ComputerMenuWithoutInventory;
|
||||||
import dan200.computercraft.shared.computer.inventory.ViewComputerMenu;
|
|
||||||
import dan200.computercraft.shared.computer.items.CommandComputerItem;
|
import dan200.computercraft.shared.computer.items.CommandComputerItem;
|
||||||
import dan200.computercraft.shared.computer.items.ComputerItem;
|
import dan200.computercraft.shared.computer.items.ComputerItem;
|
||||||
import dan200.computercraft.shared.computer.recipe.ComputerUpgradeRecipe;
|
import dan200.computercraft.shared.computer.recipe.ComputerUpgradeRecipe;
|
||||||
@@ -66,6 +66,7 @@ import dan200.computercraft.shared.peripheral.speaker.SpeakerBlockEntity;
|
|||||||
import dan200.computercraft.shared.platform.PlatformHelper;
|
import dan200.computercraft.shared.platform.PlatformHelper;
|
||||||
import dan200.computercraft.shared.platform.RegistrationHelper;
|
import dan200.computercraft.shared.platform.RegistrationHelper;
|
||||||
import dan200.computercraft.shared.platform.RegistryEntry;
|
import dan200.computercraft.shared.platform.RegistryEntry;
|
||||||
|
import dan200.computercraft.shared.pocket.apis.PocketAPI;
|
||||||
import dan200.computercraft.shared.pocket.items.PocketComputerItem;
|
import dan200.computercraft.shared.pocket.items.PocketComputerItem;
|
||||||
import dan200.computercraft.shared.pocket.peripherals.PocketModem;
|
import dan200.computercraft.shared.pocket.peripherals.PocketModem;
|
||||||
import dan200.computercraft.shared.pocket.peripherals.PocketSpeaker;
|
import dan200.computercraft.shared.pocket.peripherals.PocketSpeaker;
|
||||||
@@ -75,14 +76,17 @@ import dan200.computercraft.shared.recipe.CustomShapelessRecipe;
|
|||||||
import dan200.computercraft.shared.recipe.ImpostorShapedRecipe;
|
import dan200.computercraft.shared.recipe.ImpostorShapedRecipe;
|
||||||
import dan200.computercraft.shared.recipe.ImpostorShapelessRecipe;
|
import dan200.computercraft.shared.recipe.ImpostorShapelessRecipe;
|
||||||
import dan200.computercraft.shared.turtle.FurnaceRefuelHandler;
|
import dan200.computercraft.shared.turtle.FurnaceRefuelHandler;
|
||||||
|
import dan200.computercraft.shared.turtle.apis.TurtleAPI;
|
||||||
import dan200.computercraft.shared.turtle.blocks.TurtleBlock;
|
import dan200.computercraft.shared.turtle.blocks.TurtleBlock;
|
||||||
import dan200.computercraft.shared.turtle.blocks.TurtleBlockEntity;
|
import dan200.computercraft.shared.turtle.blocks.TurtleBlockEntity;
|
||||||
|
import dan200.computercraft.shared.turtle.core.TurtleAccessInternal;
|
||||||
import dan200.computercraft.shared.turtle.inventory.TurtleMenu;
|
import dan200.computercraft.shared.turtle.inventory.TurtleMenu;
|
||||||
import dan200.computercraft.shared.turtle.items.TurtleItem;
|
import dan200.computercraft.shared.turtle.items.TurtleItem;
|
||||||
import dan200.computercraft.shared.turtle.recipes.TurtleOverlayRecipe;
|
import dan200.computercraft.shared.turtle.recipes.TurtleOverlayRecipe;
|
||||||
import dan200.computercraft.shared.turtle.recipes.TurtleRecipe;
|
import dan200.computercraft.shared.turtle.recipes.TurtleRecipe;
|
||||||
import dan200.computercraft.shared.turtle.recipes.TurtleUpgradeRecipe;
|
import dan200.computercraft.shared.turtle.recipes.TurtleUpgradeRecipe;
|
||||||
import dan200.computercraft.shared.turtle.upgrades.*;
|
import dan200.computercraft.shared.turtle.upgrades.*;
|
||||||
|
import dan200.computercraft.shared.util.ComponentMap;
|
||||||
import net.minecraft.commands.CommandSourceStack;
|
import net.minecraft.commands.CommandSourceStack;
|
||||||
import net.minecraft.commands.synchronization.ArgumentTypeInfo;
|
import net.minecraft.commands.synchronization.ArgumentTypeInfo;
|
||||||
import net.minecraft.commands.synchronization.SingletonArgumentInfo;
|
import net.minecraft.commands.synchronization.SingletonArgumentInfo;
|
||||||
@@ -104,6 +108,7 @@ import net.minecraft.world.level.block.state.BlockState;
|
|||||||
import net.minecraft.world.level.material.MapColor;
|
import net.minecraft.world.level.material.MapColor;
|
||||||
import net.minecraft.world.level.storage.loot.predicates.LootItemConditionType;
|
import net.minecraft.world.level.storage.loot.predicates.LootItemConditionType;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.function.BiFunction;
|
import java.util.function.BiFunction;
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
@@ -142,8 +147,7 @@ public final class ModRegistry {
|
|||||||
() -> new ComputerBlock<>(computerProperties().mapColor(MapColor.STONE), BlockEntities.COMPUTER_NORMAL));
|
() -> new ComputerBlock<>(computerProperties().mapColor(MapColor.STONE), BlockEntities.COMPUTER_NORMAL));
|
||||||
public static final RegistryEntry<ComputerBlock<ComputerBlockEntity>> COMPUTER_ADVANCED = REGISTRY.register("computer_advanced",
|
public static final RegistryEntry<ComputerBlock<ComputerBlockEntity>> COMPUTER_ADVANCED = REGISTRY.register("computer_advanced",
|
||||||
() -> new ComputerBlock<>(computerProperties().mapColor(MapColor.GOLD), BlockEntities.COMPUTER_ADVANCED));
|
() -> new ComputerBlock<>(computerProperties().mapColor(MapColor.GOLD), BlockEntities.COMPUTER_ADVANCED));
|
||||||
|
public static final RegistryEntry<ComputerBlock<ComputerBlockEntity>> COMPUTER_COMMAND = REGISTRY.register("computer_command",
|
||||||
public static final RegistryEntry<ComputerBlock<CommandComputerBlockEntity>> COMPUTER_COMMAND = REGISTRY.register("computer_command",
|
|
||||||
() -> new CommandComputerBlock<>(computerProperties().strength(-1, 6000000.0F), BlockEntities.COMPUTER_COMMAND));
|
() -> new CommandComputerBlock<>(computerProperties().strength(-1, 6000000.0F), BlockEntities.COMPUTER_COMMAND));
|
||||||
|
|
||||||
public static final RegistryEntry<TurtleBlock> TURTLE_NORMAL = REGISTRY.register("turtle_normal",
|
public static final RegistryEntry<TurtleBlock> TURTLE_NORMAL = REGISTRY.register("turtle_normal",
|
||||||
@@ -186,8 +190,8 @@ public final class ModRegistry {
|
|||||||
ofBlock(Blocks.COMPUTER_NORMAL, (p, s) -> new ComputerBlockEntity(BlockEntities.COMPUTER_NORMAL.get(), p, s, ComputerFamily.NORMAL));
|
ofBlock(Blocks.COMPUTER_NORMAL, (p, s) -> new ComputerBlockEntity(BlockEntities.COMPUTER_NORMAL.get(), p, s, ComputerFamily.NORMAL));
|
||||||
public static final RegistryEntry<BlockEntityType<ComputerBlockEntity>> COMPUTER_ADVANCED =
|
public static final RegistryEntry<BlockEntityType<ComputerBlockEntity>> COMPUTER_ADVANCED =
|
||||||
ofBlock(Blocks.COMPUTER_ADVANCED, (p, s) -> new ComputerBlockEntity(BlockEntities.COMPUTER_ADVANCED.get(), p, s, ComputerFamily.ADVANCED));
|
ofBlock(Blocks.COMPUTER_ADVANCED, (p, s) -> new ComputerBlockEntity(BlockEntities.COMPUTER_ADVANCED.get(), p, s, ComputerFamily.ADVANCED));
|
||||||
public static final RegistryEntry<BlockEntityType<CommandComputerBlockEntity>> COMPUTER_COMMAND =
|
public static final RegistryEntry<BlockEntityType<ComputerBlockEntity>> COMPUTER_COMMAND =
|
||||||
ofBlock(Blocks.COMPUTER_COMMAND, (p, s) -> new CommandComputerBlockEntity(BlockEntities.COMPUTER_COMMAND.get(), p, s));
|
ofBlock(Blocks.COMPUTER_COMMAND, (p, s) -> new ComputerBlockEntity(BlockEntities.COMPUTER_COMMAND.get(), p, s, ComputerFamily.COMMAND));
|
||||||
|
|
||||||
public static final RegistryEntry<BlockEntityType<TurtleBlockEntity>> TURTLE_NORMAL =
|
public static final RegistryEntry<BlockEntityType<TurtleBlockEntity>> TURTLE_NORMAL =
|
||||||
ofBlock(Blocks.TURTLE_NORMAL, (p, s) -> new TurtleBlockEntity(BlockEntities.TURTLE_NORMAL.get(), p, s, () -> Config.turtleFuelLimit, ComputerFamily.NORMAL));
|
ofBlock(Blocks.TURTLE_NORMAL, (p, s) -> new TurtleBlockEntity(BlockEntities.TURTLE_NORMAL.get(), p, s, () -> Config.turtleFuelLimit, ComputerFamily.NORMAL));
|
||||||
@@ -293,9 +297,6 @@ public final class ModRegistry {
|
|||||||
public static final RegistryEntry<MenuType<ComputerMenuWithoutInventory>> COMPUTER = REGISTRY.register("computer",
|
public static final RegistryEntry<MenuType<ComputerMenuWithoutInventory>> COMPUTER = REGISTRY.register("computer",
|
||||||
() -> ContainerData.toType(ComputerContainerData::new, (id, inv, data) -> new ComputerMenuWithoutInventory(Menus.COMPUTER.get(), id, inv, data)));
|
() -> ContainerData.toType(ComputerContainerData::new, (id, inv, data) -> new ComputerMenuWithoutInventory(Menus.COMPUTER.get(), id, inv, data)));
|
||||||
|
|
||||||
public static final RegistryEntry<MenuType<ComputerMenuWithoutInventory>> POCKET_COMPUTER = REGISTRY.register("pocket_computer",
|
|
||||||
() -> ContainerData.toType(ComputerContainerData::new, (id, inv, data) -> new ComputerMenuWithoutInventory(Menus.POCKET_COMPUTER.get(), id, inv, data)));
|
|
||||||
|
|
||||||
public static final RegistryEntry<MenuType<ComputerMenuWithoutInventory>> POCKET_COMPUTER_NO_TERM = REGISTRY.register("pocket_computer_no_term",
|
public static final RegistryEntry<MenuType<ComputerMenuWithoutInventory>> POCKET_COMPUTER_NO_TERM = REGISTRY.register("pocket_computer_no_term",
|
||||||
() -> ContainerData.toType(ComputerContainerData::new, (id, inv, data) -> new ComputerMenuWithoutInventory(Menus.POCKET_COMPUTER_NO_TERM.get(), id, inv, data)));
|
() -> ContainerData.toType(ComputerContainerData::new, (id, inv, data) -> new ComputerMenuWithoutInventory(Menus.POCKET_COMPUTER_NO_TERM.get(), id, inv, data)));
|
||||||
|
|
||||||
@@ -313,9 +314,6 @@ public final class ModRegistry {
|
|||||||
HeldItemContainerData::new,
|
HeldItemContainerData::new,
|
||||||
(id, inventory, data) -> new HeldItemMenu(Menus.PRINTOUT.get(), id, inventory.player, data.getHand())
|
(id, inventory, data) -> new HeldItemMenu(Menus.PRINTOUT.get(), id, inventory.player, data.getHand())
|
||||||
));
|
));
|
||||||
|
|
||||||
public static final RegistryEntry<MenuType<ViewComputerMenu>> VIEW_COMPUTER = REGISTRY.register("view_computer",
|
|
||||||
() -> ContainerData.toType(ComputerContainerData::new, ViewComputerMenu::new));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static class ArgumentTypes {
|
static class ArgumentTypes {
|
||||||
@@ -456,6 +454,22 @@ public final class ModRegistry {
|
|||||||
return null;
|
return null;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
ComputerCraftAPI.registerAPIFactory(computer -> {
|
||||||
|
var turtle = computer.getComponent(ComputerComponents.TURTLE);
|
||||||
|
var metrics = Objects.requireNonNull(computer.getComponent(ComponentMap.METRICS));
|
||||||
|
return turtle == null ? null : new TurtleAPI(metrics, (TurtleAccessInternal) turtle);
|
||||||
|
});
|
||||||
|
|
||||||
|
ComputerCraftAPI.registerAPIFactory(computer -> {
|
||||||
|
var pocket = computer.getComponent(ComputerComponents.POCKET);
|
||||||
|
return pocket == null ? null : new PocketAPI(pocket);
|
||||||
|
});
|
||||||
|
|
||||||
|
ComputerCraftAPI.registerAPIFactory(computer -> {
|
||||||
|
var admin = computer.getComponent(ComputerComponents.ADMIN_COMPUTER);
|
||||||
|
return admin == null ? null : new CommandAPI(computer, admin);
|
||||||
|
});
|
||||||
|
|
||||||
VanillaDetailRegistries.ITEM_STACK.addProvider(ItemDetails::fill);
|
VanillaDetailRegistries.ITEM_STACK.addProvider(ItemDetails::fill);
|
||||||
VanillaDetailRegistries.BLOCK_IN_WORLD.addProvider(BlockDetails::fill);
|
VanillaDetailRegistries.BLOCK_IN_WORLD.addProvider(BlockDetails::fill);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ import dan200.computercraft.shared.command.text.TableBuilder;
|
|||||||
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
||||||
import dan200.computercraft.shared.computer.core.ServerComputer;
|
import dan200.computercraft.shared.computer.core.ServerComputer;
|
||||||
import dan200.computercraft.shared.computer.core.ServerContext;
|
import dan200.computercraft.shared.computer.core.ServerContext;
|
||||||
import dan200.computercraft.shared.computer.inventory.ViewComputerMenu;
|
import dan200.computercraft.shared.computer.inventory.ComputerMenuWithoutInventory;
|
||||||
import dan200.computercraft.shared.computer.metrics.basic.Aggregate;
|
import dan200.computercraft.shared.computer.metrics.basic.Aggregate;
|
||||||
import dan200.computercraft.shared.computer.metrics.basic.AggregatedMetric;
|
import dan200.computercraft.shared.computer.metrics.basic.AggregatedMetric;
|
||||||
import dan200.computercraft.shared.computer.metrics.basic.BasicComputerMetricsObserver;
|
import dan200.computercraft.shared.computer.metrics.basic.BasicComputerMetricsObserver;
|
||||||
@@ -269,7 +269,7 @@ public final class CommandComputerCraft {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AbstractContainerMenu createMenu(int id, Inventory player, Player entity) {
|
public AbstractContainerMenu createMenu(int id, Inventory player, Player entity) {
|
||||||
return new ViewComputerMenu(id, player, computer);
|
return new ComputerMenuWithoutInventory(ModRegistry.Menus.COMPUTER.get(), id, player, p -> true, computer);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return 1;
|
return 1;
|
||||||
|
|||||||
@@ -6,19 +6,23 @@ package dan200.computercraft.shared.computer.apis;
|
|||||||
|
|
||||||
import com.mojang.brigadier.tree.CommandNode;
|
import com.mojang.brigadier.tree.CommandNode;
|
||||||
import com.mojang.brigadier.tree.LiteralCommandNode;
|
import com.mojang.brigadier.tree.LiteralCommandNode;
|
||||||
|
import dan200.computercraft.api.component.AdminComputer;
|
||||||
import dan200.computercraft.api.detail.BlockReference;
|
import dan200.computercraft.api.detail.BlockReference;
|
||||||
import dan200.computercraft.api.detail.VanillaDetailRegistries;
|
import dan200.computercraft.api.detail.VanillaDetailRegistries;
|
||||||
import dan200.computercraft.api.lua.*;
|
import dan200.computercraft.api.lua.*;
|
||||||
import dan200.computercraft.core.Logging;
|
import dan200.computercraft.core.Logging;
|
||||||
import dan200.computercraft.shared.computer.blocks.CommandComputerBlockEntity;
|
|
||||||
import dan200.computercraft.shared.util.NBTUtil;
|
import dan200.computercraft.shared.util.NBTUtil;
|
||||||
|
import net.minecraft.commands.CommandSource;
|
||||||
import net.minecraft.commands.CommandSourceStack;
|
import net.minecraft.commands.CommandSourceStack;
|
||||||
import net.minecraft.core.BlockPos;
|
import net.minecraft.core.BlockPos;
|
||||||
import net.minecraft.core.registries.Registries;
|
import net.minecraft.core.registries.Registries;
|
||||||
|
import net.minecraft.network.chat.Component;
|
||||||
import net.minecraft.resources.ResourceKey;
|
import net.minecraft.resources.ResourceKey;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
import net.minecraft.server.level.ServerLevel;
|
import net.minecraft.world.level.GameRules;
|
||||||
import net.minecraft.world.level.Level;
|
import net.minecraft.world.level.Level;
|
||||||
|
import net.minecraft.world.phys.Vec2;
|
||||||
|
import net.minecraft.world.phys.Vec3;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
@@ -31,10 +35,13 @@ import java.util.*;
|
|||||||
public class CommandAPI implements ILuaAPI {
|
public class CommandAPI implements ILuaAPI {
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(CommandAPI.class);
|
private static final Logger LOG = LoggerFactory.getLogger(CommandAPI.class);
|
||||||
|
|
||||||
private final CommandComputerBlockEntity computer;
|
private final IComputerSystem computer;
|
||||||
|
private final AdminComputer admin;
|
||||||
|
private final OutputReceiver receiver = new OutputReceiver();
|
||||||
|
|
||||||
public CommandAPI(CommandComputerBlockEntity computer) {
|
public CommandAPI(IComputerSystem computer, AdminComputer admin) {
|
||||||
this.computer = computer;
|
this.computer = computer;
|
||||||
|
this.admin = admin;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -48,15 +55,14 @@ public class CommandAPI implements ILuaAPI {
|
|||||||
|
|
||||||
private Object[] doCommand(String command) {
|
private Object[] doCommand(String command) {
|
||||||
var server = computer.getLevel().getServer();
|
var server = computer.getLevel().getServer();
|
||||||
if (server == null || !server.isCommandBlockEnabled()) {
|
if (!server.isCommandBlockEnabled()) {
|
||||||
return new Object[]{ false, createOutput("Command blocks disabled by server") };
|
return new Object[]{ false, createOutput("Command blocks disabled by server") };
|
||||||
}
|
}
|
||||||
|
|
||||||
var commandManager = server.getCommands();
|
var commandManager = server.getCommands();
|
||||||
var receiver = computer.getReceiver();
|
|
||||||
try {
|
try {
|
||||||
receiver.clearOutput();
|
receiver.clearOutput();
|
||||||
var result = commandManager.performPrefixedCommand(computer.getSource(), command);
|
var result = commandManager.performPrefixedCommand(getSource(), command);
|
||||||
return new Object[]{ result > 0, receiver.copyOutput(), result };
|
return new Object[]{ result > 0, receiver.copyOutput(), result };
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
LOG.error(Logging.JAVA_ERROR, "Error running command.", t);
|
LOG.error(Logging.JAVA_ERROR, "Error running command.", t);
|
||||||
@@ -134,7 +140,6 @@ public class CommandAPI implements ILuaAPI {
|
|||||||
public final List<String> list(IArguments args) throws LuaException {
|
public final List<String> list(IArguments args) throws LuaException {
|
||||||
var server = computer.getLevel().getServer();
|
var server = computer.getLevel().getServer();
|
||||||
|
|
||||||
if (server == null) return List.of();
|
|
||||||
CommandNode<CommandSourceStack> node = server.getCommands().getDispatcher().getRoot();
|
CommandNode<CommandSourceStack> node = server.getCommands().getDispatcher().getRoot();
|
||||||
for (var j = 0; j < args.count(); j++) {
|
for (var j = 0; j < args.count(); j++) {
|
||||||
var name = args.getString(j);
|
var name = args.getString(j);
|
||||||
@@ -161,7 +166,7 @@ public class CommandAPI implements ILuaAPI {
|
|||||||
@LuaFunction
|
@LuaFunction
|
||||||
public final Object[] getBlockPosition() {
|
public final Object[] getBlockPosition() {
|
||||||
// This is probably safe to do on the Lua thread. Probably.
|
// This is probably safe to do on the Lua thread. Probably.
|
||||||
var pos = computer.getBlockPos();
|
var pos = computer.getPosition();
|
||||||
return new Object[]{ pos.getX(), pos.getY(), pos.getZ() };
|
return new Object[]{ pos.getX(), pos.getY(), pos.getZ() };
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -172,7 +177,7 @@ public class CommandAPI implements ILuaAPI {
|
|||||||
* blocks at once.
|
* blocks at once.
|
||||||
* <p>
|
* <p>
|
||||||
* Blocks are traversed by ascending y level, followed by z and x - the returned
|
* Blocks are traversed by ascending y level, followed by z and x - the returned
|
||||||
* table may be indexed using `x + z*width + y*depth*depth`.
|
* table may be indexed using `x + z*width + y*width*depth + 1`.
|
||||||
*
|
*
|
||||||
* @param minX The start x coordinate of the range to query.
|
* @param minX The start x coordinate of the range to query.
|
||||||
* @param minY The start y coordinate of the range to query.
|
* @param minY The start y coordinate of the range to query.
|
||||||
@@ -186,6 +191,24 @@ public class CommandAPI implements ILuaAPI {
|
|||||||
* @throws LuaException If trying to get information about more than 4096 blocks.
|
* @throws LuaException If trying to get information about more than 4096 blocks.
|
||||||
* @cc.since 1.76
|
* @cc.since 1.76
|
||||||
* @cc.changed 1.99 Added {@code dimension} argument.
|
* @cc.changed 1.99 Added {@code dimension} argument.
|
||||||
|
* @cc.usage Print out all blocks in a cube around the computer.
|
||||||
|
*
|
||||||
|
* <pre>{@code
|
||||||
|
* -- Get a 3x3x3 cube around the computer
|
||||||
|
* local x, y, z = commands.getBlockPosition()
|
||||||
|
* local min_x, min_y, min_z, max_x, max_y, max_z = x - 1, y - 1, z - 1, x + 1, y + 1, z + 1
|
||||||
|
* local blocks = commands.getBlockInfos(min_x, min_y, min_z, max_x, max_y, max_z)
|
||||||
|
*
|
||||||
|
* -- Then loop over all blocks and print them out.
|
||||||
|
* local width, height, depth = max_x - min_x + 1, max_y - min_y + 1, max_z - min_z + 1
|
||||||
|
* for x = min_x, max_x do
|
||||||
|
* for y = min_y, max_y do
|
||||||
|
* for z = min_z, max_z do
|
||||||
|
* print(("%d, %d %d => %s"):format(x, y, z, blocks[(x - min_x) + (z - min_z) * width + (y - min_y) * width * depth + 1].name))
|
||||||
|
* end
|
||||||
|
* end
|
||||||
|
* end
|
||||||
|
* }</pre>
|
||||||
*/
|
*/
|
||||||
@LuaFunction(mainThread = true)
|
@LuaFunction(mainThread = true)
|
||||||
public final List<Map<?, ?>> getBlockInfos(int minX, int minY, int minZ, int maxX, int maxY, int maxZ, Optional<String> dimension) throws LuaException {
|
public final List<Map<?, ?>> getBlockInfos(int minX, int minY, int minZ, int maxX, int maxY, int maxZ, Optional<String> dimension) throws LuaException {
|
||||||
@@ -201,7 +224,7 @@ public class CommandAPI implements ILuaAPI {
|
|||||||
Math.max(minY, maxY),
|
Math.max(minY, maxY),
|
||||||
Math.max(minZ, maxZ)
|
Math.max(minZ, maxZ)
|
||||||
);
|
);
|
||||||
if (world == null || !world.isInWorldBounds(min) || !world.isInWorldBounds(max)) {
|
if (!world.isInWorldBounds(min) || !world.isInWorldBounds(max)) {
|
||||||
throw new LuaException("Co-ordinates out of range");
|
throw new LuaException("Co-ordinates out of range");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -246,10 +269,9 @@ public class CommandAPI implements ILuaAPI {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private Level getLevel(Optional<String> id) throws LuaException {
|
private Level getLevel(Optional<String> id) throws LuaException {
|
||||||
var currentLevel = (ServerLevel) computer.getLevel();
|
var currentLevel = computer.getLevel();
|
||||||
if (currentLevel == null) throw new LuaException("No world exists");
|
|
||||||
|
|
||||||
if (!id.isPresent()) return currentLevel;
|
if (id.isEmpty()) return currentLevel;
|
||||||
|
|
||||||
var dimensionId = ResourceLocation.tryParse(id.get());
|
var dimensionId = ResourceLocation.tryParse(id.get());
|
||||||
if (dimensionId == null) throw new LuaException("Invalid dimension name");
|
if (dimensionId == null) throw new LuaException("Invalid dimension name");
|
||||||
@@ -259,4 +281,52 @@ public class CommandAPI implements ILuaAPI {
|
|||||||
|
|
||||||
return level;
|
return level;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private CommandSourceStack getSource() {
|
||||||
|
var name = "@";
|
||||||
|
var label = computer.getLabel();
|
||||||
|
if (label != null) name = label;
|
||||||
|
|
||||||
|
return new CommandSourceStack(receiver,
|
||||||
|
Vec3.atCenterOf(computer.getPosition()), Vec2.ZERO,
|
||||||
|
computer.getLevel(), admin.permissionLevel(),
|
||||||
|
name, Component.literal(name),
|
||||||
|
computer.getLevel().getServer(), null
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link CommandSource} that consumes output messages and stores them to a list.
|
||||||
|
*/
|
||||||
|
private final class OutputReceiver implements CommandSource {
|
||||||
|
private final List<String> output = new ArrayList<>();
|
||||||
|
|
||||||
|
void clearOutput() {
|
||||||
|
output.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
List<String> copyOutput() {
|
||||||
|
return List.copyOf(output);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void sendSystemMessage(Component textComponent) {
|
||||||
|
output.add(textComponent.getString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean acceptsSuccess() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean acceptsFailure() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean shouldInformAdmins() {
|
||||||
|
return computer.getLevel().getGameRules().getBoolean(GameRules.RULE_COMMANDBLOCKOUTPUT);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ import net.minecraft.server.level.ServerLevel;
|
|||||||
import net.minecraft.stats.Stats;
|
import net.minecraft.stats.Stats;
|
||||||
import net.minecraft.world.InteractionHand;
|
import net.minecraft.world.InteractionHand;
|
||||||
import net.minecraft.world.InteractionResult;
|
import net.minecraft.world.InteractionResult;
|
||||||
import net.minecraft.world.MenuProvider;
|
|
||||||
import net.minecraft.world.entity.LivingEntity;
|
import net.minecraft.world.entity.LivingEntity;
|
||||||
import net.minecraft.world.entity.player.Player;
|
import net.minecraft.world.entity.player.Player;
|
||||||
import net.minecraft.world.item.ItemStack;
|
import net.minecraft.world.item.ItemStack;
|
||||||
@@ -147,7 +146,7 @@ public abstract class AbstractComputerBlock<T extends AbstractComputerBlockEntit
|
|||||||
super.setPlacedBy(world, pos, state, placer, stack);
|
super.setPlacedBy(world, pos, state, placer, stack);
|
||||||
|
|
||||||
var tile = world.getBlockEntity(pos);
|
var tile = world.getBlockEntity(pos);
|
||||||
if (!world.isClientSide && tile instanceof IComputerBlockEntity computer && stack.getItem() instanceof IComputerItem item) {
|
if (!world.isClientSide && tile instanceof AbstractComputerBlockEntity computer && stack.getItem() instanceof IComputerItem item) {
|
||||||
|
|
||||||
var id = item.getComputerID(stack);
|
var id = item.getComputerID(stack);
|
||||||
if (id != -1) computer.setComputerID(id);
|
if (id != -1) computer.setComputerID(id);
|
||||||
@@ -196,13 +195,6 @@ public abstract class AbstractComputerBlock<T extends AbstractComputerBlockEntit
|
|||||||
return super.updateShape(state, direction, neighborState, level, pos, neighborPos);
|
return super.updateShape(state, direction, neighborState, level, pos, neighborPos);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
|
||||||
@Override
|
|
||||||
@Deprecated
|
|
||||||
public MenuProvider getMenuProvider(BlockState state, Level level, BlockPos pos) {
|
|
||||||
return level.getBlockEntity(pos) instanceof AbstractComputerBlockEntity computer ? computer : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Nullable
|
@Nullable
|
||||||
public <U extends BlockEntity> BlockEntityTicker<U> getTicker(Level level, BlockState state, BlockEntityType<U> type) {
|
public <U extends BlockEntity> BlockEntityTicker<U> getTicker(Level level, BlockState state, BlockEntityType<U> type) {
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ import javax.annotation.Nullable;
|
|||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
public abstract class AbstractComputerBlockEntity extends BlockEntity implements IComputerBlockEntity, Nameable, MenuProvider {
|
public abstract class AbstractComputerBlockEntity extends BlockEntity implements Nameable, MenuProvider {
|
||||||
private static final String NBT_ID = "ComputerId";
|
private static final String NBT_ID = "ComputerId";
|
||||||
private static final String NBT_LABEL = "Label";
|
private static final String NBT_LABEL = "Label";
|
||||||
private static final String NBT_ON = "On";
|
private static final String NBT_ON = "On";
|
||||||
@@ -81,7 +81,8 @@ public abstract class AbstractComputerBlockEntity extends BlockEntity implements
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean isUsable(Player player) {
|
public boolean isUsable(Player player) {
|
||||||
return BaseContainerBlockEntity.canUnlock(player, lockCode, getDisplayName())
|
return getFamily().checkUsable(player)
|
||||||
|
&& BaseContainerBlockEntity.canUnlock(player, lockCode, getDisplayName())
|
||||||
&& Container.stillValidBlockEntity(this, player, getInteractRange());
|
&& Container.stillValidBlockEntity(this, player, getInteractRange());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -299,17 +300,14 @@ public abstract class AbstractComputerBlockEntity extends BlockEntity implements
|
|||||||
for (var dir : DirectionUtil.FACINGS) updateRedstoneTo(dir);
|
for (var dir : DirectionUtil.FACINGS) updateRedstoneTo(dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public final int getComputerID() {
|
public final int getComputerID() {
|
||||||
return computerID;
|
return computerID;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public final @Nullable String getLabel() {
|
public final @Nullable String getLabel() {
|
||||||
return label;
|
return label;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public final void setComputerID(int id) {
|
public final void setComputerID(int id) {
|
||||||
if (getLevel().isClientSide || computerID == id) return;
|
if (getLevel().isClientSide || computerID == id) return;
|
||||||
|
|
||||||
@@ -317,7 +315,6 @@ public abstract class AbstractComputerBlockEntity extends BlockEntity implements
|
|||||||
BlockEntityHelpers.updateBlock(this);
|
BlockEntityHelpers.updateBlock(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public final void setLabel(@Nullable String label) {
|
public final void setLabel(@Nullable String label) {
|
||||||
if (getLevel().isClientSide || Objects.equals(this.label, label)) return;
|
if (getLevel().isClientSide || Objects.equals(this.label, label)) return;
|
||||||
|
|
||||||
@@ -327,7 +324,6 @@ public abstract class AbstractComputerBlockEntity extends BlockEntity implements
|
|||||||
BlockEntityHelpers.updateBlock(this);
|
BlockEntityHelpers.updateBlock(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public ComputerFamily getFamily() {
|
public ComputerFamily getFamily() {
|
||||||
return family;
|
return family;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import net.minecraft.world.level.block.entity.BlockEntityType;
|
|||||||
* @param <T> The type of the computer block entity.
|
* @param <T> The type of the computer block entity.
|
||||||
* @see dan200.computercraft.shared.computer.items.CommandComputerItem
|
* @see dan200.computercraft.shared.computer.items.CommandComputerItem
|
||||||
*/
|
*/
|
||||||
public class CommandComputerBlock<T extends CommandComputerBlockEntity> extends ComputerBlock<T> implements GameMasterBlock {
|
public class CommandComputerBlock<T extends ComputerBlockEntity> extends ComputerBlock<T> implements GameMasterBlock {
|
||||||
public CommandComputerBlock(Properties settings, RegistryEntry<BlockEntityType<T>> type) {
|
public CommandComputerBlock(Properties settings, RegistryEntry<BlockEntityType<T>> type) {
|
||||||
super(settings, type);
|
super(settings, type);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,114 +0,0 @@
|
|||||||
// Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: LicenseRef-CCPL
|
|
||||||
|
|
||||||
package dan200.computercraft.shared.computer.blocks;
|
|
||||||
|
|
||||||
import dan200.computercraft.shared.computer.apis.CommandAPI;
|
|
||||||
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
|
||||||
import dan200.computercraft.shared.computer.core.ServerComputer;
|
|
||||||
import dan200.computercraft.shared.config.Config;
|
|
||||||
import net.minecraft.commands.CommandSource;
|
|
||||||
import net.minecraft.commands.CommandSourceStack;
|
|
||||||
import net.minecraft.core.BlockPos;
|
|
||||||
import net.minecraft.network.chat.Component;
|
|
||||||
import net.minecraft.server.level.ServerLevel;
|
|
||||||
import net.minecraft.world.entity.player.Player;
|
|
||||||
import net.minecraft.world.level.GameRules;
|
|
||||||
import net.minecraft.world.level.block.entity.BlockEntityType;
|
|
||||||
import net.minecraft.world.level.block.state.BlockState;
|
|
||||||
import net.minecraft.world.phys.Vec2;
|
|
||||||
import net.minecraft.world.phys.Vec3;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class CommandComputerBlockEntity extends ComputerBlockEntity {
|
|
||||||
public class CommandReceiver implements CommandSource {
|
|
||||||
private final List<String> output = new ArrayList<>();
|
|
||||||
|
|
||||||
public void clearOutput() {
|
|
||||||
output.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<String> copyOutput() {
|
|
||||||
return new ArrayList<>(output);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void sendSystemMessage(Component textComponent) {
|
|
||||||
output.add(textComponent.getString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean acceptsSuccess() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean acceptsFailure() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean shouldInformAdmins() {
|
|
||||||
return getLevel().getGameRules().getBoolean(GameRules.RULE_COMMANDBLOCKOUTPUT);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private final CommandReceiver receiver;
|
|
||||||
|
|
||||||
public CommandComputerBlockEntity(BlockEntityType<? extends ComputerBlockEntity> type, BlockPos pos, BlockState state) {
|
|
||||||
super(type, pos, state, ComputerFamily.COMMAND);
|
|
||||||
receiver = new CommandReceiver();
|
|
||||||
}
|
|
||||||
|
|
||||||
public CommandReceiver getReceiver() {
|
|
||||||
return receiver;
|
|
||||||
}
|
|
||||||
|
|
||||||
public CommandSourceStack getSource() {
|
|
||||||
var computer = getServerComputer();
|
|
||||||
var name = "@";
|
|
||||||
if (computer != null) {
|
|
||||||
var label = computer.getLabel();
|
|
||||||
if (label != null) name = label;
|
|
||||||
}
|
|
||||||
|
|
||||||
return new CommandSourceStack(receiver,
|
|
||||||
Vec3.atCenterOf(worldPosition), Vec2.ZERO,
|
|
||||||
(ServerLevel) getLevel(), 2,
|
|
||||||
name, Component.literal(name),
|
|
||||||
getLevel().getServer(), null
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected ServerComputer createComputer(int id) {
|
|
||||||
var computer = super.createComputer(id);
|
|
||||||
computer.addAPI(new CommandAPI(this));
|
|
||||||
return computer;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isUsable(Player player) {
|
|
||||||
return isCommandUsable(player) && super.isUsable(player);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean isCommandUsable(Player player) {
|
|
||||||
var server = player.getServer();
|
|
||||||
if (server == null || !server.isCommandBlockEnabled()) {
|
|
||||||
player.displayClientMessage(Component.translatable("advMode.notEnabled"), true);
|
|
||||||
return false;
|
|
||||||
} else if (!canUseCommandBlock(player)) {
|
|
||||||
player.displayClientMessage(Component.translatable("advMode.notAllowed"), true);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean canUseCommandBlock(Player player) {
|
|
||||||
return Config.commandRequireCreative ? player.canUseGameMasterBlocks() : player.hasPermissions(2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -12,6 +12,7 @@ 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.inventory.ComputerMenuWithoutInventory;
|
import dan200.computercraft.shared.computer.inventory.ComputerMenuWithoutInventory;
|
||||||
import dan200.computercraft.shared.config.Config;
|
import dan200.computercraft.shared.config.Config;
|
||||||
|
import dan200.computercraft.shared.util.ComponentMap;
|
||||||
import net.minecraft.core.BlockPos;
|
import net.minecraft.core.BlockPos;
|
||||||
import net.minecraft.core.Direction;
|
import net.minecraft.core.Direction;
|
||||||
import net.minecraft.server.level.ServerLevel;
|
import net.minecraft.server.level.ServerLevel;
|
||||||
@@ -34,7 +35,8 @@ public class ComputerBlockEntity extends AbstractComputerBlockEntity {
|
|||||||
protected ServerComputer createComputer(int id) {
|
protected ServerComputer createComputer(int id) {
|
||||||
return new ServerComputer(
|
return new ServerComputer(
|
||||||
(ServerLevel) getLevel(), getBlockPos(), id, label,
|
(ServerLevel) getLevel(), getBlockPos(), id, label,
|
||||||
getFamily(), Config.computerTermWidth, Config.computerTermHeight
|
getFamily(), Config.computerTermWidth, Config.computerTermHeight,
|
||||||
|
ComponentMap.empty()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -67,7 +69,7 @@ public class ComputerBlockEntity extends AbstractComputerBlockEntity {
|
|||||||
@Nullable
|
@Nullable
|
||||||
@Override
|
@Override
|
||||||
public AbstractContainerMenu createMenu(int id, Inventory inventory, Player player) {
|
public AbstractContainerMenu createMenu(int id, Inventory inventory, Player player) {
|
||||||
return new ComputerMenuWithoutInventory(ModRegistry.Menus.COMPUTER.get(), id, inventory, this::isUsableByPlayer, createServerComputer(), getFamily());
|
return new ComputerMenuWithoutInventory(ModRegistry.Menus.COMPUTER.get(), id, inventory, this::isUsableByPlayer, createServerComputer());
|
||||||
}
|
}
|
||||||
|
|
||||||
public IPeripheral peripheral() {
|
public IPeripheral peripheral() {
|
||||||
|
|||||||
@@ -1,22 +0,0 @@
|
|||||||
// Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: LicenseRef-CCPL
|
|
||||||
|
|
||||||
package dan200.computercraft.shared.computer.blocks;
|
|
||||||
|
|
||||||
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
|
|
||||||
public interface IComputerBlockEntity {
|
|
||||||
int getComputerID();
|
|
||||||
|
|
||||||
void setComputerID(int id);
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
String getLabel();
|
|
||||||
|
|
||||||
void setLabel(@Nullable String label);
|
|
||||||
|
|
||||||
ComputerFamily getFamily();
|
|
||||||
}
|
|
||||||
@@ -4,8 +4,45 @@
|
|||||||
|
|
||||||
package dan200.computercraft.shared.computer.core;
|
package dan200.computercraft.shared.computer.core;
|
||||||
|
|
||||||
|
import dan200.computercraft.shared.config.Config;
|
||||||
|
import net.minecraft.network.chat.Component;
|
||||||
|
import net.minecraft.world.entity.player.Player;
|
||||||
|
|
||||||
public enum ComputerFamily {
|
public enum ComputerFamily {
|
||||||
NORMAL,
|
NORMAL,
|
||||||
ADVANCED,
|
ADVANCED,
|
||||||
COMMAND,
|
COMMAND;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check whether computers with this family can be used by the provided player.
|
||||||
|
* <p>
|
||||||
|
* This method is not pure. On failure, the method may send a message to the player telling them why they cannot
|
||||||
|
* interact with the computer.
|
||||||
|
*
|
||||||
|
* @param player The player trying to use a computer.
|
||||||
|
* @return Whether this computer family can be used.
|
||||||
|
*/
|
||||||
|
public boolean checkUsable(Player player) {
|
||||||
|
return switch (this) {
|
||||||
|
case NORMAL, ADVANCED -> true;
|
||||||
|
case COMMAND -> checkCommandUsable(player);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean checkCommandUsable(Player player) {
|
||||||
|
var server = player.getServer();
|
||||||
|
if (server == null || !server.isCommandBlockEnabled()) {
|
||||||
|
player.displayClientMessage(Component.translatable("advMode.notEnabled"), true);
|
||||||
|
return false;
|
||||||
|
} else if (!canUseCommandBlock(player)) {
|
||||||
|
player.displayClientMessage(Component.translatable("advMode.notAllowed"), true);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean canUseCommandBlock(Player player) {
|
||||||
|
return Config.commandRequireCreative ? player.canUseGameMasterBlocks() : player.hasPermissions(2);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,101 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2019 The CC: Tweaked Developers
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
|
package dan200.computercraft.shared.computer.core;
|
||||||
|
|
||||||
|
import dan200.computercraft.api.component.ComputerComponent;
|
||||||
|
import dan200.computercraft.api.lua.IComputerSystem;
|
||||||
|
import dan200.computercraft.api.lua.ILuaAPIFactory;
|
||||||
|
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||||
|
import dan200.computercraft.core.apis.ComputerAccess;
|
||||||
|
import dan200.computercraft.core.apis.IAPIEnvironment;
|
||||||
|
import dan200.computercraft.core.computer.ApiLifecycle;
|
||||||
|
import dan200.computercraft.shared.util.ComponentMap;
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.server.level.ServerLevel;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation of {@link IComputerSystem} for usage by externally registered APIs.
|
||||||
|
*
|
||||||
|
* @see ILuaAPIFactory
|
||||||
|
*/
|
||||||
|
final class ComputerSystem extends ComputerAccess implements IComputerSystem, ApiLifecycle {
|
||||||
|
private final ServerComputer computer;
|
||||||
|
private final IAPIEnvironment environment;
|
||||||
|
private final ComponentMap components;
|
||||||
|
|
||||||
|
private boolean active;
|
||||||
|
|
||||||
|
ComputerSystem(ServerComputer computer, IAPIEnvironment environment, ComponentMap components) {
|
||||||
|
super(environment);
|
||||||
|
this.computer = computer;
|
||||||
|
this.environment = environment;
|
||||||
|
this.components = components;
|
||||||
|
}
|
||||||
|
|
||||||
|
void activate() {
|
||||||
|
active = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void shutdown() {
|
||||||
|
unmountAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getAttachmentName() {
|
||||||
|
return "computer";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ServerLevel getLevel() {
|
||||||
|
if (!active) {
|
||||||
|
throw new IllegalStateException("""
|
||||||
|
Cannot access level when constructing the API. Computers are not guaranteed to stay in one place and
|
||||||
|
APIs should not rely on the level remaining constant. Instead, call this method when needed.
|
||||||
|
""".replace('\n', ' ').strip()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return computer.getLevel();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BlockPos getPosition() {
|
||||||
|
if (!active) {
|
||||||
|
throw new IllegalStateException("""
|
||||||
|
Cannot access computer position when constructing the API. Computers are not guaranteed to stay in one
|
||||||
|
place and APIs should not rely on the position remaining constant. Instead, call this method when
|
||||||
|
needed.
|
||||||
|
""".replace('\n', ' ').strip()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return computer.getPosition();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public String getLabel() {
|
||||||
|
return environment.getLabel();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, IPeripheral> getAvailablePeripherals() {
|
||||||
|
// TODO: Should this return peripherals on the current computer?
|
||||||
|
return Map.of();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public IPeripheral getAvailablePeripheral(String name) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T> @Nullable T getComponent(ComputerComponent<T> component) {
|
||||||
|
return components.get(component);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,7 +7,6 @@ package dan200.computercraft.shared.computer.core;
|
|||||||
import com.google.common.annotations.VisibleForTesting;
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
import dan200.computercraft.api.filesystem.FileOperationException;
|
import dan200.computercraft.api.filesystem.FileOperationException;
|
||||||
import dan200.computercraft.core.filesystem.ArchiveMount;
|
import dan200.computercraft.core.filesystem.ArchiveMount;
|
||||||
import dan200.computercraft.core.filesystem.FileSystem;
|
|
||||||
import net.minecraft.ResourceLocationException;
|
import net.minecraft.ResourceLocationException;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
import net.minecraft.server.MinecraftServer;
|
import net.minecraft.server.MinecraftServer;
|
||||||
@@ -65,9 +64,10 @@ public final class ResourceMount extends ArchiveMount<ResourceMount.FileEntry> {
|
|||||||
existingNamespace = file.getNamespace();
|
existingNamespace = file.getNamespace();
|
||||||
|
|
||||||
if (!file.getNamespace().equals(namespace)) continue;
|
if (!file.getNamespace().equals(namespace)) continue;
|
||||||
if (!FileSystem.contains(subPath, file.getPath())) continue; // Some packs seem to include the parent?
|
|
||||||
|
|
||||||
var localPath = FileSystem.toLocal(file.getPath(), subPath);
|
var localPath = getLocalPath(file.getPath(), subPath);
|
||||||
|
if (localPath == null) continue;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
getOrCreateChild(newRoot, localPath, this::createEntry);
|
getOrCreateChild(newRoot, localPath, this::createEntry);
|
||||||
} catch (ResourceLocationException e) {
|
} catch (ResourceLocationException e) {
|
||||||
|
|||||||
@@ -5,14 +5,16 @@
|
|||||||
package dan200.computercraft.shared.computer.core;
|
package dan200.computercraft.shared.computer.core;
|
||||||
|
|
||||||
import dan200.computercraft.api.ComputerCraftAPI;
|
import dan200.computercraft.api.ComputerCraftAPI;
|
||||||
|
import dan200.computercraft.api.component.AdminComputer;
|
||||||
|
import dan200.computercraft.api.component.ComputerComponents;
|
||||||
import dan200.computercraft.api.filesystem.WritableMount;
|
import dan200.computercraft.api.filesystem.WritableMount;
|
||||||
import dan200.computercraft.api.lua.ILuaAPI;
|
|
||||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||||
import dan200.computercraft.core.apis.IAPIEnvironment;
|
import dan200.computercraft.api.peripheral.WorkMonitor;
|
||||||
import dan200.computercraft.core.computer.Computer;
|
import dan200.computercraft.core.computer.Computer;
|
||||||
import dan200.computercraft.core.computer.ComputerEnvironment;
|
import dan200.computercraft.core.computer.ComputerEnvironment;
|
||||||
import dan200.computercraft.core.computer.ComputerSide;
|
import dan200.computercraft.core.computer.ComputerSide;
|
||||||
import dan200.computercraft.core.metrics.MetricsObserver;
|
import dan200.computercraft.core.metrics.MetricsObserver;
|
||||||
|
import dan200.computercraft.impl.ApiFactories;
|
||||||
import dan200.computercraft.shared.computer.menu.ComputerMenu;
|
import dan200.computercraft.shared.computer.menu.ComputerMenu;
|
||||||
import dan200.computercraft.shared.computer.terminal.NetworkedTerminal;
|
import dan200.computercraft.shared.computer.terminal.NetworkedTerminal;
|
||||||
import dan200.computercraft.shared.computer.terminal.TerminalState;
|
import dan200.computercraft.shared.computer.terminal.TerminalState;
|
||||||
@@ -21,8 +23,10 @@ import dan200.computercraft.shared.network.NetworkMessage;
|
|||||||
import dan200.computercraft.shared.network.client.ClientNetworkContext;
|
import dan200.computercraft.shared.network.client.ClientNetworkContext;
|
||||||
import dan200.computercraft.shared.network.client.ComputerTerminalClientMessage;
|
import dan200.computercraft.shared.network.client.ComputerTerminalClientMessage;
|
||||||
import dan200.computercraft.shared.network.server.ServerNetworking;
|
import dan200.computercraft.shared.network.server.ServerNetworking;
|
||||||
|
import dan200.computercraft.shared.util.ComponentMap;
|
||||||
import net.minecraft.core.BlockPos;
|
import net.minecraft.core.BlockPos;
|
||||||
import net.minecraft.server.level.ServerLevel;
|
import net.minecraft.server.level.ServerLevel;
|
||||||
|
import net.minecraft.world.entity.player.Player;
|
||||||
import net.minecraft.world.inventory.AbstractContainerMenu;
|
import net.minecraft.world.inventory.AbstractContainerMenu;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
@@ -47,7 +51,8 @@ public class ServerComputer implements InputHandler, ComputerEnvironment {
|
|||||||
private int ticksSincePing;
|
private int ticksSincePing;
|
||||||
|
|
||||||
public ServerComputer(
|
public ServerComputer(
|
||||||
ServerLevel level, BlockPos position, int computerID, @Nullable String label, ComputerFamily family, int terminalWidth, int terminalHeight
|
ServerLevel level, BlockPos position, int computerID, @Nullable String label, ComputerFamily family, int terminalWidth, int terminalHeight,
|
||||||
|
ComponentMap baseComponents
|
||||||
) {
|
) {
|
||||||
this.level = level;
|
this.level = level;
|
||||||
this.position = position;
|
this.position = position;
|
||||||
@@ -58,8 +63,27 @@ public class ServerComputer implements InputHandler, ComputerEnvironment {
|
|||||||
terminal = new NetworkedTerminal(terminalWidth, terminalHeight, family != ComputerFamily.NORMAL, this::markTerminalChanged);
|
terminal = new NetworkedTerminal(terminalWidth, terminalHeight, family != ComputerFamily.NORMAL, this::markTerminalChanged);
|
||||||
metrics = context.metrics().createMetricObserver(this);
|
metrics = context.metrics().createMetricObserver(this);
|
||||||
|
|
||||||
|
var componentBuilder = ComponentMap.builder();
|
||||||
|
componentBuilder.add(ComponentMap.METRICS, metrics);
|
||||||
|
if (family == ComputerFamily.COMMAND) {
|
||||||
|
componentBuilder.add(ComputerComponents.ADMIN_COMPUTER, new AdminComputer() {
|
||||||
|
});
|
||||||
|
}
|
||||||
|
componentBuilder.add(baseComponents);
|
||||||
|
var components = componentBuilder.build();
|
||||||
|
|
||||||
computer = new Computer(context.computerContext(), this, terminal, computerID);
|
computer = new Computer(context.computerContext(), this, terminal, computerID);
|
||||||
computer.setLabel(label);
|
computer.setLabel(label);
|
||||||
|
|
||||||
|
// Load in the externally registered APIs.
|
||||||
|
for (var factory : ApiFactories.getAll()) {
|
||||||
|
var system = new ComputerSystem(this, computer.getAPIEnvironment(), components);
|
||||||
|
var api = factory.create(system);
|
||||||
|
if (api == null) continue;
|
||||||
|
|
||||||
|
system.activate();
|
||||||
|
computer.addApi(api, system);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public ComputerFamily getFamily() {
|
public ComputerFamily getFamily() {
|
||||||
@@ -70,32 +94,20 @@ public class ServerComputer implements InputHandler, ComputerEnvironment {
|
|||||||
return level;
|
return level;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setLevel(ServerLevel level) {
|
|
||||||
this.level = level;
|
|
||||||
}
|
|
||||||
|
|
||||||
public BlockPos getPosition() {
|
public BlockPos getPosition() {
|
||||||
return position;
|
return position;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setPosition(BlockPos pos) {
|
public void setPosition(ServerLevel level, BlockPos pos) {
|
||||||
position = new BlockPos(pos);
|
this.level = level;
|
||||||
}
|
position = pos.immutable();
|
||||||
|
|
||||||
public IAPIEnvironment getAPIEnvironment() {
|
|
||||||
return computer.getAPIEnvironment();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Computer getComputer() {
|
|
||||||
return computer;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void markTerminalChanged() {
|
protected void markTerminalChanged() {
|
||||||
terminalChanged.set(true);
|
terminalChanged.set(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void tickServer() {
|
||||||
public void tickServer() {
|
|
||||||
ticksSincePing++;
|
ticksSincePing++;
|
||||||
computer.tick();
|
computer.tick();
|
||||||
if (terminalChanged.getAndSet(false)) onTerminalChanged();
|
if (terminalChanged.getAndSet(false)) onTerminalChanged();
|
||||||
@@ -113,10 +125,15 @@ public class ServerComputer implements InputHandler, ComputerEnvironment {
|
|||||||
ticksSincePing = 0;
|
ticksSincePing = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasTimedOut() {
|
boolean hasTimedOut() {
|
||||||
return ticksSincePing > 100;
|
return ticksSincePing > 100;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a bitmask returning which sides on the computer have changed, resetting the internal state.
|
||||||
|
*
|
||||||
|
* @return What sides on the computer have changed.
|
||||||
|
*/
|
||||||
public int pollAndResetChanges() {
|
public int pollAndResetChanges() {
|
||||||
return computer.pollAndResetChanges();
|
return computer.pollAndResetChanges();
|
||||||
}
|
}
|
||||||
@@ -135,6 +152,17 @@ public class ServerComputer implements InputHandler, ComputerEnvironment {
|
|||||||
ServerContext.get(level.getServer()).registry().remove(this);
|
ServerContext.get(level.getServer()).registry().remove(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check whether this computer is usable by a player.
|
||||||
|
*
|
||||||
|
* @param player The player trying to use this computer.
|
||||||
|
* @return Whether this computer can be used.
|
||||||
|
*/
|
||||||
|
public final boolean checkUsable(Player player) {
|
||||||
|
return ServerContext.get(level.getServer()).registry().get(instanceUUID) == this
|
||||||
|
&& getFamily().checkUsable(player);
|
||||||
|
}
|
||||||
|
|
||||||
private void sendToAllInteracting(Function<AbstractContainerMenu, NetworkMessage<ClientNetworkContext>> createPacket) {
|
private void sendToAllInteracting(Function<AbstractContainerMenu, NetworkMessage<ClientNetworkContext>> createPacket) {
|
||||||
var server = level.getServer();
|
var server = level.getServer();
|
||||||
|
|
||||||
@@ -175,25 +203,21 @@ public class ServerComputer implements InputHandler, ComputerEnvironment {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void turnOn() {
|
public void turnOn() {
|
||||||
// Turn on
|
|
||||||
computer.turnOn();
|
computer.turnOn();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void shutdown() {
|
public void shutdown() {
|
||||||
// Shutdown
|
|
||||||
computer.shutdown();
|
computer.shutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void reboot() {
|
public void reboot() {
|
||||||
// Reboot
|
|
||||||
computer.reboot();
|
computer.reboot();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void queueEvent(String event, @Nullable Object[] arguments) {
|
public void queueEvent(String event, @Nullable Object[] arguments) {
|
||||||
// Queue event
|
|
||||||
computer.queueEvent(event, arguments);
|
computer.queueEvent(event, arguments);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -213,10 +237,6 @@ public class ServerComputer implements InputHandler, ComputerEnvironment {
|
|||||||
computer.getEnvironment().setBundledRedstoneInput(side, combination);
|
computer.getEnvironment().setBundledRedstoneInput(side, combination);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addAPI(ILuaAPI api) {
|
|
||||||
computer.addApi(api);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setPeripheral(ComputerSide side, @Nullable IPeripheral peripheral) {
|
public void setPeripheral(ComputerSide side, @Nullable IPeripheral peripheral) {
|
||||||
computer.getEnvironment().setPeripheral(side, peripheral);
|
computer.getEnvironment().setPeripheral(side, peripheral);
|
||||||
}
|
}
|
||||||
@@ -245,6 +265,10 @@ public class ServerComputer implements InputHandler, ComputerEnvironment {
|
|||||||
return metrics;
|
return metrics;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public WorkMonitor getMainThreadMonitor() {
|
||||||
|
return computer.getMainThreadMonitor();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @Nullable WritableMount createRootMount() {
|
public @Nullable WritableMount createRootMount() {
|
||||||
return ComputerCraftAPI.createSaveDirMount(level.getServer(), "computer/" + computer.getID(), Config.computerSpaceLimit);
|
return ComputerCraftAPI.createSaveDirMount(level.getServer(), "computer/" + computer.getID(), Config.computerSpaceLimit);
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ import dan200.computercraft.core.lua.ILuaMachine;
|
|||||||
import dan200.computercraft.core.methods.MethodSupplier;
|
import dan200.computercraft.core.methods.MethodSupplier;
|
||||||
import dan200.computercraft.core.methods.PeripheralMethod;
|
import dan200.computercraft.core.methods.PeripheralMethod;
|
||||||
import dan200.computercraft.impl.AbstractComputerCraftAPI;
|
import dan200.computercraft.impl.AbstractComputerCraftAPI;
|
||||||
import dan200.computercraft.impl.ApiFactories;
|
|
||||||
import dan200.computercraft.impl.GenericSources;
|
import dan200.computercraft.impl.GenericSources;
|
||||||
import dan200.computercraft.shared.CommonHooks;
|
import dan200.computercraft.shared.CommonHooks;
|
||||||
import dan200.computercraft.shared.computer.metrics.GlobalMetrics;
|
import dan200.computercraft.shared.computer.metrics.GlobalMetrics;
|
||||||
@@ -74,7 +73,6 @@ public final class ServerContext {
|
|||||||
.computerThreads(ConfigSpec.computerThreads.get())
|
.computerThreads(ConfigSpec.computerThreads.get())
|
||||||
.mainThreadScheduler(mainThread)
|
.mainThreadScheduler(mainThread)
|
||||||
.luaFactory(luaMachine)
|
.luaFactory(luaMachine)
|
||||||
.apiFactories(ApiFactories.getAll())
|
|
||||||
.genericMethods(GenericSources.getAllMethods())
|
.genericMethods(GenericSources.getAllMethods())
|
||||||
.build();
|
.build();
|
||||||
idAssigner = new IDAssigner(storageDir.resolve("ids.json"));
|
idAssigner = new IDAssigner(storageDir.resolve("ids.json"));
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ public abstract class AbstractComputerMenu extends AbstractContainerMenu impleme
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean stillValid(Player player) {
|
public boolean stillValid(Player player) {
|
||||||
return canUse.test(player);
|
return (computer == null || computer.checkUsable(player)) && canUse.test(player);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ComputerFamily getFamily() {
|
public ComputerFamily getFamily() {
|
||||||
|
|||||||
@@ -4,7 +4,6 @@
|
|||||||
|
|
||||||
package dan200.computercraft.shared.computer.inventory;
|
package dan200.computercraft.shared.computer.inventory;
|
||||||
|
|
||||||
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
|
||||||
import dan200.computercraft.shared.computer.core.ServerComputer;
|
import dan200.computercraft.shared.computer.core.ServerComputer;
|
||||||
import dan200.computercraft.shared.container.InvisibleSlot;
|
import dan200.computercraft.shared.container.InvisibleSlot;
|
||||||
import dan200.computercraft.shared.network.container.ComputerContainerData;
|
import dan200.computercraft.shared.network.container.ComputerContainerData;
|
||||||
@@ -23,9 +22,9 @@ import java.util.function.Predicate;
|
|||||||
public class ComputerMenuWithoutInventory extends AbstractComputerMenu {
|
public class ComputerMenuWithoutInventory extends AbstractComputerMenu {
|
||||||
public ComputerMenuWithoutInventory(
|
public ComputerMenuWithoutInventory(
|
||||||
MenuType<? extends AbstractComputerMenu> type, int id, Inventory player, Predicate<Player> canUse,
|
MenuType<? extends AbstractComputerMenu> type, int id, Inventory player, Predicate<Player> canUse,
|
||||||
ServerComputer computer, ComputerFamily family
|
ServerComputer computer
|
||||||
) {
|
) {
|
||||||
super(type, id, canUse, family, computer, null);
|
super(type, id, canUse, computer.getFamily(), computer, null);
|
||||||
addSlots(player);
|
addSlots(player);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,39 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: 2017 The CC: Tweaked Developers
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: MPL-2.0
|
|
||||||
|
|
||||||
package dan200.computercraft.shared.computer.inventory;
|
|
||||||
|
|
||||||
import dan200.computercraft.shared.ModRegistry;
|
|
||||||
import dan200.computercraft.shared.computer.blocks.CommandComputerBlockEntity;
|
|
||||||
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
|
||||||
import dan200.computercraft.shared.computer.core.ServerComputer;
|
|
||||||
import dan200.computercraft.shared.computer.core.ServerContext;
|
|
||||||
import dan200.computercraft.shared.network.container.ComputerContainerData;
|
|
||||||
import net.minecraft.world.entity.player.Inventory;
|
|
||||||
import net.minecraft.world.entity.player.Player;
|
|
||||||
|
|
||||||
|
|
||||||
public class ViewComputerMenu extends ComputerMenuWithoutInventory {
|
|
||||||
public ViewComputerMenu(int id, Inventory player, ServerComputer computer) {
|
|
||||||
super(ModRegistry.Menus.VIEW_COMPUTER.get(), id, player, p -> canInteractWith(computer, p), computer, computer.getFamily());
|
|
||||||
}
|
|
||||||
|
|
||||||
public ViewComputerMenu(int id, Inventory player, ComputerContainerData data) {
|
|
||||||
super(ModRegistry.Menus.VIEW_COMPUTER.get(), id, player, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean canInteractWith(ServerComputer computer, Player player) {
|
|
||||||
// If this computer no longer exists then discard it.
|
|
||||||
if (ServerContext.get(computer.getLevel().getServer()).registry().get(computer.getInstanceUUID()) != computer) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we're a command computer then ensure we're in creative
|
|
||||||
if (computer.getFamily() == ComputerFamily.COMMAND && !CommandComputerBlockEntity.isCommandUsable(player)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -5,7 +5,7 @@
|
|||||||
package dan200.computercraft.shared.data;
|
package dan200.computercraft.shared.data;
|
||||||
|
|
||||||
import dan200.computercraft.shared.ModRegistry;
|
import dan200.computercraft.shared.ModRegistry;
|
||||||
import dan200.computercraft.shared.computer.blocks.IComputerBlockEntity;
|
import dan200.computercraft.shared.computer.blocks.AbstractComputerBlockEntity;
|
||||||
import net.minecraft.world.level.storage.loot.LootContext;
|
import net.minecraft.world.level.storage.loot.LootContext;
|
||||||
import net.minecraft.world.level.storage.loot.parameters.LootContextParam;
|
import net.minecraft.world.level.storage.loot.parameters.LootContextParam;
|
||||||
import net.minecraft.world.level.storage.loot.parameters.LootContextParams;
|
import net.minecraft.world.level.storage.loot.parameters.LootContextParams;
|
||||||
@@ -27,7 +27,7 @@ public final class HasComputerIdLootCondition implements LootItemCondition {
|
|||||||
@Override
|
@Override
|
||||||
public boolean test(LootContext lootContext) {
|
public boolean test(LootContext lootContext) {
|
||||||
var tile = lootContext.getParamOrNull(LootContextParams.BLOCK_ENTITY);
|
var tile = lootContext.getParamOrNull(LootContextParams.BLOCK_ENTITY);
|
||||||
return tile instanceof IComputerBlockEntity computer && computer.getComputerID() >= 0;
|
return tile instanceof AbstractComputerBlockEntity computer && computer.getComputerID() >= 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2024 The CC: Tweaked Developers
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
|
package dan200.computercraft.shared.integration;
|
||||||
|
|
||||||
|
import com.simibubi.create.content.contraptions.BlockMovementChecks;
|
||||||
|
import com.simibubi.create.content.contraptions.BlockMovementChecks.CheckResult;
|
||||||
|
import dan200.computercraft.shared.peripheral.modem.wired.CableBlock;
|
||||||
|
import dan200.computercraft.shared.peripheral.modem.wireless.WirelessModemBlock;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Integration with Create.
|
||||||
|
*/
|
||||||
|
public final class CreateIntegration {
|
||||||
|
public static final String ID = "create";
|
||||||
|
|
||||||
|
private CreateIntegration() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setup() {
|
||||||
|
// Allow modems to be treated as "attached" to their adjacent block.
|
||||||
|
BlockMovementChecks.registerAttachedCheck((state, world, pos, direction) -> {
|
||||||
|
var block = state.getBlock();
|
||||||
|
if (block instanceof WirelessModemBlock) {
|
||||||
|
return CheckResult.of(state.getValue(WirelessModemBlock.FACING) == direction);
|
||||||
|
} else if (block instanceof CableBlock) {
|
||||||
|
return CheckResult.of(state.getValue(CableBlock.MODEM).getFacing() == direction);
|
||||||
|
} else {
|
||||||
|
return CheckResult.PASS;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,6 +8,7 @@ import net.minecraft.core.registries.Registries;
|
|||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
import net.minecraft.tags.TagKey;
|
import net.minecraft.tags.TagKey;
|
||||||
import net.minecraft.world.level.block.Block;
|
import net.minecraft.world.level.block.Block;
|
||||||
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tags defined by external mods.
|
* Tags defined by external mods.
|
||||||
@@ -26,9 +27,9 @@ public final class ExternalModTags {
|
|||||||
/**
|
/**
|
||||||
* Create's "brittle" tag, used to determine if this block needs to be moved before its neighbours.
|
* Create's "brittle" tag, used to determine if this block needs to be moved before its neighbours.
|
||||||
*
|
*
|
||||||
* @see <a href="https://github.com/Creators-of-Create/Create/blob/mc1.20.1/dev/src/main/java/com/simibubi/create/content/contraptions/BlockMovementChecks.java">{@code BlockMovementChecks}</a>
|
* @see com.simibubi.create.content.contraptions.BlockMovementChecks#isBrittle(BlockState)
|
||||||
*/
|
*/
|
||||||
public static final TagKey<Block> CREATE_BRITTLE = make("create", "brittle");
|
public static final TagKey<Block> CREATE_BRITTLE = make(CreateIntegration.ID, "brittle");
|
||||||
|
|
||||||
private static TagKey<Block> make(String mod, String name) {
|
private static TagKey<Block> make(String mod, String name) {
|
||||||
return TagKey.create(Registries.BLOCK, new ResourceLocation(mod, name));
|
return TagKey.create(Registries.BLOCK, new ResourceLocation(mod, name));
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
package dan200.computercraft.shared.media.items;
|
package dan200.computercraft.shared.media.items;
|
||||||
|
|
||||||
import dan200.computercraft.api.media.IMedia;
|
import dan200.computercraft.api.media.IMedia;
|
||||||
import net.minecraft.network.chat.Component;
|
|
||||||
import net.minecraft.sounds.SoundEvent;
|
import net.minecraft.sounds.SoundEvent;
|
||||||
import net.minecraft.world.item.ItemStack;
|
import net.minecraft.world.item.ItemStack;
|
||||||
import net.minecraft.world.item.RecordItem;
|
import net.minecraft.world.item.RecordItem;
|
||||||
@@ -13,7 +12,7 @@ import net.minecraft.world.item.RecordItem;
|
|||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An implementation of IMedia for ItemRecords.
|
* An implementation of {@link IMedia} for {@link RecordItem}.
|
||||||
*/
|
*/
|
||||||
public final class RecordMedia implements IMedia {
|
public final class RecordMedia implements IMedia {
|
||||||
public static final RecordMedia INSTANCE = new RecordMedia();
|
public static final RecordMedia INSTANCE = new RecordMedia();
|
||||||
@@ -29,16 +28,12 @@ public final class RecordMedia implements IMedia {
|
|||||||
@Override
|
@Override
|
||||||
public @Nullable String getAudioTitle(ItemStack stack) {
|
public @Nullable String getAudioTitle(ItemStack stack) {
|
||||||
var item = stack.getItem();
|
var item = stack.getItem();
|
||||||
if (!(item instanceof RecordItem)) return null;
|
return item instanceof RecordItem record ? record.getDisplayName().getString() : null;
|
||||||
|
|
||||||
return Component.translatable(item.getDescriptionId() + ".desc").getString();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @Nullable SoundEvent getAudio(ItemStack stack) {
|
public @Nullable SoundEvent getAudio(ItemStack stack) {
|
||||||
var item = stack.getItem();
|
var item = stack.getItem();
|
||||||
if (!(item instanceof RecordItem)) return null;
|
return item instanceof RecordItem record ? record.getSound() : null;
|
||||||
|
|
||||||
return ((RecordItem) item).getSound();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ public class PocketComputerDataMessage implements NetworkMessage<ClientNetworkCo
|
|||||||
public PocketComputerDataMessage(PocketServerComputer computer, boolean sendTerminal) {
|
public PocketComputerDataMessage(PocketServerComputer computer, boolean sendTerminal) {
|
||||||
clientId = computer.getInstanceUUID();
|
clientId = computer.getInstanceUUID();
|
||||||
state = computer.getState();
|
state = computer.getState();
|
||||||
lightState = computer.getLight();
|
lightState = computer.getBrain().getLight();
|
||||||
terminal = sendTerminal ? computer.getTerminalState() : null;
|
terminal = sendTerminal ? computer.getTerminalState() : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -129,7 +129,8 @@ public class DiskDrivePeripheral implements IPeripheral {
|
|||||||
@LuaFunction
|
@LuaFunction
|
||||||
@Nullable
|
@Nullable
|
||||||
public final Object getAudioTitle() {
|
public final Object getAudioTitle() {
|
||||||
return diskDrive.getMedia().getAudioTitle();
|
var stack = diskDrive.getMedia();
|
||||||
|
return stack.media() != null ? stack.getAudioTitle() : false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -13,8 +13,6 @@ import net.minecraft.core.BlockPos;
|
|||||||
import net.minecraft.core.Direction;
|
import net.minecraft.core.Direction;
|
||||||
import net.minecraft.server.level.ServerLevel;
|
import net.minecraft.server.level.ServerLevel;
|
||||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@@ -29,8 +27,6 @@ import java.util.Objects;
|
|||||||
* @param <C> A platform-specific type, used for the invalidation callback.
|
* @param <C> A platform-specific type, used for the invalidation callback.
|
||||||
*/
|
*/
|
||||||
public final class GenericPeripheralProvider<C extends Runnable> {
|
public final class GenericPeripheralProvider<C extends Runnable> {
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(GenericPeripheralProvider.class);
|
|
||||||
|
|
||||||
private final List<ComponentLookup<? super C>> lookups = new ArrayList<>();
|
private final List<ComponentLookup<? super C>> lookups = new ArrayList<>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -273,16 +273,18 @@ public abstract class SpeakerPeripheral implements IPeripheral {
|
|||||||
* Attempt to stream some audio data to the speaker.
|
* Attempt to stream some audio data to the speaker.
|
||||||
* <p>
|
* <p>
|
||||||
* This accepts a list of audio samples as amplitudes between -128 and 127. These are stored in an internal buffer
|
* This accepts a list of audio samples as amplitudes between -128 and 127. These are stored in an internal buffer
|
||||||
* and played back at 48kHz. If this buffer is full, this function will return {@literal false}. You should wait for
|
* and played back at 48kHz. If this buffer is full, this function will return {@literal false}. Programs should
|
||||||
* a [`speaker_audio_empty`] event before trying again.
|
* wait for a [`speaker_audio_empty`] event before trying to play audio again.
|
||||||
* <p>
|
* <p>
|
||||||
* > [!NOTE]
|
* The speaker only buffers a single call to {@link #playAudio} at once. This means if you try to play a small
|
||||||
* > The speaker only buffers a single call to {@link #playAudio} at once. This means if you try to play a small
|
* number of samples, you'll have a lot of stutter. You should try to play as many samples in one call as possible
|
||||||
* > number of samples, you'll have a lot of stutter. You should try to play as many samples in one call as possible
|
* (up to 128×1024), as this reduces the chances of audio stuttering or halting, especially when the server or
|
||||||
* > (up to 128×1024), as this reduces the chances of audio stuttering or halting, especially when the server or
|
* computer is lagging.
|
||||||
* > computer is lagging.
|
|
||||||
* <p>
|
* <p>
|
||||||
* [`speaker_audio`] provides a more complete guide to using speakers
|
* While the speaker accepts 8-bit PCM audio, the audio stream is re-encoded before being played. This means that
|
||||||
|
* the supplied samples may not be played out exactly.
|
||||||
|
* <p>
|
||||||
|
* [`speaker_audio`] provides a more complete guide to using speakers.
|
||||||
*
|
*
|
||||||
* @param context The Lua context.
|
* @param context The Lua context.
|
||||||
* @param audio The audio data to play.
|
* @param audio The audio data to play.
|
||||||
|
|||||||
@@ -6,10 +6,10 @@ package dan200.computercraft.shared.pocket.apis;
|
|||||||
|
|
||||||
import dan200.computercraft.api.lua.ILuaAPI;
|
import dan200.computercraft.api.lua.ILuaAPI;
|
||||||
import dan200.computercraft.api.lua.LuaFunction;
|
import dan200.computercraft.api.lua.LuaFunction;
|
||||||
|
import dan200.computercraft.api.pocket.IPocketAccess;
|
||||||
import dan200.computercraft.api.pocket.IPocketUpgrade;
|
import dan200.computercraft.api.pocket.IPocketUpgrade;
|
||||||
import dan200.computercraft.api.upgrades.UpgradeData;
|
import dan200.computercraft.api.upgrades.UpgradeData;
|
||||||
import dan200.computercraft.impl.PocketUpgrades;
|
import dan200.computercraft.impl.PocketUpgrades;
|
||||||
import dan200.computercraft.shared.pocket.core.PocketServerComputer;
|
|
||||||
import net.minecraft.core.NonNullList;
|
import net.minecraft.core.NonNullList;
|
||||||
import net.minecraft.world.entity.player.Player;
|
import net.minecraft.world.entity.player.Player;
|
||||||
import net.minecraft.world.item.ItemStack;
|
import net.minecraft.world.item.ItemStack;
|
||||||
@@ -34,10 +34,10 @@ import java.util.Objects;
|
|||||||
* @cc.module pocket
|
* @cc.module pocket
|
||||||
*/
|
*/
|
||||||
public class PocketAPI implements ILuaAPI {
|
public class PocketAPI implements ILuaAPI {
|
||||||
private final PocketServerComputer computer;
|
private final IPocketAccess pocket;
|
||||||
|
|
||||||
public PocketAPI(PocketServerComputer computer) {
|
public PocketAPI(IPocketAccess pocket) {
|
||||||
this.computer = computer;
|
this.pocket = pocket;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -56,10 +56,10 @@ public class PocketAPI implements ILuaAPI {
|
|||||||
*/
|
*/
|
||||||
@LuaFunction(mainThread = true)
|
@LuaFunction(mainThread = true)
|
||||||
public final Object[] equipBack() {
|
public final Object[] equipBack() {
|
||||||
var entity = computer.getEntity();
|
var entity = pocket.getEntity();
|
||||||
if (!(entity instanceof Player player)) return new Object[]{ false, "Cannot find player" };
|
if (!(entity instanceof Player player)) return new Object[]{ false, "Cannot find player" };
|
||||||
var inventory = player.getInventory();
|
var inventory = player.getInventory();
|
||||||
var previousUpgrade = computer.getUpgrade();
|
var previousUpgrade = pocket.getUpgrade();
|
||||||
|
|
||||||
// Attempt to find the upgrade, starting in the main segment, and then looking in the opposite
|
// Attempt to find the upgrade, starting in the main segment, and then looking in the opposite
|
||||||
// one. We start from the position the item is currently in and loop round to the start.
|
// one. We start from the position the item is currently in and loop round to the start.
|
||||||
@@ -73,7 +73,7 @@ public class PocketAPI implements ILuaAPI {
|
|||||||
if (previousUpgrade != null) storeItem(player, previousUpgrade.getUpgradeItem());
|
if (previousUpgrade != null) storeItem(player, previousUpgrade.getUpgradeItem());
|
||||||
|
|
||||||
// Set the new upgrade
|
// Set the new upgrade
|
||||||
computer.setUpgrade(newUpgrade);
|
pocket.setUpgrade(newUpgrade);
|
||||||
|
|
||||||
return new Object[]{ true };
|
return new Object[]{ true };
|
||||||
}
|
}
|
||||||
@@ -87,13 +87,13 @@ public class PocketAPI implements ILuaAPI {
|
|||||||
*/
|
*/
|
||||||
@LuaFunction(mainThread = true)
|
@LuaFunction(mainThread = true)
|
||||||
public final Object[] unequipBack() {
|
public final Object[] unequipBack() {
|
||||||
var entity = computer.getEntity();
|
var entity = pocket.getEntity();
|
||||||
if (!(entity instanceof Player player)) return new Object[]{ false, "Cannot find player" };
|
if (!(entity instanceof Player player)) return new Object[]{ false, "Cannot find player" };
|
||||||
var previousUpgrade = computer.getUpgrade();
|
var previousUpgrade = pocket.getUpgrade();
|
||||||
|
|
||||||
if (previousUpgrade == null) return new Object[]{ false, "Nothing to unequip" };
|
if (previousUpgrade == null) return new Object[]{ false, "Nothing to unequip" };
|
||||||
|
|
||||||
computer.setUpgrade(null);
|
pocket.setUpgrade(null);
|
||||||
|
|
||||||
storeItem(player, previousUpgrade.getUpgradeItem());
|
storeItem(player, previousUpgrade.getUpgradeItem());
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,184 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2024 The CC: Tweaked Developers
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
|
package dan200.computercraft.shared.pocket.core;
|
||||||
|
|
||||||
|
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||||
|
import dan200.computercraft.api.pocket.IPocketAccess;
|
||||||
|
import dan200.computercraft.api.pocket.IPocketUpgrade;
|
||||||
|
import dan200.computercraft.api.upgrades.UpgradeData;
|
||||||
|
import dan200.computercraft.core.computer.ComputerSide;
|
||||||
|
import dan200.computercraft.shared.common.IColouredItem;
|
||||||
|
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
||||||
|
import dan200.computercraft.shared.network.client.PocketComputerDataMessage;
|
||||||
|
import dan200.computercraft.shared.network.server.ServerNetworking;
|
||||||
|
import dan200.computercraft.shared.pocket.items.PocketComputerItem;
|
||||||
|
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.item.ItemStack;
|
||||||
|
import net.minecraft.world.phys.Vec3;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds additional state for a pocket computer. This includes pocket computer upgrade,
|
||||||
|
* {@linkplain IPocketAccess#getLight() light colour} and {@linkplain IPocketAccess#getColour() colour}.
|
||||||
|
* <p>
|
||||||
|
* This state is read when the brain is created, and written back to the holding item stack when the holding entity is
|
||||||
|
* ticked (see {@link #updateItem(ItemStack)}).
|
||||||
|
*/
|
||||||
|
public final class PocketBrain implements IPocketAccess {
|
||||||
|
private final PocketServerComputer computer;
|
||||||
|
|
||||||
|
private PocketHolder holder;
|
||||||
|
private Vec3 position;
|
||||||
|
|
||||||
|
private boolean dirty = false;
|
||||||
|
private @Nullable UpgradeData<IPocketUpgrade> upgrade;
|
||||||
|
private int colour = -1;
|
||||||
|
private int lightColour = -1;
|
||||||
|
|
||||||
|
public PocketBrain(PocketHolder holder, int computerID, @Nullable String label, ComputerFamily family, @Nullable UpgradeData<IPocketUpgrade> upgrade) {
|
||||||
|
this.computer = new PocketServerComputer(this, holder, computerID, label, family);
|
||||||
|
this.holder = holder;
|
||||||
|
this.position = holder.pos();
|
||||||
|
this.upgrade = UpgradeData.copyOf(upgrade);
|
||||||
|
invalidatePeripheral();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the corresponding pocket computer for this brain.
|
||||||
|
*
|
||||||
|
* @return The pocket computer.
|
||||||
|
*/
|
||||||
|
public PocketServerComputer computer() {
|
||||||
|
return computer;
|
||||||
|
}
|
||||||
|
|
||||||
|
PocketHolder holder() {
|
||||||
|
return holder;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the position and holder for this computer.
|
||||||
|
*
|
||||||
|
* @param newHolder The new holder
|
||||||
|
*/
|
||||||
|
public void updateHolder(PocketHolder newHolder) {
|
||||||
|
position = newHolder.pos();
|
||||||
|
computer.setPosition(newHolder.level(), newHolder.blockPos());
|
||||||
|
|
||||||
|
var oldHolder = this.holder;
|
||||||
|
if (holder.equals(newHolder)) return;
|
||||||
|
holder = newHolder;
|
||||||
|
|
||||||
|
// If a new player has picked it up then rebroadcast the terminal to them
|
||||||
|
var oldPlayer = oldHolder instanceof PocketHolder.PlayerHolder p ? p.entity() : null;
|
||||||
|
if (newHolder instanceof PocketHolder.PlayerHolder player && player.entity() != oldPlayer) {
|
||||||
|
ServerNetworking.sendToPlayer(new PocketComputerDataMessage(computer, true), player.entity());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write back properties of the pocket brain to the item.
|
||||||
|
*
|
||||||
|
* @param stack The pocket computer stack to update.
|
||||||
|
* @return Whether the item was changed.
|
||||||
|
*/
|
||||||
|
public boolean updateItem(ItemStack stack) {
|
||||||
|
if (!dirty) return false;
|
||||||
|
this.dirty = false;
|
||||||
|
|
||||||
|
IColouredItem.setColourBasic(stack, colour);
|
||||||
|
PocketComputerItem.setUpgrade(stack, UpgradeData.copyOf(upgrade));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ServerLevel getLevel() {
|
||||||
|
return computer.getLevel();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Vec3 getPosition() {
|
||||||
|
// This method can be called from off-thread, and so we must use the cached position rather than rereading
|
||||||
|
// from the holder.
|
||||||
|
return position;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable Entity getEntity() {
|
||||||
|
return holder instanceof PocketHolder.EntityHolder entity && holder.isValid(computer) ? entity.entity() : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getColour() {
|
||||||
|
return colour;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setColour(int colour) {
|
||||||
|
if (this.colour == colour) return;
|
||||||
|
dirty = true;
|
||||||
|
this.colour = colour;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getLight() {
|
||||||
|
return lightColour;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setLight(int colour) {
|
||||||
|
if (colour < 0 || colour > 0xFFFFFF) colour = -1;
|
||||||
|
lightColour = colour;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CompoundTag getUpgradeNBTData() {
|
||||||
|
var upgrade = this.upgrade;
|
||||||
|
return upgrade == null ? new CompoundTag() : upgrade.data();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateUpgradeNBTData() {
|
||||||
|
dirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void invalidatePeripheral() {
|
||||||
|
var peripheral = upgrade == null ? null : upgrade.upgrade().createPeripheral(this);
|
||||||
|
computer.setPeripheral(ComputerSide.BACK, peripheral);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Deprecated(forRemoval = true)
|
||||||
|
public Map<ResourceLocation, IPeripheral> getUpgrades() {
|
||||||
|
var upgrade = this.upgrade;
|
||||||
|
return upgrade == null ? Map.of() : Collections.singletonMap(upgrade.upgrade().getUpgradeID(), computer.getPeripheral(ComputerSide.BACK));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable UpgradeData<IPocketUpgrade> getUpgrade() {
|
||||||
|
return upgrade;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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}.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void setUpgrade(@Nullable UpgradeData<IPocketUpgrade> upgrade) {
|
||||||
|
this.upgrade = upgrade;
|
||||||
|
dirty = true;
|
||||||
|
invalidatePeripheral();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,115 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2024 The CC: Tweaked Developers
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
|
package dan200.computercraft.shared.pocket.core;
|
||||||
|
|
||||||
|
import dan200.computercraft.shared.computer.core.ServerComputer;
|
||||||
|
import dan200.computercraft.shared.pocket.items.PocketComputerItem;
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.server.level.ServerLevel;
|
||||||
|
import net.minecraft.server.level.ServerPlayer;
|
||||||
|
import net.minecraft.world.entity.Entity;
|
||||||
|
import net.minecraft.world.entity.item.ItemEntity;
|
||||||
|
import net.minecraft.world.phys.Vec3;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An object that holds a pocket computer item.
|
||||||
|
*/
|
||||||
|
public sealed interface PocketHolder permits PocketHolder.EntityHolder {
|
||||||
|
/**
|
||||||
|
* The level this holder is in.
|
||||||
|
*
|
||||||
|
* @return The holder's level.
|
||||||
|
*/
|
||||||
|
ServerLevel level();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The position of this holder.
|
||||||
|
*
|
||||||
|
* @return The position of this holder.
|
||||||
|
*/
|
||||||
|
Vec3 pos();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The block position of this holder.
|
||||||
|
*
|
||||||
|
* @return The position of this holder.
|
||||||
|
*/
|
||||||
|
BlockPos blockPos();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if this holder is still valid for a particular computer.
|
||||||
|
*
|
||||||
|
* @param computer The current computer.
|
||||||
|
* @return Whether this holder is valid.
|
||||||
|
*/
|
||||||
|
boolean isValid(ServerComputer computer);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mark the pocket computer item as having changed.
|
||||||
|
*/
|
||||||
|
void setChanged();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An {@link Entity} holding a pocket computer.
|
||||||
|
*/
|
||||||
|
sealed interface EntityHolder extends PocketHolder permits PocketHolder.PlayerHolder, PocketHolder.ItemEntityHolder {
|
||||||
|
/**
|
||||||
|
* Get the entity holding this pocket computer.
|
||||||
|
*
|
||||||
|
* @return The holding entity.
|
||||||
|
*/
|
||||||
|
Entity entity();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
default ServerLevel level() {
|
||||||
|
return (ServerLevel) entity().level();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
default Vec3 pos() {
|
||||||
|
return entity().getEyePosition();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
default BlockPos blockPos() {
|
||||||
|
return entity().blockPosition();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A pocket computer in a player's slot.
|
||||||
|
*
|
||||||
|
* @param entity The current player.
|
||||||
|
* @param slot The slot the pocket computer is in.
|
||||||
|
*/
|
||||||
|
record PlayerHolder(ServerPlayer entity, int slot) implements EntityHolder {
|
||||||
|
@Override
|
||||||
|
public boolean isValid(ServerComputer computer) {
|
||||||
|
return entity().isAlive() && PocketComputerItem.isServerComputer(computer, entity().getInventory().getItem(this.slot()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setChanged() {
|
||||||
|
entity.getInventory().setChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A pocket computer in an {@link ItemEntity}.
|
||||||
|
*
|
||||||
|
* @param entity The item entity.
|
||||||
|
*/
|
||||||
|
record ItemEntityHolder(ItemEntity entity) implements EntityHolder {
|
||||||
|
@Override
|
||||||
|
public boolean isValid(ServerComputer computer) {
|
||||||
|
return entity().isAlive() && PocketComputerItem.isServerComputer(computer, this.entity().getItem());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setChanged() {
|
||||||
|
entity.setItem(entity.getItem().copy());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,12 +4,7 @@
|
|||||||
|
|
||||||
package dan200.computercraft.shared.pocket.core;
|
package dan200.computercraft.shared.pocket.core;
|
||||||
|
|
||||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
import dan200.computercraft.api.component.ComputerComponents;
|
||||||
import dan200.computercraft.api.pocket.IPocketAccess;
|
|
||||||
import dan200.computercraft.api.pocket.IPocketUpgrade;
|
|
||||||
import dan200.computercraft.api.upgrades.UpgradeData;
|
|
||||||
import dan200.computercraft.core.computer.ComputerSide;
|
|
||||||
import dan200.computercraft.shared.common.IColouredItem;
|
|
||||||
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
||||||
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;
|
||||||
@@ -18,172 +13,81 @@ import dan200.computercraft.shared.network.client.PocketComputerDataMessage;
|
|||||||
import dan200.computercraft.shared.network.client.PocketComputerDeletedClientMessage;
|
import dan200.computercraft.shared.network.client.PocketComputerDeletedClientMessage;
|
||||||
import dan200.computercraft.shared.network.server.ServerNetworking;
|
import dan200.computercraft.shared.network.server.ServerNetworking;
|
||||||
import dan200.computercraft.shared.pocket.items.PocketComputerItem;
|
import dan200.computercraft.shared.pocket.items.PocketComputerItem;
|
||||||
import net.minecraft.core.BlockPos;
|
import dan200.computercraft.shared.util.ComponentMap;
|
||||||
import net.minecraft.nbt.CompoundTag;
|
|
||||||
import net.minecraft.resources.ResourceLocation;
|
|
||||||
import net.minecraft.server.level.ServerLevel;
|
|
||||||
import net.minecraft.server.level.ServerPlayer;
|
import net.minecraft.server.level.ServerPlayer;
|
||||||
import net.minecraft.world.entity.Entity;
|
import net.minecraft.world.level.ChunkPos;
|
||||||
import net.minecraft.world.entity.LivingEntity;
|
|
||||||
import net.minecraft.world.entity.item.ItemEntity;
|
|
||||||
import net.minecraft.world.entity.player.Player;
|
|
||||||
import net.minecraft.world.item.ItemStack;
|
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import java.util.*;
|
import java.util.Set;
|
||||||
|
|
||||||
public class PocketServerComputer extends ServerComputer implements IPocketAccess {
|
/**
|
||||||
private @Nullable IPocketUpgrade upgrade;
|
* A {@link ServerComputer}-subclass for {@linkplain PocketComputerItem pocket computers}.
|
||||||
private @Nullable Entity entity;
|
* <p>
|
||||||
private ItemStack stack = ItemStack.EMPTY;
|
* This extends default {@link ServerComputer} behaviour by also syncing pocket computer state to nearby players, and
|
||||||
|
* syncing the terminal to the current player.
|
||||||
private int lightColour = -1;
|
* <p>
|
||||||
|
* The actual pocket computer state (upgrade, light) is maintained in {@link PocketBrain}. The two classes are tightly
|
||||||
|
* coupled, and maintain a reference to each other.
|
||||||
|
*
|
||||||
|
* @see PocketComputerDataMessage
|
||||||
|
* @see PocketComputerDeletedClientMessage
|
||||||
|
*/
|
||||||
|
public final class PocketServerComputer extends ServerComputer {
|
||||||
|
private final PocketBrain brain;
|
||||||
|
|
||||||
// The state the previous tick, used to determine if the state needs to be sent to the client.
|
// The state the previous tick, used to determine if the state needs to be sent to the client.
|
||||||
private int oldLightColour = -1;
|
private int oldLightColour = -1;
|
||||||
private @Nullable ComputerState oldComputerState;
|
private @Nullable ComputerState oldComputerState;
|
||||||
|
|
||||||
private final Set<ServerPlayer> tracking = new HashSet<>();
|
private Set<ServerPlayer> tracking = Set.of();
|
||||||
|
|
||||||
public PocketServerComputer(ServerLevel world, BlockPos position, int computerID, @Nullable String label, ComputerFamily family) {
|
PocketServerComputer(PocketBrain brain, PocketHolder holder, int computerID, @Nullable String label, ComputerFamily family) {
|
||||||
super(world, position, computerID, label, family, Config.pocketTermWidth, Config.pocketTermHeight);
|
super(
|
||||||
|
holder.level(), holder.blockPos(), computerID, label, family, Config.pocketTermWidth, Config.pocketTermHeight,
|
||||||
|
ComponentMap.builder().add(ComputerComponents.POCKET, brain).build()
|
||||||
|
);
|
||||||
|
this.brain = brain;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
public PocketBrain getBrain() {
|
||||||
@Override
|
return brain;
|
||||||
public Entity getEntity() {
|
|
||||||
var entity = this.entity;
|
|
||||||
if (entity == null || stack.isEmpty() || !entity.isAlive()) return null;
|
|
||||||
|
|
||||||
if (entity instanceof Player) {
|
|
||||||
var inventory = ((Player) entity).getInventory();
|
|
||||||
return inventory.items.contains(stack) || inventory.offhand.contains(stack) ? entity : null;
|
|
||||||
} else if (entity instanceof LivingEntity living) {
|
|
||||||
return living.getMainHandItem() == stack || living.getOffhandItem() == stack ? entity : null;
|
|
||||||
} else if (entity instanceof ItemEntity itemEntity) {
|
|
||||||
return itemEntity.getItem() == stack ? entity : null;
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getColour() {
|
protected void tickServer() {
|
||||||
return IColouredItem.getColourBasic(stack);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setColour(int colour) {
|
|
||||||
IColouredItem.setColourBasic(stack, colour);
|
|
||||||
updateUpgradeNBTData();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getLight() {
|
|
||||||
return lightColour;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setLight(int colour) {
|
|
||||||
if (colour < 0 || colour > 0xFFFFFF) colour = -1;
|
|
||||||
lightColour = colour;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public CompoundTag getUpgradeNBTData() {
|
|
||||||
return PocketComputerItem.getUpgradeInfo(stack);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void updateUpgradeNBTData() {
|
|
||||||
if (entity instanceof Player player) player.getInventory().setChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void invalidatePeripheral() {
|
|
||||||
var peripheral = upgrade == null ? null : upgrade.createPeripheral(this);
|
|
||||||
setPeripheral(ComputerSide.BACK, peripheral);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@Deprecated(forRemoval = true)
|
|
||||||
public Map<ResourceLocation, IPeripheral> getUpgrades() {
|
|
||||||
return upgrade == null ? Map.of() : Collections.singletonMap(upgrade.getUpgradeID(), getPeripheral(ComputerSide.BACK));
|
|
||||||
}
|
|
||||||
|
|
||||||
public @Nullable UpgradeData<IPocketUpgrade> getUpgrade() {
|
|
||||||
return upgrade == null ? null : UpgradeData.of(upgrade, getUpgradeNBTData());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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}.
|
|
||||||
*/
|
|
||||||
public void setUpgrade(@Nullable UpgradeData<IPocketUpgrade> upgrade) {
|
|
||||||
synchronized (this) {
|
|
||||||
PocketComputerItem.setUpgrade(stack, upgrade);
|
|
||||||
updateUpgradeNBTData();
|
|
||||||
this.upgrade = upgrade == null ? null : upgrade.upgrade();
|
|
||||||
invalidatePeripheral();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public synchronized void updateValues(@Nullable Entity entity, ItemStack stack, @Nullable IPocketUpgrade upgrade) {
|
|
||||||
if (entity != null) {
|
|
||||||
setLevel((ServerLevel) entity.getCommandSenderWorld());
|
|
||||||
setPosition(entity.blockPosition());
|
|
||||||
}
|
|
||||||
|
|
||||||
// If a new entity has picked it up then rebroadcast the terminal to them
|
|
||||||
if (entity != this.entity && entity instanceof ServerPlayer) markTerminalChanged();
|
|
||||||
|
|
||||||
this.entity = entity;
|
|
||||||
this.stack = stack;
|
|
||||||
|
|
||||||
if (this.upgrade != upgrade) {
|
|
||||||
this.upgrade = upgrade;
|
|
||||||
invalidatePeripheral();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void tickServer() {
|
|
||||||
super.tickServer();
|
super.tickServer();
|
||||||
|
|
||||||
// Find any players which have gone missing and remove them from the tracking list.
|
// Get the new set of players tracking the current position.
|
||||||
tracking.removeIf(player -> !player.isAlive() || player.level() != getLevel());
|
var newTracking = getLevel().getChunkSource().chunkMap.getPlayers(new ChunkPos(getPosition()), false);
|
||||||
|
var trackingChanged = tracking.size() != newTracking.size() || !tracking.containsAll(newTracking);
|
||||||
|
|
||||||
// And now find any new players, add them to the tracking list, and broadcast state where appropriate.
|
// And now find any new players, add them to the tracking list, and broadcast state where appropriate.
|
||||||
var state = getState();
|
var state = getState();
|
||||||
if (oldLightColour != lightColour || oldComputerState != state) {
|
var light = brain.getLight();
|
||||||
|
if (oldLightColour != light || oldComputerState != state) {
|
||||||
oldComputerState = state;
|
oldComputerState = state;
|
||||||
oldLightColour = lightColour;
|
oldLightColour = light;
|
||||||
|
|
||||||
// Broadcast the state to all players
|
// Broadcast the state to all players
|
||||||
tracking.addAll(getLevel().players());
|
ServerNetworking.sendToPlayers(new PocketComputerDataMessage(this, false), newTracking);
|
||||||
ServerNetworking.sendToPlayers(new PocketComputerDataMessage(this, false), tracking);
|
} else if (trackingChanged) {
|
||||||
} else {
|
|
||||||
// Broadcast the state to new players.
|
// Broadcast the state to new players.
|
||||||
List<ServerPlayer> added = new ArrayList<>();
|
var added = newTracking.stream().filter(x -> !tracking.contains(x)).toList();
|
||||||
for (var player : getLevel().players()) {
|
|
||||||
if (tracking.add(player)) added.add(player);
|
|
||||||
}
|
|
||||||
if (!added.isEmpty()) {
|
if (!added.isEmpty()) {
|
||||||
ServerNetworking.sendToPlayers(new PocketComputerDataMessage(this, false), added);
|
ServerNetworking.sendToPlayers(new PocketComputerDataMessage(this, false), added);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (trackingChanged) tracking = Set.copyOf(newTracking);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onTerminalChanged() {
|
protected void onTerminalChanged() {
|
||||||
super.onTerminalChanged();
|
super.onTerminalChanged();
|
||||||
|
|
||||||
if (entity instanceof ServerPlayer player && entity.isAlive()) {
|
if (brain.holder() instanceof PocketHolder.PlayerHolder holder && holder.isValid(this)) {
|
||||||
// Broadcast the terminal to the current player.
|
// Broadcast the terminal to the current player.
|
||||||
ServerNetworking.sendToPlayer(new PocketComputerDataMessage(this, true), player);
|
ServerNetworking.sendToPlayer(new PocketComputerDataMessage(this, true), holder.entity());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -45,12 +45,12 @@ public class PocketComputerMenuProvider implements MenuProvider {
|
|||||||
@Override
|
@Override
|
||||||
public AbstractContainerMenu createMenu(int id, Inventory inventory, Player entity) {
|
public AbstractContainerMenu createMenu(int id, Inventory inventory, Player entity) {
|
||||||
return new ComputerMenuWithoutInventory(
|
return new ComputerMenuWithoutInventory(
|
||||||
isTypingOnly ? ModRegistry.Menus.POCKET_COMPUTER_NO_TERM.get() : ModRegistry.Menus.POCKET_COMPUTER.get(), id, inventory,
|
isTypingOnly ? ModRegistry.Menus.POCKET_COMPUTER_NO_TERM.get() : ModRegistry.Menus.COMPUTER.get(), id, inventory,
|
||||||
p -> {
|
p -> {
|
||||||
var stack = p.getItemInHand(hand);
|
var stack = p.getItemInHand(hand);
|
||||||
return stack.getItem() == item && PocketComputerItem.getServerComputer(assertNonNull(entity.level().getServer()), stack) == computer;
|
return stack.getItem() == item && PocketComputerItem.getServerComputer(assertNonNull(entity.level().getServer()), stack) == computer;
|
||||||
},
|
},
|
||||||
computer, item.getFamily()
|
computer
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,21 +14,25 @@ import dan200.computercraft.core.computer.ComputerSide;
|
|||||||
import dan200.computercraft.impl.PocketUpgrades;
|
import dan200.computercraft.impl.PocketUpgrades;
|
||||||
import dan200.computercraft.shared.common.IColouredItem;
|
import dan200.computercraft.shared.common.IColouredItem;
|
||||||
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
||||||
|
import dan200.computercraft.shared.computer.core.ServerComputer;
|
||||||
|
import dan200.computercraft.shared.computer.core.ServerComputerRegistry;
|
||||||
import dan200.computercraft.shared.computer.core.ServerContext;
|
import dan200.computercraft.shared.computer.core.ServerContext;
|
||||||
import dan200.computercraft.shared.computer.items.IComputerItem;
|
import dan200.computercraft.shared.computer.items.IComputerItem;
|
||||||
import dan200.computercraft.shared.config.Config;
|
import dan200.computercraft.shared.config.Config;
|
||||||
import dan200.computercraft.shared.network.container.ComputerContainerData;
|
import dan200.computercraft.shared.network.container.ComputerContainerData;
|
||||||
import dan200.computercraft.shared.pocket.apis.PocketAPI;
|
import dan200.computercraft.shared.pocket.core.PocketBrain;
|
||||||
|
import dan200.computercraft.shared.pocket.core.PocketHolder;
|
||||||
import dan200.computercraft.shared.pocket.core.PocketServerComputer;
|
import dan200.computercraft.shared.pocket.core.PocketServerComputer;
|
||||||
import dan200.computercraft.shared.pocket.inventory.PocketComputerMenuProvider;
|
import dan200.computercraft.shared.pocket.inventory.PocketComputerMenuProvider;
|
||||||
import dan200.computercraft.shared.util.IDAssigner;
|
import dan200.computercraft.shared.util.IDAssigner;
|
||||||
|
import dan200.computercraft.shared.util.InventoryUtil;
|
||||||
import dan200.computercraft.shared.util.NBTUtil;
|
import dan200.computercraft.shared.util.NBTUtil;
|
||||||
import net.minecraft.ChatFormatting;
|
import net.minecraft.ChatFormatting;
|
||||||
import net.minecraft.nbt.CompoundTag;
|
import net.minecraft.nbt.CompoundTag;
|
||||||
import net.minecraft.network.chat.Component;
|
import net.minecraft.network.chat.Component;
|
||||||
import net.minecraft.server.MinecraftServer;
|
import net.minecraft.server.MinecraftServer;
|
||||||
import net.minecraft.server.level.ServerLevel;
|
import net.minecraft.server.level.ServerLevel;
|
||||||
import net.minecraft.world.Container;
|
import net.minecraft.server.level.ServerPlayer;
|
||||||
import net.minecraft.world.InteractionHand;
|
import net.minecraft.world.InteractionHand;
|
||||||
import net.minecraft.world.InteractionResult;
|
import net.minecraft.world.InteractionResult;
|
||||||
import net.minecraft.world.InteractionResultHolder;
|
import net.minecraft.world.InteractionResultHolder;
|
||||||
@@ -72,13 +76,33 @@ public class PocketComputerItem extends Item implements IComputerItem, IMedia, I
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean tick(ItemStack stack, Level world, Entity entity, PocketServerComputer computer) {
|
/**
|
||||||
var upgrade = getUpgrade(stack);
|
* Tick a pocket computer.
|
||||||
|
*
|
||||||
|
* @param stack The current pocket computer stack.
|
||||||
|
* @param holder The entity holding the pocket item.
|
||||||
|
* @param brain The pocket computer brain.
|
||||||
|
*/
|
||||||
|
private void tick(ItemStack stack, PocketHolder holder, PocketBrain brain) {
|
||||||
|
brain.updateHolder(holder);
|
||||||
|
|
||||||
computer.setLevel((ServerLevel) world);
|
// Update pocket upgrade
|
||||||
computer.updateValues(entity, stack, upgrade);
|
var upgrade = brain.getUpgrade();
|
||||||
|
if (upgrade != null) upgrade.upgrade().update(brain, brain.computer().getPeripheral(ComputerSide.BACK));
|
||||||
|
|
||||||
var changed = false;
|
if (updateItem(stack, brain)) holder.setChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copy properties from the brain back to the item stack.
|
||||||
|
*
|
||||||
|
* @param stack The current pocket computer stack.
|
||||||
|
* @param brain The current pocket brain.
|
||||||
|
* @return Whether the item was changed.
|
||||||
|
*/
|
||||||
|
private boolean updateItem(ItemStack stack, PocketBrain brain) {
|
||||||
|
var changed = brain.updateItem(stack);
|
||||||
|
var computer = brain.computer();
|
||||||
|
|
||||||
// Sync ID
|
// Sync ID
|
||||||
var id = computer.getID();
|
var id = computer.getID();
|
||||||
@@ -100,21 +124,20 @@ public class PocketComputerItem extends Item implements IComputerItem, IMedia, I
|
|||||||
stack.getOrCreateTag().putBoolean(NBT_ON, on);
|
stack.getOrCreateTag().putBoolean(NBT_ON, on);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update pocket upgrade
|
|
||||||
if (upgrade != null) upgrade.update(computer, computer.getPeripheral(ComputerSide.BACK));
|
|
||||||
|
|
||||||
return changed;
|
return changed;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void inventoryTick(ItemStack stack, Level world, Entity entity, int slotNum, boolean selected) {
|
public void inventoryTick(ItemStack stack, Level world, Entity entity, int slotNum, boolean selected) {
|
||||||
if (world.isClientSide) return;
|
// This (in vanilla at least) is only called for players. Don't bother to handle other entities.
|
||||||
Container inventory = entity instanceof Player player ? player.getInventory() : null;
|
if (world.isClientSide || !(entity instanceof ServerPlayer player)) return;
|
||||||
var computer = createServerComputer((ServerLevel) world, entity, inventory, stack);
|
|
||||||
computer.keepAlive();
|
|
||||||
|
|
||||||
var changed = tick(stack, world, entity, computer);
|
// If we're in the inventory, create a computer and keep it alive.
|
||||||
if (changed && inventory != null) inventory.setChanged();
|
var holder = new PocketHolder.PlayerHolder(player, slotNum);
|
||||||
|
var brain = getOrCreateBrain((ServerLevel) world, holder, stack);
|
||||||
|
brain.computer().keepAlive();
|
||||||
|
|
||||||
|
tick(stack, holder, brain);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ForgeOverride
|
@ForgeOverride
|
||||||
@@ -122,8 +145,11 @@ public class PocketComputerItem extends Item implements IComputerItem, IMedia, I
|
|||||||
var level = entity.level();
|
var level = entity.level();
|
||||||
if (level.isClientSide || level.getServer() == null) return false;
|
if (level.isClientSide || level.getServer() == null) return false;
|
||||||
|
|
||||||
|
// If we're an item entity, tick an already existing computer (as to update the position), but do not keep the
|
||||||
|
// computer alive.
|
||||||
var computer = getServerComputer(level.getServer(), stack);
|
var computer = getServerComputer(level.getServer(), stack);
|
||||||
if (computer != null && tick(stack, entity.level(), entity, computer)) entity.setItem(stack.copy());
|
if (computer != null) tick(stack, new PocketHolder.ItemEntityHolder(entity), computer.getBrain());
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -131,14 +157,18 @@ public class PocketComputerItem extends Item implements IComputerItem, IMedia, I
|
|||||||
public InteractionResultHolder<ItemStack> use(Level world, Player player, InteractionHand hand) {
|
public InteractionResultHolder<ItemStack> use(Level world, Player player, InteractionHand hand) {
|
||||||
var stack = player.getItemInHand(hand);
|
var stack = player.getItemInHand(hand);
|
||||||
if (!world.isClientSide) {
|
if (!world.isClientSide) {
|
||||||
var computer = createServerComputer((ServerLevel) world, player, player.getInventory(), stack);
|
var holder = new PocketHolder.PlayerHolder((ServerPlayer) player, InventoryUtil.getHandSlot(player, hand));
|
||||||
|
var brain = getOrCreateBrain((ServerLevel) world, holder, stack);
|
||||||
|
var computer = brain.computer();
|
||||||
computer.turnOn();
|
computer.turnOn();
|
||||||
|
|
||||||
var stop = false;
|
var stop = false;
|
||||||
var upgrade = getUpgrade(stack);
|
var upgrade = getUpgrade(stack);
|
||||||
if (upgrade != null) {
|
if (upgrade != null) {
|
||||||
computer.updateValues(player, stack, upgrade);
|
brain.updateHolder(holder);
|
||||||
stop = upgrade.onRightClick(world, computer, computer.getPeripheral(ComputerSide.BACK));
|
stop = upgrade.onRightClick(world, brain, computer.getPeripheral(ComputerSide.BACK));
|
||||||
|
// Sync back just in case. We don't need to setChanged, as we'll return the item anyway.
|
||||||
|
updateItem(stack, brain);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!stop) {
|
if (!stop) {
|
||||||
@@ -188,40 +218,49 @@ public class PocketComputerItem extends Item implements IComputerItem, IMedia, I
|
|||||||
return ComputerCraftAPI.MOD_ID;
|
return ComputerCraftAPI.MOD_ID;
|
||||||
}
|
}
|
||||||
|
|
||||||
public PocketServerComputer createServerComputer(ServerLevel level, Entity entity, @Nullable Container inventory, ItemStack stack) {
|
private PocketBrain getOrCreateBrain(ServerLevel level, PocketHolder holder, ItemStack stack) {
|
||||||
|
|
||||||
var registry = ServerContext.get(level.getServer()).registry();
|
var registry = ServerContext.get(level.getServer()).registry();
|
||||||
var computer = (PocketServerComputer) registry.get(getSessionID(stack), getInstanceID(stack));
|
{
|
||||||
if (computer == null) {
|
var computer = getServerComputer(registry, stack);
|
||||||
var computerID = getComputerID(stack);
|
if (computer != null) return computer.getBrain();
|
||||||
if (computerID < 0) {
|
|
||||||
computerID = ComputerCraftAPI.createUniqueNumberedSaveDir(level.getServer(), IDAssigner.COMPUTER);
|
|
||||||
setComputerID(stack, computerID);
|
|
||||||
}
|
|
||||||
|
|
||||||
computer = new PocketServerComputer(level, entity.blockPosition(), getComputerID(stack), getLabel(stack), getFamily());
|
|
||||||
|
|
||||||
var tag = stack.getOrCreateTag();
|
|
||||||
tag.putInt(NBT_SESSION, registry.getSessionID());
|
|
||||||
tag.putUUID(NBT_INSTANCE, computer.register());
|
|
||||||
|
|
||||||
var upgrade = getUpgrade(stack);
|
|
||||||
|
|
||||||
computer.updateValues(entity, stack, upgrade);
|
|
||||||
computer.addAPI(new PocketAPI(computer));
|
|
||||||
|
|
||||||
// Only turn on when initially creating the computer, rather than each tick.
|
|
||||||
if (isMarkedOn(stack) && entity instanceof Player) computer.turnOn();
|
|
||||||
|
|
||||||
if (inventory != null) inventory.setChanged();
|
|
||||||
}
|
}
|
||||||
computer.setLevel(level);
|
|
||||||
return computer;
|
var computerID = getComputerID(stack);
|
||||||
|
if (computerID < 0) {
|
||||||
|
computerID = ComputerCraftAPI.createUniqueNumberedSaveDir(level.getServer(), IDAssigner.COMPUTER);
|
||||||
|
setComputerID(stack, computerID);
|
||||||
|
}
|
||||||
|
|
||||||
|
var brain = new PocketBrain(holder, getComputerID(stack), getLabel(stack), getFamily(), getUpgradeWithData(stack));
|
||||||
|
var computer = brain.computer();
|
||||||
|
|
||||||
|
var tag = stack.getOrCreateTag();
|
||||||
|
tag.putInt(NBT_SESSION, registry.getSessionID());
|
||||||
|
tag.putUUID(NBT_INSTANCE, computer.register());
|
||||||
|
|
||||||
|
// Only turn on when initially creating the computer, rather than each tick.
|
||||||
|
if (isMarkedOn(stack) && holder instanceof PocketHolder.PlayerHolder) computer.turnOn();
|
||||||
|
|
||||||
|
updateItem(stack, brain);
|
||||||
|
|
||||||
|
holder.setChanged();
|
||||||
|
|
||||||
|
return brain;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isServerComputer(ServerComputer computer, ItemStack stack) {
|
||||||
|
return stack.getItem() instanceof PocketComputerItem
|
||||||
|
&& getServerComputer(computer.getLevel().getServer(), stack) == computer;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public static PocketServerComputer getServerComputer(ServerComputerRegistry registry, ItemStack stack) {
|
||||||
|
return (PocketServerComputer) registry.get(getSessionID(stack), getInstanceID(stack));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public static PocketServerComputer getServerComputer(MinecraftServer server, ItemStack stack) {
|
public static PocketServerComputer getServerComputer(MinecraftServer server, ItemStack stack) {
|
||||||
return (PocketServerComputer) ServerContext.get(server).registry().get(getSessionID(stack), getInstanceID(stack));
|
return getServerComputer(ServerContext.get(server).registry(), stack);
|
||||||
}
|
}
|
||||||
|
|
||||||
// IComputerItem implementation
|
// IComputerItem implementation
|
||||||
|
|||||||
@@ -31,8 +31,6 @@ public class PocketModem extends AbstractPocketUpgrade {
|
|||||||
public void update(IPocketAccess access, @Nullable IPeripheral peripheral) {
|
public void update(IPocketAccess access, @Nullable IPeripheral peripheral) {
|
||||||
if (!(peripheral instanceof PocketModemPeripheral modem)) return;
|
if (!(peripheral instanceof PocketModemPeripheral modem)) return;
|
||||||
|
|
||||||
modem.setLocation(access);
|
|
||||||
|
|
||||||
var state = modem.getModemState();
|
var state = modem.getModemState();
|
||||||
if (state.pollChanged()) access.setLight(state.isOpen() ? 0xBA0000 : -1);
|
if (state.pollChanged()) access.setLight(state.isOpen() ? 0xBA0000 : -1);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,31 +14,21 @@ import net.minecraft.world.phys.Vec3;
|
|||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
public class PocketModemPeripheral extends WirelessModemPeripheral {
|
public class PocketModemPeripheral extends WirelessModemPeripheral {
|
||||||
private @Nullable Level level = null;
|
private final IPocketAccess access;
|
||||||
private Vec3 position = Vec3.ZERO;
|
|
||||||
|
|
||||||
public PocketModemPeripheral(boolean advanced, IPocketAccess access) {
|
public PocketModemPeripheral(boolean advanced, IPocketAccess access) {
|
||||||
super(new ModemState(), advanced);
|
super(new ModemState(), advanced);
|
||||||
setLocation(access);
|
this.access = access;
|
||||||
}
|
|
||||||
|
|
||||||
void setLocation(IPocketAccess access) {
|
|
||||||
var entity = access.getEntity();
|
|
||||||
if (entity != null) {
|
|
||||||
level = entity.getCommandSenderWorld();
|
|
||||||
position = entity.getEyePosition(1);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Level getLevel() {
|
public Level getLevel() {
|
||||||
if (level == null) throw new IllegalStateException("Using modem before position has been defined");
|
return access.getLevel();
|
||||||
return level;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Vec3 getPosition() {
|
public Vec3 getPosition() {
|
||||||
return position;
|
return access.getPosition();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -8,15 +8,11 @@ import dan200.computercraft.api.peripheral.IPeripheral;
|
|||||||
import dan200.computercraft.api.pocket.IPocketAccess;
|
import dan200.computercraft.api.pocket.IPocketAccess;
|
||||||
import dan200.computercraft.shared.peripheral.speaker.SpeakerPosition;
|
import dan200.computercraft.shared.peripheral.speaker.SpeakerPosition;
|
||||||
import dan200.computercraft.shared.peripheral.speaker.UpgradeSpeakerPeripheral;
|
import dan200.computercraft.shared.peripheral.speaker.UpgradeSpeakerPeripheral;
|
||||||
import net.minecraft.world.level.Level;
|
|
||||||
import net.minecraft.world.phys.Vec3;
|
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
public class PocketSpeakerPeripheral extends UpgradeSpeakerPeripheral {
|
public class PocketSpeakerPeripheral extends UpgradeSpeakerPeripheral {
|
||||||
private final IPocketAccess access;
|
private final IPocketAccess access;
|
||||||
private @Nullable Level level;
|
|
||||||
private Vec3 position = Vec3.ZERO;
|
|
||||||
|
|
||||||
public PocketSpeakerPeripheral(IPocketAccess access) {
|
public PocketSpeakerPeripheral(IPocketAccess access) {
|
||||||
this.access = access;
|
this.access = access;
|
||||||
@@ -25,7 +21,7 @@ public class PocketSpeakerPeripheral extends UpgradeSpeakerPeripheral {
|
|||||||
@Override
|
@Override
|
||||||
public SpeakerPosition getPosition() {
|
public SpeakerPosition getPosition() {
|
||||||
var entity = access.getEntity();
|
var entity = access.getEntity();
|
||||||
return entity == null ? SpeakerPosition.of(level, position) : SpeakerPosition.of(entity);
|
return entity == null ? SpeakerPosition.of(access.getLevel(), access.getPosition()) : SpeakerPosition.of(entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -35,12 +31,6 @@ public class PocketSpeakerPeripheral extends UpgradeSpeakerPeripheral {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void update() {
|
public void update() {
|
||||||
var entity = access.getEntity();
|
|
||||||
if (entity != null) {
|
|
||||||
level = entity.level();
|
|
||||||
position = entity.position();
|
|
||||||
}
|
|
||||||
|
|
||||||
super.update();
|
super.update();
|
||||||
|
|
||||||
access.setLight(madeSound() ? 0x3320fc : -1);
|
access.setLight(madeSound() ? 0x3320fc : -1);
|
||||||
|
|||||||
@@ -59,7 +59,6 @@ public final class PocketComputerUpgradeRecipe extends CustomRecipe {
|
|||||||
|
|
||||||
if (computer.isEmpty()) return ItemStack.EMPTY;
|
if (computer.isEmpty()) return ItemStack.EMPTY;
|
||||||
|
|
||||||
var itemComputer = (PocketComputerItem) computer.getItem();
|
|
||||||
if (PocketComputerItem.getUpgrade(computer) != null) return ItemStack.EMPTY;
|
if (PocketComputerItem.getUpgrade(computer) != null) return ItemStack.EMPTY;
|
||||||
|
|
||||||
// Check for upgrades around the item
|
// Check for upgrades around the item
|
||||||
@@ -81,10 +80,9 @@ public final class PocketComputerUpgradeRecipe extends CustomRecipe {
|
|||||||
if (upgrade == null) return ItemStack.EMPTY;
|
if (upgrade == null) return ItemStack.EMPTY;
|
||||||
|
|
||||||
// Construct the new stack
|
// Construct the new stack
|
||||||
var computerID = itemComputer.getComputerID(computer);
|
var newStack = computer.copyWithCount(1);
|
||||||
var label = itemComputer.getLabel(computer);
|
PocketComputerItem.setUpgrade(newStack, upgrade);
|
||||||
var colour = itemComputer.getColour(computer);
|
return newStack;
|
||||||
return itemComputer.create(computerID, label, colour, upgrade);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -9,8 +9,8 @@ import dan200.computercraft.api.lua.*;
|
|||||||
import dan200.computercraft.api.turtle.TurtleCommand;
|
import dan200.computercraft.api.turtle.TurtleCommand;
|
||||||
import dan200.computercraft.api.turtle.TurtleCommandResult;
|
import dan200.computercraft.api.turtle.TurtleCommandResult;
|
||||||
import dan200.computercraft.api.turtle.TurtleSide;
|
import dan200.computercraft.api.turtle.TurtleSide;
|
||||||
import dan200.computercraft.core.apis.IAPIEnvironment;
|
|
||||||
import dan200.computercraft.core.metrics.Metrics;
|
import dan200.computercraft.core.metrics.Metrics;
|
||||||
|
import dan200.computercraft.core.metrics.MetricsObserver;
|
||||||
import dan200.computercraft.shared.peripheral.generic.methods.AbstractInventoryMethods;
|
import dan200.computercraft.shared.peripheral.generic.methods.AbstractInventoryMethods;
|
||||||
import dan200.computercraft.shared.turtle.core.*;
|
import dan200.computercraft.shared.turtle.core.*;
|
||||||
|
|
||||||
@@ -64,11 +64,11 @@ import java.util.Optional;
|
|||||||
* @cc.since 1.3
|
* @cc.since 1.3
|
||||||
*/
|
*/
|
||||||
public class TurtleAPI implements ILuaAPI {
|
public class TurtleAPI implements ILuaAPI {
|
||||||
private final IAPIEnvironment environment;
|
private final MetricsObserver metrics;
|
||||||
private final TurtleAccessInternal turtle;
|
private final TurtleAccessInternal turtle;
|
||||||
|
|
||||||
public TurtleAPI(IAPIEnvironment environment, TurtleAccessInternal turtle) {
|
public TurtleAPI(MetricsObserver metrics, TurtleAccessInternal turtle) {
|
||||||
this.environment = environment;
|
this.metrics = metrics;
|
||||||
this.turtle = turtle;
|
this.turtle = turtle;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,7 +78,7 @@ public class TurtleAPI implements ILuaAPI {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private MethodResult trackCommand(TurtleCommand command) {
|
private MethodResult trackCommand(TurtleCommand command) {
|
||||||
environment.observe(Metrics.TURTLE_OPS);
|
metrics.observe(Metrics.TURTLE_OPS);
|
||||||
return turtle.executeCommand(command);
|
return turtle.executeCommand(command);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -169,7 +169,7 @@ public class TurtleAPI implements ILuaAPI {
|
|||||||
*/
|
*/
|
||||||
@LuaFunction
|
@LuaFunction
|
||||||
public final MethodResult dig(Optional<TurtleSide> side) {
|
public final MethodResult dig(Optional<TurtleSide> side) {
|
||||||
environment.observe(Metrics.TURTLE_OPS);
|
metrics.observe(Metrics.TURTLE_OPS);
|
||||||
return trackCommand(TurtleToolCommand.dig(InteractDirection.FORWARD, side.orElse(null)));
|
return trackCommand(TurtleToolCommand.dig(InteractDirection.FORWARD, side.orElse(null)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -184,7 +184,7 @@ public class TurtleAPI implements ILuaAPI {
|
|||||||
*/
|
*/
|
||||||
@LuaFunction
|
@LuaFunction
|
||||||
public final MethodResult digUp(Optional<TurtleSide> side) {
|
public final MethodResult digUp(Optional<TurtleSide> side) {
|
||||||
environment.observe(Metrics.TURTLE_OPS);
|
metrics.observe(Metrics.TURTLE_OPS);
|
||||||
return trackCommand(TurtleToolCommand.dig(InteractDirection.UP, side.orElse(null)));
|
return trackCommand(TurtleToolCommand.dig(InteractDirection.UP, side.orElse(null)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -199,7 +199,7 @@ public class TurtleAPI implements ILuaAPI {
|
|||||||
*/
|
*/
|
||||||
@LuaFunction
|
@LuaFunction
|
||||||
public final MethodResult digDown(Optional<TurtleSide> side) {
|
public final MethodResult digDown(Optional<TurtleSide> side) {
|
||||||
environment.observe(Metrics.TURTLE_OPS);
|
metrics.observe(Metrics.TURTLE_OPS);
|
||||||
return trackCommand(TurtleToolCommand.dig(InteractDirection.DOWN, side.orElse(null)));
|
return trackCommand(TurtleToolCommand.dig(InteractDirection.DOWN, side.orElse(null)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
package dan200.computercraft.shared.turtle.blocks;
|
package dan200.computercraft.shared.turtle.blocks;
|
||||||
|
|
||||||
import com.mojang.authlib.GameProfile;
|
import com.mojang.authlib.GameProfile;
|
||||||
|
import dan200.computercraft.api.component.ComputerComponents;
|
||||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||||
import dan200.computercraft.api.turtle.ITurtleAccess;
|
import dan200.computercraft.api.turtle.ITurtleAccess;
|
||||||
import dan200.computercraft.api.turtle.ITurtleUpgrade;
|
import dan200.computercraft.api.turtle.ITurtleUpgrade;
|
||||||
@@ -17,9 +18,9 @@ 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.config.Config;
|
import dan200.computercraft.shared.config.Config;
|
||||||
import dan200.computercraft.shared.container.BasicContainer;
|
import dan200.computercraft.shared.container.BasicContainer;
|
||||||
import dan200.computercraft.shared.turtle.apis.TurtleAPI;
|
|
||||||
import dan200.computercraft.shared.turtle.core.TurtleBrain;
|
import dan200.computercraft.shared.turtle.core.TurtleBrain;
|
||||||
import dan200.computercraft.shared.turtle.inventory.TurtleMenu;
|
import dan200.computercraft.shared.turtle.inventory.TurtleMenu;
|
||||||
|
import dan200.computercraft.shared.util.ComponentMap;
|
||||||
import net.minecraft.core.BlockPos;
|
import net.minecraft.core.BlockPos;
|
||||||
import net.minecraft.core.Direction;
|
import net.minecraft.core.Direction;
|
||||||
import net.minecraft.core.NonNullList;
|
import net.minecraft.core.NonNullList;
|
||||||
@@ -75,10 +76,9 @@ public class TurtleBlockEntity extends AbstractComputerBlockEntity implements Ba
|
|||||||
protected ServerComputer createComputer(int id) {
|
protected ServerComputer createComputer(int id) {
|
||||||
var computer = new ServerComputer(
|
var computer = new ServerComputer(
|
||||||
(ServerLevel) getLevel(), getBlockPos(), id, label,
|
(ServerLevel) getLevel(), getBlockPos(), id, label,
|
||||||
getFamily(), Config.turtleTermWidth,
|
getFamily(), Config.turtleTermWidth, Config.turtleTermHeight,
|
||||||
Config.turtleTermHeight
|
ComponentMap.builder().add(ComputerComponents.TURTLE, brain).build()
|
||||||
);
|
);
|
||||||
computer.addAPI(new TurtleAPI(computer.getAPIEnvironment(), brain));
|
|
||||||
brain.setupComputer(computer);
|
brain.setupComputer(computer);
|
||||||
return computer;
|
return computer;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -290,8 +290,7 @@ public class TurtleBrain implements TurtleAccessInternal {
|
|||||||
newTurtle.transferStateFrom(oldOwner);
|
newTurtle.transferStateFrom(oldOwner);
|
||||||
|
|
||||||
var computer = newTurtle.createServerComputer();
|
var computer = newTurtle.createServerComputer();
|
||||||
computer.setLevel((ServerLevel) world);
|
computer.setPosition((ServerLevel) world, pos);
|
||||||
computer.setPosition(pos);
|
|
||||||
|
|
||||||
// Remove the old turtle
|
// Remove the old turtle
|
||||||
oldWorld.removeBlock(oldPos, false);
|
oldWorld.removeBlock(oldPos, false);
|
||||||
@@ -608,7 +607,7 @@ public class TurtleBrain implements TurtleAccessInternal {
|
|||||||
|
|
||||||
// If we've got a computer, ensure that we're allowed to perform work.
|
// If we've got a computer, ensure that we're allowed to perform work.
|
||||||
var computer = owner.getServerComputer();
|
var computer = owner.getServerComputer();
|
||||||
if (computer != null && !computer.getComputer().getMainThreadMonitor().canWork()) return;
|
if (computer != null && !computer.getMainThreadMonitor().canWork()) return;
|
||||||
|
|
||||||
// Pull a new command
|
// Pull a new command
|
||||||
var nextCommand = commandQueue.poll();
|
var nextCommand = commandQueue.poll();
|
||||||
@@ -621,7 +620,7 @@ public class TurtleBrain implements TurtleAccessInternal {
|
|||||||
|
|
||||||
// Dispatch the callback
|
// Dispatch the callback
|
||||||
if (computer == null) return;
|
if (computer == null) return;
|
||||||
computer.getComputer().getMainThreadMonitor().trackWork(end - start, TimeUnit.NANOSECONDS);
|
computer.getMainThreadMonitor().trackWork(end - start, TimeUnit.NANOSECONDS);
|
||||||
var callbackID = nextCommand.callbackID();
|
var callbackID = nextCommand.callbackID();
|
||||||
if (callbackID < 0) return;
|
if (callbackID < 0) return;
|
||||||
|
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ public final class TurtlePlayer {
|
|||||||
|
|
||||||
var player = brain.cachedPlayer;
|
var player = brain.cachedPlayer;
|
||||||
if (player == null || player.player.getGameProfile() != getProfile(access.getOwningPlayer())
|
if (player == null || player.player.getGameProfile() != getProfile(access.getOwningPlayer())
|
||||||
|| player.player.getCommandSenderWorld() != access.getLevel()) {
|
|| player.player.level() != access.getLevel()) {
|
||||||
player = brain.cachedPlayer = create(brain);
|
player = brain.cachedPlayer = create(brain);
|
||||||
} else {
|
} else {
|
||||||
player.setState(access);
|
player.setState(access);
|
||||||
|
|||||||
@@ -0,0 +1,66 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2024 The CC: Tweaked Developers
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
|
package dan200.computercraft.shared.util;
|
||||||
|
|
||||||
|
import dan200.computercraft.api.component.ComputerComponent;
|
||||||
|
import dan200.computercraft.core.metrics.MetricsObserver;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An immutable map of components.
|
||||||
|
*/
|
||||||
|
public final class ComponentMap {
|
||||||
|
public static final ComputerComponent<MetricsObserver> METRICS = ComputerComponent.create("computercraft", "metrics");
|
||||||
|
|
||||||
|
private static final ComponentMap EMPTY = new ComponentMap(Map.of());
|
||||||
|
|
||||||
|
private final Map<ComputerComponent<?>, Object> components;
|
||||||
|
|
||||||
|
private ComponentMap(Map<ComputerComponent<?>, Object> components) {
|
||||||
|
this.components = components;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public <T> @Nullable T get(ComputerComponent<T> component) {
|
||||||
|
return (T) components.get(component);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ComponentMap empty() {
|
||||||
|
return EMPTY;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Builder builder() {
|
||||||
|
return new Builder();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final class Builder {
|
||||||
|
private final Map<ComputerComponent<?>, Object> components = new HashMap<>();
|
||||||
|
|
||||||
|
private Builder() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> Builder add(ComputerComponent<T> component, T value) {
|
||||||
|
addImpl(component, value);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder add(ComponentMap components) {
|
||||||
|
for (var component : components.components.entrySet()) addImpl(component.getKey(), component.getValue());
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addImpl(ComputerComponent<?> component, Object value) {
|
||||||
|
if (components.containsKey(component)) throw new IllegalArgumentException(component + " is already set");
|
||||||
|
components.put(component, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ComponentMap build() {
|
||||||
|
return new ComponentMap(Map.copyOf(components));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -33,7 +33,6 @@ public final class IDAssigner {
|
|||||||
|
|
||||||
private final Path idFile;
|
private final Path idFile;
|
||||||
private final Path newIdFile;
|
private final Path newIdFile;
|
||||||
private boolean atomicMove = true;
|
|
||||||
private @Nullable Map<String, Integer> ids;
|
private @Nullable Map<String, Integer> ids;
|
||||||
|
|
||||||
public IDAssigner(Path path) {
|
public IDAssigner(Path path) {
|
||||||
|
|||||||
@@ -8,6 +8,9 @@ import net.minecraft.core.BlockPos;
|
|||||||
import net.minecraft.core.Direction;
|
import net.minecraft.core.Direction;
|
||||||
import net.minecraft.server.level.ServerLevel;
|
import net.minecraft.server.level.ServerLevel;
|
||||||
import net.minecraft.world.Container;
|
import net.minecraft.world.Container;
|
||||||
|
import net.minecraft.world.InteractionHand;
|
||||||
|
import net.minecraft.world.entity.player.Inventory;
|
||||||
|
import net.minecraft.world.entity.player.Player;
|
||||||
import net.minecraft.world.item.ItemStack;
|
import net.minecraft.world.item.ItemStack;
|
||||||
import net.minecraft.world.phys.EntityHitResult;
|
import net.minecraft.world.phys.EntityHitResult;
|
||||||
import net.minecraft.world.phys.Vec3;
|
import net.minecraft.world.phys.Vec3;
|
||||||
@@ -18,6 +21,20 @@ public final class InventoryUtil {
|
|||||||
private InventoryUtil() {
|
private InventoryUtil() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the inventory slot for a given hand.
|
||||||
|
*
|
||||||
|
* @param player The player to get the slot from.
|
||||||
|
* @param hand The hand to get.
|
||||||
|
* @return The current slot.
|
||||||
|
*/
|
||||||
|
public static int getHandSlot(Player player, InteractionHand hand) {
|
||||||
|
return switch (hand) {
|
||||||
|
case MAIN_HAND -> player.getInventory().selected;
|
||||||
|
case OFF_HAND -> Inventory.SLOT_OFFHAND;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
public static @Nullable Container getEntityContainer(ServerLevel level, BlockPos pos, Direction side) {
|
public static @Nullable Container getEntityContainer(ServerLevel level, BlockPos pos, Direction side) {
|
||||||
var vecStart = new Vec3(
|
var vecStart = new Vec3(
|
||||||
pos.getX() + 0.5 + 0.6 * side.getStepX(),
|
pos.getX() + 0.5 + 0.6 * side.getStepX(),
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
package dan200.computercraft.shared.util;
|
package dan200.computercraft.shared.util;
|
||||||
|
|
||||||
|
import com.google.errorprone.annotations.Keep;
|
||||||
import net.minecraft.core.BlockPos;
|
import net.minecraft.core.BlockPos;
|
||||||
import net.minecraft.resources.ResourceKey;
|
import net.minecraft.resources.ResourceKey;
|
||||||
import net.minecraft.server.level.ChunkLevel;
|
import net.minecraft.server.level.ChunkLevel;
|
||||||
@@ -132,6 +133,7 @@ public final class TickScheduler {
|
|||||||
/**
|
/**
|
||||||
* The current state of this token.
|
* The current state of this token.
|
||||||
*/
|
*/
|
||||||
|
@Keep
|
||||||
private volatile State $state = State.IDLE;
|
private volatile State $state = State.IDLE;
|
||||||
|
|
||||||
public Token(BlockEntity owner) {
|
public Token(BlockEntity owner) {
|
||||||
|
|||||||
@@ -130,5 +130,18 @@
|
|||||||
"upgrade.minecraft.diamond_hoe.adjective": "Ackerbau",
|
"upgrade.minecraft.diamond_hoe.adjective": "Ackerbau",
|
||||||
"upgrade.minecraft.diamond_pickaxe.adjective": "Bergbau",
|
"upgrade.minecraft.diamond_pickaxe.adjective": "Bergbau",
|
||||||
"upgrade.minecraft.diamond_shovel.adjective": "Graben",
|
"upgrade.minecraft.diamond_shovel.adjective": "Graben",
|
||||||
"upgrade.minecraft.diamond_sword.adjective": "Nahkampf"
|
"upgrade.minecraft.diamond_sword.adjective": "Nahkampf",
|
||||||
|
"argument.computercraft.computer.id": "Computer ID",
|
||||||
|
"argument.computercraft.computer.instance": "einzigartige Instanz ID",
|
||||||
|
"argument.computercraft.computer.label": "Computer name",
|
||||||
|
"argument.computercraft.unknown_computer_family": "Unbekannte computer familie '%s'",
|
||||||
|
"gui.computercraft.config.floppy_space_limit.tooltip": "Die maximale Dateisystem Größe von Disketten (in bytes).",
|
||||||
|
"gui.computercraft.config.http.bandwidth": "Bandbreite",
|
||||||
|
"gui.computercraft.config.http.bandwidth.global_upload": "Globales upload limit",
|
||||||
|
"gui.computercraft.config.http.bandwidth.global_download.tooltip": "Die maximale Geschwindigkeit aller Computer in bytes/s mit der Heruntergeladen werden kann.\nBereich: > 1",
|
||||||
|
"gui.computercraft.config.http.bandwidth.global_download": "Globales download limit",
|
||||||
|
"gui.computercraft.config.http.bandwidth.tooltip": "Limitiert die Bandbreite der Computer.",
|
||||||
|
"argument.computercraft.computer.family": "Computer familie",
|
||||||
|
"gui.computercraft.config.execution.max_main_global_time.tooltip": "Die maximale Zeit in millisekunden, in der Aufgaben ausgeführt werden.\nAnmerkung: Diese Zeit wird höchstwarscheinlich überschritten und dient nur als ungefähre Grenze.\nLimit: > 1",
|
||||||
|
"gui.computercraft.config.http.bandwidth.global_upload.tooltip": "Die maximale Hochladungs Geschwindigkeit aller Computer in bytes/s.\nBereich: > 1"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -218,5 +218,18 @@
|
|||||||
"upgrade.minecraft.diamond_hoe.adjective": "Contadina",
|
"upgrade.minecraft.diamond_hoe.adjective": "Contadina",
|
||||||
"upgrade.minecraft.diamond_pickaxe.adjective": "Minatrice",
|
"upgrade.minecraft.diamond_pickaxe.adjective": "Minatrice",
|
||||||
"upgrade.minecraft.diamond_shovel.adjective": "Scavatrice",
|
"upgrade.minecraft.diamond_shovel.adjective": "Scavatrice",
|
||||||
"upgrade.minecraft.diamond_sword.adjective": "Da Combattimento"
|
"upgrade.minecraft.diamond_sword.adjective": "Da Combattimento",
|
||||||
|
"tag.item.computercraft.computer": "Computer",
|
||||||
|
"tag.item.computercraft.wired_modem": "Modem cablati",
|
||||||
|
"argument.computercraft.computer.distance": "Distanza dall'entità",
|
||||||
|
"argument.computercraft.computer.family": "Famiglia computer",
|
||||||
|
"argument.computercraft.computer.id": "ID computer",
|
||||||
|
"argument.computercraft.computer.instance": "ID istanza unica",
|
||||||
|
"argument.computercraft.computer.label": "Etichetta computer",
|
||||||
|
"argument.computercraft.unknown_computer_family": "Famiglia computer '%s' sconosciuta",
|
||||||
|
"gui.computercraft.config.disabled_generic_methods": "Metodi generici disattivati",
|
||||||
|
"gui.computercraft.config.disabled_generic_methods.tooltip": "Una lista di metodi generici o sorgenti di metodi da disattivare. I metodi generici sono\nmetodi aggiunti a blocchi/entità blocchi quando non c'è un provider della periferica esplicito.\nQuesto include metodi dell'inventario (ad es. inventory.getItemDetail, inventory.pushItems) e,\nse su Forge, i metodi fluid_storage e energy_storage.\nI metodi in questa lista possono essere sia un gruppo intero di metodi (computer:inventory)\no un singolo metodo (computer:inventory#pushItems).\n",
|
||||||
|
"tag.item.computercraft.monitor": "Monitor",
|
||||||
|
"tag.item.computercraft.turtle": "Tartarughe",
|
||||||
|
"tracking_field.computercraft.java_allocation.name": "Allocazioni Java"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -102,7 +102,7 @@
|
|||||||
"item.computercraft.pocket_computer_normal.upgraded": "%s手提计算机",
|
"item.computercraft.pocket_computer_normal.upgraded": "%s手提计算机",
|
||||||
"item.computercraft.printed_book": "打印书",
|
"item.computercraft.printed_book": "打印书",
|
||||||
"item.computercraft.printed_page": "打印纸",
|
"item.computercraft.printed_page": "打印纸",
|
||||||
"item.computercraft.printed_pages": "打印纸",
|
"item.computercraft.printed_pages": "打印纸簇",
|
||||||
"item.computercraft.treasure_disk": "软盘",
|
"item.computercraft.treasure_disk": "软盘",
|
||||||
"itemGroup.computercraft": "ComputerCraft",
|
"itemGroup.computercraft": "ComputerCraft",
|
||||||
"tracking_field.computercraft.fs.name": "文件系统操作",
|
"tracking_field.computercraft.fs.name": "文件系统操作",
|
||||||
@@ -120,5 +120,41 @@
|
|||||||
"upgrade.minecraft.diamond_hoe.adjective": "耕种",
|
"upgrade.minecraft.diamond_hoe.adjective": "耕种",
|
||||||
"upgrade.minecraft.diamond_pickaxe.adjective": "采掘",
|
"upgrade.minecraft.diamond_pickaxe.adjective": "采掘",
|
||||||
"upgrade.minecraft.diamond_shovel.adjective": "挖掘",
|
"upgrade.minecraft.diamond_shovel.adjective": "挖掘",
|
||||||
"upgrade.minecraft.diamond_sword.adjective": "战斗"
|
"upgrade.minecraft.diamond_sword.adjective": "战斗",
|
||||||
|
"argument.computercraft.computer.instance": "唯一实例ID",
|
||||||
|
"commands.computercraft.dump.open_path": "查看该电脑的文件",
|
||||||
|
"gui.computercraft.config.command_require_creative": "命令电脑需要创造模式",
|
||||||
|
"gui.computercraft.config.command_require_creative.tooltip": "玩家需要处于创造模式并为管理员才能与命令计算机交互。\n这是原版命令方块的默认行为。",
|
||||||
|
"gui.computercraft.config.http.bandwidth": "带宽",
|
||||||
|
"gui.computercraft.config.http.bandwidth.global_download.tooltip": "每秒钟可以下载的字节数. 所有电脑共享该设置 (bytes/s).\n范围: > 1",
|
||||||
|
"gui.computercraft.config.http.proxy.type.tooltip": "代理使用的协议.\n允许: HTTP, HTTPS, SOCKS4, SOCKS5",
|
||||||
|
"argument.computercraft.computer.id": "电脑ID",
|
||||||
|
"argument.computercraft.computer.distance": "实体距离",
|
||||||
|
"argument.computercraft.computer.family": "电脑类别",
|
||||||
|
"argument.computercraft.computer.label": "电脑标签",
|
||||||
|
"argument.computercraft.unknown_computer_family": "未知电脑类别 '%s'",
|
||||||
|
"commands.computercraft.help.desc": "显示该帮助信息",
|
||||||
|
"gui.computercraft.config.computer_space_limit.tooltip": "计算机和海龟的磁盘空间限制,以字节为单位。",
|
||||||
|
"gui.computercraft.config.disabled_generic_methods": "禁用的通用方法",
|
||||||
|
"gui.computercraft.config.http.bandwidth.global_upload": "全局上传限速",
|
||||||
|
"gui.computercraft.config.http.bandwidth.global_download": "全局下载限速",
|
||||||
|
"gui.computercraft.config.http.bandwidth.global_upload.tooltip": "每秒钟可以上传的字节数. 所有电脑共享该设置 (bytes/s).\n范围: > 1",
|
||||||
|
"gui.computercraft.config.http.bandwidth.tooltip": "限制电脑可以使用的带宽.",
|
||||||
|
"gui.computercraft.config.http.proxy": "代理",
|
||||||
|
"gui.computercraft.config.http.proxy.host": "主机名",
|
||||||
|
"gui.computercraft.config.http.proxy.host.tooltip": "代理服务器的主机名或IP地址.",
|
||||||
|
"gui.computercraft.config.http.proxy.port": "端口",
|
||||||
|
"gui.computercraft.config.http.proxy.port.tooltip": "代理服务器的端口.\n范围: 1 ~ 65536",
|
||||||
|
"gui.computercraft.config.http.proxy.type": "代理类型",
|
||||||
|
"gui.computercraft.config.http.rules": "允许/阻止规则",
|
||||||
|
"gui.computercraft.config.http.tooltip": "控制HTTP API",
|
||||||
|
"tracking_field.computercraft.java_allocation.name": "Java分配",
|
||||||
|
"tracking_field.computercraft.max": "%s (最大)",
|
||||||
|
"tracking_field.computercraft.server_tasks.name": "服务器任务",
|
||||||
|
"tag.item.computercraft.turtle": "海龟",
|
||||||
|
"tag.item.computercraft.wired_modem": "有线调制解调器",
|
||||||
|
"tracking_field.computercraft.avg": "%s (平均)",
|
||||||
|
"tracking_field.computercraft.computer_tasks.name": "任务",
|
||||||
|
"tracking_field.computercraft.count": "%s (计数)",
|
||||||
|
"tracking_field.computercraft.http_requests.name": "HTTP请求"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,30 +2,30 @@
|
|||||||
"parent": "block/block",
|
"parent": "block/block",
|
||||||
"render_type": "translucent",
|
"render_type": "translucent",
|
||||||
"textures": {
|
"textures": {
|
||||||
"particle": "#texture"
|
"particle": "#front"
|
||||||
},
|
},
|
||||||
"elements": [
|
"elements": [
|
||||||
{
|
{
|
||||||
"from": [ 2, 2, 2 ],
|
"from": [ 2, 2, 2 ],
|
||||||
"to": [ 14, 14, 13 ],
|
"to": [ 14, 14, 13 ],
|
||||||
"faces": {
|
"faces": {
|
||||||
"down": { "uv": [ 5.75, 2.75, 2.75, 0 ], "texture": "#texture" },
|
"down": { "uv": [ 0, 0, 12, 11 ], "texture": "#bottom" },
|
||||||
"up": { "uv": [ 8.75, 0, 5.75, 2.75 ], "texture": "#texture" },
|
"up": { "uv": [ 0, 0, 12, 11 ], "texture": "#top" },
|
||||||
"north": { "uv": [ 11.5, 5.75, 8.5, 2.75 ], "texture": "#texture" },
|
"north": { "uv": [ 0, 0, 12, 12 ], "texture": "#front" },
|
||||||
"south": { "uv": [ 5.75, 5.75, 2.75, 2.75 ], "texture": "#texture" },
|
"south": { "uv": [ 0, 0, 12, 12 ], "texture": "#back" },
|
||||||
"west": { "uv": [ 8.5, 5.75, 5.75, 2.75 ], "texture": "#texture" },
|
"west": { "uv": [ 0, 0, 11, 12 ], "texture": "#left" },
|
||||||
"east": { "uv": [ 2.75, 5.75, 0, 2.75 ], "texture": "#texture" }
|
"east": { "uv": [ 0, 0, 11, 12 ], "texture": "#right" }
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"from": [ 3, 6, 13 ],
|
"from": [ 3, 6, 13 ],
|
||||||
"to": [ 13, 13, 15 ],
|
"to": [ 13, 13, 15 ],
|
||||||
"faces": {
|
"faces": {
|
||||||
"down": { "uv": [ 11.75, 0.5, 9.25, 0 ], "texture": "#texture" },
|
"down": { "uv": [ 2, 9, 12, 11 ], "texture": "#backpack" },
|
||||||
"up": { "uv": [ 14.25, 0, 11.75, 0.5 ], "texture": "#texture" },
|
"up": { "uv": [ 2, 0, 12, 2 ], "texture": "#backpack" },
|
||||||
"south": { "uv": [ 11.75, 2.25, 9.25, 0.5 ], "texture": "#texture" },
|
"south": { "uv": [ 2, 2, 12, 9 ], "texture": "#backpack" },
|
||||||
"west": { "uv": [ 12.25, 2.25, 11.75, 0.5 ], "texture": "#texture" },
|
"west": { "uv": [ 0, 2, 2, 9 ], "texture": "#backpack" },
|
||||||
"east": { "uv": [ 9.25, 2.25, 8.75, 0.5 ], "texture": "#texture" }
|
"east": { "uv": [ 12, 2, 14, 9 ], "texture": "#backpack" }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -1,53 +1,67 @@
|
|||||||
{
|
{
|
||||||
"parent": "computercraft:block/turtle_base",
|
"parent": "computercraft:block/turtle_base",
|
||||||
"textures": {
|
"textures": {
|
||||||
"texture": "computercraft:block/turtle_colour"
|
"texture": "computercraft:block/turtle_colour",
|
||||||
|
"body_back": "computercraft:block/turtle_colour_body_back",
|
||||||
|
"body_backpack": "computercraft:block/turtle_colour_body_backpack",
|
||||||
|
"body_bottom": "computercraft:block/turtle_colour_body_bottom",
|
||||||
|
"body_front": "computercraft:block/turtle_colour_body_front",
|
||||||
|
"body_left": "computercraft:block/turtle_colour_body_left",
|
||||||
|
"body_right": "computercraft:block/turtle_colour_body_right",
|
||||||
|
"body_top": "computercraft:block/turtle_colour_body_top",
|
||||||
|
"frame_back": "computercraft:block/turtle_colour_frame_back",
|
||||||
|
"frame_backpack": "computercraft:block/turtle_colour_frame_backpack",
|
||||||
|
"frame_bottom": "computercraft:block/turtle_colour_frame_bottom",
|
||||||
|
"frame_front": "computercraft:block/turtle_colour_frame_front",
|
||||||
|
"frame_left": "computercraft:block/turtle_colour_frame_left",
|
||||||
|
"frame_right": "computercraft:block/turtle_colour_frame_right",
|
||||||
|
"frame_top": "computercraft:block/turtle_colour_frame_top"
|
||||||
},
|
},
|
||||||
"elements": [
|
"elements": [
|
||||||
{
|
{
|
||||||
"from": [ 2, 2, 2 ],
|
"from": [ 2, 2, 2 ],
|
||||||
"to": [ 14, 14, 13 ],
|
"to": [ 14, 14, 13 ],
|
||||||
"faces": {
|
"faces": {
|
||||||
"down": { "uv": [ 5.75, 8.5, 2.75, 5.75 ], "texture": "#texture", "tintindex": 0 },
|
"down": { "uv": [ 0, 0, 12, 11 ], "texture": "#body_bottom", "tintindex": 0 },
|
||||||
"up": { "uv": [ 8.75, 5.75, 5.75, 8.5 ], "texture": "#texture", "tintindex": 0 },
|
"up": { "uv": [ 0, 0, 12, 11 ], "texture": "#body_top", "tintindex": 0 },
|
||||||
"north": { "uv": [ 11.5, 11.5, 8.5, 8.5 ], "texture": "#texture", "tintindex": 0 },
|
"north": { "uv": [ 0, 0, 12, 12 ], "texture": "#body_front", "tintindex": 0 },
|
||||||
"south": { "uv": [ 5.75, 11.5, 2.75, 8.5 ], "texture": "#texture", "tintindex": 0 },
|
"south": { "uv": [ 0, 0, 12, 12 ], "texture": "#body_back", "tintindex": 0 },
|
||||||
"west": { "uv": [ 8.5, 11.5, 5.75, 8.555 ], "texture": "#texture", "tintindex": 0 },
|
"west": { "uv": [ 0, 0, 11, 12 ], "texture": "#body_left", "tintindex": 0 },
|
||||||
"east": { "uv": [ 2.75, 11.5, 0, 8.5 ], "texture": "#texture", "tintindex": 0 }
|
"east": { "uv": [ 0, 0, 11, 12 ], "texture": "#body_right", "tintindex": 0 }
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"from": [ 3, 6, 13 ],
|
"from": [ 3, 6, 13 ],
|
||||||
"to": [ 13, 13, 15 ],
|
"to": [ 13, 13, 15 ],
|
||||||
"faces": {
|
"faces": {
|
||||||
"down": { "uv": [ 11.75, 6.25, 9.25, 5.75 ], "texture": "#texture", "tintindex": 0 },
|
"down": { "uv": [ 2, 9, 12, 11 ], "texture": "#body_backpack", "tintindex": 0 },
|
||||||
"up": { "uv": [ 14.25, 5.75, 11.75, 6.25 ], "texture": "#texture", "tintindex": 0 },
|
"up": { "uv": [ 2, 0, 12, 2 ], "texture": "#body_backpack", "tintindex": 0 },
|
||||||
"south": { "uv": [ 11.75, 8, 9.25, 6.25 ], "texture": "#texture", "tintindex": 0 },
|
"south": { "uv": [ 2, 2, 12, 9 ], "texture": "#body_backpack", "tintindex": 0 },
|
||||||
"west": { "uv": [ 12.25, 8, 11.75, 6.25 ], "texture": "#texture", "tintindex": 0 },
|
"west": { "uv": [ 0, 2, 2, 9 ], "texture": "#body_backpack", "tintindex": 0 },
|
||||||
"east": { "uv": [ 9.25, 8, 8.75, 6.25 ], "texture": "#texture", "tintindex": 0 }
|
"east": { "uv": [ 12, 2, 14, 9 ], "texture": "#body_backpack", "tintindex": 0 }
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"from": [ 2, 2, 2 ],
|
"from": [ 2, 2, 2 ],
|
||||||
"to": [ 14, 14, 13 ],
|
"to": [ 14, 14, 13 ],
|
||||||
"faces": {
|
"faces": {
|
||||||
"down": { "uv": [ 5.75, 2.75, 2.75, 0 ], "texture": "#texture" },
|
"down": { "uv": [ 0, 0, 12, 11 ], "texture": "#frame_bottom" },
|
||||||
"up": { "uv": [ 8.75, 0, 5.75, 2.75 ], "texture": "#texture" },
|
"up": { "uv": [ 0, 0, 12, 11 ], "texture": "#frame_top" },
|
||||||
"north": { "uv": [ 11.5, 5.75, 8.5, 2.75 ], "texture": "#texture" },
|
"north": { "uv": [ 0, 0, 12, 12 ], "texture": "#frame_front" },
|
||||||
"south": { "uv": [ 5.75, 5.75, 2.75, 2.75 ], "texture": "#texture" },
|
"south": { "uv": [ 0, 0, 12, 12 ], "texture": "#frame_back" },
|
||||||
"west": { "uv": [ 8.5, 5.75, 5.75, 2.75 ], "texture": "#texture" },
|
"west": { "uv": [ 0, 0, 11, 12 ], "texture": "#frame_left" },
|
||||||
"east": { "uv": [ 2.75, 5.75, 0, 2.75 ], "texture": "#texture" }
|
"east": { "uv": [ 0, 0, 11, 12 ], "texture": "#frame_right" }
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"from": [ 3, 6, 13 ],
|
"from": [ 3, 6, 13 ],
|
||||||
"to": [ 13, 13, 15 ],
|
"to": [ 13, 13, 15 ],
|
||||||
"faces": {
|
"faces": {
|
||||||
"down": { "uv": [ 11.75, 0.5, 9.25, 0 ], "texture": "#texture" },
|
"down": { "uv": [ 2, 9, 12, 11 ], "texture": "#frame_backpack" },
|
||||||
"up": { "uv": [ 14.25, 0, 11.75, 0.5 ], "texture": "#texture" },
|
"up": { "uv": [ 2, 0, 12, 2 ], "texture": "#frame_backpack" },
|
||||||
"south": { "uv": [ 11.75, 2.25, 9.25, 0.5 ], "texture": "#texture" },
|
"south": { "uv": [ 2, 2, 12, 9 ], "texture": "#frame_backpack" },
|
||||||
"west": { "uv": [ 12.25, 2.25, 11.75, 0.5 ], "texture": "#texture" },
|
"west": { "uv": [ 0, 2, 2, 9 ], "texture": "#frame_backpack" },
|
||||||
"east": { "uv": [ 9.25, 2.25, 8.75, 0.5 ], "texture": "#texture" }
|
"east": { "uv": [ 12, 2, 14, 9 ], "texture": "#frame_backpack" }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -1,6 +1,35 @@
|
|||||||
{
|
{
|
||||||
"parent": "computercraft:block/turtle_overlay",
|
"parent": "block/block",
|
||||||
"textures": {
|
"textures": {
|
||||||
"texture": "computercraft:block/turtle_elf_overlay"
|
"back": "computercraft:block/turtle_elf_overlay_back",
|
||||||
}
|
"backpack": "computercraft:block/turtle_elf_overlay_backpack",
|
||||||
}
|
"front": "computercraft:block/turtle_elf_overlay_front",
|
||||||
|
"left": "computercraft:block/turtle_elf_overlay_left",
|
||||||
|
"right": "computercraft:block/turtle_elf_overlay_right",
|
||||||
|
"top": "computercraft:block/turtle_elf_overlay_top"
|
||||||
|
},
|
||||||
|
"elements": [
|
||||||
|
{
|
||||||
|
"from": [ 2, 2, 2 ],
|
||||||
|
"to": [ 14, 14, 13 ],
|
||||||
|
"faces": {
|
||||||
|
"up": { "uv": [ 0, 0, 12, 11 ], "texture": "#top" },
|
||||||
|
"north": { "uv": [ 0, 0, 12, 12 ], "texture": "#front" },
|
||||||
|
"south": { "uv": [ 0, 0, 12, 12 ], "texture": "#back" },
|
||||||
|
"west": { "uv": [ 0, 0, 11, 12 ], "texture": "#left" },
|
||||||
|
"east": { "uv": [ 0, 0, 11, 12 ], "texture": "#right" }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"from": [ 3, 6, 13 ],
|
||||||
|
"to": [ 13, 13, 15 ],
|
||||||
|
"faces": {
|
||||||
|
"down": { "uv": [ 2, 9, 12, 11 ], "texture": "#backpack" },
|
||||||
|
"up": { "uv": [ 2, 0, 12, 2 ], "texture": "#backpack" },
|
||||||
|
"south": { "uv": [ 2, 2, 12, 9 ], "texture": "#backpack" },
|
||||||
|
"west": { "uv": [ 0, 2, 2, 9 ], "texture": "#backpack" },
|
||||||
|
"east": { "uv": [ 12, 2, 14, 9 ], "texture": "#backpack" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,43 +0,0 @@
|
|||||||
{
|
|
||||||
"parent": "block/block",
|
|
||||||
"textures": {
|
|
||||||
"particle": "#texture"
|
|
||||||
},
|
|
||||||
"elements": [
|
|
||||||
{
|
|
||||||
"from": [ 2, 2, 2 ],
|
|
||||||
"to": [ 14, 14, 13 ],
|
|
||||||
"faces": {
|
|
||||||
"down": { "uv": [ 2.75, 0, 5.75, 2.75 ], "texture": "#texture" },
|
|
||||||
"up": { "uv": [ 5.75, 0, 8.75, 2.75 ], "texture": "#texture" },
|
|
||||||
"north": { "uv": [ 8.5, 5.75, 11.5, 2.75 ], "texture": "#texture" },
|
|
||||||
"south": { "uv": [ 2.75, 5.75, 5.75, 2.75 ], "texture": "#texture" },
|
|
||||||
"west": { "uv": [ 0, 5.75, 2.75, 2.75 ], "texture": "#texture" },
|
|
||||||
"east": { "uv": [ 5.75, 5.75, 8.5, 2.75 ], "texture": "#texture" }
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"from": [ 3, 6, 13 ],
|
|
||||||
"to": [ 13, 13, 15 ],
|
|
||||||
"faces": {
|
|
||||||
"down": { "uv": [ 9.25, 0, 11.75, 0.5 ], "texture": "#texture" },
|
|
||||||
"up": { "uv": [ 11.75, 0, 14.25, 0.5 ], "texture": "#texture" },
|
|
||||||
"south": { "uv": [ 9.25, 2.25, 11.75, 0.5 ], "texture": "#texture" },
|
|
||||||
"west": { "uv": [ 8.75, 2.25, 9.25, 0.5 ], "texture": "#texture" },
|
|
||||||
"east": { "uv": [ 11.75, 2.25, 12.25, 0.5 ], "texture": "#texture" }
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"from": [ 1.5, 1.5, 1.5 ],
|
|
||||||
"to": [ 14.5, 14.5, 13.5 ],
|
|
||||||
"faces": {
|
|
||||||
"down": { "uv": [ 2.75, 8, 5.75, 10.75 ], "texture": "#texture" },
|
|
||||||
"up": { "uv": [ 5.75, 8, 8.75, 10.75 ], "texture": "#texture" },
|
|
||||||
"north": { "uv": [ 8.5, 13.75, 11.5, 10.75 ], "texture": "#texture" },
|
|
||||||
"south": { "uv": [ 2.75, 13.75, 5.75, 10.75 ], "texture": "#texture" },
|
|
||||||
"west": { "uv": [ 0, 13.75, 2.75, 10.75 ], "texture": "#texture" },
|
|
||||||
"east": { "uv": [ 5.75, 13.75, 8.5, 10.75 ], "texture": "#texture" }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -1,38 +1,37 @@
|
|||||||
{
|
{
|
||||||
"parent": "block/block",
|
"parent": "block/block",
|
||||||
"textures": {
|
"textures": {
|
||||||
"particle": "computercraft:block/turtle_rainbow_overlay",
|
"particle": "computercraft:block/turtle_rainbow_overlay",
|
||||||
"texture": "computercraft:block/turtle_rainbow_overlay"
|
"texture": "computercraft:block/turtle_rainbow_overlay"
|
||||||
},
|
},
|
||||||
"elements": [
|
"elements": [
|
||||||
{
|
{
|
||||||
"name": "Flag",
|
"name": "Flag",
|
||||||
"from": [1.5, 13, 10.5],
|
"from": [1.5, 13.5, 10.5],
|
||||||
"to": [2, 16.5, 15.5],
|
"to": [2, 16.5, 15.5],
|
||||||
"rotation": {"angle": 22.5, "axis": "x", "origin": [2, 11, 10.75]},
|
"rotation": {"angle": 22.5, "axis": "x", "origin": [2, 11, 10.75]},
|
||||||
"faces": {
|
"faces": {
|
||||||
"north": {"uv": [0, 0, 1, 6], "texture": "#texture"},
|
"north": {"uv": [0, 0, 1, 6], "texture": "#texture"},
|
||||||
"east": {"uv": [0, 0, 7, 6], "texture": "#texture"},
|
"east": {"uv": [0, 0, 7, 6], "texture": "#texture"},
|
||||||
"south": {"uv": [0, 0, 1, 6], "texture": "#texture"},
|
"south": {"uv": [0, 0, 1, 6], "texture": "#texture"},
|
||||||
"west": {"uv": [0, 0, 7, 6], "texture": "#texture"},
|
"west": {"uv": [0, 0, 7, 6], "texture": "#texture"},
|
||||||
"up": {"uv": [15, 0, 16, 6], "texture": "#texture"},
|
"up": {"uv": [15, 0, 16, 6], "texture": "#texture"},
|
||||||
"down": {"uv": [8, 0, 9, 6], "texture": "#texture"}
|
"down": {"uv": [8, 0, 9, 6], "texture": "#texture"}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Stick",
|
"name": "Stick",
|
||||||
"from": [1.5, 10.5, 10.5],
|
"from": [1.5, 10.5, 10.5],
|
||||||
"to": [2, 13, 11],
|
"to": [2, 13.5, 11],
|
||||||
"rotation": {"angle": 22.5, "axis": "x", "origin": [2, 11, 10.75]},
|
"rotation": {"angle": 22.5, "axis": "x", "origin": [2, 11, 10.75]},
|
||||||
"faces": {
|
"faces": {
|
||||||
"north": {"uv": [12, 0, 13, 6], "texture": "#texture"},
|
"north": {"uv": [12, 0, 13, 6], "texture": "#texture"},
|
||||||
"east": {"uv": [13, 0, 14, 6], "texture": "#texture"},
|
"east": {"uv": [13, 0, 14, 6], "texture": "#texture"},
|
||||||
"south": {"uv": [12, 0, 13, 6], "texture": "#texture"},
|
"south": {"uv": [12, 0, 13, 6], "texture": "#texture"},
|
||||||
"west": {"uv": [13, 0, 14, 6], "texture": "#texture"},
|
"west": {"uv": [13, 0, 14, 6], "texture": "#texture"},
|
||||||
"up": {"uv": [12, 6, 13, 7], "texture": "#texture"},
|
"up": {"uv": [12, 6, 13, 7], "texture": "#texture"},
|
||||||
"down": {"uv": [13, 6, 14, 7], "texture": "#texture"}
|
"down": {"uv": [13, 6, 14, 7], "texture": "#texture"}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
]
|
||||||
"display": {}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,38 +1,37 @@
|
|||||||
{
|
{
|
||||||
"parent": "block/block",
|
"parent": "block/block",
|
||||||
"textures": {
|
"textures": {
|
||||||
"particle": "computercraft:block/turtle_trans_overlay",
|
"particle": "computercraft:block/turtle_trans_overlay",
|
||||||
"texture": "computercraft:block/turtle_trans_overlay"
|
"texture": "computercraft:block/turtle_trans_overlay"
|
||||||
},
|
},
|
||||||
"elements": [
|
"elements": [
|
||||||
{
|
{
|
||||||
"name": "Flag",
|
"name": "Flag",
|
||||||
"from": [1.5, 13.5, 10.5],
|
"from": [1.5, 13.5, 10.5],
|
||||||
"to": [2, 16.5, 15.5],
|
"to": [2, 16, 15.5],
|
||||||
"rotation": {"angle": 22.5, "axis": "x", "origin": [2, 11, 10.75]},
|
"rotation": {"angle": 22.5, "axis": "x", "origin": [2, 11, 10.75]},
|
||||||
"faces": {
|
"faces": {
|
||||||
"north": {"uv": [0, 0, 1, 5], "texture": "#texture"},
|
"north": {"uv": [0, 0, 1, 5], "texture": "#texture"},
|
||||||
"east": {"uv": [0, 0, 7, 5], "texture": "#texture"},
|
"east": {"uv": [0, 0, 7, 5], "texture": "#texture"},
|
||||||
"south": {"uv": [0, 0, 1, 5], "texture": "#texture"},
|
"south": {"uv": [0, 0, 1, 5], "texture": "#texture"},
|
||||||
"west": {"uv": [0, 0, 7, 5], "texture": "#texture"},
|
"west": {"uv": [0, 0, 7, 5], "texture": "#texture"},
|
||||||
"up": {"uv": [15, 0, 16, 5], "texture": "#texture"},
|
"up": {"uv": [15, 0, 16, 5], "texture": "#texture"},
|
||||||
"down": {"uv": [15, 0, 16, 5], "texture": "#texture"}
|
"down": {"uv": [15, 0, 16, 5], "texture": "#texture"}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Stick",
|
"name": "Stick",
|
||||||
"from": [1.5, 10.5, 10.5],
|
"from": [1.5, 10.5, 10.5],
|
||||||
"to": [2, 13.5, 11],
|
"to": [2, 13.5, 11],
|
||||||
"rotation": {"angle": 22.5, "axis": "x", "origin": [2, 11, 10.75]},
|
"rotation": {"angle": 22.5, "axis": "x", "origin": [2, 11, 10.75]},
|
||||||
"faces": {
|
"faces": {
|
||||||
"north": {"uv": [12, 0, 13, 6], "texture": "#texture"},
|
"north": {"uv": [12, 0, 13, 6], "texture": "#texture"},
|
||||||
"east": {"uv": [13, 0, 14, 6], "texture": "#texture"},
|
"east": {"uv": [13, 0, 14, 6], "texture": "#texture"},
|
||||||
"south": {"uv": [12, 0, 13, 6], "texture": "#texture"},
|
"south": {"uv": [12, 0, 13, 6], "texture": "#texture"},
|
||||||
"west": {"uv": [13, 0, 14, 6], "texture": "#texture"},
|
"west": {"uv": [13, 0, 14, 6], "texture": "#texture"},
|
||||||
"up": {"uv": [12, 6, 13, 7], "texture": "#texture"},
|
"up": {"uv": [12, 6, 13, 7], "texture": "#texture"},
|
||||||
"down": {"uv": [13, 6, 14, 7], "texture": "#texture"}
|
"down": {"uv": [13, 6, 14, 7], "texture": "#texture"}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
]
|
||||||
"display": {}
|
|
||||||
}
|
}
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 533 B |
|
After Width: | Height: | Size: 489 B |
|
After Width: | Height: | Size: 421 B |
|
After Width: | Height: | Size: 495 B |
|
After Width: | Height: | Size: 502 B |
|
After Width: | Height: | Size: 517 B |
|
After Width: | Height: | Size: 490 B |
|
Before Width: | Height: | Size: 2.3 KiB |